summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--METADATA3
-rw-r--r--apex/permission/framework/Android.bp5
-rw-r--r--apex/permission/service/Android.bp1
-rw-r--r--boot/Android.bp18
-rw-r--r--config/OWNERS6
-rw-r--r--core/api/current.txt26
-rw-r--r--core/api/module-lib-current.txt4
-rw-r--r--core/api/system-current.txt62
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/annotation/RequiresFeature.java13
-rw-r--r--core/java/android/app/OWNERS3
-rw-r--r--core/java/android/apphibernation/AppHibernationManager.java79
-rw-r--r--core/java/android/apphibernation/IAppHibernationService.aidl26
-rw-r--r--core/java/android/content/Context.java11
-rw-r--r--core/java/android/content/pm/PackageManager.java32
-rw-r--r--core/java/android/graphics/fonts/OWNERS1
-rw-r--r--core/java/android/net/IConnectivityManager.aidl2
-rw-r--r--core/java/android/net/INetworkStatsService.aidl2
-rw-r--r--core/java/android/net/IpSecManager.java95
-rw-r--r--core/java/android/net/Network.java19
-rw-r--r--core/java/android/net/NetworkCapabilities.java31
-rw-r--r--core/java/android/net/NetworkIdentity.java16
-rw-r--r--core/java/android/net/NetworkRequest.java4
-rw-r--r--core/java/android/net/NetworkUtils.java8
-rw-r--r--core/java/android/net/ProxyInfo.java2
-rw-r--r--core/java/android/net/QosFilter.java13
-rw-r--r--core/java/android/net/QosSocketFilter.java38
-rw-r--r--core/java/android/net/VpnInfo.aidl (renamed from core/java/com/android/internal/net/VpnInfo.aidl)2
-rw-r--r--core/java/android/net/VpnInfo.java (renamed from core/java/com/android/internal/net/VpnInfo.java)41
-rw-r--r--core/java/android/net/vcn/VcnConfig.java18
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java142
-rw-r--r--core/java/android/net/vcn/VcnManager.java1
-rwxr-xr-xcore/java/android/os/Build.java9
-rw-r--r--core/java/android/os/storage/OWNERS4
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java1
-rw-r--r--core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl25
-rw-r--r--core/java/android/service/resumeonreboot/ResumeOnRebootService.java164
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/OWNERS3
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl2
-rw-r--r--core/java/com/android/internal/app/OWNERS2
-rw-r--r--core/java/com/android/internal/graphics/fonts/OWNERS1
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHelper.java9
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java11
-rw-r--r--core/jni/android_net_NetUtils.cpp27
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/sysprop/WatchdogProperties.sysprop9
-rw-r--r--core/sysprop/api/com.android.sysprop.watchdog-latest.txt4
-rw-r--r--core/tests/coretests/src/android/app/OWNERS5
-rw-r--r--core/tests/coretests/src/android/app/people/OWNERS1
-rw-r--r--core/tests/coretests/src/android/content/pm/OWNERS3
-rw-r--r--data/etc/OWNERS3
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--identity/java/android/security/identity/CredstoreIdentityCredential.java86
-rw-r--r--identity/java/android/security/identity/CredstoreIdentityCredentialStore.java1
-rw-r--r--identity/java/android/security/identity/CredstoreWritableIdentityCredential.java10
-rw-r--r--identity/java/android/security/identity/IdentityCredential.java155
-rw-r--r--identity/java/android/security/identity/IdentityCredentialStore.java14
-rw-r--r--keystore/java/android/security/Authorization.java2
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java10
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java5
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java240
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java3
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java8
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java7
-rw-r--r--packages/Connectivity/framework/Android.bp29
-rw-r--r--packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl49
-rw-r--r--packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl41
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java17
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java13
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--services/OWNERS5
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java580
-rw-r--r--services/core/java/com/android/server/DynamicSystemService.java24
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java6
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java1
-rw-r--r--services/core/java/com/android/server/Watchdog.java4
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java4
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationService.java349
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java109
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java68
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java112
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java2
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkDiagnostics.java2
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java11
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java9
-rw-r--r--services/core/java/com/android/server/location/timezone/OWNERS3
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java23
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java28
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java202
-rw-r--r--services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java249
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsFactory.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java2
-rw-r--r--services/core/java/com/android/server/pm/ModuleInfoProvider.java2
-rw-r--r--services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java6
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java275
-rw-r--r--services/core/xsd/Android.bp12
-rw-r--r--services/core/xsd/platform-compat/OWNERS (renamed from services/core/xsd/platform-compat-schema/OWNERS)0
-rw-r--r--services/core/xsd/platform-compat/config/platform-compat-config.xsd (renamed from services/core/xsd/platform-compat-config.xsd)0
-rw-r--r--services/core/xsd/platform-compat/config/schema/current.txt (renamed from services/core/xsd/platform-compat-schema/current.txt)0
-rw-r--r--services/core/xsd/platform-compat/config/schema/last_current.txt (renamed from services/core/xsd/platform-compat-schema/last_current.txt)0
-rw-r--r--services/core/xsd/platform-compat/config/schema/last_removed.txt (renamed from services/core/xsd/platform-compat-schema/last_removed.txt)0
-rw-r--r--services/core/xsd/platform-compat/config/schema/removed.txt (renamed from services/core/xsd/platform-compat-schema/removed.txt)0
-rw-r--r--services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd55
-rw-r--r--services/core/xsd/platform-compat/overrides/schema/current.txt51
-rw-r--r--services/core/xsd/platform-compat/overrides/schema/last_current.txt0
-rw-r--r--services/core/xsd/platform-compat/overrides/schema/last_removed.txt0
-rw-r--r--services/core/xsd/platform-compat/overrides/schema/removed.txt1
-rw-r--r--services/java/com/android/server/SystemServer.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java168
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/location/timezone/OWNERS3
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java145
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java111
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java1
-rw-r--r--services/tests/shortcutmanagerutils/OWNERS1
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java33
-rw-r--r--telephony/java/android/telephony/DataFailCause.java5
-rw-r--r--telephony/java/android/telephony/ImsiEncryptionInfo.java4
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java47
-rw-r--r--telephony/java/android/telephony/data/DataService.java19
-rw-r--r--telephony/java/android/telephony/data/IDataService.aidl3
-rw-r--r--telephony/java/android/telephony/data/SliceInfo.aidl20
-rw-r--r--telephony/java/android/telephony/data/SliceInfo.java342
-rw-r--r--telephony/java/android/telephony/ims/SipMessage.java18
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl5
-rw-r--r--tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt8
-rw-r--r--tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt9
-rw-r--r--tests/net/java/android/net/NetworkTemplateTest.kt1
-rw-r--r--tests/net/java/android/net/QosSocketFilterTest.java75
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java143
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsBaseTest.java9
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java2
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsServiceTest.java74
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java20
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java19
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java84
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java71
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java120
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTestUtils.java44
-rw-r--r--tools/stringslint/stringslint.py12
-rwxr-xr-xtools/stringslint/stringslint_sha.sh2
147 files changed, 5086 insertions, 662 deletions
diff --git a/METADATA b/METADATA
index d97975ca3b99..95577d8df535 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,4 @@
third_party {
- license_type: NOTICE
+ # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
+ license_type: RESTRICTED
}
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index c0560f61460f..1a2d5d5412ed 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -26,7 +26,10 @@ java_sdk_library {
defaults: ["framework-module-defaults"],
// Restrict access to implementation library.
- impl_library_visibility: ["//frameworks/base/apex/permission:__subpackages__"],
+ impl_library_visibility: [
+ "//frameworks/base/apex/permission:__subpackages__",
+ "//packages/modules/Permission:__subpackages__",
+ ],
srcs: [
":framework-permission-sources",
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 6e04edfe02f1..cc6f2019a253 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -27,6 +27,7 @@ java_sdk_library {
"//frameworks/base/apex/permission/tests",
"//frameworks/base/services/tests/mockingservicestests",
"//frameworks/base/services/tests/servicestests",
+ "//packages/modules/Permission/tests",
],
srcs: [
":service-permission-sources",
diff --git a/boot/Android.bp b/boot/Android.bp
new file mode 100644
index 000000000000..dd4066a7d151
--- /dev/null
+++ b/boot/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+boot_image {
+ name: "framework-boot-image",
+ image_name: "boot",
+}
diff --git a/config/OWNERS b/config/OWNERS
index d59c6f2d72ba..001038d139c4 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -4,5 +4,11 @@ include /ZYGOTE_OWNERS
per-file hiddenapi-* = andreionea@google.com, mathewi@google.com, satayev@google.com
+# art-team@ manages the boot image profiles
+per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+
# Escalations:
per-file hiddenapi-* = bdc@google.com, narayan@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 047761a0cbe9..58ac5400ec3f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12128,6 +12128,8 @@ package android.content.pm {
field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final String FEATURE_HOME_SCREEN = "android.software.home_screen";
+ field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE = "android.hardware.identity_credential";
+ field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS = "android.hardware.identity_credential_direct_access";
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
@@ -12214,7 +12216,7 @@ package android.content.pm {
field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
- field public static final int GET_INTENT_FILTERS = 32; // 0x20
+ field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
field public static final int GET_META_DATA = 128; // 0x80
field public static final int GET_PERMISSIONS = 4096; // 0x1000
field public static final int GET_PROVIDERS = 8; // 0x8
@@ -25084,6 +25086,8 @@ package android.net {
method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @NonNull @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException;
@@ -25093,6 +25097,12 @@ package android.net {
field public static final int DIRECTION_OUT = 1; // 0x1
}
+ public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ method public void close();
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ }
+
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
}
@@ -29639,6 +29649,8 @@ package android.os {
field @Deprecated public static final String RADIO;
field @Deprecated public static final String SERIAL;
field @NonNull public static final String SKU;
+ field @NonNull public static final String SOC_MANUFACTURER;
+ field @NonNull public static final String SOC_MODEL;
field public static final String[] SUPPORTED_32_BIT_ABIS;
field public static final String[] SUPPORTED_64_BIT_ABIS;
field public static final String[] SUPPORTED_ABIS;
@@ -36074,15 +36086,20 @@ package android.security.identity {
public abstract class IdentityCredential {
method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+ method @NonNull public byte[] delete(@NonNull byte[]);
method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
method @NonNull public abstract int[] getAuthenticationDataUsageCount();
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+ method @NonNull public byte[] proveOwnership(@NonNull byte[]);
method public abstract void setAllowUsingExhaustedKeys(boolean);
+ method public void setAllowUsingExpiredKeys(boolean);
method public abstract void setAvailableAuthenticationKeys(int, int);
method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
- method public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
}
public class IdentityCredentialException extends java.lang.Exception {
@@ -36092,7 +36109,7 @@ package android.security.identity {
public abstract class IdentityCredentialStore {
method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
- method @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
+ method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
method @Nullable public static android.security.identity.IdentityCredentialStore getInstance(@NonNull android.content.Context);
@@ -36308,6 +36325,7 @@ package android.security.keystore {
field public static final int ORIGIN_IMPORTED = 2; // 0x2
field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
field public static final int ORIGIN_UNKNOWN = 4; // 0x4
+ field public static final int PURPOSE_AGREE_KEY = 64; // 0x40
field public static final int PURPOSE_DECRYPT = 2; // 0x2
field public static final int PURPOSE_ENCRYPT = 1; // 0x1
field public static final int PURPOSE_SIGN = 4; // 0x4
@@ -39581,6 +39599,7 @@ package android.telephony {
field public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+ field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
@@ -40256,6 +40275,7 @@ package android.telephony {
field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
field public static final int SIGNAL_LOST = -3; // 0xfffffffd
field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb
+ field public static final int SLICE_REJECTED = 2252; // 0x8cc
field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888
field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894
field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bc4a3ca8b244..061d4ccbc473 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -14,6 +14,10 @@ package android.net {
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
}
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public int getResourceId();
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
field public static final int TRANSPORT_TEST = 7; // 0x7
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9f651e70f780..b4d60168ba91 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -44,6 +44,7 @@ package android {
field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
field public static final String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
+ field public static final String BIND_RESUME_ON_REBOOT_SERVICE = "android.permission.BIND_RESUME_ON_REBOOT_SERVICE";
field public static final String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
field public static final String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
@@ -200,6 +201,7 @@ package android {
field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+ field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -1410,6 +1412,15 @@ package android.app.usage {
}
+package android.apphibernation {
+
+ public final class AppHibernationManager {
+ method public boolean isHibernating(@NonNull String);
+ method public void setHibernating(@NonNull String, boolean);
+ }
+
+}
+
package android.bluetooth {
public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
@@ -1706,6 +1717,7 @@ package android.content {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
field public static final String BACKUP_SERVICE = "backup";
@@ -6119,15 +6131,11 @@ package android.net {
}
public final class IpSecManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
}
public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
method @NonNull public String getInterfaceName();
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
}
public static class IpSecTransform.Builder {
@@ -6414,6 +6422,7 @@ package android.net {
public abstract class QosFilter {
method @NonNull public abstract android.net.Network getNetwork();
+ method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int);
}
public final class QosSession implements android.os.Parcelable {
@@ -9062,6 +9071,18 @@ package android.service.resolver {
}
+package android.service.resumeonreboot {
+
+ public abstract class ResumeOnRebootService extends android.app.Service {
+ ctor public ResumeOnRebootService();
+ method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+ method @NonNull public abstract byte[] onUnwrap(@NonNull byte[]) throws java.io.IOException;
+ method @NonNull public abstract byte[] onWrap(@NonNull byte[], long) throws java.io.IOException;
+ field public static final String SERVICE_INTERFACE = "android.service.resumeonreboot.ResumeOnRebootService";
+ }
+
+}
+
package android.service.settings.suggestions {
public final class Suggestion implements android.os.Parcelable {
@@ -10693,6 +10714,7 @@ package android.telephony.data {
method public int getPduSessionId();
method public int getProtocolType();
method public long getRetryDurationMillis();
+ method @Nullable public android.telephony.data.SliceInfo getSliceInfo();
method @Deprecated public int getSuggestedRetryTime();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
@@ -10727,6 +10749,7 @@ package android.telephony.data {
method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
}
@@ -10799,7 +10822,7 @@ package android.telephony.data {
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
- method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @NonNull android.telephony.data.DataServiceCallback);
+ method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback);
method public void startHandover(int, @NonNull android.telephony.data.DataServiceCallback);
}
@@ -10847,6 +10870,32 @@ package android.telephony.data {
method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
}
+ public final class SliceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator();
+ method public int getMappedHplmnSliceServiceType();
+ method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator();
+ method public int getSliceServiceType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SliceInfo> CREATOR;
+ field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe
+ field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff
+ field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff
+ field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1
+ field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3
+ field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0
+ field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2
+ }
+
+ public static final class SliceInfo.Builder {
+ ctor public SliceInfo.Builder();
+ method @NonNull public android.telephony.data.SliceInfo build();
+ method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
+ method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceServiceType(int);
+ method @NonNull public android.telephony.data.SliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
+ method @NonNull public android.telephony.data.SliceInfo.Builder setSliceServiceType(int);
+ }
+
}
package android.telephony.euicc {
@@ -11879,6 +11928,7 @@ package android.telephony.ims {
ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]);
method public int describeContents();
method @NonNull public byte[] getContent();
+ method @NonNull public byte[] getEncodedMessage();
method @NonNull public String getHeaderSection();
method @NonNull public String getStartLine();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ea9e9268d06d..546e72b8f834 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1726,7 +1726,6 @@ package android.util {
method public static java.util.Map<java.lang.String,java.lang.String> getAllFeatureFlags();
method public static boolean isEnabled(android.content.Context, String);
method public static void setEnabled(android.content.Context, String, boolean);
- field public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override.";
field public static final String FFLAG_PREFIX = "sys.fflag.";
field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java
index fc93f03d76cf..08861d42be39 100644
--- a/core/java/android/annotation/RequiresFeature.java
+++ b/core/java/android/annotation/RequiresFeature.java
@@ -30,7 +30,6 @@ import java.lang.annotation.Target;
* Denotes that the annotated element requires one or more device features. This
* is used to auto-generate documentation.
*
- * @see PackageManager#hasSystemFeature(String)
* @hide
*/
@Retention(SOURCE)
@@ -38,8 +37,16 @@ import java.lang.annotation.Target;
public @interface RequiresFeature {
/**
* The name of the device feature that is required.
- *
- * @see PackageManager#hasSystemFeature(String)
*/
String value();
+
+ /**
+ * Defines the name of the method that should be called to check whether the feature is
+ * available, using the same signature format as javadoc. The feature checking method can have
+ * multiple parameters, but the feature name parameter must be of type String and must also be
+ * the first String-type parameter.
+ * <p>
+ * By default, the enforcement is {@link PackageManager#hasSystemFeature(String)}.
+ */
+ String enforcement() default("android.content.pm.PackageManager#hasSystemFeature");
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 60bfac566879..afa1560420f7 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -50,6 +50,9 @@ per-file *StatusBar* = file:/packages/SystemUI/OWNERS
# ResourcesManager
per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+# VoiceInteraction
+per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
+
# Wallpaper
per-file *Wallpaper* = file:/core/java/android/service/wallpaper/OWNERS
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
new file mode 100644
index 000000000000..8f1934c7b77a
--- /dev/null
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * This class provides an API surface for system apps to manipulate the app hibernation
+ * state of a package for the user provided in the context.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.APP_HIBERNATION_SERVICE)
+public final class AppHibernationManager {
+ private static final String TAG = "AppHibernationManager";
+ private final Context mContext;
+ private final IAppHibernationService mIAppHibernationService;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The current context associated with the user
+ *
+ * @hide
+ */
+ public AppHibernationManager(@NonNull Context context) {
+ mContext = context;
+ mIAppHibernationService = IAppHibernationService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_HIBERNATION_SERVICE));
+ }
+
+ /**
+ * Returns true if the package is hibernating, false otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isHibernating(@NonNull String packageName) {
+ try {
+ return mIAppHibernationService.isHibernating(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the package is hibernating.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setHibernating(@NonNull String packageName, boolean isHibernating) {
+ try {
+ mIAppHibernationService.setHibernating(packageName, mContext.getUserId(),
+ isHibernating);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
new file mode 100644
index 000000000000..db57ecb73051
--- /dev/null
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.apphibernation;
+
+/**
+ * Binder interface to communicate with AppHibernationService.
+ * @hide
+ */
+interface IAppHibernationService {
+ boolean isHibernating(String packageName, int userId);
+ void setHibernating(String packageName, int userId, boolean isHibernating);
+} \ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index eecdb843774a..9c8856650ae0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4539,6 +4539,17 @@ public abstract class Context {
public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
/**
+ * Use with {@link #getSystemService(String) to retrieve an
+ * {@link android.apphibernation.AppHibernationManager}} for
+ * communicating with the hibernation service.
+ * @hide
+ *
+ * @see #getSystemService(String)
+ */
+ @SystemApi
+ public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve an
* {@link android.app.backup.IBackupManager IBackupManager} for communicating
* with the backup mechanism.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 00f5fb95768f..31beb6e6a565 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -299,7 +299,10 @@ public abstract class PackageManager {
/**
* {@link PackageInfo} flag: return information about the
* intent filters supported by the activity.
+ *
+ * @deprecated The platform does not support getting {@link IntentFilter}s for the package.
*/
+ @Deprecated
public static final int GET_INTENT_FILTERS = 0x00000020;
/**
@@ -2122,6 +2125,35 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports
+ * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware
+ * at the given feature version.
+ *
+ * <p>Known feature versions include:
+ * <ul>
+ * <li><code>202009</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 11.
+ * <li><code>202101</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 12.
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE =
+ "android.hardware.identity_credential";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports
+ * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware
+ * with direct access at the given feature version.
+ * See {@link #FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known feature versions.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS =
+ "android.hardware.identity_credential_direct_access";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports one or more methods of
* reporting current location.
*/
diff --git a/core/java/android/graphics/fonts/OWNERS b/core/java/android/graphics/fonts/OWNERS
new file mode 100644
index 000000000000..18486af9d12c
--- /dev/null
+++ b/core/java/android/graphics/fonts/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 6fecee632d62..719783163ab9 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -31,6 +31,7 @@ import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.ProxyInfo;
import android.net.UidRange;
+import android.net.VpnInfo;
import android.net.QosSocketInfo;
import android.os.Bundle;
import android.os.IBinder;
@@ -43,7 +44,6 @@ import android.os.ResultReceiver;
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
/**
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 1a3dc974480c..d5aede71011f 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,11 +23,11 @@ import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
import android.os.Messenger;
-import com.android.internal.net.VpnInfo;
/** {@hide} */
interface INetworkStatsService {
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d83715c692f7..60923f5ea8c6 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -15,6 +15,8 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
@@ -628,7 +630,7 @@ public final class IpSecManager {
}
/** @hide */
- @VisibleForTesting
+ @SystemApi(client = MODULE_LIBRARIES)
public int getResourceId() {
return mResourceId;
}
@@ -705,7 +707,7 @@ public final class IpSecManager {
}
/**
- * This class represents an IpSecTunnelInterface
+ * This class represents an IpSecTunnelInterface.
*
* <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
* local endpoints for IPsec tunnels.
@@ -714,9 +716,7 @@ public final class IpSecManager {
* applied to provide IPsec security to packets sent through the tunnel. While a tunnel
* cannot be used in standalone mode within Android, the higher layers may use the tunnel
* to create Network objects which are accessible to the Android system.
- * @hide
*/
- @SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -727,23 +727,26 @@ public final class IpSecManager {
private String mInterfaceName;
private int mResourceId = INVALID_RESOURCE_ID;
- /** Get the underlying SPI held by this object. */
+ /**
+ * Get the underlying SPI held by this object.
+ *
+ * @hide
+ */
+ @SystemApi
@NonNull
public String getInterfaceName() {
return mInterfaceName;
}
/**
- * Add an address to the IpSecTunnelInterface
+ * Add an address to the IpSecTunnelInterface.
*
* <p>Add an address which may be used as the local inner address for
* tunneled traffic.
*
* @param address the local address for traffic inside the tunnel
* @param prefixLen length of the InetAddress prefix
- * @hide
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -758,15 +761,13 @@ public final class IpSecManager {
}
/**
- * Remove an address from the IpSecTunnelInterface
+ * Remove an address from the IpSecTunnelInterface.
*
- * <p>Remove an address which was previously added to the IpSecTunnelInterface
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface.
*
* @param address to be removed
* @param prefixLen length of the InetAddress prefix
- * @hide
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -817,7 +818,7 @@ public final class IpSecManager {
}
/**
- * Delete an IpSecTunnelInterface
+ * Delete an IpSecTunnelInterface.
*
* <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
* resources. Any packets bound for this interface either inbound or outbound will
@@ -839,7 +840,12 @@ public final class IpSecManager {
}
}
- /** Check that the Interface was closed properly. */
+
+ /**
+ * Check that the Interface was closed properly.
+ *
+ * @hide
+ */
@Override
protected void finalize() throws Throwable {
if (mCloseGuard != null) {
@@ -871,17 +877,52 @@ public final class IpSecManager {
* Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
*
* <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network goes away, and the onLost() callback is received.
+ * underlying network disconnects, and the {@link
+ * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
+ *
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
+ * that go through the tunnel will need a underlying network to transit to the IPsec peer.
+ * This network should almost certainly be a physical network such as WiFi.
+ * @return a new {@link IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the tunnel could not be created due to a lower-layer
+ * error
+ * @throws ResourceUnavailableException indicating that the number of opening tunnels has
+ * reached the limit.
+ */
+ @NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+
+ // TODO: Remove the need for adding two unused addresses with IPsec tunnels when {@link
+ // #createIpSecTunnelInterface(localAddress, remoteAddress, underlyingNetwork)} can be
+ // safely removed.
+ final InetAddress address = InetAddress.getLocalHost();
+ return createIpSecTunnelInterface(address, address, underlyingNetwork);
+ }
+
+ /**
+ * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
*
- * @param localAddress The local addres of the tunnel
- * @param remoteAddress The local addres of the tunnel
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
- * This network should almost certainly be a network such as WiFi with an L2 address.
- * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the socket could not be opened or bound
- * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
+ * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
+ * underlying network disconnects, and the {@link
+ * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
+ *
+ * @param localAddress The local address of the tunnel
+ * @param remoteAddress The local address of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
+ * that go through the tunnel will need a underlying network to transit to the IPsec peer.
+ * This network should almost certainly be a physical network such as WiFi.
+ * @return a new {@link IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the tunnel could not be created due to a lower-layer
+ * error
+ * @throws ResourceUnavailableException indicating that the number of opening tunnels has
+ * reached the limit.
* @hide
+ * @deprecated Callers should use {@link #createIpSecTunnelInterface(Network)}
*/
+ @Deprecated
@SystemApi
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@@ -905,16 +946,14 @@ public final class IpSecManager {
* <p>Applications should probably not use this API directly.
*
*
- * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+ * @param tunnel The {@link IpSecTunnelInterface} that will use the supplied
* transform.
- * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * @param direction the direction, {@link #DIRECTION_OUT} or {@link #DIRECTION_IN} in which
* the transform will be used.
* @param transform an {@link IpSecTransform} created in tunnel mode
- * @throws IOException indicating that the transform could not be applied due to a lower
- * layer failure.
- * @hide
+ * @throws IOException indicating that the transform could not be applied due to a lower-layer
+ * error
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index f98a1f8a220d..b07bd68a0f50 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -21,6 +21,7 @@ import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.system.ErrnoException;
import android.system.Os;
@@ -380,7 +381,13 @@ public class Network implements Parcelable {
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocket(socket.getFileDescriptor$());
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
}
/**
@@ -392,7 +399,13 @@ public class Network implements Parcelable {
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocket(socket.getFileDescriptor$());
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
}
/**
@@ -420,7 +433,7 @@ public class Network implements Parcelable {
throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
}
- final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
+ final int err = NetworkUtils.bindSocketToNetwork(fd, netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0a895b98f9fd..3843b9ab93c2 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -204,6 +204,7 @@ public final class NetworkCapabilities implements Parcelable {
NET_CAPABILITY_TEMPORARILY_NOT_METERED,
NET_CAPABILITY_OEM_PRIVATE,
NET_CAPABILITY_VEHICLE_INTERNAL,
+ NET_CAPABILITY_NOT_VCN_MANAGED,
})
public @interface NetCapability { }
@@ -399,8 +400,16 @@ public final class NetworkCapabilities implements Parcelable {
@SystemApi
public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
+ /**
+ * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
+ *
+ * TODO(b/177299683): Add additional clarifying javadoc.
+ * @hide
+ */
+ public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VEHICLE_INTERNAL;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -417,7 +426,8 @@ public final class NetworkCapabilities implements Parcelable {
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
- | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -426,16 +436,21 @@ public final class NetworkCapabilities implements Parcelable {
* can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
* get immediately torn down because they do not have the requested capability.
*/
+ // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+ // are mutable but requestable. Factories are responsible for not getting
+ // in an infinite loop about these.
private static final long NON_REQUESTABLE_CAPABILITIES =
- MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
+ MUTABLE_CAPABILITIES
+ & ~(1 << NET_CAPABILITY_TRUSTED)
+ & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Capabilities that are set by default when the object is constructed.
*/
private static final long DEFAULT_CAPABILITIES =
- (1 << NET_CAPABILITY_NOT_RESTRICTED) |
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_NOT_VPN);
+ (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_NOT_VPN);
/**
* Capabilities that suggest that a network is restricted.
@@ -495,7 +510,8 @@ public final class NetworkCapabilities implements Parcelable {
| (1 << NET_CAPABILITY_NOT_VPN)
| (1 << NET_CAPABILITY_NOT_ROAMING)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
- | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Adds the given capability to this {@code NetworkCapability} instance.
@@ -1982,6 +1998,7 @@ public final class NetworkCapabilities implements Parcelable {
case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL";
+ case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index a0dc72d4adbf..b644ed56ad8b 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -194,13 +194,15 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
subscriberId = state.subscriberId;
if (type == TYPE_WIFI) {
- if (state.networkId != null) {
- networkId = state.networkId;
- } else {
- final WifiManager wifi = (WifiManager) context.getSystemService(
- Context.WIFI_SERVICE);
- final WifiInfo info = wifi.getConnectionInfo();
- networkId = info != null ? info.getSSID() : null;
+ if (state.networkCapabilities.getSsid() != null) {
+ networkId = state.networkCapabilities.getSsid();
+ if (networkId == null) {
+ // TODO: Figure out if this code path never runs. If so, remove them.
+ final WifiManager wifi = (WifiManager) context.getSystemService(
+ Context.WIFI_SERVICE);
+ final WifiInfo info = wifi.getConnectionInfo();
+ networkId = info != null ? info.getSSID() : null;
+ }
}
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index f0c637c76ec5..04011fc6816e 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -353,7 +353,9 @@ public class NetworkRequest implements Parcelable {
* NetworkSpecifier.
*/
public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
- MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier);
+ if (networkSpecifier instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
return this;
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b5962c5bae14..8be4af7b1396 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -81,11 +81,11 @@ public class NetworkUtils {
public native static boolean bindProcessToNetworkForHostResolution(int netId);
/**
- * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * Explicitly binds {@code fd} to the network designated by {@code netId}. This
* overrides any binding via {@link #bindProcessToNetwork}.
* @return 0 on success or negative errno on failure.
*/
- public native static int bindSocketToNetwork(int socketfd, int netId);
+ public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
/**
* Protect {@code fd} from VPN connections. After protecting, data sent through
@@ -93,9 +93,7 @@ public class NetworkUtils {
* forwarded through the VPN.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static boolean protectFromVpn(FileDescriptor fd) {
- return protectFromVpn(fd.getInt$());
- }
+ public static native boolean protectFromVpn(FileDescriptor fd);
/**
* Protect {@code socketfd} from VPN connections. After protecting, data sent through
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index a202d77a211a..c9bca2876b0a 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -355,7 +355,7 @@ public class ProxyInfo implements Parcelable {
port = in.readInt();
}
String exclList = in.readString();
- String[] parsedExclList = in.readStringArray();
+ String[] parsedExclList = in.createStringArray();
ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
return proxyProperties;
}
diff --git a/core/java/android/net/QosFilter.java b/core/java/android/net/QosFilter.java
index 070546878171..ab55002e02b3 100644
--- a/core/java/android/net/QosFilter.java
+++ b/core/java/android/net/QosFilter.java
@@ -19,6 +19,8 @@ package android.net;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import java.net.InetAddress;
+
/**
* Provides the related filtering logic to the {@link NetworkAgent} to match {@link QosSession}s
* to their related {@link QosCallback}.
@@ -58,5 +60,16 @@ public abstract class QosFilter {
*/
@QosCallbackException.ExceptionType
public abstract int validate();
+
+ /**
+ * Determines whether or not the parameters is a match for the filter.
+ *
+ * @param address the local address
+ * @param startPort the start of the port range
+ * @param endPort the end of the port range
+ * @return whether the parameters match the local address of the filter
+ */
+ public abstract boolean matchesLocalAddress(@NonNull InetAddress address,
+ int startPort, int endPort);
}
diff --git a/core/java/android/net/QosSocketFilter.java b/core/java/android/net/QosSocketFilter.java
index f51a0881e6e7..2080e68f5fba 100644
--- a/core/java/android/net/QosSocketFilter.java
+++ b/core/java/android/net/QosSocketFilter.java
@@ -26,7 +26,10 @@ import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
@@ -125,4 +128,39 @@ public class QosSocketFilter extends QosFilter {
public Network getNetwork() {
return mQosSocketInfo.getNetwork();
}
+
+ /**
+ * @inheritDoc
+ */
+ @Override
+ public boolean matchesLocalAddress(@NonNull final InetAddress address, final int startPort,
+ final int endPort) {
+ if (mQosSocketInfo.getLocalSocketAddress() == null) {
+ return false;
+ }
+
+ return matchesLocalAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort,
+ endPort);
+ }
+
+ /**
+ * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} with the
+ * filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}.
+ * <p>
+ * This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked
+ * due to being final.
+ *
+ * @param filterSocketAddress the socket address of the filter
+ * @param address the address to compare the filterSocketAddressWith
+ * @param startPort the start of the port range to check
+ * @param endPort the end of the port range to check
+ */
+ @VisibleForTesting
+ public static boolean matchesLocalAddress(@NonNull final InetSocketAddress filterSocketAddress,
+ @NonNull final InetAddress address,
+ final int startPort, final int endPort) {
+ return startPort <= filterSocketAddress.getPort()
+ && endPort >= filterSocketAddress.getPort()
+ && filterSocketAddress.getAddress().equals(address);
+ }
}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/net/VpnInfo.aidl
index 6fc97be4095b..8bcaa81f3992 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/net/VpnInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
parcelable VpnInfo;
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/android/net/VpnInfo.java
index e74af5eb50de..cf58c570f21f 100644
--- a/core/java/com/android/internal/net/VpnInfo.java
+++ b/core/java/android/net/VpnInfo.java
@@ -11,11 +11,13 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,14 +25,28 @@ import java.util.Arrays;
/**
* A lightweight container used to carry information of the ongoing VPN.
- * Internal use only..
+ * Internal use only.
*
* @hide
*/
public class VpnInfo implements Parcelable {
- public int ownerUid;
- public String vpnIface;
- public String[] underlyingIfaces;
+ public final int ownerUid;
+ @Nullable
+ public final String vpnIface;
+ @Nullable
+ public final String[] underlyingIfaces;
+
+ public VpnInfo(int ownerUid, @Nullable String vpnIface, @Nullable String[] underlyingIfaces) {
+ this.ownerUid = ownerUid;
+ this.vpnIface = vpnIface;
+ this.underlyingIfaces = underlyingIfaces;
+ }
+
+ private VpnInfo(@NonNull Parcel in) {
+ this.ownerUid = in.readInt();
+ this.vpnIface = in.readString();
+ this.underlyingIfaces = in.createStringArray();
+ }
@Override
public String toString() {
@@ -47,22 +63,21 @@ public class VpnInfo implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(ownerUid);
dest.writeString(vpnIface);
dest.writeStringArray(underlyingIfaces);
}
+ @NonNull
public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
+ @NonNull
@Override
- public VpnInfo createFromParcel(Parcel source) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = source.readInt();
- info.vpnIface = source.readString();
- info.underlyingIfaces = source.readStringArray();
- return info;
+ public VpnInfo createFromParcel(@NonNull Parcel in) {
+ return new VpnInfo(in);
}
+ @NonNull
@Override
public VpnInfo[] newArray(int size) {
return new VpnInfo[size];
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index ede8faaaf261..5eb4ba6a2f8e 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -96,7 +96,11 @@ public final class VcnConfig implements Parcelable {
return mPackageName;
}
- /** Retrieves the set of configured tunnels. */
+ /**
+ * Retrieves the set of configured tunnels.
+ *
+ * @hide
+ */
@NonNull
public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
return Collections.unmodifiableSet(mGatewayConnectionConfigs);
@@ -146,7 +150,7 @@ public final class VcnConfig implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(toPersistableBundle(), flags);
}
@@ -164,8 +168,12 @@ public final class VcnConfig implements Parcelable {
}
};
- /** This class is used to incrementally build {@link VcnConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final String mPackageName;
@NonNull
@@ -182,6 +190,7 @@ public final class VcnConfig implements Parcelable {
*
* @param gatewayConnectionConfig the configuration for an individual gateway connection
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder addGatewayConnectionConfig(
@@ -196,6 +205,7 @@ public final class VcnConfig implements Parcelable {
* Builds and validates the VcnConfig.
*
* @return an immutable VcnConfig instance
+ * @hide
*/
@NonNull
public VcnConfig build() {
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d531cdb2a6e9..cead2f1caad1 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -17,6 +17,7 @@ package android.net.vcn;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,14 +26,19 @@ import android.os.PersistableBundle;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.vcn.util.PersistableBundleUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
/**
@@ -97,6 +103,26 @@ public final class VcnGatewayConnectionConfig {
ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps);
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"NET_CAPABILITY_"},
+ value = {
+ NetworkCapabilities.NET_CAPABILITY_MMS,
+ NetworkCapabilities.NET_CAPABILITY_SUPL,
+ NetworkCapabilities.NET_CAPABILITY_DUN,
+ NetworkCapabilities.NET_CAPABILITY_FOTA,
+ NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_CBS,
+ NetworkCapabilities.NET_CAPABILITY_IA,
+ NetworkCapabilities.NET_CAPABILITY_RCS,
+ NetworkCapabilities.NET_CAPABILITY_XCAP,
+ NetworkCapabilities.NET_CAPABILITY_EIMS,
+ NetworkCapabilities.NET_CAPABILITY_INTERNET,
+ NetworkCapabilities.NET_CAPABILITY_MCX,
+ })
+ public @interface VcnSupportedCapability {}
+
private static final int DEFAULT_MAX_MTU = 1500;
/**
@@ -128,10 +154,10 @@ public final class VcnGatewayConnectionConfig {
};
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
- @NonNull private final Set<Integer> mExposedCapabilities;
+ @NonNull private final SortedSet<Integer> mExposedCapabilities;
private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
- @NonNull private final Set<Integer> mUnderlyingCapabilities;
+ @NonNull private final SortedSet<Integer> mUnderlyingCapabilities;
// TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
@@ -141,14 +167,14 @@ public final class VcnGatewayConnectionConfig {
private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
@NonNull private final long[] mRetryIntervalsMs;
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public VcnGatewayConnectionConfig(
+ /** Builds a VcnGatewayConnectionConfig with the specified parameters. */
+ private VcnGatewayConnectionConfig(
@NonNull Set<Integer> exposedCapabilities,
@NonNull Set<Integer> underlyingCapabilities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
- mExposedCapabilities = exposedCapabilities;
- mUnderlyingCapabilities = underlyingCapabilities;
+ mExposedCapabilities = new TreeSet(exposedCapabilities);
+ mUnderlyingCapabilities = new TreeSet(underlyingCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
@@ -163,9 +189,9 @@ public final class VcnGatewayConnectionConfig {
final PersistableBundle underlyingCapsBundle =
in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
- mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -219,52 +245,93 @@ public final class VcnGatewayConnectionConfig {
/**
* Returns all exposed capabilities.
*
+ * <p>The returned integer-value capabilities will not contain duplicates, and will be sorted in
+ * ascending numerical order.
+ *
+ * @see Builder#addExposedCapability(int)
+ * @see Builder#clearExposedCapability(int)
* @hide
*/
@NonNull
+ public int[] getExposedCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mExposedCapabilities));
+ }
+
+ /**
+ * Returns all exposed capabilities.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getExposedCapabilities() instead
+ * @hide
+ */
+ @Deprecated
+ @NonNull
public Set<Integer> getAllExposedCapabilities() {
return Collections.unmodifiableSet(mExposedCapabilities);
}
/**
- * Checks if this config is configured to support/expose a specific capability.
+ * Returns all capabilities required of underlying networks.
*
- * @param capability the capability to check for
+ * <p>The returned integer-value capabilities will be sorted in ascending numerical order.
+ *
+ * @see Builder#addRequiredUnderlyingCapability(int)
+ * @see Builder#clearRequiredUnderlyingCapability(int)
+ * @hide
*/
- public boolean hasExposedCapability(int capability) {
- checkValidCapability(capability);
-
- return mExposedCapabilities.contains(capability);
+ @NonNull
+ public int[] getRequiredUnderlyingCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mUnderlyingCapabilities));
}
/**
* Returns all capabilities required of underlying networks.
*
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
* @hide
*/
+ @Deprecated
@NonNull
public Set<Integer> getAllUnderlyingCapabilities() {
return Collections.unmodifiableSet(mUnderlyingCapabilities);
}
/**
- * Checks if this config requires an underlying network to have the specified capability.
+ * Retrieves the configured retry intervals.
*
- * @param capability the capability to check for
+ * @see Builder#setRetryInterval(long[])
+ * @hide
*/
- public boolean requiresUnderlyingCapability(int capability) {
- checkValidCapability(capability);
-
- return mUnderlyingCapabilities.contains(capability);
+ @NonNull
+ public long[] getRetryInterval() {
+ return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
}
- /** Retrieves the configured retry intervals. */
+ /**
+ * Retrieves the configured retry intervals.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
+ * @hide
+ */
+ @Deprecated
@NonNull
public long[] getRetryIntervalsMs() {
- return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
+ return getRetryInterval();
}
- /** Retrieves the maximum MTU allowed for this Gateway Connection. */
+ /**
+ * Retrieves the maximum MTU allowed for this Gateway Connection.
+ *
+ * @see Builder.setMaxMtu(int)
+ * @hide
+ */
@IntRange(from = MIN_MTU_V6)
public int getMaxMtu() {
return mMaxMtu;
@@ -319,8 +386,12 @@ public final class VcnGatewayConnectionConfig {
&& mMaxMtu == rhs.mMaxMtu;
}
- /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -338,8 +409,10 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder addExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder addExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.add(exposedCapability);
@@ -354,8 +427,10 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder removeExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder clearExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.remove(exposedCapability);
@@ -370,8 +445,11 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder addRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder addRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.add(underlyingCapability);
@@ -390,8 +468,11 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder removeRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder clearRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.remove(underlyingCapability);
@@ -420,6 +501,7 @@ public final class VcnGatewayConnectionConfig {
* 15m]}
* @return this {@link Builder} instance, for chaining
* @see VcnManager for additional discussion on fail-safe mode
+ * @hide
*/
@NonNull
public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
@@ -441,6 +523,7 @@ public final class VcnGatewayConnectionConfig {
* @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
* the IPv6 minimum MTU of 1280. Defaults to 1500.
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
@@ -455,6 +538,7 @@ public final class VcnGatewayConnectionConfig {
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
+ * @hide
*/
@NonNull
public VcnGatewayConnectionConfig build() {
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 2ccdc2633af0..2d0a6d74cb86 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -65,6 +65,7 @@ import java.util.concurrent.Executor;
public final class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
+ /** @hide */
@VisibleForTesting
public static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 10761187d969..5ae53b502330 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -26,6 +26,7 @@ import android.app.ActivityThread;
import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
import android.text.TextUtils;
import android.util.Slog;
@@ -87,6 +88,14 @@ public class Build {
/** The end-user-visible name for the end product. */
public static final String MODEL = getString("ro.product.model");
+ /** The manufacturer of the device's primary system-on-chip. */
+ @NonNull
+ public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN);
+
+ /** The model name of the device's primary system-on-chip. */
+ @NonNull
+ public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN);
+
/** The system bootloader version number. */
public static final String BOOTLOADER = getString("ro.bootloader");
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index 7e17a082840b..ff126e12cf61 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -1,10 +1,10 @@
# Bug component: 95221
-narayan@google.com
-nandana@google.com
corinac@google.com
+nandana@google.com
zezeozue@google.com
maco@google.com
sahanas@google.com
abkaur@google.com
chiangi@google.com
+narayan@google.com
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 017f40521a81..f994d2930cd9 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -177,6 +177,7 @@ public final class KeymasterDefs {
public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN;
public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY;
public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY;
+ public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY;
// Key formats.
public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509;
diff --git a/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl
new file mode 100644
index 000000000000..d9b403ca0609
--- /dev/null
+++ b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resumeonreboot;
+
+import android.os.RemoteCallback;
+
+/** @hide */
+interface IResumeOnRebootService {
+ oneway void wrapSecret(in byte[] unwrappedBlob, in long lifeTimeInMillis, in RemoteCallback resultCallback);
+ oneway void unwrap(in byte[] wrappedBlob, in RemoteCallback resultCallback);
+} \ No newline at end of file
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
new file mode 100644
index 000000000000..4ebaa96f4be2
--- /dev/null
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resumeonreboot;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import com.android.internal.os.BackgroundThread;
+
+import java.io.IOException;
+
+/**
+ * Base class for service that provides wrapping/unwrapping of the opaque blob needed for
+ * ResumeOnReboot operation. The package needs to provide a wrap/unwrap implementation for handling
+ * the opaque blob, that's secure even when on device keystore and clock is compromised. This can
+ * be achieved by using tamper-resistant hardware such as a secure element with a secure clock, or
+ * using a remote server to store and retrieve data and manage timing.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE} permission,
+ * include an intent filter with the {@link #SERVICE_INTERFACE} action and mark the service as
+ * direct-boot aware. In addition, the package that contains the service must be granted
+ * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE}.
+ * For example:</p>
+ * <pre>
+ * &lt;service android:name=".FooResumeOnRebootService"
+ * android:exported="true"
+ * android:priority="100"
+ * android:directBootAware="true"
+ * android:permission="android.permission.BIND_RESUME_ON_REBOOT_SERVICE"&gt;
+ * &lt;intent-filter&gt;
+ * &lt;action android:name="android.service.resumeonreboot.ResumeOnRebootService" /&gt;
+ * &lt;/intent-filter&gt;
+ * &lt;/service&gt;
+ * </pre>
+ *
+ * //TODO: Replace this with public link when available.
+ *
+ * @hide
+ * @see
+ * <a href="https://goto.google.com/server-based-ror">https://goto.google.com/server-based-ror</a>
+ */
+@SystemApi
+public abstract class ResumeOnRebootService extends Service {
+
+ /**
+ * The intent that the service must respond to. Add it to the intent filter of the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.resumeonreboot.ResumeOnRebootService";
+ /** @hide */
+ public static final String UNWRAPPED_BLOB_KEY = "unrwapped_blob_key";
+ /** @hide */
+ public static final String WRAPPED_BLOB_KEY = "wrapped_blob_key";
+ /** @hide */
+ public static final String EXCEPTION_KEY = "exception_key";
+
+ private final Handler mHandler = BackgroundThread.getHandler();
+
+ /**
+ * Implementation for wrapping the opaque blob used for resume-on-reboot prior to
+ * reboot. The service should not assume any structure of the blob to be wrapped. The
+ * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
+ * if it's unable to complete the action.
+ *
+ * @param blob The opaque blob with size on the order of 100 bytes.
+ * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
+ * implementation and any attempt to unWrap the wrapped blob returned by
+ * this function after expiration should
+ * fail.
+ * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
+ * @throws IOException if the implementation is unable to wrap the blob successfully.
+ */
+ @NonNull
+ public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
+ throws IOException;
+
+ /**
+ * Implementation for unwrapping the wrapped blob used for resume-on-reboot after reboot. This
+ * operation would happen after reboot during direct boot mode (i.e before device is unlocked
+ * for the first time). The implementation should unwrap the wrapped blob in a reasonable time
+ * and returns the result or throw {@link IOException} if it's unable to complete the action
+ * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
+ * stale.
+ *
+ * @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
+ * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
+ * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+ */
+ @NonNull
+ public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
+
+ private final android.service.resumeonreboot.IResumeOnRebootService mInterface =
+ new android.service.resumeonreboot.IResumeOnRebootService.Stub() {
+
+ @Override
+ public void wrapSecret(byte[] unwrappedBlob,
+ @DurationMillisLong long lifeTimeInMillis,
+ RemoteCallback resultCallback) throws RemoteException {
+ mHandler.post(() -> {
+ try {
+ byte[] wrappedBlob = onWrap(unwrappedBlob,
+ lifeTimeInMillis);
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(WRAPPED_BLOB_KEY, wrappedBlob);
+ resultCallback.sendResult(bundle);
+ } catch (Throwable e) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e));
+ resultCallback.sendResult(bundle);
+ }
+ });
+ }
+
+ @Override
+ public void unwrap(byte[] wrappedBlob, RemoteCallback resultCallback)
+ throws RemoteException {
+ mHandler.post(() -> {
+ try {
+ byte[] unwrappedBlob = onUnwrap(wrappedBlob);
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(UNWRAPPED_BLOB_KEY, unwrappedBlob);
+ resultCallback.sendResult(bundle);
+ } catch (Throwable e) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e));
+ resultCallback.sendResult(bundle);
+ }
+ });
+ }
+ };
+
+ @Nullable
+ @Override
+ public IBinder onBind(@Nullable Intent intent) {
+ return mInterface.asBinder();
+ }
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 537498c44d5e..9d0ae30f1420 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -39,7 +39,6 @@ public class FeatureFlagUtils {
public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
- public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
/** @hide */
@@ -53,7 +52,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "true");
- DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 8f3d9f6f5881..14aa38682d2b 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -1,3 +1,6 @@
per-file FeatureFlagUtils.java = sbasi@google.com
per-file FeatureFlagUtils.java = tmfang@google.com
per-file FeatureFlagUtils.java = asapperstein@google.com
+
+per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
+per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index e6a166140d89..beef9825b72a 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -129,7 +129,7 @@ interface IBatteryStats {
void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph);
void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
void noteWifiRadioPowerState(int powerState, long timestampNs, int uid);
- void noteNetworkInterfaceType(String iface, int type);
+ void noteNetworkInterfaceForTransports(String iface, in int[] transportTypes);
void noteNetworkStatsEnabled();
void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index c5a956a81d8d..99692d0736c2 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -3,3 +3,5 @@ per-file *Resolver* = file:/packages/SystemUI/OWNERS
per-file *Chooser* = file:/packages/SystemUI/OWNERS
per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
+per-file IVoice* = file:/core/java/android/service/voice/OWNERS
+per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/com/android/internal/graphics/fonts/OWNERS b/core/java/com/android/internal/graphics/fonts/OWNERS
new file mode 100644
index 000000000000..18486af9d12c
--- /dev/null
+++ b/core/java/com/android/internal/graphics/fonts/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 8bfc28ed5e52..ae680e0febac 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -23,7 +23,6 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.SensorManager;
-import android.net.ConnectivityManager;
import android.os.BatteryStats;
import android.os.BatteryStats.Uid;
import android.os.Build;
@@ -37,6 +36,7 @@ import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -148,12 +148,11 @@ public class BatteryStatsHelper {
boolean mHasBluetoothPowerReporting = false;
public static boolean checkWifiOnly(Context context) {
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- if (cm == null) {
+ final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ if (tm == null) {
return false;
}
- return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ return !tm.isDataCapable();
}
public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1071d9f2a918..2d75b70333f2 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
@@ -32,7 +34,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.NetworkStats;
import android.net.Uri;
@@ -101,6 +102,7 @@ import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import libcore.util.EmptyArray;
@@ -6099,11 +6101,12 @@ public class BatteryStatsImpl extends BatteryStats {
}
/** @hide */
- public void noteNetworkInterfaceType(String iface, int networkType) {
+ public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) {
if (TextUtils.isEmpty(iface)) return;
+ final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes);
synchronized (mModemNetworkLock) {
- if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
+ if (displayTransport == TRANSPORT_CELLULAR) {
mModemIfaces = includeInStringArray(mModemIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces);
} else {
@@ -6113,7 +6116,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
synchronized (mWifiNetworkLock) {
- if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
+ if (displayTransport == TRANSPORT_WIFI) {
mWifiIfaces = includeInStringArray(mWifiIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces);
} else {
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 2155246cd544..e2af87ee1adf 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,7 @@
#include <vector>
+#include <android/file_descriptor_jni.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <linux/if_arp.h>
@@ -83,7 +84,7 @@ static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz,
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -93,7 +94,7 @@ static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz,
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -117,10 +118,9 @@ static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *
return (jboolean) !setNetworkForResolv(netId);
}
-static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
- jint netId)
-{
- return setNetworkForSocket(netId, socket);
+static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
+ jint netId) {
+ return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
}
static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
@@ -128,6 +128,10 @@ static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint
return (jboolean) !protectFromVpn(socket);
}
+static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+}
+
static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
{
return (jboolean) !queryUserAccess(uid, netId);
@@ -178,7 +182,7 @@ static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
int rcode;
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
@@ -205,7 +209,7 @@ static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, job
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
resNetworkCancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -231,7 +235,7 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, j
return NULL;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
@@ -271,8 +275,9 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
{ "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
- { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
- { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+ { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn },
+ { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c6a1bdd54080..24dabfc02454 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1669,6 +1669,12 @@
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
android:protectionLevel="signature|setup" />
+ <!-- Allows applications to restart the Wi-Fi subsystem.
+ @SystemApi
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
+ android:protectionLevel="signature|setup" />
+
<!-- @SystemApi @hide Allows applications to toggle airplane mode.
<p>Not for use by third-party or privileged applications.
-->
@@ -2984,6 +2990,12 @@
<permission android:name="android.permission.RECOVERY"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to do certain operations needed for
+ resume on reboot feature.
+ @hide -->
+ <permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to read system update info.
@hide -->
<permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop
index 1bcc773a9a5d..93e8b788aed9 100644
--- a/core/sysprop/WatchdogProperties.sysprop
+++ b/core/sysprop/WatchdogProperties.sysprop
@@ -16,7 +16,7 @@ module: "android.sysprop.WatchdogProperties"
owner: Platform
# To escape the watchdog timeout loop, fatal reboot the system when
-# watchdog timed out 'fatal_count' times in 'fatal_window_second'
+# watchdog timed out 'fatal_count' times in 'fatal_window_seconds'
# seconds, if both values are not 0. Default value of both is 0.
prop {
api_name: "fatal_count"
@@ -26,8 +26,9 @@ prop {
access: Readonly
}
+# See 'fatal_count' for documentation.
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
prop_name: "framework_watchdog.fatal_window.second"
scope: Internal
@@ -35,9 +36,9 @@ prop {
}
# The fatal counting can be disabled by setting property
-# 'is_fatal_ignore' to true.
+# 'should_ignore_fatal_count' to true.
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
type: Boolean
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
scope: Internal
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
index d901aef945c9..c8462111fa94 100644
--- a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
+++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
@@ -7,13 +7,13 @@ props {
prop_name: "framework_watchdog.fatal_count"
}
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
scope: Internal
prop_name: "framework_watchdog.fatal_window.second"
}
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
scope: Internal
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
}
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index bd7da0c3f209..b3f399363aef 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -1 +1,6 @@
per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Notification, DND, Status bar
+per-file *Notification* = file:/packages/SystemUI/OWNERS
+per-file *Zen* = file:/packages/SystemUI/OWNERS
+per-file *StatusBar* = file:/packages/SystemUI/OWNERS
diff --git a/core/tests/coretests/src/android/app/people/OWNERS b/core/tests/coretests/src/android/app/people/OWNERS
new file mode 100644
index 000000000000..6ec8e6aa8d81
--- /dev/null
+++ b/core/tests/coretests/src/android/app/people/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/people/OWNERS \ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 711f5f012b8b..7b7670696bfa 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,2 +1,3 @@
per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-
+per-file SigningDetailsTest.java = mpgroover@google.com
+per-file SigningDetailsTest.java = cbrubaker@google.com
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 9867d810dba2..65d3a012b129 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1,3 +1,4 @@
+alanstokes@google.com
cbrubaker@google.com
hackbod@android.com
hackbod@google.com
@@ -12,4 +13,4 @@ toddke@android.com
toddke@google.com
yamasani@google.com
-per-file preinstalled-packages* = file:/MULTIUSER_OWNERS \ No newline at end of file
+per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a185da19e71b..30501fb53678 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -344,6 +344,7 @@ applications that come with the platform
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.MOVE_PACKAGE"/>
<!-- Needed for test only -->
+ <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
<permission name="android.permission.NETWORK_AIRPLANE_MODE"/>
<permission name="android.permission.OBSERVE_APP_USAGE"/>
<permission name="android.permission.NETWORK_SCAN"/>
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 7c0af6def696..6398cee74cba 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -37,6 +37,7 @@ import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.time.Instant;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
@@ -237,12 +238,18 @@ class CredstoreIdentityCredential extends IdentityCredential {
}
private boolean mAllowUsingExhaustedKeys = true;
+ private boolean mAllowUsingExpiredKeys = false;
@Override
public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
}
+ @Override
+ public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+ }
+
private boolean mOperationHandleSet = false;
private long mOperationHandle = 0;
@@ -256,7 +263,8 @@ class CredstoreIdentityCredential extends IdentityCredential {
public long getCredstoreOperationHandle() {
if (!mOperationHandleSet) {
try {
- mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys);
+ mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
+ mAllowUsingExpiredKeys);
mOperationHandleSet = true;
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
@@ -306,7 +314,8 @@ class CredstoreIdentityCredential extends IdentityCredential {
rnsParcels,
sessionTranscript != null ? sessionTranscript : new byte[0],
readerSignature != null ? readerSignature : new byte[0],
- mAllowUsingExhaustedKeys);
+ mAllowUsingExhaustedKeys,
+ mAllowUsingExpiredKeys);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
@@ -410,6 +419,34 @@ class CredstoreIdentityCredential extends IdentityCredential {
}
@Override
+ public void storeStaticAuthenticationData(X509Certificate authenticationKey,
+ Instant expirationDate,
+ byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException {
+ try {
+ AuthKeyParcel authKeyParcel = new AuthKeyParcel();
+ authKeyParcel.x509cert = authenticationKey.getEncoded();
+ long millisSinceEpoch = (expirationDate.getEpochSecond() * 1000)
+ + (expirationDate.getNano() / 1000000);
+ mBinder.storeStaticAuthenticationDataWithExpiration(authKeyParcel,
+ millisSinceEpoch, staticAuthData);
+ } catch (CertificateEncodingException e) {
+ throw new RuntimeException("Error encoding authenticationKey", e);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) {
+ throw new UnsupportedOperationException("Not supported", e);
+ } else if (e.errorCode == ICredentialStore.ERROR_AUTHENTICATION_KEY_NOT_FOUND) {
+ throw new UnknownAuthenticationKeyException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override
public @NonNull int[] getAuthenticationDataUsageCount() {
try {
int[] usageCount = mBinder.getAuthenticationDataUsageCount();
@@ -421,4 +458,49 @@ class CredstoreIdentityCredential extends IdentityCredential {
+ e.errorCode, e);
}
}
+
+ @Override
+ public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
+ try {
+ byte[] proofOfOwnership = mBinder.proveOwnership(challenge);
+ return proofOfOwnership;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) {
+ throw new UnsupportedOperationException("Not supported", e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override
+ public @NonNull byte[] delete(@NonNull byte[] challenge) {
+ try {
+ byte[] proofOfDeletion = mBinder.deleteWithChallenge(challenge);
+ return proofOfDeletion;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
+ public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
+ try {
+ IWritableCredential binder = mBinder.update();
+ byte[] proofOfProvision =
+ CredstoreWritableIdentityCredential.personalize(binder, personalizationData);
+ return proofOfProvision;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index 129063361b35..d8d47424e2e8 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -162,5 +162,4 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore {
+ e.errorCode, e);
}
}
-
}
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
index 725e3d8e429a..d2e7984ce19f 100644
--- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -76,7 +76,14 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
@NonNull @Override
public byte[] personalize(@NonNull PersonalizationData personalizationData) {
+ return personalize(mBinder, personalizationData);
+ }
+ // Used by both personalize() and CredstoreIdentityCredential.update().
+ //
+ @NonNull
+ static byte[] personalize(IWritableCredential binder,
+ @NonNull PersonalizationData personalizationData) {
Collection<AccessControlProfile> accessControlProfiles =
personalizationData.getAccessControlProfiles();
@@ -144,7 +151,7 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
secureUserId = getRootSid();
}
try {
- byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels,
+ byte[] personalizationReceipt = binder.personalize(acpParcels, ensParcels,
secureUserId);
return personalizationReceipt;
} catch (android.os.RemoteException e) {
@@ -164,5 +171,4 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
return rootSid;
}
-
}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 4eb6e420c07f..8f175bb63edb 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -23,6 +23,7 @@ import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
+import java.time.Instant;
import java.util.Collection;
import java.util.Map;
@@ -114,6 +115,25 @@ public abstract class IdentityCredential {
public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
/**
+ * Sets whether to allow using an authentication key which has been expired if no
+ * other key is available. This must be called prior to calling
+ * {@link #getEntries(byte[], Map, byte[], byte[])}.
+ *
+ * <p>By default this is set to false.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param allowUsingExpiredKeys whether to allow using an authentication key which use count
+ * has been exceeded if no other key is available.
+ */
+ public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
* operation handle.
*
@@ -289,6 +309,21 @@ public abstract class IdentityCredential {
*
* <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey
* can be obtained using the {@link #getCredentialKeyCertificateChain()} method.
+
+ * <p>If the implementation is feature version 202101 or later,
+ * each X.509 certificate contains an X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26 which
+ * contains a DER encoded OCTET STRING with the bytes of the CBOR with the following CDDL:
+ * <pre>
+ * ProofOfBinding = [
+ * "ProofOfBinding",
+ * bstr, // Contains SHA-256(ProofOfProvisioning)
+ * ]
+ * </pre>
+ * <p>This CBOR enables an issuer to determine the exact state of the credential it
+ * returns issuer-signed data for.
+ *
+ * <p> See {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for
+ * known feature versions.
*
* @return A collection of X.509 certificates for dynamic authentication keys that need issuer
* certification.
@@ -308,16 +343,136 @@ public abstract class IdentityCredential {
* the authenticity
* and integrity of the credential data fields.
* @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+ * @deprecated Use {@link #storeStaticAuthenticationData(X509Certificate, Instant, byte[])}
+ * instead.
*/
+ @Deprecated
public abstract void storeStaticAuthenticationData(
@NonNull X509Certificate authenticationKey,
@NonNull byte[] staticAuthData)
throws UnknownAuthenticationKeyException;
/**
+ * Store authentication data associated with a dynamic authentication key.
+ *
+ * This should only be called for an authenticated key returned by
+ * {@link #getAuthKeysNeedingCertification()}.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param authenticationKey The dynamic authentication key for which certification and
+ * associated static
+ * authentication data is being provided.
+ * @param expirationDate The expiration date of the static authentication data.
+ * @param staticAuthData Static authentication data provided by the issuer that validates
+ * the authenticity
+ * and integrity of the credential data fields.
+ * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+ */
+ public void storeStaticAuthenticationData(
+ @NonNull X509Certificate authenticationKey,
+ @NonNull Instant expirationDate,
+ @NonNull byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Get the number of times the dynamic authentication keys have been used.
*
* @return int array of dynamic authentication key usage counts.
*/
public @NonNull abstract int[] getAuthenticationDataUsageCount();
+
+ /**
+ * Proves ownership of a credential.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+ * with payload set to {@code ProofOfDeletion} as defined below.</p>
+ *
+ * <p>The returned CBOR is the following:</p>
+ * <pre>
+ * ProofOfOwnership = [
+ * "ProofOfOwnership", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ * </pre>
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param challenge is a non-empty byte array whose contents should be unique, fresh and
+ * provided by the issuing authority. The value provided is embedded in the
+ * generated CBOR and enables the issuing authority to verify that the
+ * returned proof is fresh.
+ * @return the COSE_Sign1 data structure above
+ */
+ public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Deletes a credential.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+ * with payload set to {@code ProofOfDeletion} as defined below.</p>
+ *
+ * <pre>
+ * ProofOfDeletion = [
+ * "ProofOfDeletion", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ * </pre>
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param challenge is a non-empty byte array whose contents should be unique, fresh and
+ * provided by the issuing authority. The value provided is embedded in the
+ * generated CBOR and enables the issuing authority to verify that the
+ * returned proof is fresh.
+ * @return the COSE_Sign1 data structure above
+ */
+ public @NonNull byte[] delete(@NonNull byte[] challenge) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Updates the credential with new access control profiles and data items.
+ *
+ * <p>This method is similar to
+ * {@link WritableIdentityCredential#personalize(PersonalizationData)} except that it operates
+ * on an existing credential, see the documentation for that method for the format of the
+ * returned data.
+ *
+ * <p>If this call succeeds an side-effect is that all dynamic authentication keys for the
+ * credential are deleted. The application will need to use
+ * {@link #getAuthKeysNeedingCertification()} to generate replacement keys and return
+ * them for issuer certification.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param personalizationData The data to update, including access control profiles
+ * and data elements and their values, grouped into namespaces.
+ * @return A COSE_Sign1 data structure, see above.
+ */
+ public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 3843d9279900..6ccd0e892141 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -72,6 +72,17 @@ import java.lang.annotation.RetentionPolicy;
* <p>Credentials provisioned to the direct access store should <strong>always</strong> use reader
* authentication to protect data elements. The reason for this is user authentication or user
* approval of data release is not possible when the device is off.
+ *
+ * <p>The Identity Credential API is designed to be able to evolve and change over time
+ * but still provide 100% backwards compatibility. This is complicated by the fact that
+ * there may be a version skew between the API used by the application and the version
+ * implemented in secure hardware. To solve this problem, the API provides for a way
+ * for the application to query which feature version the hardware implements (if any
+ * at all) using
+ * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} and
+ * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS}.
+ * Methods which only work on certain feature versions are clearly documented as
+ * such.
*/
public abstract class IdentityCredentialStore {
IdentityCredentialStore() {}
@@ -193,7 +204,9 @@ public abstract class IdentityCredentialStore {
* @param credentialName the name of the credential to delete.
* @return {@code null} if the credential was not found, the COSE_Sign1 data structure above
* if the credential was found and deleted.
+ * @deprecated Use {@link IdentityCredential#delete(byte[])} instead.
*/
+ @Deprecated
public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
/** @hide */
@@ -201,5 +214,4 @@ public abstract class IdentityCredentialStore {
@Retention(RetentionPolicy.SOURCE)
public @interface Ciphersuite {
}
-
}
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index fcc518c374e3..21d23b1b2575 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -82,7 +82,7 @@ public class Authorization {
*
* @param locked - whether it is a lock (true) or unlock (false) event
* @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
- * password provided by the LockSettingService
+ * password provided by the LockSettingService
*
* @return 0 if successful or a {@code ResponseCode}.
*/
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 5928540b19bf..014d6882be8d 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -67,6 +67,7 @@ public abstract class KeyProperties {
PURPOSE_SIGN,
PURPOSE_VERIFY,
PURPOSE_WRAP_KEY,
+ PURPOSE_AGREE_KEY,
})
public @interface PurposeEnum {}
@@ -96,6 +97,11 @@ public abstract class KeyProperties {
public static final int PURPOSE_WRAP_KEY = 1 << 5;
/**
+ * Purpose of key: creating a shared ECDH secret through key agreement.
+ */
+ public static final int PURPOSE_AGREE_KEY = 1 << 6;
+
+ /**
* @hide
*/
public static abstract class Purpose {
@@ -113,6 +119,8 @@ public abstract class KeyProperties {
return KeymasterDefs.KM_PURPOSE_VERIFY;
case PURPOSE_WRAP_KEY:
return KeymasterDefs.KM_PURPOSE_WRAP;
+ case PURPOSE_AGREE_KEY:
+ return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
@@ -130,6 +138,8 @@ public abstract class KeyProperties {
return PURPOSE_VERIFY;
case KeymasterDefs.KM_PURPOSE_WRAP:
return PURPOSE_WRAP_KEY;
+ case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
+ return PURPOSE_AGREE_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
index 6ddaa704afa8..b631999c2c54 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
@@ -38,9 +38,10 @@ public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey impleme
public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
+ @NonNull byte[] x509EncodedForm,
@NonNull KeyStoreSecurityLevel securityLevel,
@NonNull ECParameterSpec params, @NonNull ECPoint w) {
- super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
+ super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
mParams = params;
mW = w;
}
@@ -48,7 +49,7 @@ public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey impleme
public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull ECPublicKey info) {
- this(descriptor, metadata, securityLevel, info.getParams(), info.getW());
+ this(descriptor, metadata, info.getEncoded(), securityLevel, info.getParams(), info.getW());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
new file mode 100644
index 000000000000..fc963a88c4d1
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.Tag;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keystore.KeyStoreCryptoOperation;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link KeyAgreementSpi} which provides an ECDH implementation backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi
+ implements KeyStoreCryptoOperation {
+
+ private static final String TAG = "AndroidKeyStoreKeyAgreementSpi";
+
+ /**
+ * ECDH implementation.
+ *
+ * @hide
+ */
+ public static class ECDH extends AndroidKeyStoreKeyAgreementSpi {
+ public ECDH() {
+ super(Algorithm.EC);
+ }
+ }
+
+ private final int mKeymintAlgorithm;
+
+ // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+ private AndroidKeyStorePrivateKey mKey;
+ private PublicKey mOtherPartyKey;
+
+ // Fields below are reset when engineDoFinal succeeds.
+ private KeyStoreOperation mOperation;
+ private long mOperationHandle;
+
+ protected AndroidKeyStoreKeyAgreementSpi(int keymintAlgorithm) {
+ resetAll();
+
+ mKeymintAlgorithm = keymintAlgorithm;
+ }
+
+ @Override
+ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+ resetAll();
+
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof AndroidKeyStorePrivateKey)) {
+ throw new InvalidKeyException(
+ "Only Android KeyStore private keys supported. Key: " + key);
+ }
+ // Checking the correct KEY_PURPOSE and algorithm is done by the Keymint implementation in
+ // ensureKeystoreOperationInitialized() below.
+ mKey = (AndroidKeyStorePrivateKey) key;
+
+ boolean success = false;
+ try {
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported algorithm parameters: " + params);
+ }
+ engineInit(key, random);
+ }
+
+ @Override
+ protected Key engineDoPhase(Key key, boolean lastPhase)
+ throws InvalidKeyException, IllegalStateException {
+ ensureKeystoreOperationInitialized();
+
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof PublicKey)) {
+ throw new InvalidKeyException("Only public keys supported. Key: " + key);
+ } else if (!lastPhase) {
+ throw new IllegalStateException(
+ "Only one other party supported. lastPhase must be set to true.");
+ } else if (mOtherPartyKey != null) {
+ throw new IllegalStateException(
+ "Only one other party supported. doPhase() must only be called exactly once.");
+ }
+ // The other party key will be passed as part of the doFinal() call, to prevent an
+ // additional IPC.
+ mOtherPartyKey = (PublicKey) key;
+
+ return null; // No intermediate key
+ }
+
+ @Override
+ protected byte[] engineGenerateSecret() throws IllegalStateException {
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Not initialized", e);
+ }
+
+ if (mOtherPartyKey == null) {
+ throw new IllegalStateException("Other party key not provided. Call doPhase() first.");
+ }
+ byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
+
+ try {
+ return mOperation.finish(otherPartyKeyEncoded, null);
+ } catch (KeyStoreException e) {
+ throw new ProviderException("Keystore operation failed", e);
+ } finally {
+ resetWhilePreservingInitState();
+ }
+ }
+
+ @Override
+ protected SecretKey engineGenerateSecret(String algorithm)
+ throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException {
+ byte[] generatedSecret = engineGenerateSecret();
+
+ return new SecretKeySpec(generatedSecret, algorithm);
+ }
+
+ @Override
+ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+ throws IllegalStateException, ShortBufferException {
+ byte[] generatedSecret = engineGenerateSecret();
+
+ if (generatedSecret.length > sharedSecret.length - offset) {
+ throw new ShortBufferException("Needed: " + generatedSecret.length);
+ }
+ System.arraycopy(generatedSecret, 0, sharedSecret, offset, generatedSecret.length);
+ return generatedSecret.length;
+ }
+
+ @Override
+ public long getOperationHandle() {
+ return mOperationHandle;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ resetAll();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void resetWhilePreservingInitState() {
+ KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+ mOperationHandle = 0;
+ mOperation = null;
+ mOtherPartyKey = null;
+ }
+
+ private void resetAll() {
+ resetWhilePreservingInitState();
+ mKey = null;
+ }
+
+ private void ensureKeystoreOperationInitialized()
+ throws InvalidKeyException, IllegalStateException {
+ if (mKey == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+ if (mOperation != null) {
+ return;
+ }
+
+ // We don't need to explicitly pass in any other parameters here, as they're part of the
+ // private key that is available to Keymint.
+ List<KeyParameter> parameters = new ArrayList<>();
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ Tag.PURPOSE, KeyPurpose.AGREE_KEY
+ ));
+
+ try {
+ mOperation =
+ mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
+ } catch (KeyStoreException keyStoreException) {
+ // If necessary, throw an exception due to KeyStore operation having failed.
+ InvalidKeyException e =
+ KeyStoreCryptoOperationUtils.getInvalidKeyException(mKey, keyStoreException);
+ if (e != null) {
+ throw e;
+ }
+ }
+
+ // Set the operation handle. This will be a random number, or the operation challenge if
+ // user authentication is required. If we got a challenge we check if the authorization can
+ // possibly succeed.
+ mOperationHandle =
+ KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(mOperation, mKey);
+ }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 403da189262d..164bc8669525 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -94,6 +94,9 @@ public class AndroidKeyStoreProvider extends Provider {
put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
}
+ // javax.crypto.KeyAgreement
+ put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH");
+
// java.security.SecretKeyFactory
putSecretKeyFactoryImpl("AES");
if (supports3DES) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
index 49dd77e3a3db..db3e567cb6b4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
@@ -32,13 +32,15 @@ import java.security.PublicKey;
public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
private final byte[] mCertificate;
private final byte[] mCertificateChain;
+ private final byte[] mEncoded;
public AndroidKeyStorePublicKey(@NonNull KeyDescriptor descriptor,
- @NonNull KeyMetadata metadata, @NonNull String algorithm,
- @NonNull KeyStoreSecurityLevel securityLevel) {
+ @NonNull KeyMetadata metadata, @NonNull byte[] x509EncodedForm,
+ @NonNull String algorithm, @NonNull KeyStoreSecurityLevel securityLevel) {
super(descriptor, metadata.key.nspace, metadata.authorizations, algorithm, securityLevel);
mCertificate = metadata.certificate;
mCertificateChain = metadata.certificateChain;
+ mEncoded = x509EncodedForm;
}
abstract AndroidKeyStorePrivateKey getPrivateKey();
@@ -50,7 +52,7 @@ public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implem
@Override
public byte[] getEncoded() {
- return ArrayUtils.cloneIfNotEmpty(mCertificate);
+ return ArrayUtils.cloneIfNotEmpty(mEncoded);
}
@Override
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
index b578ea9baa06..9fe6cf3c113f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
@@ -36,9 +36,11 @@ public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implem
public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
+ @NonNull byte[] x509EncodedForm,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull BigInteger modulus,
@NonNull BigInteger publicExponent) {
- super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_RSA, securityLevel);
+ super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_RSA,
+ securityLevel);
mModulus = modulus;
mPublicExponent = publicExponent;
}
@@ -46,7 +48,8 @@ public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implem
public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull RSAPublicKey info) {
- this(descriptor, metadata, securityLevel, info.getModulus(), info.getPublicExponent());
+ this(descriptor, metadata, info.getEncoded(), securityLevel, info.getModulus(),
+ info.getPublicExponent());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
new file mode 100644
index 000000000000..8db8d7699a1e
--- /dev/null
+++ b/packages/Connectivity/framework/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// TODO: use a java_library in the bootclasspath instead
+filegroup {
+ name: "framework-connectivity-sources",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/base",
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+} \ No newline at end of file
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
new file mode 100644
index 000000000000..64b556720cd2
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.NattKeepalivePacketData;
+import android.net.QosFilterParcelable;
+import android.net.TcpKeepalivePacketData;
+
+import com.android.connectivity.aidl.INetworkAgentRegistry;
+
+/**
+ * Interface to notify NetworkAgent of connectivity events.
+ * @hide
+ */
+oneway interface INetworkAgent {
+ void onRegistered(in INetworkAgentRegistry registry);
+ void onDisconnected();
+ void onBandwidthUpdateRequested();
+ void onValidationStatusChanged(int validationStatus,
+ in @nullable String captivePortalUrl);
+ void onSaveAcceptUnvalidated(boolean acceptUnvalidated);
+ void onStartNattSocketKeepalive(int slot, int intervalDurationMs,
+ in NattKeepalivePacketData packetData);
+ void onStartTcpSocketKeepalive(int slot, int intervalDurationMs,
+ in TcpKeepalivePacketData packetData);
+ void onStopSocketKeepalive(int slot);
+ void onSignalStrengthThresholdsUpdated(in int[] thresholds);
+ void onPreventAutomaticReconnect();
+ void onAddNattKeepalivePacketFilter(int slot,
+ in NattKeepalivePacketData packetData);
+ void onAddTcpKeepalivePacketFilter(int slot,
+ in TcpKeepalivePacketData packetData);
+ void onRemoveKeepalivePacketFilter(int slot);
+ void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
+ void onQosCallbackUnregistered(int qosCallbackId);
+}
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
new file mode 100644
index 000000000000..f0193db5c2e2
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.QosSession;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+/**
+ * Interface for NetworkAgents to send network network properties.
+ * @hide
+ */
+oneway interface INetworkAgentRegistry {
+ void sendNetworkCapabilities(in NetworkCapabilities nc);
+ void sendLinkProperties(in LinkProperties lp);
+ // TODO: consider replacing this by "markConnected()" and removing
+ void sendNetworkInfo(in NetworkInfo info);
+ void sendScore(int score);
+ void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
+ void sendSocketKeepaliveEvent(int slot, int reason);
+ void sendUnderlyingNetworks(in @nullable List<Network> networks);
+ void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes);
+ void sendQosSessionLost(int qosCallbackId, in QosSession session);
+ void sendQosCallbackError(int qosCallbackId, int exceptionType);
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
index 06c52942e671..fcee98d0bd0b 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
@@ -19,11 +19,8 @@ package com.android.dynsystem;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.DynamicSystemClient;
-import android.os.image.DynamicSystemManager;
-import android.util.FeatureFlagUtils;
/**
@@ -43,24 +40,10 @@ public class BootCompletedReceiver extends BroadcastReceiver {
return;
}
- DynamicSystemManager dynSystem =
- (DynamicSystemManager) context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
-
- boolean isInUse = (dynSystem != null) && dynSystem.isInUse();
-
- if (!isInUse && !featureFlagEnabled()) {
- return;
- }
-
Intent startServiceIntent = new Intent(
context, DynamicSystemInstallationService.class);
startServiceIntent.setAction(DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE);
context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM);
}
-
- private boolean featureFlagEnabled() {
- return SystemProperties.getBoolean(
- FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
- }
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index 82ea7449bf6d..64e42cc595ec 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -22,10 +22,8 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.DynamicSystemClient;
-import android.util.FeatureFlagUtils;
import android.util.Log;
/**
@@ -46,12 +44,6 @@ public class VerificationActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (!featureFlagEnabled()) {
- Log.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; activity aborted.");
- finish();
- return;
- }
-
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (km != null) {
@@ -101,11 +93,6 @@ public class VerificationActivity extends Activity {
startServiceAsUser(intent, UserHandle.SYSTEM);
}
- private boolean featureFlagEnabled() {
- return SystemProperties.getBoolean(
- FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
- }
-
static boolean isVerified(String url) {
if (url == null) return true;
return sVerifiedUrl != null && sVerifiedUrl.equals(url);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4b6862c6d186..dcbad3082bdb 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -292,6 +292,9 @@
<!-- Permission needed to test mainline permission module rollback -->
<uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
+ <!-- Permission needed to restart WiFi Subsystem -->
+ <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" />
+
<!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
<uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" />
diff --git a/services/OWNERS b/services/OWNERS
index 88d0b61a2ab6..03e0807eea62 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1 +1,6 @@
per-file Android.bp = file:platform/build/soong:/OWNERS
+
+# art-team@ manages the system server profile
+per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+
+per-file java/com/android/server/* = toddke@google.com
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5ad5805910c2..4bebe399b8bc 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -84,6 +84,7 @@ java_library_static {
":storaged_aidl",
":vold_aidl",
":platform-compat-config",
+ ":platform-compat-overrides",
":display-device-config",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9aadcf837eb8..d129b9c074a9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -133,6 +133,7 @@ import android.net.TetheringManager;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.INetdEventListener;
@@ -184,7 +185,6 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
@@ -1458,9 +1458,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
final String action = blocked ? "BLOCKED" : "UNBLOCKED";
- final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
- final int requestId = satisfiedRequest != null
- ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
+ final int requestId = nri.getActiveRequest() != null
+ ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
"%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
@@ -2730,7 +2729,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@VisibleForTesting
NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
- requests = mNetworkRequests.values().toArray(requests);
+ requests = getNrisFromGlobalRequests().toArray(requests);
// Sort the array based off the NRI containing the min requestId in its requests.
Arrays.sort(requests,
Comparator.comparingInt(nri -> Collections.min(nri.mRequests,
@@ -3435,10 +3434,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
- final NetworkAgentInfo currentNetwork = nri.mSatisfier;
+ final NetworkAgentInfo currentNetwork = nri.getSatisfier();
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
- nri.mSatisfier = null;
+ nri.setSatisfier(null, null);
sendUpdatedScoreToFactories(request, null);
}
}
@@ -3516,42 +3515,63 @@ public class ConnectivityService extends IConnectivityManager.Stub
return null;
}
- private void handleRegisterNetworkRequestWithIntent(Message msg) {
+ private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
-
- NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
+ // handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleRegisterNetworkRequestWithIntent");
+ final NetworkRequestInfo existingRequest =
+ findExistingNetworkRequestInfo(nri.mPendingIntent);
if (existingRequest != null) { // remove the existing request.
- if (DBG) log("Replacing " + existingRequest.request + " with "
- + nri.request + " because their intents matched.");
- handleReleaseNetworkRequest(existingRequest.request, getCallingUid(),
+ if (DBG) {
+ log("Replacing " + existingRequest.mRequests.get(0) + " with "
+ + nri.mRequests.get(0) + " because their intents matched.");
+ }
+ handleReleaseNetworkRequest(existingRequest.mRequests.get(0), getCallingUid(),
/* callOnUnavailable */ false);
}
handleRegisterNetworkRequest(nri);
}
- private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
+ private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
- mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
- if (nri.request.isListen()) {
- for (NetworkAgentInfo network : mNetworkAgentInfos) {
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- network.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(network, "REGISTER", nri.request);
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.put(req, nri);
+ if (req.isListen()) {
+ for (final NetworkAgentInfo network : mNetworkAgentInfos) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && network.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(network, "REGISTER", req);
+ }
}
}
}
rematchAllNetworksAndRequests();
- if (nri.request.isRequest() && nri.mSatisfier == null) {
- sendUpdatedScoreToFactories(nri.request, null);
+ // If an active request exists, return as its score has already been sent if needed.
+ if (null != nri.getActiveRequest()) {
+ return;
+ }
+
+ // As this request was not satisfied on rematch and thus never had any scores sent to the
+ // factories, send null now for each request of type REQUEST.
+ for (final NetworkRequest req : nri.mRequests) {
+ if (!req.isRequest()) {
+ continue;
+ }
+ sendUpdatedScoreToFactories(req, null);
}
}
- private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
- int callingUid) {
- NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
+ private void handleReleaseNetworkRequestWithIntent(@NonNull final PendingIntent pendingIntent,
+ final int callingUid) {
+ final NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
if (nri != null) {
- handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false);
+ // handleReleaseNetworkRequestWithIntent() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequestWithIntent");
+ handleReleaseNetworkRequest(
+ nri.mRequests.get(0),
+ callingUid,
+ /* callOnUnavailable */ false);
}
}
@@ -3605,6 +3625,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
return false;
}
for (final NetworkRequest req : nri.mRequests) {
+ // This multilayer listen request is satisfied therefore no further requests need to be
+ // evaluated deeming this network not a potential satisfier.
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ return false;
+ }
// As non-multilayer listen requests have already returned, the below would only happen
// for a multilayer request therefore continue to the next request if available.
if (req.isListen()) {
@@ -3625,7 +3650,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- || nri.mSatisfier.getCurrentScore()
+ || nri.getSatisfier().getCurrentScore()
< candidate.getCurrentScoreAsValidated();
return isNetworkNeeded;
}
@@ -3650,30 +3675,45 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nri;
}
- private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
+ private void ensureNotMultilayerRequest(@NonNull final NetworkRequestInfo nri,
+ final String callingMethod) {
+ if (nri.isMultilayerRequest()) {
+ throw new IllegalStateException(
+ callingMethod + " does not support multilayer requests.");
+ }
+ }
+
+ private void handleTimedOutNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
- if (mNetworkRequests.get(nri.request) == null) {
+ // handleTimedOutNetworkRequest() is part of the requestNetwork() flow which works off of a
+ // single NetworkRequest and thus does not apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleTimedOutNetworkRequest");
+ if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
return;
}
- if (nri.mSatisfier != null) {
+ if (nri.getSatisfier() != null) {
return;
}
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (timeout)");
+ if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
+ log("releasing " + nri.mRequests.get(0) + " (timeout)");
}
handleRemoveNetworkRequest(nri);
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+ callCallbackForRequest(
+ nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
}
- private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid,
- boolean callOnUnavailable) {
+ private void handleReleaseNetworkRequest(@NonNull final NetworkRequest request,
+ final int callingUid,
+ final boolean callOnUnavailable) {
final NetworkRequestInfo nri =
getNriForAppRequest(request, callingUid, "release NetworkRequest");
if (nri == null) {
return;
}
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (release request)");
+ // handleReleaseNetworkRequest() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequest");
+ if (VDBG || (DBG && request.isRequest())) {
+ log("releasing " + request + " (release request)");
}
handleRemoveNetworkRequest(nri);
if (callOnUnavailable) {
@@ -3681,42 +3721,88 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
+ private void handleRemoveNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
nri.unlinkDeathRecipient();
- mNetworkRequests.remove(nri.request);
-
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.remove(req);
+ if (req.isListen()) {
+ removeListenRequestFromNetworks(req);
+ }
+ }
mNetworkRequestCounter.decrementCount(nri.mUid);
-
mNetworkRequestInfoLogs.log("RELEASE " + nri);
- if (nri.request.isRequest()) {
- boolean wasKept = false;
- final NetworkAgentInfo nai = nri.mSatisfier;
- if (nai != null) {
- boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
- nai.removeRequest(nri.request.requestId);
- if (VDBG || DDBG) {
- log(" Removing from current network " + nai.toShortString()
- + ", leaving " + nai.numNetworkRequests() + " requests.");
- }
- // If there are still lingered requests on this network, don't tear it down,
- // but resume lingering instead.
- final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
- notifyNetworkLosing(nai, now);
- }
- if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
- teardownUnneededNetwork(nai);
- } else {
- wasKept = true;
- }
- nri.mSatisfier = null;
- if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
- // Went from foreground to background.
- updateCapabilitiesForNetwork(nai);
- }
+
+ if (null != nri.getActiveRequest()) {
+ if (nri.getActiveRequest().isRequest()) {
+ removeSatisfiedNetworkRequestFromNetwork(nri);
+ } else {
+ nri.setSatisfier(null, null);
+ }
+ }
+
+ cancelNpiRequests(nri);
+ }
+
+ private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
+ for (final NetworkRequest req : nri.mRequests) {
+ cancelNpiRequest(req);
+ }
+ }
+
+ private void cancelNpiRequest(@NonNull final NetworkRequest req) {
+ if (req.isRequest()) {
+ for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.cancelRequest(req);
+ }
+ }
+ }
+
+ private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) {
+ // listens don't have a singular affected Network. Check all networks to see
+ // if this listen request applies and remove it.
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.removeRequest(req.requestId);
+ if (req.networkCapabilities.hasSignalStrength()
+ && nai.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", req);
+ }
+ }
+ }
+
+ /**
+ * Remove a NetworkRequestInfo's satisfied request from its 'satisfier' (NetworkAgentInfo) and
+ * manage the necessary upkeep (linger, teardown networks, etc.) when doing so.
+ * @param nri the NetworkRequestInfo to disassociate from its current NetworkAgentInfo
+ */
+ private void removeSatisfiedNetworkRequestFromNetwork(@NonNull final NetworkRequestInfo nri) {
+ boolean wasKept = false;
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ if (nai != null) {
+ final int requestLegacyType = nri.getActiveRequest().legacyType;
+ final boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
+ nai.removeRequest(nri.getActiveRequest().requestId);
+ if (VDBG || DDBG) {
+ log(" Removing from current network " + nai.toShortString()
+ + ", leaving " + nai.numNetworkRequests() + " requests.");
+ }
+ // If there are still lingered requests on this network, don't tear it down,
+ // but resume lingering instead.
+ final long now = SystemClock.elapsedRealtime();
+ if (updateLingerState(nai, now)) {
+ notifyNetworkLosing(nai, now);
+ }
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
+ if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
+ teardownUnneededNetwork(nai);
+ } else {
+ wasKept = true;
+ }
+ nri.setSatisfier(null, null);
+ if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
+ // Went from foreground to background.
+ updateCapabilitiesForNetwork(nai);
}
// Maintain the illusion. When this request arrived, we might have pretended
@@ -3724,15 +3810,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
// connected. Now that this request has gone away, we might have to pretend
// that the network disconnected. LegacyTypeTracker will generate that
// phantom disconnect for this type.
- if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ if (requestLegacyType != TYPE_NONE) {
boolean doRemove = true;
if (wasKept) {
// check if any of the remaining requests for this network are for the
// same legacy type - if so, don't remove the nai
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest otherRequest = nai.requestAt(i);
- if (otherRequest.legacyType == nri.request.legacyType &&
- otherRequest.isRequest()) {
+ if (otherRequest.legacyType == requestLegacyType
+ && otherRequest.isRequest()) {
if (DBG) log(" still have other legacy request - leaving");
doRemove = false;
}
@@ -3740,21 +3826,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (doRemove) {
- mLegacyTypeTracker.remove(nri.request.legacyType, nai, false);
- }
- }
-
- for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.cancelRequest(nri.request);
- }
- } else {
- // listens don't have a singular affectedNetwork. Check all networks to see
- // if this listen request applies and remove it.
- for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- nai.removeRequest(nri.request.requestId);
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
+ mLegacyTypeTracker.remove(requestLegacyType, nai, false);
}
}
}
@@ -4832,16 +4904,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (interfaces.isEmpty()) return null;
- VpnInfo info = new VpnInfo();
- info.ownerUid = nai.networkCapabilities.getOwnerUid();
- info.vpnIface = nai.linkProperties.getInterfaceName();
// Must be non-null or NetworkStatsService will crash.
// Cannot happen in production code because Vpn only registers the NetworkAgent after the
// tun or ipsec interface is created.
- if (info.vpnIface == null) return null;
- info.underlyingIfaces = interfaces.toArray(new String[0]);
+ if (nai.linkProperties.getInterfaceName() == null) return null;
- return info;
+ return new VpnInfo(nai.networkCapabilities.getOwnerUid(),
+ nai.linkProperties.getInterfaceName(),
+ interfaces.toArray(new String[0]));
}
/**
@@ -5417,18 +5487,38 @@ public class ConnectivityService extends IConnectivityManager.Stub
/**
* Tracks info about the requester.
- * Also used to notice when the calling process dies so we can self-expire
+ * Also used to notice when the calling process dies so as to self-expire
*/
@VisibleForTesting
protected class NetworkRequestInfo implements IBinder.DeathRecipient {
final List<NetworkRequest> mRequests;
- final NetworkRequest request;
+
+ // mSatisfier and mActiveRequest rely on one another therefore set them together.
+ void setSatisfier(
+ @Nullable final NetworkAgentInfo satisfier,
+ @Nullable final NetworkRequest activeRequest) {
+ mSatisfier = satisfier;
+ mActiveRequest = activeRequest;
+ }
// The network currently satisfying this request, or null if none. Must only be touched
// on the handler thread. This only makes sense for network requests and not for listens,
// as defined by NetworkRequest#isRequest(). For listens, this is always null.
@Nullable
- NetworkAgentInfo mSatisfier;
+ private NetworkAgentInfo mSatisfier;
+ NetworkAgentInfo getSatisfier() {
+ return mSatisfier;
+ }
+
+ // The request in mRequests assigned to a network agent. This is null if none of the
+ // requests in mRequests can be satisfied. This member has the constraint of only being
+ // accessible on the handler thread.
+ @Nullable
+ private NetworkRequest mActiveRequest;
+ NetworkRequest getActiveRequest() {
+ return mActiveRequest;
+ }
+
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
private final IBinder mBinder;
@@ -5437,7 +5527,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
final Messenger messenger;
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mPendingIntent = pi;
@@ -5451,7 +5540,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
super();
messenger = m;
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mBinder = binder;
@@ -5481,20 +5569,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return Collections.unmodifiableList(tempRequests);
}
- private NetworkRequest getSatisfiedRequest() {
- if (mSatisfier == null) {
- return null;
- }
-
- for (NetworkRequest req : mRequests) {
- if (mSatisfier.isSatisfyingRequest(req.requestId)) {
- return req;
- }
- }
-
- return null;
- }
-
void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
@@ -5541,6 +5615,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet<>();
synchronized (nai) {
+ // mNetworkRequests may contain the same value multiple times in case of
+ // multilayer requests. It won't matter in this case because the thresholds
+ // will then be the same and be deduplicated as they enter the `thresholds` set.
+ // TODO : have mNetworkRequests be a Set<NetworkRequestInfo> or the like.
for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
for (final NetworkRequest req : nri.mRequests) {
if (req.networkCapabilities.hasSignalStrength()
@@ -5580,7 +5658,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (ns == null) {
return;
}
- MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
+ if (ns instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
}
private void ensureValid(NetworkCapabilities nc) {
@@ -5916,13 +5996,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
+ public void declareNetworkRequestUnfulfillable(@NonNull final NetworkRequest request) {
if (request.hasTransport(TRANSPORT_TEST)) {
enforceNetworkFactoryOrTestNetworksPermission();
} else {
enforceNetworkFactoryPermission();
}
- mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true));
+ final NetworkRequestInfo nri = mNetworkRequests.get(request);
+ if (nri != null) {
+ // declareNetworkRequestUnfulfillable() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "declareNetworkRequestUnfulfillable");
+ mHandler.post(() -> handleReleaseNetworkRequest(
+ nri.mRequests.get(0), mDeps.getCallingUid(), true));
+ }
}
// NOTE: Accessed on multiple threads, must be synchronized on itself.
@@ -6110,7 +6196,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai.networkAgentPortalData = lp.getCaptivePortalData();
}
- private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+ private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp,
@NonNull LinkProperties oldLp) {
int netId = networkAgent.network.getNetId();
@@ -6119,8 +6205,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// the LinkProperties for the network are accurate.
networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
- updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
- networkAgent.networkInfo.getType());
+ updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
// update filtering rules, need to happen after the interface update so netd knows about the
// new interface (the interface name -> index map becomes initialized)
@@ -6259,7 +6344,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void updateInterfaces(final @Nullable LinkProperties newLp,
final @Nullable LinkProperties oldLp, final int netId,
- final @Nullable NetworkCapabilities caps, final int legacyType) {
+ final @NonNull NetworkCapabilities caps) {
final CompareResult<String> interfaceDiff = new CompareResult<>(
oldLp != null ? oldLp.getAllInterfaceNames() : null,
newLp != null ? newLp.getAllInterfaceNames() : null);
@@ -6270,7 +6355,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (DBG) log("Adding iface " + iface + " to network " + netId);
mNetd.networkAddInterface(netId, iface);
wakeupModifyInterface(iface, caps, true);
- bs.noteNetworkInterfaceType(iface, legacyType);
+ bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes());
} catch (Exception e) {
loge("Exception adding interface: " + e);
}
@@ -6542,6 +6627,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
* maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
* and foreground status).
*/
+ @NonNull
private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
// Don't complain for VPNs since they're not driven by requests and there is no risk of
@@ -6598,6 +6684,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
return newNc;
}
+ private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai,
+ NetworkCapabilities prevNc, NetworkCapabilities newNc) {
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (prevSuspended != suspended) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ }
+ if (prevSuspended != suspended || prevRoaming != roaming) {
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
+ }
+
/**
* Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
*
@@ -6629,25 +6734,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
processListenRequests(nai);
- final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- if (prevSuspended != suspended || prevRoaming != roaming) {
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
- : ConnectivityManager.CALLBACK_RESUMED);
- // updateNetworkInfo will mix in the suspended info from the capabilities and
- // take appropriate action for the network having possibly changed state.
- updateNetworkInfo(nai, nai.networkInfo);
- }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests();
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc);
final boolean oldMetered = prevNc.isMetered();
final boolean newMetered = newNc.isMetered();
@@ -6847,6 +6940,39 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ private void sendUpdatedScoreToFactories(
+ @NonNull final NetworkReassignment.RequestReassignment event) {
+ // If a request of type REQUEST is now being satisfied by a new network.
+ if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) {
+ sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork);
+ }
+
+ // If a previously satisfied request of type REQUEST is no longer being satisfied.
+ if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest()
+ && event.mOldNetworkRequest != event.mNewNetworkRequest) {
+ sendUpdatedScoreToFactories(event.mOldNetworkRequest, null);
+ }
+
+ cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo);
+ }
+
+ /**
+ * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than
+ * its currently satisfied active request.
+ * @param nri the NRI to cancel lower priority requests for.
+ */
+ private void cancelMultilayerLowerPriorityNpiRequests(
+ @NonNull final NetworkRequestInfo nri) {
+ if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) {
+ return;
+ }
+
+ final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest);
+ for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) {
+ cancelNpiRequest(nri.mRequests.get(i));
+ }
+ }
+
private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
@Nullable NetworkAgentInfo nai) {
final int score;
@@ -6867,21 +6993,35 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
+ private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) {
ensureRunningOnConnectivityServiceThread();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = nri.mSatisfier;
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkProvider.ID_NONE;
+ for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ break;
+ }
+ if (req.isListen()) {
+ continue;
+ }
+ // Only set the nai for the request it is satisfying.
+ final NetworkAgentInfo nai =
+ nri.getActiveRequest() == req ? nri.getSatisfier() : null;
+ final int score;
+ final int serial;
+ if (null != nai) {
+ score = nai.getCurrentScore();
+ serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = NetworkProvider.ID_NONE;
+ }
+ npi.requestNetwork(req, score, serial);
+ // For multilayer requests, don't send lower priority requests if a higher priority
+ // request is already satisfied.
+ if (null != nai) {
+ break;
+ }
}
- npi.requestNetwork(nri.request, score, serial);
}
}
@@ -6890,7 +7030,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
Intent intent = new Intent();
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+ // If apps could file multi-layer requests with PendingIntents, they'd need to know
+ // which of the layer is satisfied alongside with some ID for the request. Hence, if
+ // such an API is ever implemented, there is no doubt the right request to send in
+ // EXTRA_NETWORK_REQUEST is mActiveRequest, and whatever ID would be added would need to
+ // be sent as a separate extra.
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.getActiveRequest());
nri.mPendingIntentSent = true;
sendIntent(nri.mPendingIntent, intent);
}
@@ -6920,8 +7065,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
releasePendingNetworkRequestWithDelay(pendingIntent);
}
- private void callCallbackForRequest(NetworkRequestInfo nri,
- NetworkAgentInfo networkAgent, int notificationType, int arg1) {
+ private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkAgentInfo networkAgent, final int notificationType,
+ final int arg1) {
if (nri.messenger == null) {
// Default request has no msgr. Also prevents callbacks from being invoked for
// NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
@@ -6929,8 +7075,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
Bundle bundle = new Bundle();
+ // In the case of multi-layer NRIs, the first request is not necessarily the one that
+ // is satisfied. This is vexing, but the ConnectivityManager code that receives this
+ // callback is only using the request as a token to identify the callback, so it doesn't
+ // matter too much at this point as long as the callback can be found.
+ // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed.
- putParcelable(bundle, new NetworkRequest(nri.request));
+ final NetworkRequest nrForCallback = new NetworkRequest(nri.mRequests.get(0));
+ putParcelable(bundle, nrForCallback);
Message msg = Message.obtain();
if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
putParcelable(bundle, networkAgent.network);
@@ -6943,7 +7095,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nri.request.getRequestorPackageName()));
+ nc, nri.mUid, nrForCallback.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6962,7 +7114,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nri.request.getRequestorPackageName()));
+ netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -6981,12 +7133,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
try {
if (VDBG) {
String notification = ConnectivityManager.getCallbackName(notificationType);
- log("sending notification " + notification + " for " + nri.request);
+ log("sending notification " + notification + " for " + nrForCallback);
}
nri.messenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
- loge("RemoteException caught trying to send a callback msg for " + nri.request);
+ loge("RemoteException caught trying to send a callback msg for " + nrForCallback);
}
}
@@ -7062,19 +7214,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
- nai.removeRequest(nri.request.requestId);
+ nai.removeRequest(nr.requestId);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
}
}
}
private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
nai.addRequest(nr);
@@ -7086,19 +7244,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
// An accumulator class to gather the list of changes that result from a rematch.
private static class NetworkReassignment {
static class RequestReassignment {
- @NonNull public final NetworkRequestInfo mRequest;
+ @NonNull public final NetworkRequestInfo mNetworkRequestInfo;
+ @NonNull public final NetworkRequest mOldNetworkRequest;
+ @NonNull public final NetworkRequest mNewNetworkRequest;
@Nullable public final NetworkAgentInfo mOldNetwork;
@Nullable public final NetworkAgentInfo mNewNetwork;
- RequestReassignment(@NonNull final NetworkRequestInfo request,
+ RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo,
+ @NonNull final NetworkRequest oldNetworkRequest,
+ @NonNull final NetworkRequest newNetworkRequest,
@Nullable final NetworkAgentInfo oldNetwork,
@Nullable final NetworkAgentInfo newNetwork) {
- mRequest = request;
+ mNetworkRequestInfo = networkRequestInfo;
+ mOldNetworkRequest = oldNetworkRequest;
+ mNewNetworkRequest = newNetworkRequest;
mOldNetwork = oldNetwork;
mNewNetwork = newNetwork;
}
public String toString() {
- return mRequest.mRequests.get(0).requestId + " : "
+ return mNetworkRequestInfo.mRequests.get(0).requestId + " : "
+ (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null")
+ " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null");
}
@@ -7116,7 +7280,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// sure this stays true, but without imposing this expensive check on all
// reassignments on all user devices.
for (final RequestReassignment existing : mReassignments) {
- if (existing.mRequest.equals(reassignment.mRequest)) {
+ if (existing.mNetworkRequestInfo.equals(reassignment.mNetworkRequestInfo)) {
throw new IllegalStateException("Trying to reassign ["
+ reassignment + "] but already have ["
+ existing + "]");
@@ -7131,7 +7295,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Nullable
private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
for (final RequestReassignment event : getRequestReassignments()) {
- if (nri == event.mRequest) return event;
+ if (nri == event.mNetworkRequestInfo) return event;
}
return null;
}
@@ -7158,6 +7322,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkRequest previousRequest,
+ @NonNull final NetworkRequest newRequest,
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
@@ -7167,58 +7333,98 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
- previousSatisfier.removeRequest(nri.request.requestId);
- previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs);
+ previousSatisfier.removeRequest(previousRequest.requestId);
+ previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
- newSatisfier.unlingerRequest(nri.request.requestId);
- if (!newSatisfier.addRequest(nri.request)) {
+ newSatisfier.unlingerRequest(newRequest.requestId);
+ if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
- + nri.request);
+ + newRequest);
}
} else {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
- + " request " + nri.request.requestId);
+ + " request " + previousRequest.requestId);
}
- previousSatisfier.removeRequest(nri.request.requestId);
+ previousSatisfier.removeRequest(previousRequest.requestId);
}
- nri.mSatisfier = newSatisfier;
+ nri.setSatisfier(newSatisfier, newRequest);
}
+ /**
+ * This function is triggered when something can affect what network should satisfy what
+ * request, and it computes the network reassignment from the passed collection of requests to
+ * network match to the one that the system should now have. That data is encoded in an
+ * object that is a list of changes, each of them having an NRI, and old satisfier, and a new
+ * satisfier.
+ *
+ * After the reassignment is computed, it is applied to the state objects.
+ *
+ * @param networkRequests the nri objects to evaluate for possible network reassignment
+ * @return NetworkReassignment listing of proposed network assignment changes
+ */
@NonNull
- private NetworkReassignment computeNetworkReassignment() {
- ensureRunningOnConnectivityServiceThread();
+ private NetworkReassignment computeNetworkReassignment(
+ @NonNull final Collection<NetworkRequestInfo> networkRequests) {
final NetworkReassignment changes = new NetworkReassignment();
// Gather the list of all relevant agents and sort them by score.
final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (!nai.everConnected) continue;
+ if (!nai.everConnected) {
+ continue;
+ }
nais.add(nai);
}
- for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+ for (final NetworkRequestInfo nri : networkRequests) {
+ // Non-multilayer listen requests can be ignored.
+ if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ continue;
+ }
+ NetworkAgentInfo bestNetwork = null;
+ NetworkRequest bestRequest = null;
+ for (final NetworkRequest req : nri.mRequests) {
+ bestNetwork = mNetworkRanker.getBestNetwork(req, nais);
+ // Stop evaluating as the highest possible priority request is satisfied.
+ if (null != bestNetwork) {
+ bestRequest = req;
+ break;
+ }
+ }
if (bestNetwork != nri.mSatisfier) {
// bestNetwork may be null if no network can satisfy this request.
changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, nri.mSatisfier, bestNetwork));
+ nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
}
}
return changes;
}
+ private Set<NetworkRequestInfo> getNrisFromGlobalRequests() {
+ return new HashSet<>(mNetworkRequests.values());
+ }
+
/**
- * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
+ * Attempt to rematch all Networks with all NetworkRequests. This may result in Networks
* being disconnected.
*/
private void rematchAllNetworksAndRequests() {
+ rematchNetworksAndRequests(getNrisFromGlobalRequests());
+ }
+
+ /**
+ * Attempt to rematch all Networks with given NetworkRequests. This may result in Networks
+ * being disconnected.
+ */
+ private void rematchNetworksAndRequests(
+ @NonNull final Set<NetworkRequestInfo> networkRequests) {
+ ensureRunningOnConnectivityServiceThread();
// TODO: This may be slow, and should be optimized.
final long now = SystemClock.elapsedRealtime();
- final NetworkReassignment changes = computeNetworkReassignment();
+ final NetworkReassignment changes = computeNetworkReassignment(networkRequests);
if (VDBG || DDBG) {
log(changes.debugString());
} else if (DBG) {
@@ -7243,8 +7449,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// the linger status.
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
- event.mNewNetwork, now);
+ updateSatisfiersForRematchRequest(event.mNetworkRequestInfo,
+ event.mOldNetworkRequest, event.mNewNetworkRequest,
+ event.mOldNetwork, event.mNewNetwork,
+ now);
}
final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
@@ -7296,12 +7504,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
// trying to connect if they know they cannot match it.
// TODO - this could get expensive if there are a lot of outstanding requests for this
// network. Think of a way to reduce this. Push netid->request mapping to each factory?
- sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
+ sendUpdatedScoreToFactories(event);
if (null != event.mNewNetwork) {
- notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
- callCallbackForRequest(event.mRequest, event.mOldNetwork,
+ callCallbackForRequest(event.mNetworkRequestInfo, event.mOldNetwork,
ConnectivityManager.CALLBACK_LOST, 0);
}
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index f2b63a642c29..88ce2208adcb 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -22,7 +22,6 @@ import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsiServiceCallback;
-import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -30,7 +29,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
import android.util.Slog;
import java.io.File;
@@ -88,16 +87,17 @@ public class DynamicSystemService extends IDynamicSystemService.Stub {
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
final int userId = UserHandle.myUserId();
- final StorageVolume[] volumes =
- StorageManager.getVolumeList(userId, StorageManager.FLAG_FOR_WRITE);
- for (StorageVolume volume : volumes) {
- if (volume.isEmulated()) continue;
- if (!volume.isRemovable()) continue;
- if (!Environment.MEDIA_MOUNTED.equals(volume.getState())) continue;
- File sdCard = volume.getPathFile();
- if (sdCard.isDirectory()) {
- path = new File(sdCard, dsuSlot).getPath();
- break;
+ final StorageManager sm = mContext.getSystemService(StorageManager.class);
+ for (VolumeInfo volume : sm.getVolumes()) {
+ if (volume.getType() != volume.TYPE_PUBLIC) {
+ continue;
+ }
+ if (!volume.isMountedWritable()) {
+ continue;
+ }
+ File sd_internal = volume.getInternalPathForUser(userId);
+ if (sd_internal != null) {
+ path = new File(sd_internal, dsuSlot).getPath();
}
}
if (path.isEmpty()) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 5c34584d0adf..4e2519b47a47 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3297,6 +3297,12 @@ class StorageManagerService extends IStorageManager.Stub
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
if (isFsEncrypted) {
+ // When a user has secure lock screen, require secret to actually unlock.
+ // This check is mostly in place for emulation mode.
+ if (StorageManager.isFileEncryptedEmulatedOnly() &&
+ mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(secret)) {
+ throw new IllegalStateException("Secret required to unlock secure user " + userId);
+ }
try {
mVold.unlockUserKey(userId, serialNumber, encodeBytes(token),
encodeBytes(secret));
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e8687e57a07b..a08d066513c7 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -242,6 +242,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
nc.setAdministratorUids(administratorUids);
if (!isMetered) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 630548df4b0b..ab24015a1174 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -704,7 +704,7 @@ public class Watchdog extends Thread {
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
if (!Build.IS_USER && isCrashLoopFound()
- && !WatchdogProperties.is_fatal_ignore().orElse(false)) {
+ && !WatchdogProperties.should_ignore_fatal_count().orElse(false)) {
breakCrashLoop();
}
Process.killProcess(Process.myPid());
@@ -783,7 +783,7 @@ public class Watchdog extends Thread {
private boolean isCrashLoopFound() {
int fatalCount = WatchdogProperties.fatal_count().orElse(0);
long fatalWindowMs = TimeUnit.SECONDS.toMillis(
- WatchdogProperties.fatal_window_second().orElse(0));
+ WatchdogProperties.fatal_window_seconds().orElse(0));
if (fatalCount == 0 || fatalWindowMs == 0) {
if (fatalCount != fatalWindowMs) {
Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together",
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3c538806be01..9986085224b1 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1066,9 +1066,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
@Override
- public void noteNetworkInterfaceType(String iface, int networkType) {
+ public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) {
enforceCallingPermission();
- mStats.noteNetworkInterfaceType(iface, networkType);
+ mStats.noteNetworkInterfaceForTransports(iface, transportTypes);
}
@Override
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
new file mode 100644
index 000000000000..508bb01e50a8
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_USER_ADDED;
+import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
+import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.apphibernation.IAppHibernationService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ShellCallback;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * System service that manages app hibernation state, a state apps can enter that means they are
+ * not being actively used and can be optimized for storage. The actual policy for determining
+ * if an app should hibernate is managed by PermissionController code.
+ */
+public final class AppHibernationService extends SystemService {
+ private static final String TAG = "AppHibernationService";
+
+ /**
+ * Lock for accessing any in-memory hibernation state
+ */
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final IPackageManager mIPackageManager;
+ private final IActivityManager mIActivityManager;
+ private final UserManager mUserManager;
+ @GuardedBy("mLock")
+ private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public AppHibernationService(@NonNull Context context) {
+ this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
+ ActivityManager.getService(),
+ context.getSystemService(UserManager.class));
+ }
+
+ @VisibleForTesting
+ AppHibernationService(@NonNull Context context, IPackageManager packageManager,
+ IActivityManager activityManager, UserManager userManager) {
+ super(context);
+ mContext = context;
+ mIPackageManager = packageManager;
+ mIActivityManager = activityManager;
+ mUserManager = userManager;
+
+ final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_USER_ADDED);
+ intentFilter.addAction(ACTION_USER_REMOVED);
+ userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ synchronized (mLock) {
+ final List<UserInfo> users = mUserManager.getUsers();
+ // TODO: Pull from persistent disk storage. For now, just make from scratch.
+ for (UserInfo user : users) {
+ addUserPackageStatesL(user.id);
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether a package is hibernating for a given user.
+ *
+ * @param packageName the package to check
+ * @param userId the user to check
+ * @return true if package is hibernating for the user
+ */
+ public boolean isHibernating(String packageName, int userId) {
+ userId = handleIncomingUser(userId, "isHibernating");
+ synchronized (mLock) {
+ final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
+ if (packageStates == null) {
+ throw new IllegalArgumentException("No user associated with user id " + userId);
+ }
+ final UserPackageState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+ return pkgState != null ? pkgState.hibernated : null;
+ }
+ }
+
+ /**
+ * Set whether the package is hibernating for the given user.
+ *
+ * @param packageName package to modify state
+ * @param userId user
+ * @param isHibernating new hibernation state
+ */
+ public void setHibernating(String packageName, int userId, boolean isHibernating) {
+ userId = handleIncomingUser(userId, "setHibernating");
+ synchronized (mLock) {
+ if (!mUserStates.contains(userId)) {
+ throw new IllegalArgumentException("No user associated with user id " + userId);
+ }
+ Map<String, UserPackageState> packageStates = mUserStates.get(userId);
+ UserPackageState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+
+ if (pkgState.hibernated == isHibernating) {
+ return;
+ }
+
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ if (isHibernating) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
+ mIActivityManager.forceStopPackage(packageName, userId);
+ mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
+ null /* observer */);
+ } else {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
+ mIPackageManager.setPackageStoppedState(packageName, false, userId);
+ }
+ pkgState.hibernated = isHibernating;
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed to hibernate due to manager not being available", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Binder.restoreCallingIdentity(caller);
+ }
+
+ // TODO: Support package level hibernation when package is hibernating for all users
+ }
+ }
+
+ /**
+ * Populates {@link #mUserStates} with the users installed packages. The caller should hold
+ * {@link #mLock}.
+ *
+ * @param userId user id to add installed packages for
+ */
+ private void addUserPackageStatesL(int userId) {
+ Map<String, UserPackageState> packages = new ArrayMap<>();
+ List<PackageInfo> packageList;
+ try {
+ packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager not available.", e);
+ }
+
+ for (PackageInfo pkg : packageList) {
+ packages.put(pkg.packageName, new UserPackageState());
+ }
+ mUserStates.put(userId, packages);
+ }
+
+ private void onUserAdded(int userId) {
+ synchronized (mLock) {
+ addUserPackageStatesL(userId);
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mUserStates.remove(userId);
+ }
+ }
+
+ private void onPackageAdded(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ mUserStates.get(userId).put(packageName, new UserPackageState());
+ }
+ }
+
+ private void onPackageRemoved(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ mUserStates.get(userId).remove(packageName);
+ }
+ }
+
+ /**
+ * Private helper method to get the real user id and enforce permission checks.
+ *
+ * @param userId user id to handle
+ * @param name name to use for exceptions
+ * @return real user id
+ */
+ private int handleIncomingUser(int userId, @NonNull String name) {
+ int callingUid = Binder.getCallingUid();
+ try {
+ return mIActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ false /* allowAll */, true /* requireFull */, name, null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
+
+ static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
+ final AppHibernationService mService;
+
+ AppHibernationServiceStub(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public boolean isHibernating(String packageName, int userId) {
+ return mService.isHibernating(packageName, userId);
+ }
+
+ @Override
+ public void setHibernating(String packageName, int userId, boolean isHibernating) {
+ mService.setHibernating(packageName, userId, isHibernating);
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+ new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
+ }
+
+ // Broadcast receiver for user and package add/removal events
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(userId);
+ }
+ if (ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
+ }
+ if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
+ // Package removal/add is part of an update, so no need to modify package state.
+ return;
+ }
+
+ if (ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, userId);
+ } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, userId);
+ }
+ }
+ }
+ };
+
+ /**
+ * Whether app hibernation is enabled on this device.
+ *
+ * @return true if enabled, false otherwise
+ */
+ public static boolean isAppHibernationEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_APP_HIBERNATION,
+ AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED,
+ false /* defaultValue */);
+ }
+
+ /**
+ * Data class that contains hibernation state info of a package for a user.
+ */
+ private static final class UserPackageState {
+ public boolean hibernated;
+ // TODO: Track whether hibernation is exempted by the user
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
new file mode 100644
index 000000000000..869885e28958
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command implementation for {@link AppHibernationService}.
+ */
+final class AppHibernationShellCommand extends ShellCommand {
+ private static final String USER_OPT = "--user";
+ private static final int SUCCESS = 0;
+ private static final int ERROR = -1;
+ private final AppHibernationService mService;
+
+ AppHibernationShellCommand(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "set-state":
+ return runSetState();
+ case "get-state":
+ return runGetState();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runSetState() {
+ int userId = parseUserOption();
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return ERROR;
+ }
+
+ String newStateRaw = getNextArgRequired();
+ if (newStateRaw == null) {
+ getErrPrintWriter().println("Error: No state to set specified");
+ return ERROR;
+ }
+ boolean newState = Boolean.parseBoolean(newStateRaw);
+
+ mService.setHibernating(pkg, userId, newState);
+ return SUCCESS;
+ }
+
+ private int runGetState() {
+ int userId = parseUserOption();
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: No package specified");
+ return ERROR;
+ }
+ boolean isHibernating = mService.isHibernating(pkg, userId);
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println(isHibernating);
+ return SUCCESS;
+ }
+
+ private int parseUserOption() {
+ String option = getNextOption();
+ if (TextUtils.equals(option, USER_OPT)) {
+ return UserHandle.parseUserArg(getNextArgRequired());
+ }
+ return UserHandle.USER_CURRENT;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("App hibernation (app_hibernation) commands: ");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" set-state [--user USER_ID] PACKAGE true|false");
+ pw.println(" Sets the hibernation state of the package to value specified");
+ pw.println("");
+ pw.println(" get-state [--user USER_ID] PACKAGE");
+ pw.println(" Gets the hibernation state of the package");
+ pw.println("");
+ }
+}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 9ba957ef27ae..e3757dfc6a59 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,8 +23,11 @@ import android.content.pm.ApplicationInfo;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.compat.config.Change;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.OverrideValue;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -253,6 +256,71 @@ public final class CompatChange extends CompatibilityChangeInfo {
return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
}
+ /**
+ * Checks whether a change has any package overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyPackageOverride() {
+ return mDeferredOverrides != null && !mDeferredOverrides.isEmpty();
+ }
+
+ /**
+ * Checks whether a change has any deferred overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyDeferredOverride() {
+ return mPackageOverrides != null && !mPackageOverrides.isEmpty();
+ }
+
+ void loadOverrides(ChangeOverrides changeOverrides) {
+ if (mDeferredOverrides == null) {
+ mDeferredOverrides = new HashMap<>();
+ }
+ mDeferredOverrides.clear();
+ for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
+ mDeferredOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+
+ if (mPackageOverrides == null) {
+ mPackageOverrides = new HashMap<>();
+ }
+ mPackageOverrides.clear();
+ for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
+ mPackageOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+ }
+
+ ChangeOverrides saveOverrides() {
+ if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) {
+ return null;
+ }
+ ChangeOverrides changeOverrides = new ChangeOverrides();
+ changeOverrides.setChangeId(getId());
+ ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred();
+ List<OverrideValue> deferredList = deferredOverrides.getOverrideValue();
+ if (mDeferredOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ deferredList.add(override);
+ }
+ }
+ changeOverrides.setDeferred(deferredOverrides);
+ ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
+ List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
+ if (mPackageOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mPackageOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ validatedList.add(override);
+ }
+ }
+ changeOverrides.setValidated(validatedOverrides);
+ return changeOverrides;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 69686a2e4678..6b77b9d4ce39 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -34,7 +34,10 @@ import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
-import com.android.server.compat.config.XmlParser;
+import com.android.server.compat.config.Config;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.Overrides;
+import com.android.server.compat.overrides.XmlWriter;
import com.android.server.pm.ApexManager;
import org.xmlpull.v1.XmlPullParserException;
@@ -60,11 +63,14 @@ import javax.xml.datatype.DatatypeConfigurationException;
final class CompatConfig {
private static final String TAG = "CompatConfig";
+ private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
+ private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator;
+ private File mOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -83,6 +89,8 @@ final class CompatConfig {
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
+ File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE);
+ config.initOverrides(overridesFile);
config.invalidateCache();
return config;
}
@@ -202,6 +210,17 @@ final class CompatConfig {
* @throws IllegalStateException if overriding is not allowed
*/
boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled);
+ saveOverrides();
+ invalidateCache();
+ return alreadyKnown;
+ }
+
+ /**
+ * Unsafe version of {@link #addOverride(long, String, boolean)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = true;
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -224,7 +243,6 @@ final class CompatConfig {
throw new IllegalStateException("Should only be able to override changes that "
+ "are allowed or can be deferred.");
}
- invalidateCache();
}
return alreadyKnown;
}
@@ -282,6 +300,17 @@ final class CompatConfig {
* @return {@code true} if an override existed;
*/
boolean removeOverride(long changeId, String packageName) {
+ boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
+ saveOverrides();
+ invalidateCache();
+ return overrideExists;
+ }
+
+ /**
+ * Unsafe version of {@link #removeOverride(long, String)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean removeOverrideUnsafe(long changeId, String packageName) {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
@@ -300,7 +329,6 @@ final class CompatConfig {
}
}
}
- invalidateCache();
return overrideExists;
}
@@ -315,12 +343,13 @@ final class CompatConfig {
void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
synchronized (mChanges) {
for (Long changeId : overrides.enabledChanges()) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
for (Long changeId : overrides.disabledChanges()) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -337,8 +366,9 @@ final class CompatConfig {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange change = mChanges.valueAt(i);
- removeOverride(change.getId(), packageName);
+ removeOverrideUnsafe(change.getId(), packageName);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -372,8 +402,10 @@ final class CompatConfig {
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -386,8 +418,10 @@ final class CompatConfig {
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -494,7 +528,8 @@ final class CompatConfig {
private void readConfig(File configFile) {
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
- for (Change change : XmlParser.read(in).getCompatChange()) {
+ Config config = com.android.server.compat.config.XmlParser.read(in);
+ for (Change change : config.getCompatChange()) {
Slog.d(TAG, "Adding: " + change.toString());
addChange(new CompatChange(change));
}
@@ -503,6 +538,65 @@ final class CompatConfig {
}
}
+ void initOverrides(File overridesFile) {
+ if (!overridesFile.exists()) {
+ mOverridesFile = overridesFile;
+ // There have not been any overrides added yet.
+ return;
+ }
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) {
+ Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in);
+ for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) {
+ long changeId = changeOverrides.getChangeId();
+ CompatChange compatChange = mChanges.get(changeId);
+ if (compatChange == null) {
+ Slog.w(TAG, "Change ID " + changeId + " not found. "
+ + "Skipping overrides for it.");
+ continue;
+ }
+ compatChange.loadOverrides(changeOverrides);
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
+ return;
+ }
+ mOverridesFile = overridesFile;
+ }
+
+ /**
+ * Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml
+ */
+ void saveOverrides() {
+ if (mOverridesFile == null) {
+ return;
+ }
+ synchronized (mChanges) {
+ // Create the file if it doesn't already exist
+ try {
+ mOverridesFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not create override config file: " + e.toString());
+ return;
+ }
+ try (PrintWriter out = new PrintWriter(mOverridesFile)) {
+ XmlWriter writer = new XmlWriter(out);
+ Overrides overrides = new Overrides();
+ List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+ for (int idx = 0; idx < mChanges.size(); ++idx) {
+ CompatChange c = mChanges.valueAt(idx);
+ ChangeOverrides changeOverrides = c.saveOverrides();
+ if (changeOverrides != null) {
+ changeOverridesList.add(changeOverrides);
+ }
+ }
+ XmlWriter.write(writer, overrides);
+ } catch (IOException e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ }
+
IOverrideValidator getOverrideValidator() {
return mOverrideValidator;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ab0360b0395a..b2824846008c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -329,7 +329,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
private final QosCallbackTracker mQosCallbackTracker;
public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
- LinkProperties lp, NetworkCapabilities nc, int score, Context context,
+ @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
int creatorUid, QosCallbackTracker qosCallbackTracker) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index a7be657ae7a3..5e6b9f39b40a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -686,7 +686,7 @@ public class NetworkDiagnostics {
mHostname = hostname;
mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{"
- + TextUtils.emptyIfNull(mHostname) + "}";
+ + (mHostname == null ? "" : mHostname) + "}";
}
private SSLSocket setupSSLSocket() throws IOException {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index fb1e8197ccff..8ce6746bc7cb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -70,6 +70,7 @@ import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -109,7 +110,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
@@ -1816,18 +1816,15 @@ public class Vpn {
}
/**
- * This method should only be called by ConnectivityService because it doesn't
- * have enough data to fill VpnInfo.primaryUnderlyingIface field.
+ * This method should not be called if underlying interfaces field is needed, because it doesn't
+ * have enough data to fill VpnInfo.underlyingIfaces field.
*/
public synchronized VpnInfo getVpnInfo() {
if (!isRunningLocked()) {
return null;
}
- VpnInfo info = new VpnInfo();
- info.ownerUid = mOwnerUID;
- info.vpnIface = mInterface;
- return info;
+ return new VpnInfo(mOwnerUID, mInterface, null);
}
public synchronized boolean appliesToUid(int uid) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index ab289ea6f081..f876e1ad4b88 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -901,7 +901,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@ServiceThreadOnly
void setArcStatus(boolean enabled) {
- // TODO(shubang): add tests
assertRunOnServiceThread();
HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8e50bb4885d8..5d1c4e6715f1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -621,7 +621,14 @@ public class HdmiControlService extends SystemService {
mWakeUpMessageReceived = false;
if (isTvDeviceEnabled()) {
- mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup());
+ boolean autoWakeupEnabled =
+ readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
+ boolean autoDeviceOffEnabled =
+ readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
+
+ mCecController.setOption(OptionKey.WAKEUP, autoWakeupEnabled);
+ tv().setAutoWakeup(autoWakeupEnabled);
+ tv().setAutoDeviceOff(autoDeviceOffEnabled);
}
int reason = -1;
switch (initiatedBy) {
diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff188dbd8..000000000000
--- a/services/core/java/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 81d07cc11527..c4225eda7d24 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -88,6 +88,7 @@ class LockSettingsStorage {
private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
+ private static final String REBOOT_ESCROW_SERVER_BLOB = "reboot.escrow.server.blob.key";
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
@@ -317,6 +318,22 @@ class LockSettingsStorage {
deleteFile(getRebootEscrowFile(userId));
}
+ public void writeRebootEscrowServerBlob(byte[] serverBlob) {
+ writeFile(getRebootEscrowServerBlob(), serverBlob);
+ }
+
+ public byte[] readRebootEscrowServerBlob() {
+ return readFile(getRebootEscrowServerBlob());
+ }
+
+ public boolean hasRebootEscrowServerBlob() {
+ return hasFile(getRebootEscrowServerBlob());
+ }
+
+ public void removeRebootEscrowServerBlob() {
+ deleteFile(getRebootEscrowServerBlob());
+ }
+
public boolean hasPassword(int userId) {
return hasFile(getLockPasswordFilename(userId));
}
@@ -445,6 +462,12 @@ class LockSettingsStorage {
return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
}
+ @VisibleForTesting
+ String getRebootEscrowServerBlob() {
+ // There is a single copy of server blob for all users.
+ return getLockCredentialFilePathForUser(UserHandle.USER_SYSTEM, REBOOT_ESCROW_SERVER_BLOB);
+ }
+
private String getLockCredentialFilePathForUser(int userId, String basename) {
String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
SYSTEM_DIRECTORY;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index fbec91576ca1..06962d414009 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -124,26 +124,28 @@ class RebootEscrowManager {
static class Injector {
protected Context mContext;
private final RebootEscrowKeyStoreManager mKeyStoreManager;
- private final RebootEscrowProviderInterface mRebootEscrowProvider;
+ private final LockSettingsStorage mStorage;
+ private RebootEscrowProviderInterface mRebootEscrowProvider;
- Injector(Context context) {
+ Injector(Context context, LockSettingsStorage storage) {
mContext = context;
+ mStorage = storage;
mKeyStoreManager = new RebootEscrowKeyStoreManager();
+ }
- RebootEscrowProviderInterface rebootEscrowProvider = null;
- // TODO(xunchang) add implementation for server based ror.
+ private RebootEscrowProviderInterface createRebootEscrowProvider() {
+ RebootEscrowProviderInterface rebootEscrowProvider;
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false)) {
- Slog.e(TAG, "Server based ror isn't implemented yet.");
+ rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
rebootEscrowProvider = new RebootEscrowProviderHalImpl();
}
- if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
- mRebootEscrowProvider = rebootEscrowProvider;
- } else {
- mRebootEscrowProvider = null;
+ if (rebootEscrowProvider.hasRebootEscrowSupport()) {
+ return rebootEscrowProvider;
}
+ return null;
}
public Context getContext() {
@@ -159,6 +161,12 @@ class RebootEscrowManager {
}
public RebootEscrowProviderInterface getRebootEscrowProvider() {
+ // Initialize for the provider lazily. Because the device_config and service
+ // implementation apps may change when system server is running.
+ if (mRebootEscrowProvider == null) {
+ mRebootEscrowProvider = createRebootEscrowProvider();
+ }
+
return mRebootEscrowProvider;
}
@@ -177,7 +185,7 @@ class RebootEscrowManager {
}
RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
- this(new Injector(context), callbacks, storage);
+ this(new Injector(context, storage), callbacks, storage);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
new file mode 100644
index 000000000000..ba1a680ba7fb
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
+
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of the {@link RebootEscrowProviderInterface} by communicating with server to
+ * encrypt & decrypt the blob.
+ */
+class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
+ private static final String TAG = "RebootEscrowProvider";
+
+ // Timeout for service binding
+ private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
+
+ /**
+ * Use the default lifetime of 10 minutes. The lifetime covers the following activities:
+ * Server wrap secret -> device reboot -> server unwrap blob.
+ */
+ private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000;
+
+ private final LockSettingsStorage mStorage;
+
+ private final Injector mInjector;
+
+ static class Injector {
+ private ResumeOnRebootServiceConnection mServiceConnection = null;
+
+ Injector(Context context) {
+ mServiceConnection = new ResumeOnRebootServiceProvider(context).getServiceConnection();
+ if (mServiceConnection == null) {
+ Slog.e(TAG, "Failed to resolve resume on reboot server service.");
+ }
+ }
+
+ Injector(ResumeOnRebootServiceConnection serviceConnection) {
+ mServiceConnection = serviceConnection;
+ }
+
+ @Nullable
+ private ResumeOnRebootServiceConnection getServiceConnection() {
+ return mServiceConnection;
+ }
+
+ long getServiceTimeoutInSeconds() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+ "server_based_service_timeout_in_seconds",
+ DEFAULT_SERVICE_TIMEOUT_IN_SECONDS);
+ }
+
+ long getServerBlobLifetimeInMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+ "server_based_server_blob_lifetime_in_millis",
+ DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS);
+ }
+ }
+
+ RebootEscrowProviderServerBasedImpl(Context context, LockSettingsStorage storage) {
+ this(storage, new Injector(context));
+ }
+
+ @VisibleForTesting
+ RebootEscrowProviderServerBasedImpl(LockSettingsStorage storage, Injector injector) {
+ mStorage = storage;
+ mInjector = injector;
+ }
+
+ @Override
+ public boolean hasRebootEscrowSupport() {
+ return mInjector.getServiceConnection() != null;
+ }
+
+ private byte[] unwrapServerBlob(byte[] serverBlob, SecretKey decryptionKey) throws
+ TimeoutException, RemoteException, IOException {
+ ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+ if (serviceConnection == null) {
+ Slog.w(TAG, "Had reboot escrow data for users, but resume on reboot server"
+ + " service is unavailable");
+ return null;
+ }
+
+ // Decrypt with k_k from the key store first.
+ byte[] decryptedBlob = AesEncryptionUtil.decrypt(decryptionKey, serverBlob);
+ if (decryptedBlob == null) {
+ Slog.w(TAG, "Decrypted server blob should not be null");
+ return null;
+ }
+
+ // Ask the server connection service to decrypt the inner layer, to get the reboot
+ // escrow key (k_s).
+ serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+ byte[] escrowKeyBytes = serviceConnection.unwrap(decryptedBlob,
+ mInjector.getServiceTimeoutInSeconds());
+ serviceConnection.unbindService();
+
+ return escrowKeyBytes;
+ }
+
+ @Override
+ public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
+ byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
+ // Delete the server blob in storage.
+ mStorage.removeRebootEscrowServerBlob();
+ if (serverBlob == null) {
+ Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
+ return null;
+ }
+
+ try {
+ byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
+ if (escrowKeyBytes == null) {
+ Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
+ return null;
+ } else if (escrowKeyBytes.length != 32) {
+ Slog.e(TAG, "Decrypted reboot escrow key has incorrect size "
+ + escrowKeyBytes.length);
+ return null;
+ }
+
+ return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
+ } catch (TimeoutException | RemoteException | IOException e) {
+ Slog.w(TAG, "Failed to decrypt the server blob ", e);
+ return null;
+ }
+ }
+
+ @Override
+ public void clearRebootEscrowKey() {
+ mStorage.removeRebootEscrowServerBlob();
+ }
+
+ private byte[] wrapEscrowKey(byte[] escrowKeyBytes, SecretKey encryptionKey) throws
+ TimeoutException, RemoteException, IOException {
+ ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+ if (serviceConnection == null) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key: resume on reboot server"
+ + " service is unavailable");
+ return null;
+ }
+
+ serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+ // Ask the server connection service to encrypt the reboot escrow key.
+ byte[] serverEncryptedBlob = serviceConnection.wrapBlob(escrowKeyBytes,
+ mInjector.getServerBlobLifetimeInMillis(), mInjector.getServiceTimeoutInSeconds());
+ serviceConnection.unbindService();
+
+ if (serverEncryptedBlob == null) {
+ Slog.w(TAG, "Server encrypted reboot escrow key cannot be null");
+ return null;
+ }
+
+ // Additionally wrap the server blob with a local key.
+ return AesEncryptionUtil.encrypt(encryptionKey, serverEncryptedBlob);
+ }
+
+ @Override
+ public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
+ mStorage.removeRebootEscrowServerBlob();
+ try {
+ byte[] wrappedBlob = wrapEscrowKey(escrowKey.getKeyBytes(), encryptionKey);
+ if (wrappedBlob == null) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key");
+ return false;
+ }
+ mStorage.writeRebootEscrowServerBlob(wrappedBlob);
+
+ Slog.i(TAG, "Reboot escrow key encrypted and stored.");
+ return true;
+ } catch (TimeoutException | RemoteException | IOException e) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key ", e);
+ }
+
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
new file mode 100644
index 000000000000..8399f54764e0
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.service.resumeonreboot.IResumeOnRebootService;
+import android.service.resumeonreboot.ResumeOnRebootService;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/** @hide */
+public class ResumeOnRebootServiceProvider {
+
+ private static final String PROVIDER_PACKAGE = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_OTA, "resume_on_reboot_service_package", "");
+ private static final String PROVIDER_REQUIRED_PERMISSION =
+ Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE;
+ private static final String TAG = "ResumeOnRebootServiceProvider";
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ public ResumeOnRebootServiceProvider(Context context) {
+ this(context, context.getPackageManager());
+ }
+
+ @VisibleForTesting
+ public ResumeOnRebootServiceProvider(Context context, PackageManager packageManager) {
+ this.mContext = context;
+ this.mPackageManager = packageManager;
+ }
+
+ @Nullable
+ private ServiceInfo resolveService() {
+ Intent intent = new Intent();
+ intent.setAction(ResumeOnRebootService.SERVICE_INTERFACE);
+ if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) {
+ intent.setPackage(PROVIDER_PACKAGE);
+ }
+
+ List<ResolveInfo> resolvedIntents =
+ mPackageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY);
+ for (ResolveInfo resolvedInfo : resolvedIntents) {
+ if (resolvedInfo.serviceInfo != null
+ && PROVIDER_REQUIRED_PERMISSION.equals(resolvedInfo.serviceInfo.permission)) {
+ return resolvedInfo.serviceInfo;
+ }
+ }
+ return null;
+ }
+
+ /** Creates a new {@link ResumeOnRebootServiceConnection} */
+ @Nullable
+ public ResumeOnRebootServiceConnection getServiceConnection() {
+ ServiceInfo serviceInfo = resolveService();
+ if (serviceInfo == null) {
+ return null;
+ }
+ return new ResumeOnRebootServiceConnection(mContext, serviceInfo.getComponentName());
+ }
+
+ /**
+ * Connection class used for contacting the registered {@link IResumeOnRebootService}
+ */
+ public static class ResumeOnRebootServiceConnection {
+
+ private static final String TAG = "ResumeOnRebootServiceConnection";
+ private final Context mContext;
+ private final ComponentName mComponentName;
+ private IResumeOnRebootService mBinder;
+
+ private ResumeOnRebootServiceConnection(Context context,
+ @NonNull ComponentName componentName) {
+ mContext = context;
+ mComponentName = componentName;
+ }
+
+ /** Unbind from the service */
+ public void unbindService() {
+ mContext.unbindService(new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinder = null;
+
+ }
+ });
+ }
+
+ /** Bind to the service */
+ public void bindToService(long timeOut) throws TimeoutException {
+ if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+ CountDownLatch connectionLatch = new CountDownLatch(1);
+ Intent intent = new Intent();
+ intent.setComponent(mComponentName);
+ final boolean success = mContext.bindServiceAsUser(intent, new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mBinder = IResumeOnRebootService.Stub.asInterface(service);
+ connectionLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ },
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ BackgroundThread.getHandler(), UserHandle.SYSTEM);
+
+ if (!success) {
+ Slog.e(TAG, "Binding: " + mComponentName + " u" + UserHandle.SYSTEM
+ + " failed.");
+ return;
+ }
+ waitForLatch(connectionLatch, "serviceConnection", timeOut);
+ }
+ }
+
+ /** Wrap opaque blob */
+ public byte[] wrapBlob(byte[] unwrappedBlob, long lifeTimeInMillis,
+ long timeOutInMillis)
+ throws RemoteException, TimeoutException, IOException {
+ if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+ throw new RemoteException("Service not bound");
+ }
+ CountDownLatch binderLatch = new CountDownLatch(1);
+ ResumeOnRebootServiceCallback
+ resultCallback =
+ new ResumeOnRebootServiceCallback(
+ binderLatch);
+ mBinder.wrapSecret(unwrappedBlob, lifeTimeInMillis, new RemoteCallback(resultCallback));
+ waitForLatch(binderLatch, "wrapSecret", timeOutInMillis);
+ if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) {
+ throwTypedException(resultCallback.getResult().getParcelable(
+ ResumeOnRebootService.EXCEPTION_KEY));
+ }
+ return resultCallback.mResult.getByteArray(ResumeOnRebootService.WRAPPED_BLOB_KEY);
+ }
+
+ /** Unwrap wrapped blob */
+ public byte[] unwrap(byte[] wrappedBlob, long timeOut)
+ throws RemoteException, TimeoutException, IOException {
+ if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+ throw new RemoteException("Service not bound");
+ }
+ CountDownLatch binderLatch = new CountDownLatch(1);
+ ResumeOnRebootServiceCallback
+ resultCallback =
+ new ResumeOnRebootServiceCallback(
+ binderLatch);
+ mBinder.unwrap(wrappedBlob, new RemoteCallback(resultCallback));
+ waitForLatch(binderLatch, "unWrapSecret", timeOut);
+ if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) {
+ throwTypedException(resultCallback.getResult().getParcelable(
+ ResumeOnRebootService.EXCEPTION_KEY));
+ }
+ return resultCallback.getResult().getByteArray(
+ ResumeOnRebootService.UNWRAPPED_BLOB_KEY);
+ }
+
+ private void throwTypedException(
+ ParcelableException exception)
+ throws IOException {
+ if (exception.getCause() instanceof IOException) {
+ exception.maybeRethrow(IOException.class);
+ } else if (exception.getCause() instanceof IllegalStateException) {
+ exception.maybeRethrow(IllegalStateException.class);
+ } else {
+ // This should not happen. Wrap the cause in IllegalStateException so that it
+ // doesn't disrupt the exception handling
+ throw new IllegalStateException(exception.getCause());
+ }
+ }
+
+ private void waitForLatch(CountDownLatch latch, String reason, long timeOut)
+ throws TimeoutException {
+ try {
+ if (!latch.await(timeOut, TimeUnit.SECONDS)) {
+ throw new TimeoutException("Latch wait for " + reason + " elapsed");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("Latch wait for " + reason + " interrupted");
+ }
+ }
+ }
+
+ private static class ResumeOnRebootServiceCallback implements
+ RemoteCallback.OnResultListener {
+
+ private final CountDownLatch mResultLatch;
+ private Bundle mResult;
+
+ private ResumeOnRebootServiceCallback(CountDownLatch resultLatch) {
+ this.mResultLatch = resultLatch;
+ }
+
+ @Override
+ public void onResult(@Nullable Bundle result) {
+ this.mResult = result;
+ mResultLatch.countDown();
+ }
+
+ private Bundle getResult() {
+ return mResult;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e9868fde3059..4faa7903c630 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -27,6 +27,7 @@ import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
import android.annotation.Nullable;
import android.net.INetd;
import android.net.NetworkStats;
+import android.net.VpnInfo;
import android.net.util.NetdService;
import android.os.RemoteException;
import android.os.StrictMode;
@@ -34,7 +35,6 @@ import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 81a6641de8a4..4be7b483af16 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -105,6 +105,7 @@ import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
@@ -143,7 +144,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 06706cd06e11..0ffc1ed9e90c 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -184,7 +184,7 @@ public class ModuleInfoProvider {
List<PackageInfo> allPackages;
try {
allPackages = mPackageManager.getInstalledPackages(
- flags | PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM).getList();
+ flags | PackageManager.MATCH_APEX, UserHandle.getCallingUserId()).getList();
} catch (RemoteException e) {
Slog.w(TAG, "Unable to retrieve all package names", e);
return Collections.emptyList();
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index e1feb5aab869..6427ae2dc13c 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -24,6 +24,9 @@ import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.ParcelUuid;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
import java.util.Objects;
/**
@@ -72,7 +75,8 @@ public class UnderlyingNetworkTracker extends Handler {
@NonNull public final LinkProperties linkProperties;
public final boolean blocked;
- private UnderlyingNetworkRecord(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 4e0c0c54923b..8805fa2f4dbb 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -24,7 +24,6 @@ import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
@@ -36,8 +35,6 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.RouteInfo;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -54,7 +51,6 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
-import android.telephony.TelephonyManager;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -126,7 +122,9 @@ public class VcnGatewayConnection extends StateMachine {
private static final int TOKEN_ALL = Integer.MIN_VALUE;
private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
- private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int TEARDOWN_TIMEOUT_SECONDS = 5;
private interface EventInfo {}
@@ -360,11 +358,25 @@ public class VcnGatewayConnection extends StateMachine {
*/
private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
- @NonNull private final DisconnectedState mDisconnectedState = new DisconnectedState();
- @NonNull private final DisconnectingState mDisconnectingState = new DisconnectingState();
- @NonNull private final ConnectingState mConnectingState = new ConnectingState();
- @NonNull private final ConnectedState mConnectedState = new ConnectedState();
- @NonNull private final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final DisconnectedState mDisconnectedState = new DisconnectedState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final DisconnectingState mDisconnectingState = new DisconnectingState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final ConnectingState mConnectingState = new ConnectingState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final ConnectedState mConnectedState = new ConnectedState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@@ -403,13 +415,6 @@ public class VcnGatewayConnection extends StateMachine {
private int mCurrentToken = -1;
/**
- * The next usable token.
- *
- * <p>A new token MUST be used for all new IKE sessions.
- */
- private int mNextToken = 0;
-
- /**
* The number of unsuccessful attempts since the last successful connection.
*
* <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
@@ -430,7 +435,7 @@ public class VcnGatewayConnection extends StateMachine {
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
* Migrating states, null otherwise.
*/
- private IkeSession mIkeSession;
+ private VcnIkeSession mIkeSession;
/**
* The last known child configuration.
@@ -455,7 +460,8 @@ public class VcnGatewayConnection extends StateMachine {
this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
}
- private VcnGatewayConnection(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnGatewayConnectionConfig connectionConfig,
@@ -508,7 +514,6 @@ public class VcnGatewayConnection extends StateMachine {
EVENT_DISCONNECT_REQUESTED,
TOKEN_ALL,
new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
- quit();
// TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
// is also called asynchronously when a NetworkAgent becomes unwanted
@@ -654,7 +659,7 @@ public class VcnGatewayConnection extends StateMachine {
protected void teardownNetwork() {
if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkInfo(buildNetworkInfo(false /* isConnected */));
+ mNetworkAgent.unregister();
mNetworkAgent = null;
}
}
@@ -667,6 +672,8 @@ public class VcnGatewayConnection extends StateMachine {
protected void handleDisconnectRequested(String msg) {
Slog.v(TAG, "Tearing down. Cause: " + msg);
+ mIsRunning = false;
+
teardownNetwork();
teardownIke();
@@ -697,7 +704,37 @@ public class VcnGatewayConnection extends StateMachine {
*/
private class DisconnectedState extends BaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() {
+ if (!mIsRunning) {
+ quitNow(); // Ignore all queued events; cleanup is complete.
+ }
+
+ if (mIkeSession != null || mNetworkAgent != null) {
+ Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState");
+ }
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ // First network found; start tunnel
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (mUnderlying != null) {
+ transitionTo(mConnectingState);
+ }
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ mIsRunning = false;
+
+ quitNow();
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
private abstract class ActiveBaseState extends BaseState {
@@ -732,7 +769,70 @@ public class VcnGatewayConnection extends StateMachine {
*/
private class DisconnectingState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ if (mIkeSession == null) {
+ Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
+ sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+ return;
+ }
+
+ // If underlying network has already been lost, save some time and just kill the session
+ if (mUnderlying == null) {
+ // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
+ mIkeSession.kill();
+ return;
+ }
+
+ sendMessageDelayed(
+ EVENT_TEARDOWN_TIMEOUT_EXPIRED,
+ mCurrentToken,
+ TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If we received a new underlying network, continue.
+ if (mUnderlying != null) {
+ break;
+ }
+
+ // Fallthrough; no network exists to send IKE close session requests.
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
+ mIkeSession.kill();
+
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ teardownNetwork();
+
+ String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
+ if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // Will trigger EVENT_SESSION_CLOSED immediately.
+ mIkeSession.kill();
+ break;
+ }
+
+ // Otherwise we are already in the process of shutting down.
+ break;
+ case EVENT_SESSION_CLOSED:
+ mIkeSession = null;
+
+ if (mIsRunning && mUnderlying != null) {
+ transitionTo(mRetryTimeoutState);
+ } else {
+ teardownNetwork();
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
/**
@@ -769,20 +869,6 @@ public class VcnGatewayConnection extends StateMachine {
protected void processStateMsg(Message msg) {}
}
- // TODO: Remove this when migrating to new NetworkAgent API
- private static NetworkInfo buildNetworkInfo(boolean isConnected) {
- NetworkInfo info =
- new NetworkInfo(
- ConnectivityManager.TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- "MOBILE",
- "VCN");
- info.setDetailedState(
- isConnected ? DetailedState.CONNECTED : DetailedState.DISCONNECTED, null, null);
-
- return info;
- }
-
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
@@ -893,7 +979,64 @@ public class VcnGatewayConnection extends StateMachine {
}
}
- /** External dependencies used by VcnGatewayConnection, for injection in tests. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() {
+ return mUnderlyingNetworkTrackerCallback;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkRecord getUnderlyingNetwork() {
+ return mUnderlying;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) {
+ mUnderlying = record;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ boolean isRunning() {
+ return mIsRunning;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIsRunning(boolean isRunning) {
+ mIsRunning = isRunning;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession getIkeSession() {
+ return mIkeSession;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIkeSession(@Nullable VcnIkeSession session) {
+ mIkeSession = session;
+ }
+
+ private IkeSessionParams buildIkeParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ private ChildSessionParams buildChildParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession buildIkeSession() {
+ final int token = ++mCurrentToken;
+
+ return mDeps.newIkeSession(
+ mVcnContext,
+ buildIkeParams(),
+ buildChildParams(),
+ new IkeSessionCallbackImpl(token),
+ new ChildSessionCallbackImpl(token));
+ }
+
+ /** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
/** Builds a new UnderlyingNetworkTracker. */
@@ -905,19 +1048,67 @@ public class VcnGatewayConnection extends StateMachine {
}
/** Builds a new IkeSession. */
- public IkeSession newIkeSession(
+ public VcnIkeSession newIkeSession(
VcnContext vcnContext,
IkeSessionParams ikeSessionParams,
ChildSessionParams childSessionParams,
IkeSessionCallback ikeSessionCallback,
ChildSessionCallback childSessionCallback) {
- return new IkeSession(
- vcnContext.getContext(),
+ return new VcnIkeSession(
+ vcnContext,
ikeSessionParams,
childSessionParams,
- new HandlerExecutor(new Handler(vcnContext.getLooper())),
ikeSessionCallback,
childSessionCallback);
}
}
+
+ /** Proxy implementation of IKE session, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnIkeSession {
+ private final IkeSession mImpl;
+
+ public VcnIkeSession(
+ VcnContext vcnContext,
+ IkeSessionParams ikeSessionParams,
+ ChildSessionParams childSessionParams,
+ IkeSessionCallback ikeSessionCallback,
+ ChildSessionCallback childSessionCallback) {
+ mImpl =
+ new IkeSession(
+ vcnContext.getContext(),
+ ikeSessionParams,
+ childSessionParams,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ ikeSessionCallback,
+ childSessionCallback);
+ }
+
+ /** Creates a new IKE Child session. */
+ public void openChildSession(
+ @NonNull ChildSessionParams childSessionParams,
+ @NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.openChildSession(childSessionParams, childSessionCallback);
+ }
+
+ /** Closes an IKE session as identified by the ChildSessionCallback. */
+ public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.closeChildSession(childSessionCallback);
+ }
+
+ /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
+ public void close() {
+ mImpl.close();
+ }
+
+ /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
+ public void kill() {
+ mImpl.kill();
+ }
+
+ /** Sets the underlying network used by the IkeSession. */
+ public void setNetwork(@NonNull Network network) {
+ mImpl.setNetwork(network);
+ }
+ }
}
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index b7d6424450f3..3690afc1bc41 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -8,11 +8,19 @@ xsd_config {
xsd_config {
name: "platform-compat-config",
- srcs: ["platform-compat-config.xsd"],
- api_dir: "platform-compat-schema",
+ srcs: ["platform-compat/config/platform-compat-config.xsd"],
+ api_dir: "platform-compat/config/schema",
package_name: "com.android.server.compat.config",
}
+xsd_config {
+ name: "platform-compat-overrides",
+ srcs: ["platform-compat/overrides/platform-compat-overrides.xsd"],
+ api_dir: "platform-compat/overrides/schema",
+ package_name: "com.android.server.compat.overrides",
+ gen_writer: true,
+}
+
xsd_config {
name: "display-device-config",
diff --git a/services/core/xsd/platform-compat-schema/OWNERS b/services/core/xsd/platform-compat/OWNERS
index f8c3520e9fa8..f8c3520e9fa8 100644
--- a/services/core/xsd/platform-compat-schema/OWNERS
+++ b/services/core/xsd/platform-compat/OWNERS
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat/config/platform-compat-config.xsd
index a62e2c385766..a62e2c385766 100644
--- a/services/core/xsd/platform-compat-config.xsd
+++ b/services/core/xsd/platform-compat/config/platform-compat-config.xsd
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat/config/schema/current.txt
index fb8bbefd8374..fb8bbefd8374 100644
--- a/services/core/xsd/platform-compat-schema/current.txt
+++ b/services/core/xsd/platform-compat/config/schema/current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/config/schema/last_current.txt
index e69de29bb2d1..e69de29bb2d1 100644
--- a/services/core/xsd/platform-compat-schema/last_current.txt
+++ b/services/core/xsd/platform-compat/config/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/config/schema/last_removed.txt
index e69de29bb2d1..e69de29bb2d1 100644
--- a/services/core/xsd/platform-compat-schema/last_removed.txt
+++ b/services/core/xsd/platform-compat/config/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/config/schema/removed.txt
index d802177e249b..d802177e249b 100644
--- a/services/core/xsd/platform-compat-schema/removed.txt
+++ b/services/core/xsd/platform-compat/config/schema/removed.txt
diff --git a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
new file mode 100644
index 000000000000..e27e1b8ca89d
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- This defines the format of the XML file used to store compat config overrides in
+ ~ /data/misc/appcompat/compat_framework_overrides.xml
+-->
+<xs:schema version="2.0" elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+
+ <xs:complexType name="override-value">
+ <xs:attribute type="xs:string" name="packageName" use="required" />
+ <xs:attribute type="xs:boolean" name="enabled" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="change-overrides">
+ <xs:attribute type="xs:long" name="changeId" use="required"/>
+ <xs:element name="validated">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="deferred">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:complexType>
+
+ <xs:element name="overrides">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="change-overrides" type="change-overrides" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/services/core/xsd/platform-compat/overrides/schema/current.txt b/services/core/xsd/platform-compat/overrides/schema/current.txt
new file mode 100644
index 000000000000..08b82072747b
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/current.txt
@@ -0,0 +1,51 @@
+// Signature format: 2.0
+package com.android.server.compat.overrides {
+
+ public class ChangeOverrides {
+ ctor public ChangeOverrides();
+ method public long getChangeId();
+ method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
+ method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
+ method public void setChangeId(long);
+ method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
+ method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
+ }
+
+ public static class ChangeOverrides.Deferred {
+ ctor public ChangeOverrides.Deferred();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public static class ChangeOverrides.Validated {
+ ctor public ChangeOverrides.Validated();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public class OverrideValue {
+ ctor public OverrideValue();
+ method public boolean getEnabled();
+ method public String getPackageName();
+ method public void setEnabled(boolean);
+ method public void setPackageName(String);
+ }
+
+ public class Overrides {
+ ctor public Overrides();
+ method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public class XmlWriter implements java.io.Closeable {
+ ctor public XmlWriter(java.io.PrintWriter);
+ method public void close();
+ method public static void write(com.android.server.compat.overrides.XmlWriter, com.android.server.compat.overrides.Overrides) throws java.io.IOException;
+ }
+
+}
+
diff --git a/services/core/xsd/platform-compat/overrides/schema/last_current.txt b/services/core/xsd/platform-compat/overrides/schema/last_current.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat/overrides/schema/last_removed.txt b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat/overrides/schema/removed.txt b/services/core/xsd/platform-compat/overrides/schema/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 516c64217177..6089a52ae0bb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.apphibernation.AppHibernationService;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -220,6 +221,8 @@ public final class SystemServer {
"com.android.server.appwidget.AppWidgetService";
private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.voiceinteraction.VoiceInteractionManagerService";
+ private static final String APP_HIBERNATION_SERVICE_CLASS =
+ "com.android.server.apphibernation.AppHibernationService";
private static final String PRINT_MANAGER_SERVICE_CLASS =
"com.android.server.print.PrintManagerService";
private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
@@ -457,7 +460,7 @@ public final class SystemServer {
}
try {
- Thread.sleep(checkInterval);
+ Thread.sleep(checkInterval * 1000);
} catch (InterruptedException ex) {
continue;
}
@@ -1863,6 +1866,12 @@ public final class SystemServer {
mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ if (AppHibernationService.isAppHibernationEnabled()) {
+ t.traceBegin("StartAppHibernationService");
+ mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
t.traceBegin("StartGestureLauncher");
mSystemServiceManager.startService(GestureLauncherService.class);
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
new file mode 100644
index 000000000000..d0370b6c25e9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.returnsArgAt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.UserManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link com.android.server.apphibernation.AppHibernationService}
+ */
+@SmallTest
+public final class AppHibernationServiceTest {
+ private static final String PACKAGE_SCHEME = "package";
+ private static final String PACKAGE_NAME_1 = "package1";
+ private static final String PACKAGE_NAME_2 = "package2";
+ private static final int USER_ID_1 = 1;
+ private static final int USER_ID_2 = 2;
+
+ private AppHibernationService mAppHibernationService;
+ private BroadcastReceiver mBroadcastReceiver;
+ @Mock
+ private Context mContext;
+ @Mock
+ private IPackageManager mIPackageManager;
+ @Mock
+ private IActivityManager mIActivityManager;
+ @Mock
+ private UserManager mUserManager;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+
+ mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
+ mIActivityManager, mUserManager);
+
+ verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
+ mBroadcastReceiver = mReceiverCaptor.getValue();
+
+ List<UserInfo> userList = new ArrayList<>();
+ userList.add(new UserInfo(USER_ID_1, "user 1", 0 /* flags */));
+ doReturn(userList).when(mUserManager).getUsers();
+
+ List<PackageInfo> userPackages = new ArrayList<>();
+ userPackages.add(makePackageInfo(PACKAGE_NAME_1));
+
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(anyInt(), eq(USER_ID_1));
+
+ doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
+ anyInt(), anyBoolean(), anyBoolean(), any(), any());
+
+ mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ }
+
+ @Test
+ public void testSetHibernating_packageIsHibernating() {
+ // WHEN we hibernate a package for a user
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // THEN the package is marked hibernating for the user
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernating_newPackageAdded_packageIsHibernating() {
+ // WHEN a new package is added and it is hibernated
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ mAppHibernationService.setHibernating(PACKAGE_NAME_2, USER_ID_1, true);
+
+ // THEN the new package is hibernated
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_2, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernating_newUserAdded_packageIsHibernating() throws RemoteException {
+ // WHEN a new user is added and a package from the user is hibernated
+ List<PackageInfo> userPackages = new ArrayList<>();
+ userPackages.add(makePackageInfo(PACKAGE_NAME_1));
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(anyInt(), eq(USER_ID_2));
+ Intent intent = new Intent(Intent.ACTION_USER_ADDED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_2, true);
+
+ // THEN the new user's package is hibernated
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_2));
+ }
+
+ @Test
+ public void testIsHibernating_packageReplaced_stillReturnsHibernating() {
+ // GIVEN a package is currently hibernated
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN the package is removed but marked as replacing
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ // THEN the package is still hibernating
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ private static PackageInfo makePackageInfo(String packageName) {
+ PackageInfo pkg = new PackageInfo();
+ pkg.packageName = packageName;
+ return pkg;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index ac8dc341999a..a53ff9bc7fdc 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -44,6 +44,8 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@@ -69,6 +71,10 @@ public class CompatConfigTest {
os.close();
}
+ private String readFile(File file) throws IOException {
+ return new String(Files.readAllBytes(Paths.get(file.toURI())));
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -499,4 +505,86 @@ public class CompatConfigTest {
assertThat(compatConfig.isChangeEnabled(1236L,
ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
}
+
+ @Test
+ public void testSaveOverrides() throws Exception {
+ File overridesFile = new File(createTempDir(), "overrides.xml");
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build());
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ compatConfig.addOverride(1L, "foo.bar", true);
+ compatConfig.addOverride(2L, "bar.baz", false);
+
+ assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<overrides>\n"
+ + " <change-overrides changeId=\"1\">\n"
+ + " <validated>\n"
+ + " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
+ + " </override-value>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + " <change-overrides changeId=\"2\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " <override-value packageName=\"bar.baz\" enabled=\"false\">\n"
+ + " </override-value>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + "</overrides>\n");
+ }
+
+ @Test
+ public void testLoadOverrides() throws Exception {
+ File tempDir = createTempDir();
+ File overridesFile = new File(tempDir, "overrides.xml");
+ // Change 1 is enabled for foo.bar (validated)
+ // Change 2 is disabled for bar.baz (deferred)
+ String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<overrides>"
+ + "<change-overrides changeId=\"1\">"
+ + "<deferred/>"
+ + "<validated>"
+ + "<override-value packageName=\"foo.bar\" enabled=\"true\"/>"
+ + "</validated>"
+ + "</change-overrides>"
+ + "<change-overrides changeId=\"2\">"
+ + "<deferred>"
+ + "<override-value packageName=\"bar.baz\" enabled=\"false\"/>"
+ + "</deferred>"
+ + "<validated/>"
+ + "</change-overrides>"
+ + "</overrides>";
+ writeToFile(tempDir, "overrides.xml", xmlData);
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
+ assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index dd98c4b09b78..09dd3e3e50db 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -538,6 +538,15 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ public void setArcStatus() {
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
+
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ @Test
@Ignore("b/151150320")
public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
HdmiCecMessage message =
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff188dbd8..000000000000
--- a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 1581d9ac1811..691d174f55f8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -82,6 +82,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
}
@Override
+ String getRebootEscrowServerBlob() {
+ return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath();
+ }
+
+ @Override
protected File getSyntheticPasswordDirectoryForUser(int userId) {
return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser(
userId).getAbsolutePath());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index f74e45b6e59b..a4ba4c86a8fd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
@@ -52,6 +53,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
import org.junit.Before;
import org.junit.Test;
@@ -92,6 +94,7 @@ public class RebootEscrowManagerTests {
private UserManager mUserManager;
private RebootEscrowManager.Callbacks mCallbacks;
private IRebootEscrow mRebootEscrow;
+ private ResumeOnRebootServiceConnection mServiceConnection;
private RebootEscrowKeyStoreManager mKeyStoreManager;
LockSettingsStorageTestable mStorage;
@@ -108,6 +111,7 @@ public class RebootEscrowManagerTests {
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
+ private final ResumeOnRebootServiceConnection mServiceConnection;
private final RebootEscrowProviderInterface mRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
@@ -116,10 +120,11 @@ public class RebootEscrowManagerTests {
MockInjector(Context context, UserManager userManager,
IRebootEscrow rebootEscrow,
RebootEscrowKeyStoreManager keyStoreManager,
+ LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
- super(context);
+ super(context, storage);
mRebootEscrow = rebootEscrow;
-
+ mServiceConnection = null;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@Override
@@ -133,6 +138,22 @@ public class RebootEscrowManagerTests {
mInjected = injected;
}
+ MockInjector(Context context, UserManager userManager,
+ ResumeOnRebootServiceConnection serviceConnection,
+ RebootEscrowKeyStoreManager keyStoreManager,
+ LockSettingsStorageTestable storage,
+ MockableRebootEscrowInjected injected) {
+ super(context, storage);
+ mServiceConnection = serviceConnection;
+ mRebootEscrow = null;
+ RebootEscrowProviderServerBasedImpl.Injector injector =
+ new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
+ mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
+ mUserManager = userManager;
+ mKeyStoreManager = keyStoreManager;
+ mInjected = injected;
+ }
+
@Override
public UserManager getUserManager() {
return mUserManager;
@@ -165,6 +186,7 @@ public class RebootEscrowManagerTests {
mUserManager = mock(UserManager.class);
mCallbacks = mock(RebootEscrowManager.Callbacks.class);
mRebootEscrow = mock(IRebootEscrow.class);
+ mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class);
mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES");
@@ -186,7 +208,12 @@ public class RebootEscrowManagerTests {
when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
mInjected = mock(MockableRebootEscrowInjected.class);
mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
- mKeyStoreManager, mInjected), mCallbacks, mStorage);
+ mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
+ }
+
+ private void setServerBasedRebootEscrowProvider() throws Exception {
+ mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager,
+ mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
}
@Test
@@ -202,6 +229,19 @@ public class RebootEscrowManagerTests {
}
@Test
+ public void prepareRebootEscrowServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
@@ -246,6 +286,28 @@ public class RebootEscrowManagerTests {
}
@Test
+ public void armServiceServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
public void armService_HalFailure_NonFatal() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
@@ -346,6 +408,40 @@ public class RebootEscrowManagerTests {
}
@Test
+ public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+
+ when(mInjected.getBootCount()).thenReturn(0);
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ // Use x -> x for both wrap & unwrap functions.
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // pretend reboot happens here
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+
+ when(mServiceConnection.unwrap(any(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ mService.loadRebootEscrowDataIfAvailable();
+ verify(mServiceConnection).unwrap(any(), anyLong());
+ assertTrue(metricsSuccessCaptor.getValue());
+ verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+ }
+
+ @Test
public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
when(mInjected.getBootCount()).thenReturn(0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
new file mode 100644
index 000000000000..bc1e025dd99f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowProviderServerBasedImplTests {
+ private SecretKey mKeyStoreEncryptionKey;
+ private RebootEscrowKey mRebootEscrowKey;
+ private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection;
+ private LockSettingsStorageTestable mStorage;
+ private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider;
+ private Answer<byte[]> mFakeEncryption;
+
+ private static final byte[] TEST_AES_KEY = new byte[] {
+ 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+ 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+ 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+ 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
+ mRebootEscrowKey = RebootEscrowKey.generate();
+ mServiceConnection = mock(
+ ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class);
+
+ Context context = new ContextWrapper(InstrumentationRegistry.getContext());
+ mStorage = new LockSettingsStorageTestable(context,
+ new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
+ mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage,
+ new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection));
+
+ mFakeEncryption = invocation -> {
+ byte[] secret = invocation.getArgument(0);
+ for (int i = 0; i < secret.length; i++) {
+ secret[i] = (byte) (secret[i] ^ 0xf);
+ }
+ return secret;
+ };
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_loopback_success() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption);
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes()));
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(
+ invocation -> {
+ byte[] secret = invocation.getArgument(0);
+ for (int i = 0; i < secret.length; i++) {
+ secret[i] = (byte) (secret[i] ^ 0xe);
+ }
+ return secret;
+ }
+ );
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // Expect to get wrong key bytes
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes());
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // Expect to get null key bytes when the server service fails to unwrap the blob.
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertNull(ks);
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java
new file mode 100644
index 000000000000..b9af82b64c02
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.service.resumeonreboot.ResumeOnRebootService;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class ResumeOnRebootServiceProviderTests {
+
+ @Mock
+ Context mMockContext;
+ @Mock
+ PackageManager mMockPackageManager;
+ @Mock
+ ResolveInfo mMockResolvedInfo;
+ @Mock
+ ServiceInfo mMockServiceInfo;
+ @Mock
+ ComponentName mMockComponentName;
+ @Captor
+ ArgumentCaptor<Intent> mIntentArgumentCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getUserId()).thenReturn(0);
+ when(mMockResolvedInfo.serviceInfo).thenReturn(mMockServiceInfo);
+ when(mMockServiceInfo.getComponentName()).thenReturn(mMockComponentName);
+ }
+
+ @Test
+ public void noServiceFound() throws Exception {
+ when(mMockPackageManager.queryIntentServices(any(),
+ eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn(
+ null);
+ assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+ mMockPackageManager).getServiceConnection()).isNull();
+ }
+
+ @Test
+ public void serviceNotGuardedWithPermission() throws Exception {
+ ArrayList<ResolveInfo> resultList = new ArrayList<>();
+ when(mMockServiceInfo.permission).thenReturn("");
+ resultList.add(mMockResolvedInfo);
+ when(mMockPackageManager.queryIntentServices(any(), any())).thenReturn(
+ resultList);
+ assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+ mMockPackageManager).getServiceConnection()).isNull();
+ }
+
+ @Test
+ public void serviceResolved() throws Exception {
+ ArrayList<ResolveInfo> resultList = new ArrayList<>();
+ resultList.add(mMockResolvedInfo);
+ when(mMockServiceInfo.permission).thenReturn(
+ Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE);
+ when(mMockPackageManager.queryIntentServices(any(),
+ eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn(
+ resultList);
+
+ assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+ mMockPackageManager).getServiceConnection()).isNotNull();
+
+ verify(mMockPackageManager).queryIntentServices(mIntentArgumentCaptor.capture(),
+ eq(PackageManager.MATCH_SYSTEM_ONLY));
+ assertThat(mIntentArgumentCaptor.getValue().getAction()).isEqualTo(
+ ResumeOnRebootService.SERVICE_INTERFACE);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 4db7ce2e6ef5..df19aeb13707 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2051,6 +2051,7 @@ public class NetworkPolicyManagerServiceTest {
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.setSSID(TEST_SSID);
return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
}
diff --git a/services/tests/shortcutmanagerutils/OWNERS b/services/tests/shortcutmanagerutils/OWNERS
new file mode 100644
index 000000000000..d825dfd7cf00
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e3eb0b582b1c..3a9896a5a91d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2774,6 +2774,30 @@ public class CarrierConfigManager {
public static final String IMSI_KEY_DOWNLOAD_URL_STRING = "imsi_key_download_url_string";
/**
+ * String representation of a carrier's public key used for IMSI encryption for ePDG. If this
+ * is provided, the device will use it as a fallback when no key exists on device, but the key
+ * download will still initiate.
+ * Example string:
+ * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234
+ * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----"
+ * @hide
+ */
+ public static final String IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING =
+ "imsi_carrier_public_key_epdg_string";
+
+ /**
+ * String representation of a carrier's public key used for IMSI encryption for WLAN. If this
+ * is provided, the device will use it as a fallback when no key exists on device, but the key
+ * download will still initiate.
+ * Example string:
+ * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234
+ * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----"
+ * @hide
+ */
+ public static final String IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING =
+ "imsi_carrier_public_key_wlan_string";
+
+ /**
* Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask.
* 0 indicates that neither EPDG or WLAN is enabled.
* 1 indicates that key type TelephonyManager#KEY_TYPE_EPDG is enabled.
@@ -4089,6 +4113,12 @@ public class CarrierConfigManager {
"default_rtt_mode_int";
/**
+ * Indicates whether RTT is supported while roaming.
+ */
+ public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL =
+ "rtt_supported_while_roaming_bool";
+
+ /**
* Indicates if auto-configuration server is used for the RCS config
* Reference: GSMA RCC.14
*/
@@ -4445,6 +4475,8 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0);
sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
+ sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING, null);
+ sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING, null);
sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL,
false);
sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
@@ -4453,6 +4485,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
+ sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index d502da9fb9ec..99a77ae5d133 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -915,6 +915,8 @@ public final class DataFailCause {
public static final int IPV6_PREFIX_UNAVAILABLE = 0x8CA;
/** System preference change back to SRAT during handoff */
public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
+ /** Data call fail due to the slice not being allowed for the data call. */
+ public static final int SLICE_REJECTED = 0x8CC;
//IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
@@ -985,7 +987,7 @@ public final class DataFailCause {
* the authentication failed.
*/
public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001;
- /** IKE message timeout, tunnel setup failed due to no response from EPDG */
+ /** IKE message timeout, tunnel setup failed due to no response from EPDG */
public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002;
/** IKE Certification validation failure */
public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003;
@@ -1419,6 +1421,7 @@ public final class DataFailCause {
sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED");
sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
+ sFailCauseMap.put(SLICE_REJECTED, "SLICE_REJECTED");
sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION");
sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED");
sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
diff --git a/telephony/java/android/telephony/ImsiEncryptionInfo.java b/telephony/java/android/telephony/ImsiEncryptionInfo.java
index 75a79d62d2aa..4978692d3964 100644
--- a/telephony/java/android/telephony/ImsiEncryptionInfo.java
+++ b/telephony/java/android/telephony/ImsiEncryptionInfo.java
@@ -163,8 +163,8 @@ public final class ImsiEncryptionInfo implements Parcelable {
public String toString(){
return "[ImsiEncryptionInfo "
+ "mcc=" + mcc
- + "mnc=" + mnc
- + "publicKey=" + publicKey
+ + " mnc=" + mnc
+ + " publicKey=" + publicKey
+ ", keyIdentifier=" + keyIdentifier
+ ", keyType=" + keyType
+ ", expirationTime=" + expirationTime
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 8348502586a5..46ec4a39fd21 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -136,6 +136,7 @@ public final class DataCallResponse implements Parcelable {
private final int mPduSessionId;
private final Qos mDefaultQos;
private final List<QosSession> mQosSessions;
+ private final SliceInfo mSliceInfo;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -186,6 +187,7 @@ public final class DataCallResponse implements Parcelable {
mPduSessionId = PDU_SESSION_ID_NOT_SET;
mDefaultQos = null;
mQosSessions = new ArrayList<>();
+ mSliceInfo = null;
}
private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -194,7 +196,8 @@ public final class DataCallResponse implements Parcelable {
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
- @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) {
+ @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions,
+ @Nullable SliceInfo sliceInfo) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -216,6 +219,7 @@ public final class DataCallResponse implements Parcelable {
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
mQosSessions = qosSessions;
+ mSliceInfo = sliceInfo;
}
/** @hide */
@@ -243,6 +247,7 @@ public final class DataCallResponse implements Parcelable {
mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
mQosSessions = new ArrayList<>();
source.readList(mQosSessions, QosSession.class.getClassLoader());
+ mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
}
/**
@@ -368,7 +373,7 @@ public final class DataCallResponse implements Parcelable {
}
/**
- * @return default QOS of the data call received from the network
+ * @return default QOS of the data connection received from the network
*
* @hide
*/
@@ -379,16 +384,24 @@ public final class DataCallResponse implements Parcelable {
}
/**
- * @return All the dedicated bearer QOS sessions of the data call received from the network
+ * @return All the dedicated bearer QOS sessions of the data connection received from the
+ * network.
*
* @hide
*/
-
@NonNull
public List<QosSession> getQosSessions() {
return mQosSessions;
}
+ /**
+ * @return The slice info related to this data connection.
+ */
+ @Nullable
+ public SliceInfo getSliceInfo() {
+ return mSliceInfo;
+ }
+
@NonNull
@Override
public String toString() {
@@ -411,6 +424,7 @@ public final class DataCallResponse implements Parcelable {
.append(" pduSessionId=").append(getPduSessionId())
.append(" defaultQos=").append(mDefaultQos)
.append(" qosSessions=").append(mQosSessions)
+ .append(" sliceInfo=").append(mSliceInfo)
.append("}");
return sb.toString();
}
@@ -454,7 +468,8 @@ public final class DataCallResponse implements Parcelable {
&& mHandoverFailureMode == other.mHandoverFailureMode
&& mPduSessionId == other.mPduSessionId
&& isQosSame
- && isQosSessionsSame;
+ && isQosSessionsSame
+ && Objects.equals(mSliceInfo, other.mSliceInfo);
}
@Override
@@ -462,7 +477,7 @@ public final class DataCallResponse implements Parcelable {
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosSessions);
+ mQosSessions, mSliceInfo);
}
@Override
@@ -493,6 +508,7 @@ public final class DataCallResponse implements Parcelable {
dest.writeParcelable((NrQos)mDefaultQos, flags);
}
dest.writeList(mQosSessions);
+ dest.writeParcelable(mSliceInfo, flags);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -576,6 +592,8 @@ public final class DataCallResponse implements Parcelable {
private List<QosSession> mQosSessions = new ArrayList<>();
+ private SliceInfo mSliceInfo;
+
/**
* Default constructor for Builder.
*/
@@ -799,6 +817,21 @@ public final class DataCallResponse implements Parcelable {
}
/**
+ * The Slice used for this data connection.
+ * <p/>
+ * If a handover occurs from EPDG to 5G,
+ * this is the {@link SliceInfo} used in {@link DataService#setupDataCall}.
+ *
+ * @param sliceInfo the slice info for the data call
+ *
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setSliceInfo(@Nullable SliceInfo sliceInfo) {
+ mSliceInfo = sliceInfo;
+ return this;
+ }
+
+ /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
@@ -807,7 +840,7 @@ public final class DataCallResponse implements Parcelable {
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, mQosSessions);
+ mDefaultQos, mQosSessions, mSliceInfo);
}
}
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 2ec965101930..03c2ef9d9baa 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -194,13 +194,19 @@ public abstract class DataService extends Service {
* The standard range of values are 1-15 while 0 means no pdu session id
* was attached to this call. Reference: 3GPP TS 24.007 section
* 11.2.3.1b.
+ * @param sliceInfo used within the data connection when a handover occurs from EPDG to 5G.
+ * The value is null unless the access network is
+ * {@link android.telephony.AccessNetworkConstants.AccessNetworkType#NGRAN} and a
+ * handover is occurring from EPDG to 5G. If the slice passed is rejected, then
+ * {@link DataCallResponse#getCause()} is
+ * {@link android.telephony.DataFailCause#SLICE_REJECTED}.
* @param callback The result callback for this request.
*/
public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
boolean isRoaming, boolean allowRoaming,
@SetupDataReason int reason,
@Nullable LinkProperties linkProperties,
- @IntRange(from = 0, to = 15) int pduSessionId,
+ @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo,
@NonNull DataServiceCallback callback) {
/* Call the old version since the new version isn't supported */
setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason,
@@ -392,10 +398,11 @@ public abstract class DataService extends Service {
public final int reason;
public final LinkProperties linkProperties;
public final int pduSessionId;
+ public final SliceInfo sliceInfo;
public final IDataServiceCallback callback;
SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
boolean allowRoaming, int reason, LinkProperties linkProperties,
- int pduSessionId, IDataServiceCallback callback) {
+ int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) {
this.accessNetworkType = accessNetworkType;
this.dataProfile = dataProfile;
this.isRoaming = isRoaming;
@@ -403,6 +410,7 @@ public abstract class DataService extends Service {
this.linkProperties = linkProperties;
this.reason = reason;
this.pduSessionId = pduSessionId;
+ this.sliceInfo = sliceInfo;
this.callback = callback;
}
}
@@ -513,6 +521,7 @@ public abstract class DataService extends Service {
setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId,
+ setupDataCallRequest.sliceInfo,
(setupDataCallRequest.callback != null)
? new DataServiceCallback(setupDataCallRequest.callback)
: null);
@@ -676,10 +685,12 @@ public abstract class DataService extends Service {
@Override
public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile,
boolean isRoaming, boolean allowRoaming, int reason,
- LinkProperties linkProperties, int pduSessionId, IDataServiceCallback callback) {
+ LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo,
+ IDataServiceCallback callback) {
mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0,
new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
- allowRoaming, reason, linkProperties, pduSessionId, callback))
+ allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
+ callback))
.sendToTarget();
}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 3f1f033d6f11..e0b9a1a9bb5a 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -19,6 +19,7 @@ package android.telephony.data;
import android.net.LinkProperties;
import android.telephony.data.DataProfile;
import android.telephony.data.IDataServiceCallback;
+import android.telephony.data.SliceInfo;
/**
* {@hide}
@@ -29,7 +30,7 @@ oneway interface IDataService
void removeDataServiceProvider(int slotId);
void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
boolean allowRoaming, int reason, in LinkProperties linkProperties,
- int pduSessionId, IDataServiceCallback callback);
+ int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback);
void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
IDataServiceCallback callback);
diff --git a/telephony/java/android/telephony/data/SliceInfo.aidl b/telephony/java/android/telephony/data/SliceInfo.aidl
new file mode 100644
index 000000000000..286ea5e4f8c7
--- /dev/null
+++ b/telephony/java/android/telephony/data/SliceInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable SliceInfo;
diff --git a/telephony/java/android/telephony/data/SliceInfo.java b/telephony/java/android/telephony/data/SliceInfo.java
new file mode 100644
index 000000000000..51857a7b4908
--- /dev/null
+++ b/telephony/java/android/telephony/data/SliceInfo.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents a S-NSSAI as defined in 3GPP TS 24.501.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SliceInfo implements Parcelable {
+ /**
+ * When set on a Slice Differentiator, this value indicates that there is no corresponding
+ * Slice.
+ */
+ public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1;
+
+ /**
+ * Indicates that the service type is not present.
+ */
+ public static final int SLICE_SERVICE_TYPE_NONE = 0;
+
+ /**
+ * Slice suitable for the handling of 5G enhanced Mobile Broadband.
+ */
+ public static final int SLICE_SERVICE_TYPE_EMBB = 1;
+
+ /**
+ * Slice suitable for the handling of ultra-reliable low latency communications.
+ */
+ public static final int SLICE_SERVICE_TYPE_URLLC = 2;
+
+ /**
+ * Slice suitable for the handling of massive IoT.
+ */
+ public static final int SLICE_SERVICE_TYPE_MIOT = 3;
+
+ /**
+ * The min acceptable value for a Slice Differentiator
+ */
+ @SuppressLint("MinMaxConstant")
+ public static final int MIN_SLICE_DIFFERENTIATOR = -1;
+
+ /**
+ * The max acceptable value for a Slice Differentiator
+ */
+ @SuppressLint("MinMaxConstant")
+ public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE;
+
+ /** @hide */
+ @IntDef(prefix = { "SLICE_SERVICE_TYPE_" }, value = {
+ SLICE_SERVICE_TYPE_NONE,
+ SLICE_SERVICE_TYPE_EMBB,
+ SLICE_SERVICE_TYPE_URLLC,
+ SLICE_SERVICE_TYPE_MIOT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SliceServiceType {}
+
+
+ @SliceServiceType
+ private final int mSliceServiceType;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private final int mSliceDifferentiator;
+ @SliceServiceType
+ private final int mMappedHplmnSliceServiceType;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private final int mMappedHplmnSliceDifferentiator;
+
+ private SliceInfo(@SliceServiceType int sliceServiceType,
+ int sliceDifferentiator, int mappedHplmnSliceServiceType,
+ int mappedHplmnSliceDifferentiator) {
+ mSliceServiceType = sliceServiceType;
+ mSliceDifferentiator = sliceDifferentiator;
+ mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+ mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+ }
+
+ /**
+ * The type of service provided by the slice.
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @SliceServiceType
+ public int getSliceServiceType() {
+ return mSliceServiceType;
+ }
+
+ /**
+ * Identifies the slice from others with the same Slice Service Type.
+ * <p/>
+ * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if {@link #getSliceServiceType} returns
+ * {@link #SLICE_SERVICE_TYPE_NONE}.
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ public int getSliceDifferentiator() {
+ return mSliceDifferentiator;
+ }
+
+ /**
+ * Corresponds to a Slice Info (S-NSSAI) of the HPLMN.
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @SliceServiceType
+ public int getMappedHplmnSliceServiceType() {
+ return mMappedHplmnSliceServiceType;
+ }
+
+ /**
+ * This Slice Differentiator corresponds to a {@link SliceInfo} (S-NSSAI) of the HPLMN;
+ * {@link #getSliceDifferentiator()} is mapped to this value.
+ * <p/>
+ * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if either of the following are true:
+ * <ul>
+ * <li>{@link #getSliceDifferentiator()} returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE}</li>
+ * <li>{@link #getMappedHplmnSliceServiceType()} returns {@link #SLICE_SERVICE_TYPE_NONE}</li>
+ * </ul>
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ public int getMappedHplmnSliceDifferentiator() {
+ return mMappedHplmnSliceDifferentiator;
+ }
+
+ private SliceInfo(@NonNull Parcel in) {
+ mSliceServiceType = in.readInt();
+ mSliceDifferentiator = in.readInt();
+ mMappedHplmnSliceServiceType = in.readInt();
+ mMappedHplmnSliceDifferentiator = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSliceServiceType);
+ dest.writeInt(mSliceDifferentiator);
+ dest.writeInt(mMappedHplmnSliceServiceType);
+ dest.writeInt(mMappedHplmnSliceDifferentiator);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<SliceInfo> CREATOR =
+ new Parcelable.Creator<SliceInfo>() {
+ @Override
+ @NonNull
+ public SliceInfo createFromParcel(@NonNull Parcel source) {
+ return new SliceInfo(source);
+ }
+
+ @Override
+ @NonNull
+ public SliceInfo[] newArray(int size) {
+ return new SliceInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "SliceInfo{"
+ + "mSliceServiceType=" + sliceServiceTypeToString(mSliceServiceType)
+ + ", mSliceDifferentiator=" + mSliceDifferentiator
+ + ", mMappedHplmnSliceServiceType="
+ + sliceServiceTypeToString(mMappedHplmnSliceServiceType)
+ + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator
+ + '}';
+ }
+
+ private static String sliceServiceTypeToString(@SliceServiceType int sliceServiceType) {
+ switch(sliceServiceType) {
+ case SLICE_SERVICE_TYPE_NONE:
+ return "NONE";
+ case SLICE_SERVICE_TYPE_EMBB:
+ return "EMBB";
+ case SLICE_SERVICE_TYPE_URLLC:
+ return "URLLC";
+ case SLICE_SERVICE_TYPE_MIOT:
+ return "MIOT";
+ default:
+ return Integer.toString(sliceServiceType);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SliceInfo sliceInfo = (SliceInfo) o;
+ return mSliceServiceType == sliceInfo.mSliceServiceType
+ && mSliceDifferentiator == sliceInfo.mSliceDifferentiator
+ && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType
+ && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType,
+ mMappedHplmnSliceDifferentiator);
+ }
+
+ /**
+ * Provides a convenient way to set the fields of a {@link SliceInfo} when creating a
+ * new instance.
+ *
+ * <p>The example below shows how you might create a new {@code SliceInfo}:
+ *
+ * <pre><code>
+ *
+ * SliceInfo response = new SliceInfo.Builder()
+ * .setSliceServiceType(SLICE_SERVICE_TYPE_URLLC)
+ * .build();
+ * </code></pre>
+ */
+ public static final class Builder {
+ @SliceServiceType
+ private int mSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private int mSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+ @SliceServiceType
+ private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Set the Slice Service Type.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setSliceServiceType(@SliceServiceType int mSliceServiceType) {
+ this.mSliceServiceType = mSliceServiceType;
+ return this;
+ }
+
+ /**
+ * Set the Slice Differentiator.
+ * <p/>
+ * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+ * corresponding Slice.
+ *
+ * @throws IllegalArgumentException if the parameter is not between
+ * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setSliceDifferentiator(
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ int sliceDifferentiator) {
+ if (sliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+ || sliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+ throw new IllegalArgumentException("The slice diffentiator value is out of range");
+ }
+ this.mSliceDifferentiator = sliceDifferentiator;
+ return this;
+ }
+
+ /**
+ * Set the HPLMN Slice Service Type.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setMappedHplmnSliceServiceType(
+ @SliceServiceType int mappedHplmnSliceServiceType) {
+ this.mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+ return this;
+ }
+
+ /**
+ * Set the HPLMN Slice Differentiator.
+ * <p/>
+ * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+ * corresponding Slice of the HPLMN.
+ *
+ * @throws IllegalArgumentException if the parameter is not between
+ * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setMappedHplmnSliceDifferentiator(
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ int mappedHplmnSliceDifferentiator) {
+ if (mappedHplmnSliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+ || mappedHplmnSliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+ throw new IllegalArgumentException("The slice diffentiator value is out of range");
+ }
+ this.mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+ return this;
+ }
+
+ /**
+ * Build the {@link SliceInfo}.
+ *
+ * @return the {@link SliceInfo} object.
+ */
+ @NonNull
+ public SliceInfo build() {
+ return new SliceInfo(this.mSliceServiceType, this.mSliceDifferentiator,
+ this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index 006cca84e44b..9cfa640fce18 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -16,6 +16,8 @@
package android.telephony.ims;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Build;
@@ -39,6 +41,7 @@ import java.util.Objects;
public final class SipMessage implements Parcelable {
// Should not be set to true for production!
private static final boolean IS_DEBUGGING = Build.IS_ENG;
+ private static final String CRLF = "\r\n";
private final String mStartLine;
private final String mHeaderSection;
@@ -165,4 +168,19 @@ public final class SipMessage implements Parcelable {
result = 31 * result + Arrays.hashCode(mContent);
return result;
}
+
+ /**
+ * @return the UTF-8 encoded SIP message.
+ */
+ public @NonNull byte[] getEncodedMessage() {
+ byte[] header = new StringBuilder()
+ .append(mStartLine)
+ .append(mHeaderSection)
+ .append(CRLF)
+ .toString().getBytes(UTF_8);
+ byte[] sipMessage = new byte[header.length + mContent.length];
+ System.arraycopy(header, 0, sipMessage, 0, header.length);
+ System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length);
+ return sipMessage;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 053774241900..e556664bb323 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2367,6 +2367,11 @@ interface ITelephony {
boolean setCarrierSingleRegistrationEnabledOverride(int subId, String enabled);
/**
+ * Sends a device to device message; only for use through shell.
+ */
+ void sendDeviceToDeviceMessage(int message, int value);
+
+ /**
* Gets the config of RCS VoLTE single registration enabled for the carrier/subscription.
*/
boolean getCarrierSingleRegistrationEnabled(int subId);
diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
index e9227e94da98..eb04f6907748 100644
--- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
+++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
@@ -131,6 +131,10 @@ class PlatformCompatCommandNotInstalledTest {
assertThat(platformCompat.isChangeEnabled(TEST_CHANGE_ID, appInfo)).isEqualTo(params.result)
}
- private fun command(command: String) =
- FileReader(uiAutomation.executeShellCommand(command).fileDescriptor).readText()
+ private fun command(command: String): String {
+ val fileDescriptor = uiAutomation.executeShellCommand(command)
+ return String(ParcelFileDescriptor.AutoCloseInputStream(fileDescriptor).use {
+ inputStream -> inputStream.readBytes()
+ })
+ }
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 16c486562f53..083c8c8741da 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -38,6 +38,7 @@ import android.net.metrics.IpConnectivityLog
import android.os.ConditionVariable
import android.os.IBinder
import android.os.INetworkManagementService
+import android.os.UserHandle
import android.testing.TestableContext
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -55,10 +56,13 @@ import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.AdditionalAnswers
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
@@ -143,7 +147,10 @@ class ConnectivityServiceIntegrationTest {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any())
+ val asUserCtx = mock(Context::class.java, AdditionalAnswers.delegatesTo<Context>(context))
+ doReturn(UserHandle.ALL).`when`(asUserCtx).user
+ doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt())
+ doNothing().`when`(context).sendStickyBroadcast(any(), any())
networkStackClient = TestNetworkStackClient(realContext)
networkStackClient.init()
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 9ba56e44fe88..91fcbc0fd5d7 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -67,6 +67,7 @@ class NetworkTemplateTest {
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
+ setSSID(ssid)
}
return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
}
diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/tests/net/java/android/net/QosSocketFilterTest.java
new file mode 100644
index 000000000000..ad58960eaadd
--- /dev/null
+++ b/tests/net/java/android/net/QosSocketFilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+@RunWith(AndroidJUnit4.class)
+@androidx.test.filters.SmallTest
+public class QosSocketFilterTest {
+
+ @Test
+ public void testPortExactMatch() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertTrue(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 10, 10));
+
+ }
+
+ @Test
+ public void testPortLessThanStart() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 8), addressB, 10, 10));
+ }
+
+ @Test
+ public void testPortGreaterThanEnd() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 18), addressB, 10, 10));
+ }
+
+ @Test
+ public void testPortBetweenStartAndEnd() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertTrue(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 8, 18));
+ }
+
+ @Test
+ public void testAddressesDontMatch() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 10, 10));
+ }
+}
+
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f893e9eea486..81c16887c6ee 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -64,6 +64,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -201,6 +202,7 @@ import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -245,7 +247,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
@@ -5963,23 +5964,18 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
&& nc.hasTransport(TRANSPORT_WIFI));
-
- // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
- // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
callback.assertNoCallback();
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Unsuspend cellular and then switch back to it.
- // The same bug happens in the opposite direction: the VPN's capabilities correctly have
- // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+ // Unsuspend cellular and then switch back to it. The VPN remains not suspended.
mCellNetworkAgent.resume();
callback.assertNoCallback();
mWiFiNetworkAgent.disconnect();
@@ -5996,12 +5992,11 @@ public class ConnectivityServiceTest {
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Re-suspending the current network fixes the problem.
+ // Suspend cellular and expect no connectivity.
mCellNetworkAgent.suspend();
callback.expectCapabilitiesThat(mMockVpn,
nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6017,6 +6012,7 @@ public class ConnectivityServiceTest {
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ // Resume cellular and expect that connectivity comes back.
mCellNetworkAgent.resume();
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6407,10 +6403,7 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // While the SUSPENDED callback should in theory be sent here, it is not. This is
- // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
- // been public and are deprecated and slated for removal, there is no sense in spending
- // resources fixing this bug now.
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
@@ -6422,8 +6415,7 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
- // worth anybody's time to fix.
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Disconnect cell. Receive update without even removing the dead network from the
@@ -7335,39 +7327,68 @@ public class ConnectivityServiceTest {
b2.expectBroadcast();
}
+ /**
+ * Test mutable and requestable network capabilities such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the
+ * {@code ConnectivityService} re-assign the networks accordingly.
+ */
@Test
- public final void testLoseTrusted() throws Exception {
- final NetworkRequest trustedRequest = new NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_TRUSTED)
- .build();
- final TestNetworkCallback trustedCallback = new TestNetworkCallback();
- mCm.requestNetwork(trustedRequest, trustedCallback);
-
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- mCellNetworkAgent.connect(true);
- trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
-
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.connect(true);
- trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ public final void testLoseMutableAndRequestableCaps() throws Exception {
+ final int[] testCaps = new int [] {
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_NOT_VCN_MANAGED
+ };
+ for (final int testCap : testCaps) {
+ // Create requests with and without the testing capability.
+ final TestNetworkCallback callbackWithCap = new TestNetworkCallback();
+ final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(),
+ callbackWithCap);
+ mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(),
+ callbackWithoutCap);
+
+ // Setup networks with testing capability and verify the default network changes.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.addCapability(testCap);
+ mCellNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(testCap);
+ mWiFiNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
+
+ // Remove the testing capability on wifi, verify the callback and default network
+ // changes back to cellular.
+ mWiFiNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
+ // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
+ // it.
+ if (testCap == NET_CAPABILITY_TRUSTED) {
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
+ }
- mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- verify(mMockNetd).networkClearDefault();
+ mCellNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ callbackWithoutCap.assertNoCallback();
+ if (testCap == NET_CAPABILITY_TRUSTED) {
+ verify(mMockNetd).networkClearDefault();
+ }
- mCm.unregisterNetworkCallback(trustedCallback);
+ mCm.unregisterNetworkCallback(callbackWithCap);
+ mCm.unregisterNetworkCallback(callbackWithoutCap);
+ }
}
- @Ignore // 40%+ flakiness : figure out why and re-enable.
@Test
public final void testBatteryStatsNetworkType() throws Exception {
final LinkProperties cellLp = new LinkProperties();
@@ -7375,8 +7396,8 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
reset(mBatteryStatsService);
final LinkProperties wifiLp = new LinkProperties();
@@ -7384,18 +7405,20 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(),
- TYPE_WIFI);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(),
+ new int[] { TRANSPORT_WIFI });
reset(mBatteryStatsService);
mCellNetworkAgent.disconnect();
+ mWiFiNetworkAgent.disconnect();
cellLp.setInterfaceName("wifi0");
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
+ mCellNetworkAgent.disconnect();
}
/**
@@ -7468,8 +7491,8 @@ public class ConnectivityServiceTest {
assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -7489,7 +7512,8 @@ public class ConnectivityServiceTest {
// Make sure BatteryStats was not told about any v4- interfaces, as none should have
// come online yet.
waitForIdle();
- verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt());
+ verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"),
+ any());
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
@@ -7542,8 +7566,8 @@ public class ConnectivityServiceTest {
assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
for (final LinkProperties stackedLp : stackedLpsAfterChange) {
- verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(
+ stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR });
}
reset(mMockNetd);
when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
@@ -8323,8 +8347,7 @@ public class ConnectivityServiceTest {
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
- final VpnInfo vpnInfo = new VpnInfo();
- vpnInfo.ownerUid = vpnOwnerUid;
+ final VpnInfo vpnInfo = new VpnInfo(vpnOwnerUid, null, null);
mMockVpn.setVpnInfo(vpnInfo);
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 3aafe0b075f2..1b33930e96a9 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -33,8 +33,7 @@ import static android.net.NetworkStats.TAG_NONE;
import static org.junit.Assert.assertEquals;
import android.net.NetworkStats;
-
-import com.android.internal.net.VpnInfo;
+import android.net.VpnInfo;
/** Superclass with utilities for NetworkStats(Service|Factory)Test */
abstract class NetworkStatsBaseTest {
@@ -113,10 +112,6 @@ abstract class NetworkStatsBaseTest {
}
static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = UID_VPN;
- info.vpnIface = vpnIface;
- info.underlyingIfaces = underlyingIfaces;
- return info;
+ return new VpnInfo(UID_VPN, vpnIface, underlyingIfaces);
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index e4996d981fac..76647a69de33 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -36,13 +36,13 @@ import static org.junit.Assert.fail;
import android.content.res.Resources;
import android.net.NetworkStats;
import android.net.TrafficStats;
+import android.net.VpnInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
-import com.android.internal.net.VpnInfo;
import libcore.io.IoUtils;
import libcore.io.Streams;
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index c7836297df75..b4e37de2267f 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -21,7 +21,6 @@ import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -44,6 +43,7 @@ import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
+import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
@@ -86,6 +86,7 @@ import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -104,7 +105,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -146,7 +146,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
private static final String IMSI_2 = "310260";
private static final String TEST_SSID = "AndroidAP";
- private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard();
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
@@ -291,7 +291,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
-
// modify some number on wifi, and trigger poll event
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
@@ -567,61 +566,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
}
@Test
- public void testUid3gWimaxCombinedByTemplate() throws Exception {
- // pretend that network comes online
- expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
-
- // create some traffic
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
- mService.incrementOperationCount(UID_RED, 0xF00D, 5);
-
- forcePollAndWaitForIdle();
-
- // verify service recorded history
- assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
-
-
- // now switch over to wimax network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- states = new NetworkState[] {buildWimaxState(TEST_IFACE2)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
- forcePollAndWaitForIdle();
-
-
- // create traffic on second network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
- mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
-
- forcePollAndWaitForIdle();
-
- // verify that ALL_MOBILE template combines both
- assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
- }
-
- @Test
public void testMobileStatsByRatType() throws Exception {
final NetworkTemplate template3g =
buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
@@ -1503,6 +1447,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ capabilities.setSSID(TEST_SSID);
return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
}
@@ -1524,17 +1469,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
}
- private static NetworkState buildWimaxState(@NonNull String iface) {
- final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(iface);
- final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
- return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null);
- }
-
private NetworkStats buildEmptyStats() {
return new NetworkStats(getElapsedRealtime(), 0);
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index dfd0c8a75172..86a15912b6b4 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -28,6 +28,7 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -39,6 +40,12 @@ public class VcnGatewayConnectionConfigTest {
NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+
+ static {
+ Arrays.sort(EXPOSED_CAPS);
+ Arrays.sort(UNDERLYING_CAPS);
+ }
+
public static final long[] RETRY_INTERVALS_MS =
new long[] {
TimeUnit.SECONDS.toMillis(5),
@@ -124,12 +131,13 @@ public class VcnGatewayConnectionConfigTest {
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
- for (int cap : EXPOSED_CAPS) {
- config.hasExposedCapability(cap);
- }
- for (int cap : UNDERLYING_CAPS) {
- config.requiresUnderlyingCapability(cap);
- }
+ int[] exposedCaps = config.getExposedCapabilities();
+ Arrays.sort(exposedCaps);
+ assertArrayEquals(EXPOSED_CAPS, exposedCaps);
+
+ int[] underlyingCaps = config.getRequiredUnderlyingCapabilities();
+ Arrays.sort(underlyingCaps);
+ assertArrayEquals(UNDERLYING_CAPS, underlyingCaps);
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
assertEquals(MAX_MTU, config.getMaxMtu());
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 29cfdb6fd5c6..f0cdde33f822 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -18,6 +18,7 @@ package com.android.server;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -138,11 +139,16 @@ public class VcnManagementServiceTest {
private final IBinder mMockIBinder = mock(IBinder.class);
public VcnManagementServiceTest() throws Exception {
- setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
- setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
setupSystemService(
- mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
- setupSystemService(mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
+ mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ setupSystemService(
+ mMockContext, mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ setupSystemService(
+ mMockContext,
+ mSubMgr,
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE,
+ SubscriptionManager.class);
+ setupSystemService(mMockContext, mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
@@ -186,11 +192,6 @@ public class VcnManagementServiceTest {
mTestLooper.dispatchAll();
}
- private void setupSystemService(Object service, String name, Class<?> serviceClass) {
- doReturn(name).when(mMockContext).getSystemServiceName(serviceClass);
- doReturn(service).when(mMockContext).getSystemService(name);
- }
-
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
.when(mSubMgr)
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
new file mode 100644
index 000000000000..4ecd21503165
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testEnterWhileNotRunningTriggersQuit() throws Exception {
+ final VcnGatewayConnection vgc =
+ new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+
+ vgc.setIsRunning(false);
+ vgc.transitionTo(vgc.mDisconnectedState);
+ mTestLooper.dispatchAll();
+
+ assertNull(vgc.getCurrentState());
+ }
+
+ @Test
+ public void testNetworkChangesTriggerStateTransitions() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testNullNetworkDoesNotTriggerStateTransition() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertNull(mGatewayConnection.getCurrentState());
+ verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
new file mode 100644
index 000000000000..d0fec55a6827
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testIkeSessionClosed() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTimeoutExpired() throws Exception {
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ mTestLooper.dispatchAll();
+
+ verify(mMockIkeSession).kill();
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ // Should do nothing; already tearing down.
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
new file mode 100644
index 000000000000..346785907fcf
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.IpSecManager;
+import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+
+import com.android.server.IpSecService;
+
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+
+import java.util.UUID;
+
+public class VcnGatewayConnectionTestBase {
+ protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+ protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+ protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+ protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
+ new UnderlyingNetworkRecord(
+ new Network(0),
+ new NetworkCapabilities(),
+ new LinkProperties(),
+ false /* blocked */);
+ protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
+ new UnderlyingNetworkRecord(
+ new Network(1),
+ new NetworkCapabilities(),
+ new LinkProperties(),
+ false /* blocked */);
+
+ @NonNull protected final Context mContext;
+ @NonNull protected final TestLooper mTestLooper;
+ @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull protected final VcnContext mVcnContext;
+ @NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayConnection.Dependencies mDeps;
+ @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+
+ @NonNull protected final IpSecService mIpSecSvc;
+
+ protected VcnIkeSession mMockIkeSession;
+ protected VcnGatewayConnection mGatewayConnection;
+
+ public VcnGatewayConnectionTestBase() {
+ mContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnContext = mock(VcnContext.class);
+ mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mDeps = mock(VcnGatewayConnection.Dependencies.class);
+ mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
+
+ mIpSecSvc = mock(IpSecService.class);
+ setupIpSecManager(mContext, mIpSecSvc);
+
+ doReturn(mContext).when(mVcnContext).getContext();
+ doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
+ doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+
+ doReturn(mUnderlyingNetworkTracker)
+ .when(mDeps)
+ .newUnderlyingNetworkTracker(any(), any(), any());
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ IpSecTunnelInterfaceResponse resp =
+ new IpSecTunnelInterfaceResponse(
+ IpSecManager.Status.OK,
+ TEST_IPSEC_TUNNEL_RESOURCE_ID,
+ TEST_IPSEC_TUNNEL_IFACE);
+ doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
+
+ mMockIkeSession = mock(VcnIkeSession.class);
+ doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
+
+ mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+ }
+
+ protected IkeSessionCallback getIkeSessionCallback() {
+ ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any());
+ return captor.getValue();
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
new file mode 100644
index 000000000000..2b1080650d6d
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.net.IpSecManager;
+
+import com.android.server.IpSecService;
+
+public class VcnTestUtils {
+ /** Mock system services by directly mocking the *Manager interface. */
+ public static void setupSystemService(
+ Context mockContext, Object service, String name, Class<?> serviceClass) {
+ doReturn(name).when(mockContext).getSystemServiceName(serviceClass);
+ doReturn(service).when(mockContext).getSystemService(name);
+ }
+
+ /** Mock IpSecService by mocking the underlying service binder. */
+ public static IpSecManager setupIpSecManager(Context mockContext, IpSecService service) {
+ doReturn(Context.IPSEC_SERVICE).when(mockContext).getSystemServiceName(IpSecManager.class);
+
+ final IpSecManager ipSecMgr = new IpSecManager(mockContext, service);
+ doReturn(ipSecMgr).when(mockContext).getSystemService(Context.IPSEC_SERVICE);
+
+ // Return to ensure this doesn't get reaped.
+ return ipSecMgr;
+ }
+}
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index afe91cda37b0..15088fc81e88 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -1,4 +1,5 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
+#-*- coding: utf-8 -*-
# Copyright (C) 2018 The Android Open Source Project
#
@@ -33,9 +34,6 @@ In general:
import re, sys, codecs
import lxml.etree as ET
-reload(sys)
-sys.setdefaultencoding('utf8')
-
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
@@ -118,7 +116,7 @@ def lint(path):
raw = f.read()
if len(raw.strip()) == 0:
return warnings
- tree = ET.fromstring(raw)
+ tree = ET.fromstring(bytes(raw, encoding='utf-8'))
root = tree #tree.getroot()
last_comment = None
@@ -231,6 +229,6 @@ for b in before:
if len(after) > 0:
for a in sorted(after.keys()):
- print after[a]
- print
+ print(after[a])
+ print()
sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index bd80bb4e6f3f..bd0569873197 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,5 +1,5 @@
#!/bin/bash
LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
- python $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
+ python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
done