summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--StubLibraries.bp5
-rw-r--r--core/api/current.txt33
-rw-r--r--core/api/module-lib-current.txt4
-rw-r--r--core/api/system-current.txt26
-rw-r--r--core/java/android/app/AppOpsManager.java2
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java15
-rw-r--r--core/java/android/content/pm/ResolveInfo.java33
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationInfo.java122
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationManager.java273
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationState.java135
-rw-r--r--core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl4
-rw-r--r--core/java/android/net/IOnCompleteListener.aidl (renamed from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl)2
-rw-r--r--core/java/android/os/Process.java4
-rw-r--r--core/java/android/os/RemoteCallbackList.java18
-rw-r--r--core/java/android/telephony/TelephonyCallback.java3
-rw-r--r--core/java/android/view/translation/ITranslationManager.aidl4
-rw-r--r--core/java/android/view/translation/UiTranslationManager.java147
-rw-r--r--core/java/android/view/translation/UiTranslationStateCallback.java48
-rw-r--r--core/java/com/android/internal/util/LocationPermissionChecker.java303
-rw-r--r--core/java/com/android/internal/util/Protocol.java2
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java296
-rw-r--r--keystore/java/android/security/KeyStore.java2
-rw-r--r--media/java/android/media/MediaFormat.java4
-rw-r--r--packages/Connectivity/framework/api/current.txt1
-rw-r--r--packages/Connectivity/framework/api/module-lib-current.txt1
-rw-r--r--packages/Connectivity/framework/api/system-current.txt8
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityManager.java144
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java46
-rw-r--r--packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl8
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkAgent.java6
-rw-r--r--packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java8
-rw-r--r--packages/Connectivity/framework/src/android/net/VpnTransportInfo.java9
-rw-r--r--packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java8
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml10
-rw-r--r--packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java4
-rw-r--r--services/core/java/com/android/server/BluetoothDeviceConfigListener.java16
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java46
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java393
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java55
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java6
-rw-r--r--services/core/java/com/android/server/connectivity/MockableSystemProperties.java7
-rw-r--r--services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java87
-rw-r--r--services/core/java/com/android/server/pm/ComponentResolver.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java9
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java16
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java6
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java3
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java32
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java20
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java393
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java8
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java69
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java35
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java10
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java16
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java5
-rw-r--r--services/incremental/IncrementalService.cpp54
-rw-r--r--services/incremental/ServiceWrappers.cpp4
-rw-r--r--services/incremental/ServiceWrappers.h2
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp7
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/tests/PackageManagerServiceTests/unit/Android.bp7
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt5
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt278
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java48
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt372
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt425
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt12
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt18
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt31
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt15
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java (renamed from services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java)181
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS2
-rw-r--r--services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS1
-rw-r--r--services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS1
-rw-r--r--services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS1
-rw-r--r--services/tests/servicestests/assets/OwnersTest/OWNERS1
-rw-r--r--services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java15
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerService.java23
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java53
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java14
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl36
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java470
-rw-r--r--tests/net/java/com/android/server/LegacyTypeTrackerTest.kt127
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java2
108 files changed, 3765 insertions, 1618 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bff222e7d42f..c61f7c684efd 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -437,10 +437,6 @@ droidstubs {
name: "hwbinder-stubs-docs",
srcs: [
"core/java/android/os/HidlSupport.java",
- "core/java/android/annotation/IntDef.java",
- "core/java/android/annotation/IntRange.java",
- "core/java/android/annotation/NonNull.java",
- "core/java/android/annotation/SystemApi.java",
"core/java/android/os/HidlMemory.java",
"core/java/android/os/HwBinder.java",
"core/java/android/os/HwBlob.java",
@@ -466,6 +462,7 @@ droidstubs {
java_library_static {
name: "hwbinder.stubs",
sdk_version: "core_current",
+ libs: ["stub-annotations"],
srcs: [
":hwbinder-stubs-docs",
],
diff --git a/core/api/current.txt b/core/api/current.txt
index 2b5196b3b5f7..18c049fe2141 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8257,13 +8257,13 @@ package android.app.usage {
}
public class NetworkStatsManager {
- method public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
- method public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
- method public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
- method public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback);
method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler);
method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback);
@@ -22318,6 +22318,8 @@ package android.media {
field public static final String MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
field public static final String MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
field public static final String MIMETYPE_AUDIO_MPEG = "audio/mpeg";
+ field public static final String MIMETYPE_AUDIO_MPEGH_MHA1 = "audio/mha1";
+ field public static final String MIMETYPE_AUDIO_MPEGH_MHM1 = "audio/mhm1";
field public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
field public static final String MIMETYPE_AUDIO_OPUS = "audio/opus";
field public static final String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
@@ -40528,6 +40530,7 @@ package android.telephony {
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_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
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";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -40573,6 +40576,7 @@ package android.telephony {
field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+ field public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL = "vt_upgrade_supported_for_downgraded_rtt_call";
field public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = "vvm_cellular_data_required_bool";
field public static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
field public static final String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
@@ -42181,6 +42185,10 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
}
+ public static interface TelephonyCallback.PhysicalChannelConfigListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+ }
+
public static interface TelephonyCallback.PreciseDataConnectionStateListener {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
}
@@ -52403,6 +52411,17 @@ package android.view.translation {
method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest);
}
+ public final class UiTranslationManager {
+ method public void registerUiTranslationStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.translation.UiTranslationStateCallback);
+ method public void unregisterUiTranslationStateCallback(@NonNull android.view.translation.UiTranslationStateCallback);
+ }
+
+ public interface UiTranslationStateCallback {
+ method public void onFinished();
+ method public void onPaused();
+ method public void onStarted(@NonNull String, @NonNull String);
+ }
+
public final class ViewTranslationRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.view.autofill.AutofillId getAutofillId();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index a140e8ad095e..51b6967609af 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -258,6 +258,10 @@ package android.os {
method public default int getStability();
}
+ public class Process {
+ field public static final int VPN_UID = 1016; // 0x3f8
+ }
+
public class StatsServiceManager {
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0eb5553e11fa..2a99aaa94d2a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2837,21 +2837,29 @@ package android.content.pm.verify.domain {
method @NonNull public String getPackageName();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationInfo> CREATOR;
+ field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
+ field public static final int STATE_MODIFIABLE_UNVERIFIED = 3; // 0x3
+ field public static final int STATE_MODIFIABLE_VERIFIED = 4; // 0x4
+ field public static final int STATE_NO_RESPONSE = 0; // 0x0
+ field public static final int STATE_SUCCESS = 1; // 0x1
+ field public static final int STATE_UNMODIFIABLE = 2; // 0x2
}
public final class DomainVerificationManager {
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
- method public static boolean isStateModifiable(int);
- method public static boolean isStateVerified(int);
method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
- method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public int setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public int setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+ field public static final int ERROR_DOMAIN_SET_ID_INVALID = 1; // 0x1
+ field public static final int ERROR_DOMAIN_SET_ID_NULL = 2; // 0x2
+ field public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3; // 0x3
+ field public static final int ERROR_INVALID_STATE_CODE = 6; // 0x6
+ field public static final int ERROR_UNABLE_TO_APPROVE = 5; // 0x5
+ field public static final int ERROR_UNKNOWN_DOMAIN = 4; // 0x4
field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
- field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
- field public static final int STATE_NO_RESPONSE = 0; // 0x0
- field public static final int STATE_SUCCESS = 1; // 0x1
+ field public static final int STATUS_OK = 0; // 0x0
}
public final class DomainVerificationRequest implements android.os.Parcelable {
@@ -11609,10 +11617,6 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
}
- public static interface TelephonyCallback.PhysicalChannelConfigListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
- }
-
public static interface TelephonyCallback.PreciseCallStateListener {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 60765fe948ff..27b19bcd31a1 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -9148,7 +9148,7 @@ public class AppOpsManager {
try {
sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
FULL_LOG, false);
- } catch (SecurityException e) {
+ } catch (Exception e) {
// This should not happen, but it may, in rare cases
sFullLog = false;
}
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 098d8b6c6058..9f1132b605ef 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -24,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.WorkerThread;
import android.app.usage.NetworkStats.Bucket;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -201,6 +202,7 @@ public class NetworkStatsManager {
* default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered {@link NetworkStats.Bucket#METERED_ALL},
* and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -219,6 +221,7 @@ public class NetworkStatsManager {
* @return Bucket object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public Bucket querySummaryForDevice(int networkType, String subscriberId,
long startTime, long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
@@ -240,6 +243,7 @@ public class NetworkStatsManager {
* uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
* metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
* {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,6 +262,7 @@ public class NetworkStatsManager {
* @return Bucket object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
@@ -283,6 +288,7 @@ public class NetworkStatsManager {
* means buckets' start and end timestamps are going to be the same as the 'startTime' and
* 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
* be the same.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -301,6 +307,7 @@ public class NetworkStatsManager {
* @return Statistics object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
@@ -326,9 +333,11 @@ public class NetworkStatsManager {
/**
* Query network usage statistics details for a given uid.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
+ @WorkerThread
public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
long startTime, long endTime, int uid) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -344,9 +353,11 @@ public class NetworkStatsManager {
/**
* Query network usage statistics details for a given uid and tag.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
+ @WorkerThread
public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -365,6 +376,7 @@ public class NetworkStatsManager {
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -387,6 +399,7 @@ public class NetworkStatsManager {
* @return Statistics object or null if an error happened during statistics collection.
* @throws SecurityException if permissions are insufficient to read network statistics.
*/
+ @WorkerThread
public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
NetworkTemplate template;
@@ -425,6 +438,7 @@ public class NetworkStatsManager {
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
+ * This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -443,6 +457,7 @@ public class NetworkStatsManager {
* @return Statistics object or null if permissions are insufficient or error happened during
* statistics collection.
*/
+ @WorkerThread
public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index fe8e4d7b1bb2..6f07dd7a24e8 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -19,6 +19,7 @@ package android.content.pm;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -183,6 +184,17 @@ public class ResolveInfo implements Parcelable {
@SystemApi
public boolean handleAllWebDataURI;
+ /**
+ * Whether the resolved {@link IntentFilter} declares {@link Intent#CATEGORY_BROWSABLE} and is
+ * thus allowed to automatically resolve an {@link Intent} as it's assumed the action is safe
+ * for the user.
+ *
+ * Note that the above doesn't apply when this is the only result is returned in the candidate
+ * set, as the system will not prompt before opening the result. It only applies when there are
+ * multiple candidates.
+ */
+ private final boolean mAutoResolutionAllowed;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ComponentInfo getComponentInfo() {
@@ -364,8 +376,26 @@ public class ResolveInfo implements Parcelable {
&& INTENT_FORWARDER_ACTIVITY.equals(activityInfo.targetActivity);
}
+ /**
+ * @see #mAutoResolutionAllowed
+ * @hide
+ */
+ public boolean isAutoResolutionAllowed() {
+ return mAutoResolutionAllowed;
+ }
+
public ResolveInfo() {
targetUserId = UserHandle.USER_CURRENT;
+
+ // It's safer to assume that an unaware caller that constructs a ResolveInfo doesn't
+ // accidentally mark a result as auto resolveable.
+ mAutoResolutionAllowed = false;
+ }
+
+ /** @hide */
+ public ResolveInfo(boolean autoResolutionAllowed) {
+ targetUserId = UserHandle.USER_CURRENT;
+ mAutoResolutionAllowed = autoResolutionAllowed;
}
public ResolveInfo(ResolveInfo orig) {
@@ -386,6 +416,7 @@ public class ResolveInfo implements Parcelable {
system = orig.system;
targetUserId = orig.targetUserId;
handleAllWebDataURI = orig.handleAllWebDataURI;
+ mAutoResolutionAllowed = orig.mAutoResolutionAllowed;
isInstantAppAvailable = orig.isInstantAppAvailable;
}
@@ -450,6 +481,7 @@ public class ResolveInfo implements Parcelable {
dest.writeInt(noResourceId ? 1 : 0);
dest.writeInt(iconResourceId);
dest.writeInt(handleAllWebDataURI ? 1 : 0);
+ dest.writeInt(mAutoResolutionAllowed ? 1 : 0);
dest.writeInt(isInstantAppAvailable ? 1 : 0);
}
@@ -498,6 +530,7 @@ public class ResolveInfo implements Parcelable {
noResourceId = source.readInt() != 0;
iconResourceId = source.readInt();
handleAllWebDataURI = source.readInt() != 0;
+ mAutoResolutionAllowed = source.readInt() != 0;
isInstantAppAvailable = source.readInt() != 0;
}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
index 7c335b1d26dd..62277ef671e3 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -43,9 +43,50 @@ import java.util.UUID;
*/
@SystemApi
@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
- genEqualsHashCode = true)
+ genEqualsHashCode = true, genHiddenConstDefs = true)
public final class DomainVerificationInfo implements Parcelable {
+ // Implementation note: the following states are OUTPUT only. Any value that is synonymous with
+ // a value in DomainVerificationState must be the EXACT same integer, so that state
+ // transformation does not have to occur when sending input into the system, assuming that the
+ // system only accepts those synonymous values. The public API values declared here are only
+ // used when exiting the system server to prepare this data object for consumption by the
+ // verification agent. These constants should only be referenced inside public API classes.
+ // The server must use DomainVerificationState.
+
+ /**
+ * No response has been recorded by either the system or any verification agent.
+ */
+ public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+
+ /**
+ * The domain has been explicitly verified.
+ */
+ public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+
+ /**
+ * Indicates the host cannot be modified by the verification agent.
+ */
+ public static final int STATE_UNMODIFIABLE = 2;
+
+ /**
+ * Indicates the host can be modified by the verification agent and is not considered verified.
+ */
+ public static final int STATE_MODIFIABLE_UNVERIFIED = 3;
+
+ /**
+ * Indicates the host can be modified by the verification agent and is considered verified.
+ */
+ public static final int STATE_MODIFIABLE_VERIFIED = 4;
+
+ /**
+ * The first available custom response code. This and any greater integer, along with {@link
+ * #STATE_SUCCESS} are the only values settable by the verification agent. All custom values
+ * will be treated as if the domain is unverified.
+ */
+ public static final int STATE_FIRST_VERIFIER_DEFINED =
+ DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+
/**
* A domain verification ID for use in later API calls. This represents the snapshot of the
* domains for a package on device, and will be invalidated whenever the package changes.
@@ -74,16 +115,12 @@ public final class DomainVerificationInfo implements Parcelable {
/**
* Map of host names to their current state. State is an integer, which defaults to {@link
- * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
- * verification agent (the intended consumer of this API), which can be equal to {@link
- * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
- * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+ * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+ * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+ * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
* <p>
- * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
- * verification agent may be able to act on these reserved values, and this ability can be
- * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
- * the agent attempt to verify all domains that it can modify the state of, even if it does not
- * understand the meaning of those values.
+ * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+ * that the agent attempt to verify all domains that it can modify the state of.
*/
@NonNull
private final Map<String, Integer> mHostToStateMap;
@@ -112,6 +149,39 @@ public final class DomainVerificationInfo implements Parcelable {
//@formatter:off
+ /** @hide */
+ @android.annotation.IntDef(prefix = "STATE_", value = {
+ STATE_NO_RESPONSE,
+ STATE_SUCCESS,
+ STATE_UNMODIFIABLE,
+ STATE_MODIFIABLE_UNVERIFIED,
+ STATE_MODIFIABLE_VERIFIED,
+ STATE_FIRST_VERIFIER_DEFINED
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface State {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String stateToString(@State int value) {
+ switch (value) {
+ case STATE_NO_RESPONSE:
+ return "STATE_NO_RESPONSE";
+ case STATE_SUCCESS:
+ return "STATE_SUCCESS";
+ case STATE_UNMODIFIABLE:
+ return "STATE_UNMODIFIABLE";
+ case STATE_MODIFIABLE_UNVERIFIED:
+ return "STATE_MODIFIABLE_UNVERIFIED";
+ case STATE_MODIFIABLE_VERIFIED:
+ return "STATE_MODIFIABLE_VERIFIED";
+ case STATE_FIRST_VERIFIER_DEFINED:
+ return "STATE_FIRST_VERIFIER_DEFINED";
+ default: return Integer.toHexString(value);
+ }
+ }
+
/**
* Creates a new DomainVerificationInfo.
*
@@ -134,16 +204,12 @@ public final class DomainVerificationInfo implements Parcelable {
* The package name that this data corresponds to.
* @param hostToStateMap
* Map of host names to their current state. State is an integer, which defaults to {@link
- * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
- * verification agent (the intended consumer of this API), which can be equal to {@link
- * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
- * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+ * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+ * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+ * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
* <p>
- * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
- * verification agent may be able to act on these reserved values, and this ability can be
- * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
- * the agent attempt to verify all domains that it can modify the state of, even if it does not
- * understand the meaning of those values.
+ * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+ * that the agent attempt to verify all domains that it can modify the state of.
* @hide
*/
@DataClass.Generated.Member
@@ -195,16 +261,12 @@ public final class DomainVerificationInfo implements Parcelable {
/**
* Map of host names to their current state. State is an integer, which defaults to {@link
- * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
- * verification agent (the intended consumer of this API), which can be equal to {@link
- * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
- * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+ * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+ * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+ * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
* <p>
- * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
- * verification agent may be able to act on these reserved values, and this ability can be
- * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
- * the agent attempt to verify all domains that it can modify the state of, even if it does not
- * understand the meaning of those values.
+ * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+ * that the agent attempt to verify all domains that it can modify the state of.
*/
@DataClass.Generated.Member
public @NonNull Map<String,Integer> getHostToStateMap() {
@@ -320,10 +382,10 @@ public final class DomainVerificationInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1614721812023L,
+ time = 1615317187669L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
- inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+ inputSignatures = "public static final int STATE_NO_RESPONSE\npublic static final int STATE_SUCCESS\npublic static final int STATE_UNMODIFIABLE\npublic static final int STATE_MODIFIABLE_UNVERIFIED\npublic static final int STATE_MODIFIABLE_VERIFIED\npublic static final int STATE_FIRST_VERIFIER_DEFINED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index f7c81bcffda3..55e19f2727bf 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -60,154 +60,97 @@ public final class DomainVerificationManager {
"android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
/**
- * No response has been recorded by either the system or any verification agent.
+ * Default return code for when a method has succeeded.
*
* @hide
*/
@SystemApi
- public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+ public static final int STATUS_OK = 0;
/**
- * The verification agent has explicitly verified the domain at some point.
+ * The provided domain set ID was invalid, probably due to the package being updated between
+ * the initial request that provided the ID and the method call that used it. This usually
+ * means the work being processed by the verification agent is outdated and a new request
+ * should be scheduled, which should already be in progress as part of the
+ * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
*
* @hide
*/
@SystemApi
- public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+ public static final int ERROR_DOMAIN_SET_ID_INVALID = 1;
/**
- * The first available custom response code. This and any greater integer, along with {@link
- * #STATE_SUCCESS} are the only values settable by the verification agent. All values will be
- * treated as if the domain is unverified.
+ * The provided domain set ID was null. This is a developer error.
*
* @hide
*/
@SystemApi
- public static final int STATE_FIRST_VERIFIER_DEFINED =
- DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+ public static final int ERROR_DOMAIN_SET_ID_NULL = 2;
/**
+ * The provided set of domains was null or empty. This is a developer error.
+ *
* @hide
*/
- @NonNull
- public static String stateToDebugString(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_NO_RESPONSE:
- return "none";
- case DomainVerificationState.STATE_SUCCESS:
- return "verified";
- case DomainVerificationState.STATE_APPROVED:
- return "approved";
- case DomainVerificationState.STATE_DENIED:
- return "denied";
- case DomainVerificationState.STATE_MIGRATED:
- return "migrated";
- case DomainVerificationState.STATE_RESTORED:
- return "restored";
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- return "legacy_failure";
- case DomainVerificationState.STATE_SYS_CONFIG:
- return "system_configured";
- default:
- return String.valueOf(state);
- }
- }
+ @SystemApi
+ public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3;
/**
- * Checks if a state considers the corresponding domain to be successfully verified. The domain
- * verification agent may use this to determine whether or not to re-verify a domain.
+ * The provided set of domains contains a domain not declared by the target package. This
+ * usually means the work being processed by the verification agent is outdated and a new
+ * request should be scheduled, which should already be in progress as part of the
+ * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
*
* @hide
*/
@SystemApi
- public static boolean isStateVerified(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_APPROVED:
- case DomainVerificationState.STATE_MIGRATED:
- case DomainVerificationState.STATE_RESTORED:
- case DomainVerificationState.STATE_SYS_CONFIG:
- return true;
- case DomainVerificationState.STATE_NO_RESPONSE:
- case DomainVerificationState.STATE_DENIED:
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- default:
- return false;
- }
- }
+ public static final int ERROR_UNKNOWN_DOMAIN = 4;
/**
- * Checks if a state is modifiable by the domain verification agent. This is useful as the
- * platform may add new state codes in newer versions, and older verification agents can use
- * this method to determine if a state can be changed without having to be aware of what the new
- * state means.
+ * The system was unable to select the domain for approval. This indicates another application
+ * has been granted a higher approval, usually through domain verification, and the target
+ * package is unable to override it.
*
* @hide
*/
@SystemApi
- public static boolean isStateModifiable(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_NO_RESPONSE:
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_MIGRATED:
- case DomainVerificationState.STATE_RESTORED:
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- return true;
- case DomainVerificationState.STATE_APPROVED:
- case DomainVerificationState.STATE_DENIED:
- case DomainVerificationState.STATE_SYS_CONFIG:
- return false;
- default:
- return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
- }
- }
+ public static final int ERROR_UNABLE_TO_APPROVE = 5;
/**
- * For determine re-verify policy. This is hidden from the domain verification agent so that no
- * behavior is made based on the result.
+ * The provided state code is incorrect. The domain verification agent is only allowed to
+ * assign {@link DomainVerificationInfo#STATE_SUCCESS} or error codes equal to or greater than
+ * {@link DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED}.
*
* @hide
*/
- public static boolean isStateDefault(@DomainVerificationState.State int state) {
- switch (state) {
- case DomainVerificationState.STATE_NO_RESPONSE:
- case DomainVerificationState.STATE_MIGRATED:
- case DomainVerificationState.STATE_RESTORED:
- return true;
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_APPROVED:
- case DomainVerificationState.STATE_DENIED:
- case DomainVerificationState.STATE_LEGACY_FAILURE:
- case DomainVerificationState.STATE_SYS_CONFIG:
- default:
- return false;
- }
- }
+ @SystemApi
+ public static final int ERROR_INVALID_STATE_CODE = 6;
/**
+ * Used to communicate through {@link ServiceSpecificException}. Should not be exposed as API.
+ *
* @hide
*/
- public static final int ERROR_INVALID_DOMAIN_SET = 1;
- /**
- * @hide
- */
- public static final int ERROR_NAME_NOT_FOUND = 2;
+ public static final int INTERNAL_ERROR_NAME_NOT_FOUND = 1;
/**
* @hide
*/
@IntDef(prefix = {"ERROR_"}, value = {
- ERROR_INVALID_DOMAIN_SET,
- ERROR_NAME_NOT_FOUND,
+ ERROR_DOMAIN_SET_ID_INVALID,
+ ERROR_DOMAIN_SET_ID_NULL,
+ ERROR_DOMAIN_SET_NULL_OR_EMPTY,
+ ERROR_UNKNOWN_DOMAIN,
+ ERROR_UNABLE_TO_APPROVE,
+ ERROR_INVALID_STATE_CODE
})
- private @interface Error {
+ public @interface Error {
}
private final Context mContext;
private final IDomainVerificationManager mDomainVerificationManager;
-
/**
* System service to access the domain verification APIs.
* <p>
@@ -289,27 +232,24 @@ public final class DomainVerificationManager {
* @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
* @param domains List of host names to change the state of.
* @param state See {@link DomainVerificationInfo#getHostToStateMap()}.
- * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
- * invalid. This usually means the work being processed by the
- * verification agent is outdated and a new request should be
- * scheduled, if one has not already been done as part of the
- * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
* @throws NameNotFoundException If the ID is known to be good, but the package is
* unavailable. This may be because the package is installed on
* a volume that is no longer mounted. This error is
* unrecoverable until the package is available again, and
* should not be re-tried except on a time scheduled basis.
+ * @return error code or {@link #STATUS_OK} if successful
+ *
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
- public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
- @DomainVerificationState.State int state) throws NameNotFoundException {
+ public int setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ int state) throws NameNotFoundException {
try {
- mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
+ return mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
new DomainSet(domains), state);
} catch (Exception e) {
- Exception converted = rethrow(e, domainSetId);
+ Exception converted = rethrow(e, null);
if (converted instanceof NameNotFoundException) {
throw (NameNotFoundException) converted;
} else if (converted instanceof RuntimeException) {
@@ -338,7 +278,7 @@ public final class DomainVerificationManager {
mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
allowed, mContext.getUserId());
} catch (Exception e) {
- Exception converted = rethrow(e, packageName);
+ Exception converted = rethrow(e, null);
if (converted instanceof NameNotFoundException) {
throw (NameNotFoundException) converted;
} else if (converted instanceof RuntimeException) {
@@ -372,24 +312,24 @@ public final class DomainVerificationManager {
* @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
* @param domains The domains to toggle the state of.
* @param enabled Whether or not the app should automatically open the domains specified.
- * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
- * invalid.
* @throws NameNotFoundException If the ID is known to be good, but the package is
* unavailable. This may be because the package is installed on
* a volume that is no longer mounted. This error is
* unrecoverable until the package is available again, and
* should not be re-tried except on a time scheduled basis.
+ * @return error code or {@link #STATUS_OK} if successful
+ *
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
- public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled) throws NameNotFoundException {
try {
- mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
- new DomainSet(domains), enabled, mContext.getUserId());
+ return mDomainVerificationManager.setDomainVerificationUserSelection(
+ domainSetId.toString(), new DomainSet(domains), enabled, mContext.getUserId());
} catch (Exception e) {
- Exception converted = rethrow(e, domainSetId);
+ Exception converted = rethrow(e, null);
if (converted instanceof NameNotFoundException) {
throw (NameNotFoundException) converted;
} else if (converted instanceof RuntimeException) {
@@ -447,123 +387,22 @@ public final class DomainVerificationManager {
}
}
- private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
- return rethrow(exception, domainSetId, null);
- }
-
private Exception rethrow(Exception exception, @Nullable String packageName) {
- return rethrow(exception, null, packageName);
- }
-
- private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
- @Nullable String packageName) {
if (exception instanceof ServiceSpecificException) {
- int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
+ int serviceSpecificErrorCode = ((ServiceSpecificException) exception).errorCode;
if (packageName == null) {
packageName = exception.getMessage();
}
- @Error int managerErrorCode = packedErrorCode & 0xFFFF;
- switch (managerErrorCode) {
- case ERROR_INVALID_DOMAIN_SET:
- int errorSpecificCode = packedErrorCode >> 16;
- return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
- domainSetId, packageName, errorSpecificCode));
- case ERROR_NAME_NOT_FOUND:
- return new NameNotFoundException(packageName);
- default:
- return exception;
+ if (serviceSpecificErrorCode == INTERNAL_ERROR_NAME_NOT_FOUND) {
+ return new NameNotFoundException(packageName);
}
+
+ return exception;
} else if (exception instanceof RemoteException) {
return ((RemoteException) exception).rethrowFromSystemServer();
} else {
return exception;
}
}
-
- /**
- * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
- * provided by the caller is no longer valid. This may be recoverable, and the caller should
- * re-query the package name associated with the ID using
- * {@link #getDomainVerificationInfo(String)}
- * in order to check. If that also fails, then the package is no longer known to the device and
- * thus all pending work for it should be dropped.
- *
- * @hide
- */
- public static class InvalidDomainSetException extends IllegalArgumentException {
-
- public static final int REASON_ID_NULL = 1;
- public static final int REASON_ID_INVALID = 2;
- public static final int REASON_SET_NULL_OR_EMPTY = 3;
- public static final int REASON_UNKNOWN_DOMAIN = 4;
- public static final int REASON_UNABLE_TO_APPROVE = 5;
-
- /**
- * @hide
- */
- @IntDef({
- REASON_ID_NULL,
- REASON_ID_INVALID,
- REASON_SET_NULL_OR_EMPTY,
- REASON_UNKNOWN_DOMAIN,
- REASON_UNABLE_TO_APPROVE
- })
- public @interface Reason {
- }
-
- public static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName,
- @Reason int reason) {
- switch (reason) {
- case REASON_ID_NULL:
- return "Domain set ID cannot be null";
- case REASON_ID_INVALID:
- return "Domain set ID " + domainSetId + " has been invalidated";
- case REASON_SET_NULL_OR_EMPTY:
- return "Domain set cannot be null or empty";
- case REASON_UNKNOWN_DOMAIN:
- return "Domain set contains value that was not declared by the target package "
- + packageName;
- case REASON_UNABLE_TO_APPROVE:
- return "Domain set contains value that was owned by another package";
- default:
- return "Unknown failure";
- }
- }
-
- @Reason
- private final int mReason;
-
- @Nullable
- private final UUID mDomainSetId;
-
- @Nullable
- private final String mPackageName;
-
- /**
- * @hide
- */
- public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
- @Reason int reason) {
- super(buildMessage(domainSetId, packageName, reason));
- mDomainSetId = domainSetId;
- mPackageName = packageName;
- mReason = reason;
- }
-
- @Nullable
- public UUID getDomainSetId() {
- return mDomainSetId;
- }
-
- @Nullable
- public String getPackageName() {
- return mPackageName;
- }
-
- @Reason
- public int getReason() {
- return mReason;
- }
- }
}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationState.java b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
index 17593ef2aeb1..8e28042bf581 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationState.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
@@ -17,15 +17,13 @@
package android.content.pm.verify.domain;
import android.annotation.IntDef;
+import android.annotation.NonNull;
/**
* @hide
*/
public interface DomainVerificationState {
- /**
- * @hide
- */
@IntDef({
STATE_NO_RESPONSE,
STATE_SUCCESS,
@@ -42,12 +40,12 @@ public interface DomainVerificationState {
// TODO(b/159952358): Document all the places that states need to be updated when one is added
/**
- * @see DomainVerificationManager#STATE_NO_RESPONSE
+ * @see DomainVerificationInfo#STATE_NO_RESPONSE
*/
int STATE_NO_RESPONSE = 0;
/**
- * @see DomainVerificationManager#STATE_SUCCESS
+ * @see DomainVerificationInfo#STATE_SUCCESS
*/
int STATE_SUCCESS = 1;
@@ -94,7 +92,132 @@ public interface DomainVerificationState {
int STATE_SYS_CONFIG = 7;
/**
- * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED
+ * @see DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED
*/
int STATE_FIRST_VERIFIER_DEFINED = 0b10000000000;
+
+ @NonNull
+ static String stateToDebugString(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ return "none";
+ case DomainVerificationState.STATE_SUCCESS:
+ return "verified";
+ case DomainVerificationState.STATE_APPROVED:
+ return "approved";
+ case DomainVerificationState.STATE_DENIED:
+ return "denied";
+ case DomainVerificationState.STATE_MIGRATED:
+ return "migrated";
+ case DomainVerificationState.STATE_RESTORED:
+ return "restored";
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ return "legacy_failure";
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return "system_configured";
+ default:
+ return String.valueOf(state);
+ }
+ }
+
+ /**
+ * For determining re-verify policy. This is hidden from the domain verification agent so that
+ * no behavior is made based on the result.
+ */
+ static boolean isDefault(@State int state) {
+ switch (state) {
+ case STATE_NO_RESPONSE:
+ case STATE_MIGRATED:
+ case STATE_RESTORED:
+ return true;
+ case STATE_SUCCESS:
+ case STATE_APPROVED:
+ case STATE_DENIED:
+ case STATE_LEGACY_FAILURE:
+ case STATE_SYS_CONFIG:
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if a state considers the corresponding domain to be successfully verified. The domain
+ * verification agent may use this to determine whether or not to re-verify a domain.
+ */
+ static boolean isVerified(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_MIGRATED:
+ case DomainVerificationState.STATE_RESTORED:
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return true;
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_DENIED:
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if a state is modifiable by the domain verification agent. This is useful as the
+ * platform may add new state codes in newer versions, and older verification agents can use
+ * this method to determine if a state can be changed without having to be aware of what the new
+ * state means.
+ */
+ static boolean isModifiable(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_MIGRATED:
+ case DomainVerificationState.STATE_RESTORED:
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ return true;
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_DENIED:
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return false;
+ default:
+ return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+ }
+ }
+
+ /**
+ * Whether the state is migrated when updating a package. Generally this is only for states
+ * that maintain verification state or were set by an explicit user or developer action.
+ */
+ static boolean shouldMigrate(@State int state) {
+ switch (state) {
+ case STATE_SUCCESS:
+ case STATE_MIGRATED:
+ case STATE_RESTORED:
+ case STATE_APPROVED:
+ case STATE_DENIED:
+ return true;
+ case STATE_NO_RESPONSE:
+ case STATE_LEGACY_FAILURE:
+ case STATE_SYS_CONFIG:
+ case STATE_FIRST_VERIFIER_DEFINED:
+ default:
+ return false;
+ }
+ }
+
+ @DomainVerificationInfo.State
+ static int convertToInfoState(@State int internalState) {
+ if (internalState >= STATE_FIRST_VERIFIER_DEFINED) {
+ return internalState;
+ } else if (internalState == STATE_NO_RESPONSE) {
+ return DomainVerificationInfo.STATE_NO_RESPONSE;
+ } else if (internalState == STATE_SUCCESS) {
+ return DomainVerificationInfo.STATE_SUCCESS;
+ } else if (!isModifiable(internalState)) {
+ return DomainVerificationInfo.STATE_UNMODIFIABLE;
+ } else if (isVerified(internalState)) {
+ return DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED;
+ } else {
+ return DomainVerificationInfo.STATE_MODIFIABLE_UNVERIFIED;
+ }
+ }
}
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
index 332b92544581..53205f3ea470 100644
--- a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -40,10 +40,10 @@ interface IDomainVerificationManager {
@nullable
List<DomainOwner> getOwnersForDomain(String domain, int userId);
- void setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
+ int setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);
- void setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
+ int setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
boolean enabled, int userId);
}
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/core/java/android/net/IOnCompleteListener.aidl
index 7979afc54f90..4bb89f6c89e4 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/core/java/android/net/IOnCompleteListener.aidl
@@ -18,6 +18,6 @@
package android.net;
/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
+oneway interface IOnCompleteListener {
void onComplete();
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e75e224d9a6f..ab1f688d60ca 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,8 +16,11 @@
package android.os;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.system.ErrnoException;
@@ -108,6 +111,7 @@ public class Process {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int VPN_UID = 1016;
/**
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index df4ade09753b..d89c3d591d46 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -21,6 +21,7 @@ import android.util.ArrayMap;
import android.util.Slog;
import java.io.PrintWriter;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -354,6 +355,23 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
+ * Performs {@code action} on each callback and associated cookie, calling {@link
+ * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
+ *
+ * @hide
+ */
+ public <C> void broadcast(BiConsumer<E, C> action) {
+ int itemCount = beginBroadcast();
+ try {
+ for (int i = 0; i < itemCount; i++) {
+ action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+ }
+ } finally {
+ finishBroadcast();
+ }
+ }
+
+ /**
* Returns the number of registered callbacks. Note that the number of registered
* callbacks may differ from the value returned by {@link #beginBroadcast()} since
* the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index a0875a275ec6..d0000005c237 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -1353,10 +1353,7 @@ public class TelephonyCallback {
/**
* Interface for current physical channel configuration listener.
- *
- * @hide
*/
- @SystemApi
public interface PhysicalChannelConfigListener {
/**
* Callback invoked when the current physical channel configuration has changed
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index 7f6c4b474d3a..d347f31eb934 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -17,6 +17,7 @@
package android.view.translation;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import com.android.internal.os.IResultReceiver;
@@ -40,4 +41,7 @@ oneway interface ITranslationManager {
void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec,
in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId,
int userId);
+
+ void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId);
+ void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId);
}
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index 7c73e701b7c8..852ffe8303b1 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -16,28 +16,36 @@
package android.view.translation;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.assist.ActivityId;
import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillId;
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
+// TODO(b/178044703): Describe what UI Translation is.
/**
* The {@link UiTranslationManager} class provides ways for apps to use the ui translation
* function in framework.
- *
- * @hide
*/
-@SystemApi
public final class UiTranslationManager {
private static final String TAG = "UiTranslationManager";
@@ -88,6 +96,14 @@ public final class UiTranslationManager {
public @interface UiTranslationState {
}
+ // Keys for the data transmitted in the internal UI Translation state callback.
+ /** @hide */
+ public static final String EXTRA_STATE = "state";
+ /** @hide */
+ public static final String EXTRA_SOURCE_LOCALE = "source_locale";
+ /** @hide */
+ public static final String EXTRA_TARGET_LOCALE = "target_locale";
+
@NonNull
private final Context mContext;
@@ -111,9 +127,12 @@ public final class UiTranslationManager {
* @param destSpec {@link TranslationSpec} for the translated data.
* @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
int taskId) {
@@ -141,8 +160,11 @@ public final class UiTranslationManager {
* @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
* @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
@NonNull ActivityId activityId) {
@@ -171,9 +193,12 @@ public final class UiTranslationManager {
* NOTE: Please use {@code finishTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void finishTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
@@ -191,8 +216,11 @@ public final class UiTranslationManager {
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void finishTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -212,9 +240,12 @@ public final class UiTranslationManager {
* NOTE: Please use {@code pauseTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void pauseTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
@@ -232,8 +263,11 @@ public final class UiTranslationManager {
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void pauseTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -253,9 +287,12 @@ public final class UiTranslationManager {
* NOTE: Please use {@code resumeTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
+ *
+ * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void resumeTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
@@ -273,8 +310,11 @@ public final class UiTranslationManager {
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ @SystemApi
public void resumeTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -286,4 +326,105 @@ public final class UiTranslationManager {
throw e.rethrowFromSystemServer();
}
}
+
+ // TODO(b/178044703): Fix the View API link when it becomes public.
+ /**
+ * Register for notifications of UI Translation state changes on the foreground activity. This
+ * is available to the owning application itself and also the current input method.
+ * <p>
+ * The application whose UI is being translated can use this to customize the UI Translation
+ * behavior in ways that aren't made easy by methods like
+ * View#onCreateTranslationRequest().
+ * <p>
+ * Input methods can use this to offer complementary features to UI Translation; for example,
+ * enabling outgoing message translation when the system is translating incoming messages in a
+ * communication app.
+ *
+ * @param callback the callback to register for receiving the state change
+ * notifications
+ */
+ public void registerUiTranslationStateCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull UiTranslationStateCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ synchronized (mCallbacks) {
+ if (mCallbacks.containsKey(callback)) {
+ Log.w(TAG, "registerUiTranslationStateCallback: callback already registered;"
+ + " ignoring.");
+ return;
+ }
+ final IRemoteCallback remoteCallback =
+ new UiTranslationStateRemoteCallback(executor, callback);
+ try {
+ mService.registerUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCallbacks.put(callback, remoteCallback);
+ }
+ }
+
+ /**
+ * Unregister {@code callback}.
+ *
+ * @see #registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)
+ */
+ public void unregisterUiTranslationStateCallback(@NonNull UiTranslationStateCallback callback) {
+ Objects.requireNonNull(callback);
+
+ synchronized (mCallbacks) {
+ final IRemoteCallback remoteCallback = mCallbacks.get(callback);
+ if (remoteCallback == null) {
+ Log.w(TAG, "unregisterUiTranslationStateCallback: callback not found; ignoring.");
+ return;
+ }
+ try {
+ mService.unregisterUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCallbacks.remove(callback);
+ }
+ }
+
+ @NonNull
+ @GuardedBy("mCallbacks")
+ private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>();
+
+ private static class UiTranslationStateRemoteCallback extends IRemoteCallback.Stub {
+ private final Executor mExecutor;
+ private final UiTranslationStateCallback mCallback;
+
+ UiTranslationStateRemoteCallback(Executor executor,
+ UiTranslationStateCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void sendResult(Bundle bundle) {
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> onStateChange(bundle)));
+ }
+
+ private void onStateChange(Bundle bundle) {
+ int state = bundle.getInt(EXTRA_STATE);
+ switch (state) {
+ case STATE_UI_TRANSLATION_STARTED:
+ case STATE_UI_TRANSLATION_RESUMED:
+ mCallback.onStarted(
+ bundle.getString(EXTRA_SOURCE_LOCALE),
+ bundle.getString(EXTRA_TARGET_LOCALE));
+ break;
+ case STATE_UI_TRANSLATION_PAUSED:
+ mCallback.onPaused();
+ break;
+ case STATE_UI_TRANSLATION_FINISHED:
+ mCallback.onFinished();
+ break;
+ default:
+ Log.wtf(TAG, "Unexpected translation state:" + state);
+ }
+ }
+ }
}
diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java
new file mode 100644
index 000000000000..1946b703935d
--- /dev/null
+++ b/core/java/android/view/translation/UiTranslationStateCallback.java
@@ -0,0 +1,48 @@
+/*
+ * 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.view.translation;
+
+import android.annotation.NonNull;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for listening to UI Translation state changes. See {@link
+ * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}.
+ */
+public interface UiTranslationStateCallback {
+
+ /**
+ * The system is requesting translation of the UI from {@code sourceLocale} to {@code
+ * targetLocale}.
+ * <p>
+ * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
+ * changed; or called again after {@link #onPaused()}.
+ */
+ void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale);
+
+ /**
+ * The system is requesting that the application temporarily show the UI contents in their
+ * original language.
+ */
+ void onPaused();
+
+ /**
+ * The UI Translation session has ended.
+ */
+ void onFinished();
+}
diff --git a/core/java/com/android/internal/util/LocationPermissionChecker.java b/core/java/com/android/internal/util/LocationPermissionChecker.java
deleted file mode 100644
index d67bd7a853c8..000000000000
--- a/core/java/com/android/internal/util/LocationPermissionChecker.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * 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.internal.util;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.net.NetworkStack;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-
-/**
- * The methods used for location permission and location mode checking.
- *
- * @hide
- */
-public class LocationPermissionChecker {
-
- private static final String TAG = "LocationPermissionChecker";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"LOCATION_PERMISSION_CHECK_STATUS_"}, value = {
- SUCCEEDED,
- ERROR_LOCATION_MODE_OFF,
- ERROR_LOCATION_PERMISSION_MISSING,
- })
- public @interface LocationPermissionCheckStatus{}
-
- // The location permission check succeeded.
- public static final int SUCCEEDED = 0;
- // The location mode turns off for the caller.
- public static final int ERROR_LOCATION_MODE_OFF = 1;
- // The location permission isn't granted for the caller.
- public static final int ERROR_LOCATION_PERMISSION_MISSING = 2;
-
- private final Context mContext;
- private final AppOpsManager mAppOpsManager;
-
- public LocationPermissionChecker(Context context) {
- mContext = context;
- mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- }
-
- /**
- * Check location permission granted by the caller.
- *
- * This API check if the location mode enabled for the caller and the caller has
- * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
- *
- * @param pkgName package name of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- *
- * @return {@code true} returns if the caller has location permission and the location mode is
- * enabled.
- */
- public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
- int uid, @Nullable String message) {
- return checkLocationPermissionInternal(pkgName, featureId, uid, message) == SUCCEEDED;
- }
-
- /**
- * Check location permission granted by the caller.
- *
- * This API check if the location mode enabled for the caller and the caller has
- * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
- * Compared with {@link #checkLocationPermission(String, String, int, String)}, this API returns
- * the detail information about the checking result, including the reason why it's failed and
- * logs the error for the caller.
- *
- * @param pkgName package name of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- *
- * @return {@link LocationPermissionCheckStatus} the result of the location permission check.
- */
- public @LocationPermissionCheckStatus int checkLocationPermissionWithDetailInfo(
- String pkgName, @Nullable String featureId, int uid, @Nullable String message) {
- final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
- switch (result) {
- case ERROR_LOCATION_MODE_OFF:
- Log.e(TAG, "Location mode is disabled for the device");
- break;
- case ERROR_LOCATION_PERMISSION_MISSING:
- Log.e(TAG, "UID " + uid + " has no location permission");
- break;
- }
- return result;
- }
-
- /**
- * Enforce the caller has location permission.
- *
- * This API determines if the location mode enabled for the caller and the caller has
- * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
- * SecurityException is thrown if the caller has no permission or the location mode is disabled.
- *
- * @param pkgName package name of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- */
- public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
- @Nullable String message) throws SecurityException {
- final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
-
- switch (result) {
- case ERROR_LOCATION_MODE_OFF:
- throw new SecurityException("Location mode is disabled for the device");
- case ERROR_LOCATION_PERMISSION_MISSING:
- throw new SecurityException("UID " + uid + " has no location permission");
- }
- }
-
- private int checkLocationPermissionInternal(String pkgName, @Nullable String featureId,
- int uid, @Nullable String message) {
- checkPackage(uid, pkgName);
-
- // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK
- // are granted a bypass.
- if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
- || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) {
- return SUCCEEDED;
- }
-
- // Location mode must be enabled
- if (!isLocationModeEnabled()) {
- return ERROR_LOCATION_MODE_OFF;
- }
-
- // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
- // location information.
- if (!checkCallersLocationPermission(pkgName, featureId, uid,
- true /* coarseForTargetSdkLessThanQ */, message)) {
- return ERROR_LOCATION_PERMISSION_MISSING;
- }
- return SUCCEEDED;
- }
-
- /**
- * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
- * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level)
- * and a corresponding app op is allowed for this package and uid.
- *
- * @param pkgName PackageName of the application requesting access
- * @param featureId The feature in the package
- * @param uid The uid of the package
- * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
- * else (false or targetSDK >= Q) then will check for FINE
- * @param message A message describing why the permission was checked. Only needed if this is
- * not inside of a two-way binder call from the data receiver
- */
- public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
- int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
-
- boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
-
- String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
- if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
- // Having FINE permission implies having COARSE permission (but not the reverse)
- permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
- }
- if (getUidPermission(permissionType, uid) == PackageManager.PERMISSION_DENIED) {
- return false;
- }
-
- // Always checking FINE - even if will not enforce. This will record the request for FINE
- // so that a location request by the app is surfaced to the user.
- boolean isFineLocationAllowed = noteAppOpAllowed(
- AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
- if (isFineLocationAllowed) {
- return true;
- }
- if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
- return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid,
- message);
- }
- return false;
- }
-
- /**
- * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
- */
- public boolean isLocationModeEnabled() {
- final LocationManager LocationManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- try {
- return LocationManager.isLocationEnabledForUser(UserHandle.of(
- getCurrentUser()));
- } catch (Exception e) {
- Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
- return false;
- }
- }
-
- private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mContext.getPackageManager().getApplicationInfoAsUser(
- packageName, 0,
- UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
- < versionCode) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // In case of exception, assume unknown app (more strict checking)
- // Note: This case will never happen since checkPackage is
- // called to verify validity before checking App's version.
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return false;
- }
-
- private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
- int uid, @Nullable String message) {
- return mAppOpsManager.noteOp(op, uid, pkgName, featureId, message)
- == AppOpsManager.MODE_ALLOWED;
- }
-
- private void checkPackage(int uid, String pkgName)
- throws SecurityException {
- if (pkgName == null) {
- throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
- }
- mAppOpsManager.checkPackage(uid, pkgName);
- }
-
- @VisibleForTesting
- protected int getCurrentUser() {
- return ActivityManager.getCurrentUser();
- }
-
- private int getUidPermission(String permissionType, int uid) {
- // We don't care about pid, pass in -1
- return mContext.checkPermission(permissionType, -1, uid);
- }
-
- /**
- * Returns true if the |uid| holds NETWORK_SETTINGS permission.
- */
- public boolean checkNetworkSettingsPermission(int uid) {
- return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
- */
- public boolean checkNetworkSetupWizardPermission(int uid) {
- return getUidPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the |uid| holds NETWORK_STACK permission.
- */
- public boolean checkNetworkStackPermission(int uid) {
- return getUidPermission(android.Manifest.permission.NETWORK_STACK, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
- */
- public boolean checkMainlineNetworkStackPermission(int uid) {
- return getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
-}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 276cad9f6d6d..bbd738bc9eb6 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -59,8 +59,6 @@ public class Protocol {
public static final int BASE_TETHERING = 0x00050000;
public static final int BASE_NSD_MANAGER = 0x00060000;
public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000;
- public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000;
- public static final int BASE_NETWORK_AGENT = 0x00081000;
public static final int BASE_NETWORK_FACTORY = 0x00083000;
public static final int BASE_ETHERNET = 0x00084000;
public static final int BASE_LOWPAN = 0x00085000;
diff --git a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
deleted file mode 100644
index 7175f562d7ef..000000000000
--- a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * 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.internal.util;
-
-import static android.Manifest.permission.NETWORK_SETTINGS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.HashMap;
-
-/** Unit tests for {@link LocationPermissionChecker}. */
-public class LocationPermissionCheckerTest {
-
- public static final String TAG = "ConnectivityUtilTest";
-
- // Mock objects for testing
- @Mock private Context mMockContext;
- @Mock private PackageManager mMockPkgMgr;
- @Mock private ApplicationInfo mMockApplInfo;
- @Mock private AppOpsManager mMockAppOps;
- @Mock private UserManager mMockUserManager;
- @Mock private LocationManager mLocationManager;
-
- private static final String TEST_PKG_NAME = "com.google.somePackage";
- private static final String TEST_FEATURE_ID = "com.google.someFeature";
- private static final int MANAGED_PROFILE_UID = 1100000;
- private static final int OTHER_USER_UID = 1200000;
-
- private final String mInteractAcrossUsersFullPermission =
- "android.permission.INTERACT_ACROSS_USERS_FULL";
- private final String mManifestStringCoarse =
- Manifest.permission.ACCESS_COARSE_LOCATION;
- private final String mManifestStringFine =
- Manifest.permission.ACCESS_FINE_LOCATION;
-
- // Test variables
- private int mWifiScanAllowApps;
- private int mUid;
- private int mCoarseLocationPermission;
- private int mAllowCoarseLocationApps;
- private int mFineLocationPermission;
- private int mAllowFineLocationApps;
- private int mNetworkSettingsPermission;
- private int mCurrentUser;
- private boolean mIsLocationEnabled;
- private boolean mThrowSecurityException;
- private Answer<Integer> mReturnPermission;
- private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
- private LocationPermissionChecker mChecker;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- initTestVars();
- }
-
- private void setupMocks() throws Exception {
- when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any()))
- .thenReturn(mMockApplInfo);
- when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
- when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,
- TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps);
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),
- eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
- .thenReturn(mAllowCoarseLocationApps);
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),
- eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
- .thenReturn(mAllowFineLocationApps);
- if (mThrowSecurityException) {
- doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong"
- + " to application bound to user " + mUid))
- .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME);
- }
- when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
- .thenReturn(mMockAppOps);
- when(mMockContext.getSystemService(Context.USER_SERVICE))
- .thenReturn(mMockUserManager);
- when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
- }
-
- private void setupTestCase() throws Exception {
- setupMocks();
- setupMockInterface();
- mChecker = new LocationPermissionChecker(mMockContext);
- }
-
- private void initTestVars() {
- mPermissionsList.clear();
- mReturnPermission = createPermissionAnswer();
- mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
- mUid = OTHER_USER_UID;
- mThrowSecurityException = true;
- mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
- mIsLocationEnabled = false;
- mCurrentUser = ActivityManager.getCurrentUser();
- mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
- mFineLocationPermission = PackageManager.PERMISSION_DENIED;
- mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
- mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
- mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED;
- }
-
- private void setupMockInterface() {
- Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid());
- doAnswer(mReturnPermission).when(mMockContext).checkPermission(
- anyString(), anyInt(), anyInt());
- when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM,
- UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)))
- .thenReturn(true);
- when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid))
- .thenReturn(mCoarseLocationPermission);
- when(mMockContext.checkPermission(mManifestStringFine, -1, mUid))
- .thenReturn(mFineLocationPermission);
- when(mMockContext.checkPermission(NETWORK_SETTINGS, -1, mUid))
- .thenReturn(mNetworkSettingsPermission);
- when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled);
- }
-
- private Answer<Integer> createPermissionAnswer() {
- return new Answer<Integer>() {
- @Override
- public Integer answer(InvocationOnMock invocation) {
- int myUid = (int) invocation.getArguments()[1];
- String myPermission = (String) invocation.getArguments()[0];
- mPermissionsList.get(myPermission);
- if (mPermissionsList.containsKey(myPermission)) {
- int uid = mPermissionsList.get(myPermission);
- if (myUid == uid) {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- return PackageManager.PERMISSION_DENIED;
- }
- };
- }
-
- @Test
- public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception {
- mIsLocationEnabled = true;
- mThrowSecurityException = false;
- mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- mUid = mCurrentUser;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.SUCCEEDED, result);
- }
-
- @Test
- public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception {
- mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- mIsLocationEnabled = true;
- mThrowSecurityException = false;
- mUid = mCurrentUser;
- mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.SUCCEEDED, result);
- }
-
- @Test
- public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception {
- mThrowSecurityException = true;
- mIsLocationEnabled = true;
- mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- setupTestCase();
-
- assertThrows(SecurityException.class,
- () -> mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
- }
-
- @Test
- public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception {
- mThrowSecurityException = false;
- mIsLocationEnabled = true;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
- }
-
- @Test
- public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception {
- mThrowSecurityException = false;
- mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- mIsLocationEnabled = true;
- mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
- mUid = MANAGED_PROFILE_UID;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
- verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
- }
-
- @Test
- public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception {
- mThrowSecurityException = false;
- mUid = MANAGED_PROFILE_UID;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
- mIsLocationEnabled = false;
-
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result);
- }
-
- @Test
- public void testenforceCanAccessScanResults_LocationModeDisabledHasNetworkSettings()
- throws Exception {
- mThrowSecurityException = false;
- mIsLocationEnabled = false;
- mNetworkSettingsPermission = PackageManager.PERMISSION_GRANTED;
- setupTestCase();
-
- final int result =
- mChecker.checkLocationPermissionWithDetailInfo(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
- assertEquals(LocationPermissionChecker.SUCCEEDED, result);
- }
-
-
- private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
- try {
- r.run();
- Assert.fail("Expected " + exceptionClass + " to be thrown.");
- } catch (Exception exception) {
- assertTrue(exceptionClass.isInstance(exception));
- }
- }
-}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 937f01ce3767..a08f390c9fd3 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -207,7 +207,7 @@ public class KeyStore {
case UserState.LSKF_LOCKED:
return KeyStore.State.LOCKED;
default:
- throw new AssertionError(KeyStore.VALUE_CORRUPTED);
+ throw new AssertionError(userState);
}
}
ret = mBinder.getState(userId);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index f8a642a68dd7..4e8a273ecc9e 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -161,6 +161,10 @@ public final class MediaFormat {
public static final String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
+ /** MIME type for MPEG-H Audio single stream */
+ public static final String MIMETYPE_AUDIO_MPEGH_MHA1 = "audio/mha1";
+ /** MIME type for MPEG-H Audio single stream, encapsulated in MHAS */
+ public static final String MIMETYPE_AUDIO_MPEGH_MHM1 = "audio/mhm1";
/**
* MIME type for HEIF still image data encoded in HEVC.
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 243e4ca4295a..f22d4b7b779a 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -87,6 +87,7 @@ package android.net {
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method @Deprecated public static boolean isNetworkTypeValid(int);
+ method public void registerBestMatchingNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index cc95a7cc2413..bb296476c72f 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -11,6 +11,7 @@ package android.net {
method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
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);
field public static final String PRIVATE_DNS_MODE_OFF = "off";
field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index a98f14ea9408..4dca411cca24 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -56,7 +56,7 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
- method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -67,6 +67,8 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+ field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0
+ field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
@@ -78,10 +80,6 @@ package android.net {
field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
}
- public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
- method public void onComplete();
- }
-
@Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
method @Deprecated public void onTetheringFailed();
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 7bfc9702ba6a..ba5eb1090dbf 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -64,6 +64,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -76,7 +77,6 @@ import android.util.SparseIntArray;
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.Protocol;
import libcore.net.event.NetworkEventDispatcher;
@@ -971,6 +971,33 @@ public class ConnectivityManager {
}
/**
+ * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+ * Specify that the traffic for this user should by follow the default rules.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0;
+
+ /**
+ * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+ * Specify that the traffic for this user should by default go on a network with
+ * {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network
+ * if no such network is available.
+ * @hide
+ */
+ @SystemApi
+ public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ PROFILE_NETWORK_PREFERENCE_DEFAULT,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE
+ })
+ public @interface ProfileNetworkPreference {
+ }
+
+ /**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
*
@@ -3523,29 +3550,28 @@ public class ConnectivityManager {
}
}
- private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
/** @hide */
- public static final int CALLBACK_PRECHECK = BASE + 1;
+ public static final int CALLBACK_PRECHECK = 1;
/** @hide */
- public static final int CALLBACK_AVAILABLE = BASE + 2;
+ public static final int CALLBACK_AVAILABLE = 2;
/** @hide arg1 = TTL */
- public static final int CALLBACK_LOSING = BASE + 3;
+ public static final int CALLBACK_LOSING = 3;
/** @hide */
- public static final int CALLBACK_LOST = BASE + 4;
+ public static final int CALLBACK_LOST = 4;
/** @hide */
- public static final int CALLBACK_UNAVAIL = BASE + 5;
+ public static final int CALLBACK_UNAVAIL = 5;
/** @hide */
- public static final int CALLBACK_CAP_CHANGED = BASE + 6;
+ public static final int CALLBACK_CAP_CHANGED = 6;
/** @hide */
- public static final int CALLBACK_IP_CHANGED = BASE + 7;
+ public static final int CALLBACK_IP_CHANGED = 7;
/** @hide obj = NetworkCapabilities, arg1 = seq number */
- private static final int EXPIRE_LEGACY_REQUEST = BASE + 8;
+ private static final int EXPIRE_LEGACY_REQUEST = 8;
/** @hide */
- public static final int CALLBACK_SUSPENDED = BASE + 9;
+ public static final int CALLBACK_SUSPENDED = 9;
/** @hide */
- public static final int CALLBACK_RESUMED = BASE + 10;
+ public static final int CALLBACK_RESUMED = 10;
/** @hide */
- public static final int CALLBACK_BLK_CHANGED = BASE + 11;
+ public static final int CALLBACK_BLK_CHANGED = 11;
/** @hide */
public static String getCallbackName(int whichCallback) {
@@ -4241,9 +4267,27 @@ public class ConnectivityManager {
}
/**
- * @hide
+ * Registers to receive notifications about the best matching network which satisfy the given
+ * {@link NetworkRequest}. The callbacks will continue to be called until
+ * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
+ * called.
+ *
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * {@link #registerNetworkCallback} and its variants and {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+ * networks change state.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
- // TODO: Make it public api.
@SuppressLint("ExecutorRegistration")
public void registerBestMatchingNetworkCallback(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
@@ -5049,19 +5093,6 @@ public class ConnectivityManager {
}
/**
- * Listener for {@link #setOemNetworkPreference(OemNetworkPreferences, Executor,
- * OnSetOemNetworkPreferenceListener)}.
- * @hide
- */
- @SystemApi
- public interface OnSetOemNetworkPreferenceListener {
- /**
- * Called when setOemNetworkPreference() successfully completes.
- */
- void onComplete();
- }
-
- /**
* Used by automotive devices to set the network preferences used to direct traffic at an
* application level as per the given OemNetworkPreferences. An example use-case would be an
* automotive OEM wanting to provide connectivity for applications critical to the usage of a
@@ -5083,16 +5114,16 @@ public class ConnectivityManager {
@RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE)
public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference,
@Nullable @CallbackExecutor final Executor executor,
- @Nullable final OnSetOemNetworkPreferenceListener listener) {
+ @Nullable final Runnable listener) {
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
if (null != listener) {
Objects.requireNonNull(executor, "Executor must be non-null");
}
- final IOnSetOemNetworkPreferenceListener listenerInternal = listener == null ? null :
- new IOnSetOemNetworkPreferenceListener.Stub() {
+ final IOnCompleteListener listenerInternal = listener == null ? null :
+ new IOnCompleteListener.Stub() {
@Override
public void onComplete() {
- executor.execute(listener::onComplete);
+ executor.execute(listener::run);
}
};
@@ -5104,6 +5135,52 @@ public class ConnectivityManager {
}
}
+ /**
+ * Request that a user profile is put by default on a network matching a given preference.
+ *
+ * See the documentation for the individual preferences for a description of the supported
+ * behaviors.
+ *
+ * @param profile the profile concerned.
+ * @param preference the preference for this profile.
+ * @param executor an executor to execute the listener on. Optional if listener is null.
+ * @param listener an optional listener to listen for completion of the operation.
+ * @throws IllegalArgumentException if {@code profile} is not a valid user profile.
+ * @throws SecurityException if missing the appropriate permissions.
+ * @hide
+ */
+ // This function is for establishing per-profile default networking and can only be called by
+ // the device policy manager, running as the system server. It would make no sense to call it
+ // on a context for a user because it does not establish a setting on behalf of a user, rather
+ // it establishes a setting for a user on behalf of the DPM.
+ @SuppressLint({"UserHandle"})
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+ @ProfileNetworkPreference final int preference,
+ @Nullable @CallbackExecutor final Executor executor,
+ @Nullable final Runnable listener) {
+ if (null != listener) {
+ Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
+ }
+ final IOnCompleteListener proxy;
+ if (null == listener) {
+ proxy = null;
+ } else {
+ proxy = new IOnCompleteListener.Stub() {
+ @Override
+ public void onComplete() {
+ executor.execute(listener::run);
+ }
+ };
+ }
+ try {
+ mService.setProfileNetworkPreference(profile, preference, proxy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
// The first network ID of IPSec tunnel interface.
private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512
// The network ID range of IPSec tunnel interface.
@@ -5124,8 +5201,7 @@ public class ConnectivityManager {
/**
* Get private DNS mode from settings.
*
- * @param context The Context to get its ContentResolver to query the private DNS mode from
- * settings.
+ * @param context The Context to query the private DNS mode from settings.
* @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants.
*
* @hide
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
new file mode 100644
index 000000000000..d4543654522f
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -0,0 +1,46 @@
+/*
+ * 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.net;
+
+/**
+ * A manager class for connectivity module settings.
+ *
+ * @hide
+ */
+public class ConnectivitySettingsManager {
+
+ private ConnectivitySettingsManager() {}
+
+ /**
+ * Whether to automatically switch away from wifi networks that lose Internet access.
+ * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
+ * avoids such networks. Valid values are:
+ *
+ * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+ * null: Ask the user whether to switch away from bad wifi.
+ * 1: Avoid bad wifi.
+ */
+ public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
+
+ /**
+ * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
+ * overridden by the system based on device or application state. If null, the value
+ * specified by config_networkMeteredMultipathPreference is used.
+ */
+ public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
+ "network_metered_multipath_preference";
+}
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1bbf1a95fcca..d83cc163b53f 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -20,7 +20,7 @@ import android.app.PendingIntent;
import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager;
import android.net.IConnectivityDiagnosticsCallback;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
import android.net.INetworkActivityListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
@@ -43,6 +43,7 @@ import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.os.UserHandle;
import com.android.connectivity.aidl.INetworkAgent;
@@ -215,5 +216,8 @@ interface IConnectivityManager
void unregisterQosCallback(in IQosCallback callback);
void setOemNetworkPreference(in OemNetworkPreferences preference,
- in IOnSetOemNetworkPreferenceListener listener);
+ in IOnCompleteListener listener);
+
+ void setProfileNetworkPreference(in UserHandle profile, int preference,
+ in IOnCompleteListener listener);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index b3ab0ee8bd3c..a127c6f6de26 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -37,7 +37,6 @@ import android.util.Log;
import com.android.connectivity.aidl.INetworkAgent;
import com.android.connectivity.aidl.INetworkAgentRegistry;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Protocol;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -125,7 +124,10 @@ public abstract class NetworkAgent {
*/
public final int providerId;
- private static final int BASE = Protocol.BASE_NETWORK_AGENT;
+ // ConnectivityService parses message constants from itself and NetworkAgent with MessageUtils
+ // for debugging purposes, and crashes if some messages have the same values.
+ // TODO: have ConnectivityService store message names in different maps and remove this base
+ private static final int BASE = 200;
/**
* Sent by ConnectivityService to the NetworkAgent to inform it of
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 48bd29769f83..5a76cd6d6b0f 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -73,6 +73,14 @@ public final class OemNetworkPreferences implements Parcelable {
private final Bundle mNetworkMappings;
/**
+ * Return whether this object is empty.
+ * @hide
+ */
+ public boolean isEmpty() {
+ return mNetworkMappings.keySet().size() == 0;
+ }
+
+ /**
* Return the currently built application package name to {@link OemNetworkPreference} mappings.
* @return the current network preferences map.
*/
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index c5100794899a..cd8f4c06de65 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -22,9 +22,6 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.SparseArray;
-
-import com.android.internal.util.MessageUtils;
import java.util.Objects;
@@ -38,9 +35,6 @@ import java.util.Objects;
*/
@SystemApi(client = MODULE_LIBRARIES)
public final class VpnTransportInfo implements TransportInfo, Parcelable {
- private static final SparseArray<String> sTypeToString =
- MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"});
-
/** Type of this VPN. */
public final int type;
@@ -63,8 +57,7 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable {
@Override
public String toString() {
- final String typeString = sTypeToString.get(type, "VPN_TYPE_???");
- return String.format("VpnTransportInfo{%s}", typeString);
+ return String.format("VpnTransportInfo{type=%d}", type);
}
@Override
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 739ddada50b4..6a49aa2576c3 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -16,8 +16,8 @@
package android.net.util;
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+import static android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
+import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
@@ -110,8 +110,8 @@ public class MultinetworkPolicyTracker {
mHandler = handler;
mAvoidBadWifiCallback = avoidBadWifiCallback;
mSettingsUris = Arrays.asList(
- Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
- Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
+ Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
+ Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
mResolver = mContext.getContentResolver();
mSettingObserver = new SettingObserver();
mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
index 7d98c76a40ba..06c81921fd3f 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -42,4 +42,14 @@
-->
</string-array>
+ <string-array translatable="false" name="config_legacy_networktype_restore_timers">
+ <item>2,60000</item><!-- mobile_mms -->
+ <item>3,60000</item><!-- mobile_supl -->
+ <item>4,60000</item><!-- mobile_dun -->
+ <item>5,60000</item><!-- mobile_hipri -->
+ <item>10,60000</item><!-- mobile_fota -->
+ <item>11,60000</item><!-- mobile_ims -->
+ <item>12,60000</item><!-- mobile_cbs -->
+ </string-array>
+
</resources> \ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
index 00ec2df0e6f1..da8aee56276c 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -17,11 +17,11 @@
<overlayable name="ServiceConnectivityResourcesConfig">
<policy type="product|system|vendor">
<!-- Configuration values for ConnectivityService -->
+ <item type="array" name="config_legacy_networktype_restore_timers"/>
<item type="string" name="config_networkCaptivePortalServerUrl"/>
<item type="integer" name="config_networkTransitionTimeout"/>
<item type="array" name="config_wakeonlan_supported_interfaces"/>
-
</policy>
</overlayable>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 841a49e6d4fd..cbfd4d8ad07b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -75,7 +75,8 @@ public class WifiStatusTracker {
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
- private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ private final NetworkCallback mNetworkCallback =
+ new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
@Override
public void onAvailable(
Network network, NetworkCapabilities networkCapabilities,
@@ -131,7 +132,8 @@ public class WifiStatusTracker {
}
}
};
- private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
+ private final NetworkCallback mDefaultNetworkCallback =
+ new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
// network is now the default network, and its capabilities are nc.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index a78c223bf883..378907c400d7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -73,6 +73,16 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
}
@Override
+ void onIlluminationStarting() {
+ setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ void onIlluminationStopped() {
+ setVisibility(View.VISIBLE);
+ }
+
+ @Override
public boolean dozeTimeTick() {
// TODO: burnin
mFingerprintDrawable.dozeTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 2cd367d03102..3e3451e64b49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -445,11 +445,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
final ArrayList<String> tiles = new ArrayList<String>();
boolean addedDefault = false;
Set<String> addedSpecs = new ArraySet<>();
- // TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- tiles.add("internet");
- addedSpecs.add("internet");
- }
for (String tile : tileList.split(",")) {
tile = tile.trim();
if (tile.isEmpty()) continue;
@@ -457,17 +452,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (!addedDefault) {
List<String> defaultSpecs = getDefaultSpecs(context);
for (String spec : defaultSpecs) {
- // TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(
- context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (spec.equals("wifi") || spec.equals("cell")) {
- continue;
- }
- } else {
- if (spec.equals("internet")) {
- continue;
- }
- }
if (!addedSpecs.contains(spec)) {
tiles.add(spec);
addedSpecs.add(spec);
@@ -476,18 +460,40 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
addedDefault = true;
}
} else {
- // TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (tile.equals("wifi") || tile.equals("cell")) {
- continue;
- }
- }
if (!addedSpecs.contains(tile)) {
tiles.add(tile);
addedSpecs.add(tile);
}
}
}
+ // TODO(b/174753536): Move it into the config file.
+ // Only do the below hacking when at least one of the below tiles exist
+ // --InternetTile
+ // --WiFiTile
+ // --CellularTIle
+ if (tiles.contains("internet") || tiles.contains("wifi") || tiles.contains("cell")) {
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ if (!tiles.contains("internet")) {
+ tiles.add("internet");
+ }
+ if (tiles.contains("wifi")) {
+ tiles.remove("wifi");
+ }
+ if (tiles.contains("cell")) {
+ tiles.remove("cell");
+ }
+ } else {
+ if (tiles.contains("internet")) {
+ tiles.remove("internet");
+ }
+ if (!tiles.contains("wifi")) {
+ tiles.add("wifi");
+ }
+ if (!tiles.contains("cell")) {
+ tiles.add("cell");
+ }
+ }
+ }
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 7eeb4bd19f1a..32b41ec0ab66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -115,9 +115,32 @@ public class TileQueryHelper {
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
// TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (!possibleTiles.contains("internet")) {
- possibleTiles.add("internet");
+ // Only do the below hacking when at least one of the below tiles exist
+ // --InternetTile
+ // --WiFiTile
+ // --CellularTIle
+ if (possibleTiles.contains("internet") || possibleTiles.contains("wifi")
+ || possibleTiles.contains("cell")) {
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ if (!possibleTiles.contains("internet")) {
+ possibleTiles.add("internet");
+ }
+ if (possibleTiles.contains("wifi")) {
+ possibleTiles.remove("wifi");
+ }
+ if (possibleTiles.contains("cell")) {
+ possibleTiles.remove("cell");
+ }
+ } else {
+ if (possibleTiles.contains("internet")) {
+ possibleTiles.remove("internet");
+ }
+ if (!possibleTiles.contains("wifi")) {
+ possibleTiles.add("wifi");
+ }
+ if (!possibleTiles.contains("cell")) {
+ possibleTiles.add("cell");
+ }
}
}
for (String spec : possibleTiles) {
@@ -125,15 +148,6 @@ public class TileQueryHelper {
// Do not include CustomTile. Those will be created by `addPackageTiles`.
if (spec.startsWith(CustomTile.PREFIX)) continue;
// TODO(b/174753536): Move it into the config file.
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (spec.equals("wifi") || spec.equals("cell")) {
- continue;
- }
- } else {
- if (spec.equals("internet")) {
- continue;
- }
- }
final QSTile tile = host.createTile(spec);
if (tile == null) {
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index fbdaf9cdae20..db039b44d91a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkScoreManager;
@@ -307,7 +308,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback);
}
- ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
+ NetworkCallback callback =
+ new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO){
private Network mLastNetwork;
private NetworkCapabilities mLastNetworkCapabilities;
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
index 2dcf82ff9410..611a37de70f4 100644
--- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
+++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
@@ -17,6 +17,9 @@
package com.android.server;
import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import java.util.ArrayList;
/**
* The BluetoothDeviceConfigListener handles system device config change callback and checks
@@ -30,10 +33,12 @@ import android.provider.DeviceConfig;
class BluetoothDeviceConfigListener {
private static final String TAG = "BluetoothDeviceConfigListener";
- BluetoothManagerService mService;
+ private final BluetoothManagerService mService;
+ private final boolean mLogDebug;
- BluetoothDeviceConfigListener(BluetoothManagerService service) {
+ BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) {
mService = service;
+ mLogDebug = logDebug;
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_BLUETOOTH,
(Runnable r) -> r.run(),
@@ -47,6 +52,13 @@ class BluetoothDeviceConfigListener {
if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
return;
}
+ if (mLogDebug) {
+ ArrayList<String> flags = new ArrayList<>();
+ for (String name : properties.getKeyset()) {
+ flags.add(name + "='" + properties.getString(name, "") + "'");
+ }
+ Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags));
+ }
boolean foundInit = false;
for (String name : properties.getKeyset()) {
if (name.startsWith("INIT_")) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index dc24ffdb936d..f0c9ba93b080 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -454,6 +454,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
&& state == BluetoothProfile.STATE_DISCONNECTED
&& !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
onInitFlagsChanged();
}
}
@@ -820,6 +821,35 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return enabledProfiles;
}
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
+ 0) != 0;
+ }
+
+ // Monitor change of BLE scan only mode settings.
+ private void registerForProvisioningStateChange() {
+ ContentObserver contentObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (!isDeviceProvisioned()) {
+ if (DBG) {
+ Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not "
+ + "provisioned");
+ }
+ return;
+ }
+ if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) {
+ Slog.i(TAG, "Device provisioned, reactivating pending flag changes");
+ onInitFlagsChanged();
+ }
+ }
+ };
+
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false,
+ contentObserver);
+ }
+
// Monitor change of BLE scan only mode settings.
private void registerForBleScanModeChange() {
ContentObserver contentObserver = new ContentObserver(null) {
@@ -1385,7 +1415,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (mBluetoothAirplaneModeListener != null) {
mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
}
- mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this);
+ registerForProvisioningStateChange();
+ mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG);
}
/**
@@ -2229,12 +2260,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+ + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+ + " ms due to existing connections");
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_INIT_FLAGS_CHANGED,
+ DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
+ break;
+ }
+ if (!isDeviceProvisioned()) {
+ Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+ + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+ + "ms because device is not provisioned");
mHandler.sendEmptyMessageDelayed(
MESSAGE_INIT_FLAGS_CHANGED,
DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
break;
}
if (mBluetooth != null && isEnabled()) {
+ Slog.i(TAG, "Restarting Bluetooth due to init flag change");
restartForReason(
BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f666490d13aa..3194bdcaad18 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -17,6 +17,10 @@
package com.android.server;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
+import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
@@ -28,15 +32,30 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
@@ -87,6 +106,7 @@ import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivitySettingsManager;
import android.net.DataStallReportParcelable;
import android.net.DnsResolverServiceManager;
import android.net.ICaptivePortal;
@@ -98,7 +118,7 @@ import android.net.INetworkActivityListener;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
@@ -111,7 +131,6 @@ import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMonitorManager;
@@ -193,13 +212,13 @@ import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.server.connectivity.AutodestructReference;
@@ -215,6 +234,7 @@ import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
+import com.android.server.connectivity.ProfileNetworkPreferences;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -559,8 +579,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
/**
- * used internally when setting the default networks for OemNetworkPreferences.
- * obj = OemNetworkPreferences
+ * Used internally when setting the default networks for OemNetworkPreferences.
+ * obj = Pair<OemNetworkPreferences, listener>
*/
private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48;
@@ -570,6 +590,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int EVENT_REPORT_NETWORK_ACTIVITY = 49;
/**
+ * Used internally when setting a network preference for a user profile.
+ * obj = Pair<ProfileNetworkPreference, Listener>
+ */
+ private static final int EVENT_SET_PROFILE_NETWORK_PREFERENCE = 50;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -618,11 +644,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private UserManager mUserManager;
- private NetworkConfig[] mNetConfigs;
- private int mNetworksDefined;
-
// the set of network types that can only be enabled by system/sig apps
- private List mProtectedNetworks;
+ private List<Integer> mProtectedNetworks;
private Set<String> mWolSupportedInterfaces;
@@ -712,18 +735,63 @@ public class ConnectivityService extends IConnectivityManager.Stub
* They are therefore not thread-safe with respect to each other.
* - getNetworkForType() can be called at any time on binder threads. It is synchronized
* on mTypeLists to be thread-safe with respect to a concurrent remove call.
+ * - getRestoreTimerForType(type) is also synchronized on mTypeLists.
* - dump is thread-safe with respect to concurrent add and remove calls.
*/
private final ArrayList<NetworkAgentInfo> mTypeLists[];
@NonNull
private final ConnectivityService mService;
+ // Restore timers for requestNetworkForFeature (network type -> timer in ms). Types without
+ // an entry have no timer (equivalent to -1). Lazily loaded.
+ @NonNull
+ private ArrayMap<Integer, Integer> mRestoreTimers = new ArrayMap<>();
+
LegacyTypeTracker(@NonNull ConnectivityService service) {
mService = service;
mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
}
- public void addSupportedType(int type) {
+ public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) {
+ final PackageManager pm = ctx.getPackageManager();
+ if (pm.hasSystemFeature(FEATURE_WIFI)) {
+ addSupportedType(TYPE_WIFI);
+ }
+ if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) {
+ addSupportedType(TYPE_WIFI_P2P);
+ }
+ if (tm.isDataCapable()) {
+ // Telephony does not have granular support for these types: they are either all
+ // supported, or none is supported
+ addSupportedType(TYPE_MOBILE);
+ addSupportedType(TYPE_MOBILE_MMS);
+ addSupportedType(TYPE_MOBILE_SUPL);
+ addSupportedType(TYPE_MOBILE_DUN);
+ addSupportedType(TYPE_MOBILE_HIPRI);
+ addSupportedType(TYPE_MOBILE_FOTA);
+ addSupportedType(TYPE_MOBILE_IMS);
+ addSupportedType(TYPE_MOBILE_CBS);
+ addSupportedType(TYPE_MOBILE_IA);
+ addSupportedType(TYPE_MOBILE_EMERGENCY);
+ }
+ if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) {
+ addSupportedType(TYPE_BLUETOOTH);
+ }
+ if (pm.hasSystemFeature(FEATURE_WATCH)) {
+ // TYPE_PROXY is only used on Wear
+ addSupportedType(TYPE_PROXY);
+ }
+ // Ethernet is often not specified in the configs, although many devices can use it via
+ // USB host adapters. Add it as long as the ethernet service is here.
+ if (ctx.getSystemService(Context.ETHERNET_SERVICE) != null) {
+ addSupportedType(TYPE_ETHERNET);
+ }
+
+ // Always add TYPE_VPN as a supported type
+ addSupportedType(TYPE_VPN);
+ }
+
+ private void addSupportedType(int type) {
if (mTypeLists[type] != null) {
throw new IllegalStateException(
"legacy list for type " + type + "already initialized");
@@ -744,6 +812,35 @@ public class ConnectivityService extends IConnectivityManager.Stub
return null;
}
+ public int getRestoreTimerForType(int type) {
+ synchronized (mTypeLists) {
+ if (mRestoreTimers == null) {
+ mRestoreTimers = loadRestoreTimers();
+ }
+ return mRestoreTimers.getOrDefault(type, -1);
+ }
+ }
+
+ private ArrayMap<Integer, Integer> loadRestoreTimers() {
+ final String[] configs = mService.mResources.get().getStringArray(
+ com.android.connectivity.resources.R.array
+ .config_legacy_networktype_restore_timers);
+ final ArrayMap<Integer, Integer> ret = new ArrayMap<>(configs.length);
+ for (final String config : configs) {
+ final String[] splits = TextUtils.split(config, ",");
+ if (splits.length != 2) {
+ logwtf("Invalid restore timer token count: " + config);
+ continue;
+ }
+ try {
+ ret.put(Integer.parseInt(splits[0]), Integer.parseInt(splits[1]));
+ } catch (NumberFormatException e) {
+ logwtf("Invalid restore timer number format: " + config, e);
+ }
+ }
+ return ret;
+ }
+
private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
boolean isDefaultNetwork) {
if (DBG) {
@@ -1166,64 +1263,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
-
- // TODO: What is the "correct" way to do determine if this is a wifi only device?
- boolean wifiOnly = mSystemProperties.getBoolean("ro.radio.noril", false);
- log("wifiOnly=" + wifiOnly);
- String[] naStrings = context.getResources().getStringArray(
- com.android.internal.R.array.networkAttributes);
- for (String naString : naStrings) {
- try {
- NetworkConfig n = new NetworkConfig(naString);
- if (VDBG) log("naString=" + naString + " config=" + n);
- if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
- loge("Error in networkAttributes - ignoring attempt to define type " +
- n.type);
- continue;
- }
- if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) {
- log("networkAttributes - ignoring mobile as this dev is wifiOnly " +
- n.type);
- continue;
- }
- if (mNetConfigs[n.type] != null) {
- loge("Error in networkAttributes - ignoring attempt to redefine type " +
- n.type);
- continue;
- }
- mLegacyTypeTracker.addSupportedType(n.type);
-
- mNetConfigs[n.type] = n;
- mNetworksDefined++;
- } catch(Exception e) {
- // ignore it - leave the entry null
- }
- }
-
- // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config.
- if (mNetConfigs[TYPE_VPN] == null) {
- // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we
- // don't need to add TYPE_VPN to mNetConfigs.
- mLegacyTypeTracker.addSupportedType(TYPE_VPN);
- mNetworksDefined++; // used only in the log() statement below.
- }
-
- // Do the same for Ethernet, since it's often not specified in the configs, although many
- // devices can use it via USB host adapters.
- if (mNetConfigs[TYPE_ETHERNET] == null
- && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
- mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
- mNetworksDefined++;
- }
-
- if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
-
- mProtectedNetworks = new ArrayList<Integer>();
+ mLegacyTypeTracker.loadSupportedTypes(mContext, mTelephonyManager);
+ mProtectedNetworks = new ArrayList<>();
int[] protectedNetworks = context.getResources().getIntArray(
com.android.internal.R.array.config_protectedNetworks);
for (int p : protectedNetworks) {
- if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) {
+ if (mLegacyTypeTracker.isTypeSupported(p) && !mProtectedNetworks.contains(p)) {
mProtectedNetworks.add(p);
} else {
if (DBG) loge("Ignoring protectedNetwork " + p);
@@ -1290,11 +1335,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
+ return createDefaultNetworkCapabilitiesForUidRange(new UidRange(uid, uid));
+ }
+
+ private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRange(
+ @NonNull final UidRange uids) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
- netCap.setSingleUid(uid);
+ netCap.setUids(Collections.singleton(uids));
return netCap;
}
@@ -2604,13 +2654,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
} catch (RemoteException | ServiceSpecificException e) {
loge("Can't set TCP buffer sizes:" + e);
}
-
- final Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TCP_DEFAULT_INIT_RWND,
- mSystemProperties.getInt("net.tcp.default_init_rwnd", 0));
- if (rwndValue != 0) {
- mSystemProperties.setTcpInitRwnd(rwndValue);
- }
}
@Override
@@ -2627,9 +2670,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// if the system property isn't set, use the value for the apn type
int ret = RESTORE_DEFAULT_NETWORK_DELAY;
- if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) &&
- (mNetConfigs[networkType] != null)) {
- ret = mNetConfigs[networkType].restoreTime;
+ if (mLegacyTypeTracker.isTypeSupported(networkType)) {
+ ret = mLegacyTypeTracker.getRestoreTimerForType(networkType);
}
return ret;
}
@@ -4533,12 +4575,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
break;
case EVENT_SET_OEM_NETWORK_PREFERENCE: {
- final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg =
- (Pair<OemNetworkPreferences,
- IOnSetOemNetworkPreferenceListener>) msg.obj;
+ final Pair<OemNetworkPreferences, IOnCompleteListener> arg =
+ (Pair<OemNetworkPreferences, IOnCompleteListener>) msg.obj;
handleSetOemNetworkPreference(arg.first, arg.second);
break;
}
+ case EVENT_SET_PROFILE_NETWORK_PREFERENCE: {
+ final Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener> arg =
+ (Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener>)
+ msg.obj;
+ handleSetProfileNetworkPreference(arg.first, arg.second);
+ }
case EVENT_REPORT_NETWORK_ACTIVITY:
mNetworkActivityTracker.handleReportNetworkActivity();
break;
@@ -4837,6 +4884,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
Log.wtf(TAG, s);
}
+ private static void logwtf(String s, Throwable t) {
+ Log.wtf(TAG, s, t);
+ }
+
private static void loge(String s) {
Log.e(TAG, s);
}
@@ -5109,6 +5160,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void onUserRemoved(UserHandle user) {
mPermissionMonitor.onUserRemoved(user);
+ // If there was a network preference for this user, remove it.
+ handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
+ null /* listener */);
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
}
@@ -5907,10 +5961,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
- // Current OEM network preferences.
+ // Current OEM network preferences. This object must only be written to on the handler thread.
+ // Since it is immutable and always non-null, other threads may read it if they only care
+ // about seeing a consistent object but not that it is current.
@NonNull
private OemNetworkPreferences mOemNetworkPreferences =
new OemNetworkPreferences.Builder().build();
+ // Current per-profile network preferences. This object follows the same threading rules as
+ // the OEM network preferences above.
+ @NonNull
+ private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
// The always-on request for an Internet-capable network that apps without a specific default
// fall back to.
@@ -8185,7 +8245,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
+ ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null);
}
@Override
@@ -8890,13 +8950,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
private int transportTypeToLegacyType(int type) {
switch (type) {
case NetworkCapabilities.TRANSPORT_CELLULAR:
- return ConnectivityManager.TYPE_MOBILE;
+ return TYPE_MOBILE;
case NetworkCapabilities.TRANSPORT_WIFI:
- return ConnectivityManager.TYPE_WIFI;
+ return TYPE_WIFI;
case NetworkCapabilities.TRANSPORT_BLUETOOTH:
- return ConnectivityManager.TYPE_BLUETOOTH;
+ return TYPE_BLUETOOTH;
case NetworkCapabilities.TRANSPORT_ETHERNET:
- return ConnectivityManager.TYPE_ETHERNET;
+ return TYPE_ETHERNET;
default:
loge("Unexpected transport in transportTypeToLegacyType: " + type);
}
@@ -9111,6 +9171,143 @@ public class ConnectivityService extends IConnectivityManager.Stub
mQosCallbackTracker.unregisterCallback(callback);
}
+ // Network preference per-profile and OEM network preferences can't be set at the same
+ // time, because it is unclear what should happen if both preferences are active for
+ // one given UID. To make it possible, the stack would have to clarify what would happen
+ // in case both are active at the same time. The implementation may have to be adjusted
+ // to implement the resulting rules. For example, a priority could be defined between them,
+ // where the OEM preference would be considered less or more important than the enterprise
+ // preference ; this would entail implementing the priorities somehow, e.g. by doing
+ // UID arithmetic with UID ranges or passing a priority to netd so that the routing rules
+ // are set at the right level. Other solutions are possible, e.g. merging of the
+ // preferences for the relevant UIDs.
+ private static void throwConcurrentPreferenceException() {
+ throw new IllegalStateException("Can't set NetworkPreferenceForUser and "
+ + "set OemNetworkPreference at the same time");
+ }
+
+ /**
+ * Request that a user profile is put by default on a network matching a given preference.
+ *
+ * See the documentation for the individual preferences for a description of the supported
+ * behaviors.
+ *
+ * @param profile the profile concerned.
+ * @param preference the preference for this profile, as one of the PROFILE_NETWORK_PREFERENCE_*
+ * constants.
+ * @param listener an optional listener to listen for completion of the operation.
+ */
+ @Override
+ public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+ @ConnectivityManager.ProfileNetworkPreference final int preference,
+ @Nullable final IOnCompleteListener listener) {
+ Objects.requireNonNull(profile);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (DBG) {
+ log("setProfileNetworkPreference " + profile + " to " + preference);
+ }
+ if (profile.getIdentifier() < 0) {
+ throw new IllegalArgumentException("Must explicitly specify a user handle ("
+ + "UserHandle.CURRENT not supported)");
+ }
+ final UserManager um;
+ try {
+ um = mContext.createContextAsUser(profile, 0 /* flags */)
+ .getSystemService(UserManager.class);
+ } catch (IllegalStateException e) {
+ throw new IllegalArgumentException("Profile does not exist");
+ }
+ if (!um.isManagedProfile()) {
+ throw new IllegalArgumentException("Profile must be a managed profile");
+ }
+ // Strictly speaking, mOemNetworkPreferences should only be touched on the
+ // handler thread. However it is an immutable object, so reading the reference is
+ // safe - it's just possible the value is slightly outdated. For the final check,
+ // see #handleSetProfileNetworkPreference. But if this can be caught here it is a
+ // lot easier to understand, so opportunistically check it.
+ if (!mOemNetworkPreferences.isEmpty()) {
+ throwConcurrentPreferenceException();
+ }
+ final NetworkCapabilities nc;
+ switch (preference) {
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
+ nc = null;
+ break;
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
+ final UidRange uids = UidRange.createForUser(profile);
+ nc = createDefaultNetworkCapabilitiesForUidRange(uids);
+ nc.addCapability(NET_CAPABILITY_ENTERPRISE);
+ nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid preference in setProfileNetworkPreference");
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE,
+ new Pair<>(new ProfileNetworkPreferences.Preference(profile, nc), listener)));
+ }
+
+ private void validateNetworkCapabilitiesOfProfileNetworkPreference(
+ @Nullable final NetworkCapabilities nc) {
+ if (null == nc) return; // Null caps are always allowed. It means to remove the setting.
+ ensureRequestableCapabilities(nc);
+ }
+
+ private ArraySet<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
+ @NonNull final ProfileNetworkPreferences prefs) {
+ final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
+ for (final ProfileNetworkPreferences.Preference pref : prefs.preferences) {
+ // The NRI for a user should be comprised of two layers:
+ // - The request for the capabilities
+ // - The request for the default network, for fallback. Create an image of it to
+ // have the correct UIDs in it (also a request can only be part of one NRI, because
+ // of lookups in 1:1 associations like mNetworkRequests).
+ // Note that denying a fallback can be implemented simply by not adding the second
+ // request.
+ final ArrayList<NetworkRequest> nrs = new ArrayList<>();
+ nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities));
+ nrs.add(createDefaultRequest());
+ setNetworkRequestUids(nrs, pref.capabilities.getUids());
+ final NetworkRequestInfo nri = new NetworkRequestInfo(nrs);
+ result.add(nri);
+ }
+ return result;
+ }
+
+ private void handleSetProfileNetworkPreference(
+ @NonNull final ProfileNetworkPreferences.Preference preference,
+ @Nullable final IOnCompleteListener listener) {
+ // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+ // particular because it's not clear what preference should win in case both apply
+ // to the same app.
+ // The binder call has already checked this, but as mOemNetworkPreferences is only
+ // touched on the handler thread, it's theoretically not impossible that it has changed
+ // since.
+ if (!mOemNetworkPreferences.isEmpty()) {
+ // This may happen on a device with an OEM preference set when a user is removed.
+ // In this case, it's safe to ignore. In particular this happens in the tests.
+ loge("handleSetProfileNetworkPreference, but OEM network preferences not empty");
+ return;
+ }
+
+ validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
+
+ mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
+ final ArraySet<NetworkRequestInfo> nris =
+ createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences);
+ replaceDefaultNetworkRequestsForPreference(nris);
+ // Finally, rematch.
+ rematchAllNetworksAndRequests();
+
+ if (null != listener) {
+ try {
+ listener.onComplete();
+ } catch (RemoteException e) {
+ loge("Listener for setProfileNetworkPreference has died");
+ }
+ }
+ }
+
private void enforceAutomotiveDevice() {
final boolean isAutomotiveDevice =
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
@@ -9129,17 +9326,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
* Calling this will overwrite the existing preference.
*
* @param preference {@link OemNetworkPreferences} The application network preference to be set.
- * @param listener {@link ConnectivityManager.OnSetOemNetworkPreferenceListener} Listener used
+ * @param listener {@link ConnectivityManager.OnCompleteListener} Listener used
* to communicate completion of setOemNetworkPreference();
*/
@Override
public void setOemNetworkPreference(
@NonNull final OemNetworkPreferences preference,
- @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+ @Nullable final IOnCompleteListener listener) {
enforceAutomotiveDevice();
enforceOemNetworkPreferencesPermission();
+ if (!mProfileNetworkPreferences.isEmpty()) {
+ // Strictly speaking, mProfileNetworkPreferences should only be touched on the
+ // handler thread. However it is an immutable object, so reading the reference is
+ // safe - it's just possible the value is slightly outdated. For the final check,
+ // see #handleSetOemPreference. But if this can be caught here it is a
+ // lot easier to understand, so opportunistically check it.
+ throwConcurrentPreferenceException();
+ }
+
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
validateOemNetworkPreferences(preference);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
@@ -9158,11 +9364,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handleSetOemNetworkPreference(
@NonNull final OemNetworkPreferences preference,
- @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+ @Nullable final IOnCompleteListener listener) {
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
if (DBG) {
log("set OEM network preferences :" + preference.toString());
}
+ // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+ // particular because it's not clear what preference should win in case both apply
+ // to the same app.
+ // The binder call has already checked this, but as mOemNetworkPreferences is only
+ // touched on the handler thread, it's theoretically not impossible that it has changed
+ // since.
+ if (!mProfileNetworkPreferences.isEmpty()) {
+ logwtf("handleSetOemPreference, but per-profile network preferences not empty");
+ return;
+ }
+
final ArraySet<NetworkRequestInfo> nris =
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
replaceDefaultNetworkRequestsForPreference(nris);
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index d10cf4dd0505..fa3771ac70fa 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -23,6 +23,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -104,6 +105,9 @@ import java.util.concurrent.TimeUnit;
public class PersistentDataBlockService extends SystemService {
private static final String TAG = PersistentDataBlockService.class.getSimpleName();
+ private static final String GSI_SANDBOX = "/data/gsi_persistent_data";
+ private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
+
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
@@ -128,12 +132,13 @@ public class PersistentDataBlockService extends SystemService {
private static final String FLASH_LOCK_UNLOCKED = "0";
private final Context mContext;
- private final String mDataBlockFile;
+ private final boolean mIsRunningDSU;
private final Object mLock = new Object();
private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
private int mAllowedUid = -1;
private long mBlockDeviceSize;
+ private String mDataBlockFile;
@GuardedBy("mLock")
private boolean mIsWritable = true;
@@ -142,6 +147,7 @@ public class PersistentDataBlockService extends SystemService {
super(context);
mContext = context;
mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+ mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
mBlockDeviceSize = -1; // Load lazily
}
@@ -286,14 +292,28 @@ public class PersistentDataBlockService extends SystemService {
return true;
}
+ private FileOutputStream getBlockOutputStream() throws IOException {
+ if (!mIsRunningDSU) {
+ return new FileOutputStream(new File(mDataBlockFile));
+ } else {
+ File sandbox = new File(GSI_SANDBOX);
+ File realpdb = new File(SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP));
+ if (!sandbox.exists()) {
+ FileUtils.copy(realpdb, sandbox);
+ mDataBlockFile = GSI_SANDBOX;
+ }
+ Slog.i(TAG, "PersistentDataBlock copy-on-write");
+ return new FileOutputStream(sandbox);
+ }
+ }
+
private boolean computeAndWriteDigestLocked() {
byte[] digest = computeDigestLocked(null);
if (digest != null) {
DataOutputStream outputStream;
try {
- outputStream = new DataOutputStream(
- new FileOutputStream(new File(mDataBlockFile)));
- } catch (FileNotFoundException e) {
+ outputStream = new DataOutputStream(getBlockOutputStream());
+ } catch (IOException e) {
Slog.e(TAG, "partition not available?", e);
return false;
}
@@ -358,8 +378,8 @@ public class PersistentDataBlockService extends SystemService {
private void formatPartitionLocked(boolean setOemUnlockEnabled) {
DataOutputStream outputStream;
try {
- outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
- } catch (FileNotFoundException e) {
+ outputStream = new DataOutputStream(getBlockOutputStream());
+ } catch (IOException e) {
Slog.e(TAG, "partition not available?", e);
return;
}
@@ -384,8 +404,8 @@ public class PersistentDataBlockService extends SystemService {
private void doSetOemUnlockEnabledLocked(boolean enabled) {
FileOutputStream outputStream;
try {
- outputStream = new FileOutputStream(new File(mDataBlockFile));
- } catch (FileNotFoundException e) {
+ outputStream = getBlockOutputStream();
+ } catch (IOException e) {
Slog.e(TAG, "partition not available", e);
return;
}
@@ -461,8 +481,8 @@ public class PersistentDataBlockService extends SystemService {
DataOutputStream outputStream;
try {
- outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
- } catch (FileNotFoundException e) {
+ outputStream = new DataOutputStream(getBlockOutputStream());
+ } catch (IOException e) {
Slog.e(TAG, "partition not available?", e);
return -1;
}
@@ -547,6 +567,17 @@ public class PersistentDataBlockService extends SystemService {
public void wipe() {
enforceOemUnlockWritePermission();
+ if (mIsRunningDSU) {
+ File sandbox = new File(GSI_SANDBOX);
+ if (sandbox.exists()) {
+ if (sandbox.delete()) {
+ mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+ } else {
+ Slog.e(TAG, "Failed to wipe sandbox persistent data block");
+ }
+ }
+ return;
+ }
synchronized (mLock) {
int ret = nativeWipe(mDataBlockFile);
@@ -706,8 +737,8 @@ public class PersistentDataBlockService extends SystemService {
private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
FileOutputStream outputStream;
try {
- outputStream = new FileOutputStream(new File(mDataBlockFile));
- } catch (FileNotFoundException e) {
+ outputStream = getBlockOutputStream();
+ } catch (IOException e) {
Slog.e(TAG, "partition not available", e);
return;
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 140f24f7cf8f..051cd9907bee 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -64,7 +64,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 71bac577a4a0..ce728bc7c315 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -62,6 +62,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<ISession> {
// Nothing to do here
}
+ @Override
public void start(@NonNull Callback callback) {
super.start(callback);
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index ddcfcad59203..adffba280c7f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -63,6 +63,12 @@ class FingerprintResetLockoutClient extends HalClientMonitor<ISession> {
}
@Override
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
+ @Override
protected void startHalOperation() {
try {
getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
index ef767341d609..a25b89ac039a 100644
--- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -17,7 +17,6 @@
package com.android.server.connectivity;
import android.os.SystemProperties;
-import android.sysprop.NetworkProperties;
public class MockableSystemProperties {
@@ -32,10 +31,4 @@ public class MockableSystemProperties {
public boolean getBoolean(String key, boolean def) {
return SystemProperties.getBoolean(key, def);
}
- /**
- * Set net.tcp_def_init_rwnd to the tcp initial receive window size.
- */
- public void setTcpInitRwnd(int value) {
- NetworkProperties.tcp_init_rwnd(value);
- }
}
diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
new file mode 100644
index 000000000000..dd2815d9e2e3
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
@@ -0,0 +1,87 @@
+/*
+ * 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.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.UserHandle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A data class containing all the per-profile network preferences.
+ *
+ * A given profile can only have one preference.
+ */
+public class ProfileNetworkPreferences {
+ /**
+ * A single preference, as it applies to a given user profile.
+ */
+ public static class Preference {
+ @NonNull public final UserHandle user;
+ // Capabilities are only null when sending an object to remove the setting for a user
+ @Nullable public final NetworkCapabilities capabilities;
+
+ public Preference(@NonNull final UserHandle user,
+ @Nullable final NetworkCapabilities capabilities) {
+ this.user = user;
+ this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
+ }
+
+ /** toString */
+ public String toString() {
+ return "[ProfileNetworkPreference user=" + user + " caps=" + capabilities + "]";
+ }
+ }
+
+ @NonNull public final List<Preference> preferences;
+
+ public ProfileNetworkPreferences() {
+ preferences = Collections.EMPTY_LIST;
+ }
+
+ private ProfileNetworkPreferences(@NonNull final List<Preference> list) {
+ preferences = Collections.unmodifiableList(list);
+ }
+
+ /**
+ * Returns a new object consisting of this object plus the passed preference.
+ *
+ * If a preference already exists for the same user, it will be replaced by the passed
+ * preference. Passing a Preference object containing a null capabilities object is equivalent
+ * to (and indeed, implemented as) removing the preference for this user.
+ */
+ public ProfileNetworkPreferences plus(@NonNull final Preference pref) {
+ final ArrayList<Preference> newPrefs = new ArrayList<>();
+ for (final Preference existingPref : preferences) {
+ if (!existingPref.user.equals(pref.user)) {
+ newPrefs.add(existingPref);
+ }
+ }
+ if (null != pref.capabilities) {
+ newPrefs.add(pref);
+ }
+ return new ProfileNetworkPreferences(newPrefs);
+ }
+
+ public boolean isEmpty() {
+ return preferences.isEmpty();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 4be509b3f464..1d556fec31ea 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1492,7 +1492,7 @@ public class ComponentResolver {
}
return null;
}
- final ResolveInfo res = new ResolveInfo();
+ final ResolveInfo res = new ResolveInfo(info.hasCategory(Intent.CATEGORY_BROWSABLE));
res.activityInfo = ai;
if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dfe72b26f72a..1530e41917c7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1768,6 +1768,11 @@ public class PackageManagerService extends IPackageManager.Stub
public int[] getAllUserIds() {
return mUserManager.getUserIds();
}
+
+ @Override
+ public boolean doesUserExist(@UserIdInt int userId) {
+ return mUserManager.exists(userId);
+ }
}
/**
@@ -2639,8 +2644,7 @@ public class PackageManagerService extends IPackageManager.Stub
// We'll want to include browser possibilities in a few cases
boolean includeBrowser = false;
- if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
- matchFlags)) {
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
result.addAll(undefinedList);
// Maybe add one for the other profile.
if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
@@ -25693,6 +25697,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) {
mPermissionManager.onUserCreated(userId);
mLegacyPermissionManager.grantDefaultPermissions(userId);
+ mDomainVerificationManager.clearUser(userId);
}
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 3b77c39cc31b..0c8e36b75425 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -189,7 +189,7 @@ public class ArtStatsLogUtils {
private static int getDexMetadataType(String dexMetadataPath) {
if (dexMetadataPath == null) {
- return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA;
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE;
}
StrictJarFile jarFile = null;
try {
@@ -199,17 +199,21 @@ public class ArtStatsLogUtils {
boolean hasProfile = findFileName(jarFile, PROFILE_DEX_METADATA);
boolean hasVdex = findFileName(jarFile, VDEX_DEX_METADATA);
if (hasProfile && hasVdex) {
- return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX;
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX;
} else if (hasProfile) {
- return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE;
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE;
} else if (hasVdex) {
- return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX;
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX;
} else {
- return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA;
+ return ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN;
}
} catch (IOException ignore) {
Slog.e(TAG, "Error when parsing dex metadata " + dexMetadataPath);
- return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ERROR_DEX_METADATA;
+ return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_ERROR;
} finally {
try {
if (jarFile != null) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 349561d3f1d1..37f317557aeb 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -986,7 +986,7 @@ public class DexManager {
* Fetches the battery manager object and caches it if it hasn't been fetched already.
*/
private BatteryManager getBatteryManager() {
- if (mBatteryManager == null) {
+ if (mBatteryManager == null && mContext != null) {
mBatteryManager = mContext.getSystemService(BatteryManager.class);
}
@@ -1008,10 +1008,6 @@ public class DexManager {
&& mPowerManager.getCurrentThermalStatus()
>= PowerManager.THERMAL_STATUS_SEVERE);
- if (DEBUG) {
- Log.d(TAG, "Battery, thermal, or memory are critical: " + isBtmCritical);
- }
-
return isBtmCritical;
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index b61fd8d633f6..39ed4882c69c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
-import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -151,7 +150,7 @@ public class DomainVerificationDebug {
Integer state = reusedMap.valueAt(stateIndex);
writer.print(domain);
writer.print(": ");
- writer.println(DomainVerificationManager.stateToDebugString(state));
+ writer.println(DomainVerificationState.stateToDebugString(state));
}
writer.decreaseIndent();
writer.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index 1721a18f4f60..f4bcd3e65913 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -141,6 +141,12 @@ public class DomainVerificationEnforcer {
"Caller is not allowed to edit other users");
}
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
@@ -161,6 +167,12 @@ public class DomainVerificationEnforcer {
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user selections");
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
if (packageName == null) {
return true;
}
@@ -184,6 +196,12 @@ public class DomainVerificationEnforcer {
}
}
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
@@ -197,6 +215,12 @@ public class DomainVerificationEnforcer {
"Caller is not allowed to edit other users");
}
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
+
return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
@@ -221,6 +245,12 @@ public class DomainVerificationEnforcer {
mContext.enforcePermission(
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
callingPid, callingUid, "Caller is not allowed to query user selections");
+
+ if (!mCallback.doesUserExist(callingUserId)) {
+ throw new SecurityException("User " + callingUserId + " does not exist");
+ } else if (!mCallback.doesUserExist(targetUserId)) {
+ throw new SecurityException("User " + targetUserId + " does not exist");
+ }
}
public interface Callback {
@@ -229,5 +259,7 @@ public class DomainVerificationEnforcer {
* if the package was not installed
*/
boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId);
+
+ boolean doesUserExist(@UserIdInt int userId);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index b4bcde726173..73182732cdaf 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -333,10 +333,10 @@ public interface DomainVerificationManagerInternal {
@Nullable
UUID getDomainVerificationInfoId(@NonNull String packageName);
+ @DomainVerificationManager.Error
@RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
- void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
- @NonNull Set<String> domains, int state)
- throws IllegalArgumentException, NameNotFoundException;
+ int setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+ @NonNull Set<String> domains, int state) throws NameNotFoundException;
interface Connection extends DomainVerificationEnforcer.Callback {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
index a7a52e0cd10c..2a17c6d4cec5 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -24,7 +24,6 @@ import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.pm.verify.domain.IDomainVerificationManager;
import android.os.ServiceSpecificException;
@@ -61,11 +60,12 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
}
}
+ @DomainVerificationManager.Error
@Override
- public void setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
+ public int setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
int state) {
try {
- mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
+ return mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
domainSet.getDomains(), state);
} catch (Exception e) {
throw rethrow(e);
@@ -82,11 +82,12 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
}
}
+ @DomainVerificationManager.Error
@Override
- public void setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
+ public int setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
boolean enabled, @UserIdInt int userId) {
try {
- mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
+ return mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
domainSet.getDomains(), enabled, userId);
} catch (Exception e) {
throw rethrow(e);
@@ -116,14 +117,9 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
}
private RuntimeException rethrow(Exception exception) throws RuntimeException {
- if (exception instanceof InvalidDomainSetException) {
- int packedErrorCode = DomainVerificationManager.ERROR_INVALID_DOMAIN_SET;
- packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
- return new ServiceSpecificException(packedErrorCode,
- ((InvalidDomainSetException) exception).getPackageName());
- } else if (exception instanceof NameNotFoundException) {
+ if (exception instanceof NameNotFoundException) {
return new ServiceSpecificException(
- DomainVerificationManager.ERROR_NAME_NOT_FOUND);
+ DomainVerificationManager.INTERNAL_ERROR_NAME_NOT_FOUND);
} else if (exception instanceof RuntimeException) {
return (RuntimeException) exception;
} else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index e85bbe41f747..a0e252a8a28a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -19,6 +19,7 @@ package com.android.server.pm.verify.domain;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
+import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -34,7 +35,6 @@ import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
import android.content.pm.verify.domain.DomainVerificationState;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.pm.verify.domain.IDomainVerificationManager;
@@ -243,17 +243,17 @@ public class DomainVerificationService extends SystemService
throws NameNotFoundException {
mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
synchronized (mLock) {
- DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
- if (pkgState == null) {
- return null;
- }
-
AndroidPackage pkg = mConnection.getPackageLocked(packageName);
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- Map<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ return null;
+ }
+
+ ArrayMap<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
// TODO(b/159952358): Should the domain list be cached?
ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
@@ -267,44 +267,56 @@ public class DomainVerificationService extends SystemService
DomainVerificationState.STATE_NO_RESPONSE);
}
+ final int mapSize = hostToStateMap.size();
+ for (int index = 0; index < mapSize; index++) {
+ int internalValue = hostToStateMap.valueAt(index);
+ int publicValue = DomainVerificationState.convertToInfoState(internalValue);
+ hostToStateMap.setValueAt(index, publicValue);
+ }
+
// TODO(b/159952358): Do not return if no values are editable (all ignored states)?
return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap);
}
}
- public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
- int state) throws InvalidDomainSetException, NameNotFoundException {
+ @DomainVerificationManager.Error
+ public int setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ int state) throws NameNotFoundException {
if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) {
if (state != DomainVerificationState.STATE_SUCCESS) {
- throw new IllegalArgumentException(
- "Verifier can only set STATE_SUCCESS or codes greater than or equal to "
- + "STATE_FIRST_VERIFIER_DEFINED");
+ return DomainVerificationManager.ERROR_INVALID_STATE_CODE;
}
}
- setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId, domains,
- state);
+ return setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId,
+ domains, state);
}
+ @DomainVerificationManager.Error
@Override
- public void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+ public int setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
@NonNull Set<String> domains, int state)
- throws InvalidDomainSetException, NameNotFoundException {
+ throws NameNotFoundException {
mEnforcer.assertApprovedVerifier(callingUid, mProxy);
synchronized (mLock) {
List<String> verifiedDomains = new ArrayList<>();
- DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+ GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
true /* forAutoVerify */, callingUid, null /* userId */);
+ if (result.isError()) {
+ return result.getErrorCode();
+ }
+
+ DomainVerificationPkgState pkgState = result.getPkgState();
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
for (String domain : domains) {
Integer previousState = stateMap.get(domain);
if (previousState != null
- && !DomainVerificationManager.isStateModifiable(previousState)) {
+ && !DomainVerificationState.isModifiable(previousState)) {
continue;
}
- if (DomainVerificationManager.isStateVerified(state)) {
+ if (DomainVerificationState.isVerified(state)) {
verifiedDomains.add(domain);
}
@@ -318,6 +330,7 @@ public class DomainVerificationService extends SystemService
}
mConnection.scheduleWriteSettings();
+ return DomainVerificationManager.STATUS_OK;
}
@Override
@@ -460,17 +473,24 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- pkgState.getOrCreateUserState(userId)
- .setLinkHandlingAllowed(allowed);
+ if (userId == UserHandle.USER_ALL) {
+ for (int aUserId : mConnection.getAllUserIds()) {
+ pkgState.getOrCreateUserState(aUserId)
+ .setLinkHandlingAllowed(allowed);
+ }
+ } else {
+ pkgState.getOrCreateUserState(userId)
+ .setLinkHandlingAllowed(allowed);
+ }
}
}
mConnection.scheduleWriteSettings();
}
- public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
- throws InvalidDomainSetException, NameNotFoundException {
+ throws NameNotFoundException {
synchronized (mLock) {
final int callingUid = mConnection.getCallingUid();
// Pass null for package name here and do the app visibility enforcement inside
@@ -478,14 +498,17 @@ public class DomainVerificationService extends SystemService
// ID reason if the target app is invisible
if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(),
null /* packageName */, userId)) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_ID_INVALID);
+ return DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID;
}
- DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+ GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
false /* forAutoVerify */, callingUid, userId);
- DomainVerificationInternalUserState userState =
- pkgState.getOrCreateUserState(userId);
+ if (result.isError()) {
+ return result.getErrorCode();
+ }
+
+ DomainVerificationPkgState pkgState = result.getPkgState();
+ DomainVerificationInternalUserState userState = pkgState.getOrCreateUserState(userId);
// Disable other packages if approving this one. Note that this check is only done for
// enabling. This allows an escape hatch in case multiple packages somehow get selected.
@@ -503,8 +526,7 @@ public class DomainVerificationService extends SystemService
userId, APPROVAL_LEVEL_NONE + 1, mConnection::getPackageSettingLocked);
int highestApproval = packagesToLevel.second;
if (highestApproval > APPROVAL_LEVEL_SELECTION) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_UNABLE_TO_APPROVE);
+ return DomainVerificationManager.ERROR_UNABLE_TO_APPROVE;
}
domainToApprovedPackages.put(domain, packagesToLevel.first);
@@ -544,6 +566,7 @@ public class DomainVerificationService extends SystemService
}
mConnection.scheduleWriteSettings();
+ return DomainVerificationManager.STATUS_OK;
}
@Override
@@ -636,16 +659,16 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
synchronized (mLock) {
- DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
- if (pkgState == null) {
- return null;
- }
-
AndroidPackage pkg = mConnection.getPackageLocked(packageName);
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ return null;
+ }
+
ArraySet<String> webDomains = mCollector.collectAllWebDomains(pkg);
int webDomainsSize = webDomains.size();
@@ -659,7 +682,7 @@ public class DomainVerificationService extends SystemService
Integer state = stateMap.get(host);
int domainState;
- if (state != null && DomainVerificationManager.isStateVerified(state)) {
+ if (state != null && DomainVerificationState.isVerified(state)) {
domainState = DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
} else if (enabledHosts.contains(host)) {
domainState = DomainVerificationUserState.DOMAIN_STATE_SELECTED;
@@ -793,19 +816,12 @@ public class DomainVerificationService extends SystemService
Integer oldStateInteger = oldStateMap.get(domain);
if (oldStateInteger != null) {
int oldState = oldStateInteger;
- switch (oldState) {
- case DomainVerificationState.STATE_SUCCESS:
- case DomainVerificationState.STATE_RESTORED:
- case DomainVerificationState.STATE_MIGRATED:
- newStateMap.put(domain, oldState);
- break;
- default:
- // In all other cases, the state code is left unset
- // (STATE_NO_RESPONSE) to signal to the verification agent that any
- // existing error has been cleared and the domain should be
- // re-attempted. This makes update of a package a signal to
- // re-verify.
- break;
+ // If the following case fails, the state code is left unset
+ // (STATE_NO_RESPONSE) to signal to the verification agent that any existing
+ // error has been cleared and the domain should be re-attempted. This makes
+ // update of a package a signal to re-verify.
+ if (DomainVerificationState.shouldMigrate(oldState)) {
+ newStateMap.put(domain, oldState);
}
}
}
@@ -858,13 +874,13 @@ public class DomainVerificationService extends SystemService
boolean sendBroadcast = true;
DomainVerificationPkgState pkgState;
- pkgState = mSettings.getPendingState(pkgName);
+ pkgState = mSettings.removePendingState(pkgName);
if (pkgState != null) {
// Don't send when attaching from pending read, which is usually boot scan. Re-send on
// boot is handled in a separate method once all packages are added.
sendBroadcast = false;
} else {
- pkgState = mSettings.getRestoredState(pkgName);
+ pkgState = mSettings.removeRestoredState(pkgName);
}
AndroidPackage pkg = newPkgSetting.getPkg();
@@ -872,7 +888,7 @@ public class DomainVerificationService extends SystemService
boolean hasAutoVerifyDomains = !domains.isEmpty();
boolean isPendingOrRestored = pkgState != null;
if (isPendingOrRestored) {
- pkgState.setId(domainSetId);
+ pkgState = new DomainVerificationPkgState(pkgState, domainSetId, hasAutoVerifyDomains);
} else {
pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
}
@@ -1097,28 +1113,25 @@ public class DomainVerificationService extends SystemService
* @param userIdForFilter which user to filter app access to, or null if the caller has already
* validated package visibility
*/
+ @CheckResult
@GuardedBy("mLock")
- private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
+ private GetAttachedResult getAndValidateAttachedLocked(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
- @Nullable Integer userIdForFilter)
- throws InvalidDomainSetException, NameNotFoundException {
+ @Nullable Integer userIdForFilter) throws NameNotFoundException {
if (domainSetId == null) {
- throw new InvalidDomainSetException(null, null,
- InvalidDomainSetException.REASON_ID_NULL);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL);
}
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(domainSetId);
if (pkgState == null) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_ID_INVALID);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
}
String pkgName = pkgState.getPackageName();
if (userIdForFilter != null
&& mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
- throw new InvalidDomainSetException(domainSetId, null,
- InvalidDomainSetException.REASON_ID_INVALID);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
}
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
@@ -1127,8 +1140,8 @@ public class DomainVerificationService extends SystemService
}
if (CollectionUtils.isEmpty(domains)) {
- throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
- InvalidDomainSetException.REASON_SET_NULL_OR_EMPTY);
+ return GetAttachedResult.error(
+ DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY);
}
AndroidPackage pkg = pkgSetting.getPkg();
ArraySet<String> declaredDomains = forAutoVerify
@@ -1136,11 +1149,10 @@ public class DomainVerificationService extends SystemService
: mCollector.collectAllWebDomains(pkg);
if (domains.retainAll(declaredDomains)) {
- throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
- InvalidDomainSetException.REASON_UNKNOWN_DOMAIN);
+ return GetAttachedResult.error(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN);
}
- return pkgState;
+ return GetAttachedResult.success(pkgState);
}
@Override
@@ -1185,7 +1197,7 @@ public class DomainVerificationService extends SystemService
/**
* Determine whether or not a broadcast should be sent at boot for the given {@param pkgState}.
* Sends only if the only states recorded are default as decided by {@link
- * DomainVerificationManager#isStateDefault(int)}.
+ * DomainVerificationState#isDefault(int)}.
*
* If any other state is set, it's assumed that the domain verification agent is aware of the
* package and has already scheduled future verification requests.
@@ -1199,7 +1211,7 @@ public class DomainVerificationService extends SystemService
int statesSize = stateMap.size();
for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) {
Integer state = stateMap.valueAt(stateIndex);
- if (!DomainVerificationManager.isStateDefault(state)) {
+ if (!DomainVerificationState.isDefault(state)) {
return false;
}
}
@@ -1318,83 +1330,50 @@ public class DomainVerificationService extends SystemService
@NonNull Function<String, PackageSetting> pkgSettingFunction) {
String domain = intent.getData().getHost();
- // Collect package names
- ArrayMap<String, Integer> packageApprovals = new ArrayMap<>();
+ // Collect valid infos
+ ArrayMap<ResolveInfo, Integer> infoApprovals = new ArrayMap<>();
int infosSize = infos.size();
for (int index = 0; index < infosSize; index++) {
- packageApprovals.put(infos.get(index).getComponentInfo().packageName,
- APPROVAL_LEVEL_NONE);
+ final ResolveInfo info = infos.get(index);
+ // Only collect for intent filters that can auto resolve
+ if (info.isAutoResolutionAllowed()) {
+ infoApprovals.put(info, null);
+ }
}
// Find all approval levels
- int highestApproval = fillMapWithApprovalLevels(packageApprovals, domain, userId,
+ int highestApproval = fillMapWithApprovalLevels(infoApprovals, domain, userId,
pkgSettingFunction);
if (highestApproval == APPROVAL_LEVEL_NONE) {
return Pair.create(emptyList(), highestApproval);
}
- // Filter to highest, non-zero packages
- ArraySet<String> approvedPackages = new ArraySet<>();
- int approvalsSize = packageApprovals.size();
- for (int index = 0; index < approvalsSize; index++) {
- if (packageApprovals.valueAt(index) == highestApproval) {
- approvedPackages.add(packageApprovals.keyAt(index));
+ // Filter to highest, non-zero infos
+ for (int index = infoApprovals.size() - 1; index >= 0; index--) {
+ if (infoApprovals.valueAt(index) != highestApproval) {
+ infoApprovals.removeAt(index);
}
}
- ArraySet<String> filteredPackages = new ArraySet<>();
- if (highestApproval == APPROVAL_LEVEL_LEGACY_ASK) {
+ if (highestApproval != APPROVAL_LEVEL_LEGACY_ASK) {
// To maintain legacy behavior while the Settings API is not implemented,
// show the chooser if all approved apps are marked ask, skipping the
// last app, last declaration filtering.
- filteredPackages.addAll(approvedPackages);
- } else {
- // Filter to last installed package
- long latestInstall = Long.MIN_VALUE;
- int approvedSize = approvedPackages.size();
- for (int index = 0; index < approvedSize; index++) {
- String packageName = approvedPackages.valueAt(index);
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
- if (pkgSetting == null) {
- continue;
- }
- long installTime = pkgSetting.getFirstInstallTime();
- if (installTime > latestInstall) {
- latestInstall = installTime;
- filteredPackages.clear();
- filteredPackages.add(packageName);
- } else if (installTime == latestInstall) {
- filteredPackages.add(packageName);
- }
- }
+ filterToLastFirstInstalled(infoApprovals, pkgSettingFunction);
}
- // Filter to approved ResolveInfos
- ArrayMap<String, List<ResolveInfo>> approvedInfos = new ArrayMap<>();
- for (int index = 0; index < infosSize; index++) {
- ResolveInfo info = infos.get(index);
- String packageName = info.getComponentInfo().packageName;
- if (filteredPackages.contains(packageName)) {
- List<ResolveInfo> infosPerPackage = approvedInfos.get(packageName);
- if (infosPerPackage == null) {
- infosPerPackage = new ArrayList<>();
- approvedInfos.put(packageName, infosPerPackage);
- }
- infosPerPackage.add(info);
- }
+ // Easier to transform into list as the filterToLastDeclared method
+ // requires swapping indexes, which doesn't work with ArrayMap keys
+ final int size = infoApprovals.size();
+ List<ResolveInfo> finalList = new ArrayList<>(size);
+ for (int index = 0; index < size; index++) {
+ finalList.add(infoApprovals.keyAt(index));
}
- List<ResolveInfo> finalList;
- if (highestApproval == APPROVAL_LEVEL_LEGACY_ASK) {
- // If legacy ask, skip the last declaration filtering
- finalList = new ArrayList<>();
- int size = approvedInfos.size();
- for (int index = 0; index < size; index++) {
- finalList.addAll(approvedInfos.valueAt(index));
- }
- } else {
+ // If legacy ask, skip the last declaration filtering
+ if (highestApproval != APPROVAL_LEVEL_LEGACY_ASK) {
// Find the last declared ResolveInfo per package
- finalList = filterToLastDeclared(approvedInfos, pkgSettingFunction);
+ filterToLastDeclared(finalList, pkgSettingFunction);
}
return Pair.create(finalList, highestApproval);
@@ -1403,68 +1382,127 @@ public class DomainVerificationService extends SystemService
/**
* @return highest approval level found
*/
- private int fillMapWithApprovalLevels(@NonNull ArrayMap<String, Integer> inputMap,
+ @ApprovalLevel
+ private int fillMapWithApprovalLevels(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
@NonNull String domain, @UserIdInt int userId,
@NonNull Function<String, PackageSetting> pkgSettingFunction) {
int highestApproval = APPROVAL_LEVEL_NONE;
int size = inputMap.size();
for (int index = 0; index < size; index++) {
- String packageName = inputMap.keyAt(index);
+ if (inputMap.valueAt(index) != null) {
+ // Already filled by previous iteration
+ continue;
+ }
+
+ ResolveInfo info = inputMap.keyAt(index);
+ final String packageName = info.getComponentInfo().packageName;
PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null) {
- inputMap.setValueAt(index, APPROVAL_LEVEL_NONE);
+ fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE);
continue;
}
int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain);
highestApproval = Math.max(highestApproval, approval);
- inputMap.setValueAt(index, approval);
+ fillInfoMapForSamePackage(inputMap, packageName, approval);
}
return highestApproval;
}
+ private void fillInfoMapForSamePackage(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
+ @NonNull String targetPackageName, @ApprovalLevel int level) {
+ final int size = inputMap.size();
+ for (int index = 0; index < size; index++) {
+ final String packageName = inputMap.keyAt(index).getComponentInfo().packageName;
+ if (Objects.equals(targetPackageName, packageName)) {
+ inputMap.setValueAt(index, level);
+ }
+ }
+ }
+
@NonNull
- private List<ResolveInfo> filterToLastDeclared(
- @NonNull ArrayMap<String, List<ResolveInfo>> inputMap,
+ private void filterToLastFirstInstalled(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
@NonNull Function<String, PackageSetting> pkgSettingFunction) {
- List<ResolveInfo> finalList = new ArrayList<>(inputMap.size());
-
- int inputSize = inputMap.size();
- for (int inputIndex = 0; inputIndex < inputSize; inputIndex++) {
- String packageName = inputMap.keyAt(inputIndex);
- List<ResolveInfo> infos = inputMap.valueAt(inputIndex);
+ // First, find the package with the latest first install time
+ String targetPackageName = null;
+ long latestInstall = Long.MIN_VALUE;
+ final int size = inputMap.size();
+ for (int index = 0; index < size; index++) {
+ ResolveInfo info = inputMap.keyAt(index);
+ String packageName = info.getComponentInfo().packageName;
PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ if (pkgSetting == null) {
+ continue;
+ }
+
+ long installTime = pkgSetting.getFirstInstallTime();
+ if (installTime > latestInstall) {
+ latestInstall = installTime;
+ targetPackageName = packageName;
+ }
+ }
+
+ // Then, remove all infos that don't match the package
+ for (int index = inputMap.size() - 1; index >= 0; index--) {
+ ResolveInfo info = inputMap.keyAt(index);
+ if (!Objects.equals(targetPackageName, info.getComponentInfo().packageName)) {
+ inputMap.removeAt(index);
+ }
+ }
+ }
+
+ @NonNull
+ private void filterToLastDeclared(@NonNull List<ResolveInfo> inputList,
+ @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ // Must call size each time as the size of the list will decrease
+ for (int index = 0; index < inputList.size(); index++) {
+ ResolveInfo info = inputList.get(index);
+ String targetPackageName = info.getComponentInfo().packageName;
+ PackageSetting pkgSetting = pkgSettingFunction.apply(targetPackageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
continue;
}
- ResolveInfo result = null;
- int highestIndex = -1;
- int infosSize = infos.size();
- for (int infoIndex = 0; infoIndex < infosSize; infoIndex++) {
- ResolveInfo info = infos.get(infoIndex);
- List<ParsedActivity> activities = pkg.getActivities();
- int activitiesSize = activities.size();
- for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
- if (Objects.equals(activities.get(activityIndex).getComponentName(),
- info.getComponentInfo().getComponentName())) {
- if (activityIndex > highestIndex) {
- highestIndex = activityIndex;
- result = info;
- }
- break;
- }
+ ResolveInfo result = info;
+ int highestIndex = indexOfIntentFilterEntry(pkg, result);
+
+ // Search backwards so that lower results can be removed as they're found
+ for (int searchIndex = inputList.size() - 1; searchIndex >= index + 1; searchIndex--) {
+ ResolveInfo searchInfo = inputList.get(searchIndex);
+ if (!Objects.equals(targetPackageName, searchInfo.getComponentInfo().packageName)) {
+ continue;
}
+
+ int entryIndex = indexOfIntentFilterEntry(pkg, searchInfo);
+ if (entryIndex > highestIndex) {
+ highestIndex = entryIndex;
+ result = searchInfo;
+ }
+
+ // Always remove the entry so that the current index
+ // is left as the sole candidate of the target package
+ inputList.remove(searchIndex);
}
- // Shouldn't be null, but might as well be safe
- if (result != null) {
- finalList.add(result);
+ // Swap the current index for the result, leaving this as
+ // the only entry with the target package name
+ inputList.set(index, result);
+ }
+ }
+
+ private int indexOfIntentFilterEntry(@NonNull AndroidPackage pkg,
+ @NonNull ResolveInfo target) {
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+ for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+ if (Objects.equals(activities.get(activityIndex).getComponentName(),
+ target.getComponentInfo().getComponentName())) {
+ return activityIndex;
}
}
- return finalList;
+ return -1;
}
@Override
@@ -1472,8 +1510,7 @@ public class DomainVerificationService extends SystemService
@NonNull List<ResolveInfo> candidates,
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
String packageName = pkgSetting.getName();
- if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
- resolveInfoFlags)) {
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, intent, userId, false, "not valid intent");
}
@@ -1542,7 +1579,7 @@ public class DomainVerificationService extends SystemService
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
// Check if the exact host matches
Integer state = stateMap.get(host);
- if (state != null && DomainVerificationManager.isStateVerified(state)) {
+ if (state != null && DomainVerificationState.isVerified(state)) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, debugObject, userId, true,
"host verified exactly");
@@ -1553,7 +1590,7 @@ public class DomainVerificationService extends SystemService
// Otherwise see if the host matches a verified domain by wildcard
int stateMapSize = stateMap.size();
for (int index = 0; index < stateMapSize; index++) {
- if (!DomainVerificationManager.isStateVerified(stateMap.valueAt(index))) {
+ if (!DomainVerificationState.isVerified(stateMap.valueAt(index))) {
continue;
}
@@ -1672,4 +1709,40 @@ public class DomainVerificationService extends SystemService
Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for "
+ debugObject + " for user " + userId + ": " + reason);
}
+
+ private static class GetAttachedResult {
+
+ @Nullable
+ private DomainVerificationPkgState mPkgState;
+
+ private int mErrorCode;
+
+ GetAttachedResult(@Nullable DomainVerificationPkgState pkgState, int errorCode) {
+ mPkgState = pkgState;
+ mErrorCode = errorCode;
+ }
+
+ @NonNull
+ static GetAttachedResult error(@DomainVerificationManager.Error int errorCode) {
+ return new GetAttachedResult(null, errorCode);
+ }
+
+ @NonNull
+ static GetAttachedResult success(@NonNull DomainVerificationPkgState pkgState) {
+ return new GetAttachedResult(pkgState, DomainVerificationManager.STATUS_OK);
+ }
+
+ @NonNull
+ DomainVerificationPkgState getPkgState() {
+ return mPkgState;
+ }
+
+ boolean isError() {
+ return mErrorCode != DomainVerificationManager.STATUS_OK;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
index f3d1dbb1f6ad..8b59da7bb944 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -256,16 +256,16 @@ class DomainVerificationSettings {
}
@Nullable
- public DomainVerificationPkgState getPendingState(@NonNull String pkgName) {
+ public DomainVerificationPkgState removePendingState(@NonNull String pkgName) {
synchronized (mLock) {
- return mPendingPkgStates.get(pkgName);
+ return mPendingPkgStates.remove(pkgName);
}
}
@Nullable
- public DomainVerificationPkgState getRestoredState(@NonNull String pkgName) {
+ public DomainVerificationPkgState removeRestoredState(@NonNull String pkgName) {
synchronized (mLock) {
- return mRestoredPkgStates.get(pkgName);
+ return mRestoredPkgStates.remove(pkgName);
}
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index 94767f555574..7e755fa384db 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -38,6 +38,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
public class DomainVerificationShell {
@@ -226,22 +228,19 @@ public class DomainVerificationShell {
userId = translateUserId(userId, "runSetAppLinksUserState");
- String enabledString = commandHandler.getNextArgRequired();
+ String enabledArg = commandHandler.getNextArg();
+ if (TextUtils.isEmpty(enabledArg)) {
+ commandHandler.getErrPrintWriter().println("Error: enabled param not specified");
+ return false;
+ }
- // Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
- // accidentally parsed as a boolean
boolean enabled;
- switch (enabledString) {
- case "true":
- enabled = true;
- break;
- case "false":
- enabled = false;
- break;
- default:
- commandHandler.getErrPrintWriter().println(
- "Invalid enabled param: " + enabledString);
- return false;
+ try {
+ enabled = parseEnabled(enabledArg);
+ } catch (IllegalArgumentException e) {
+ commandHandler.getErrPrintWriter()
+ .println("Error: invalid enabled param: " + e.getMessage());
+ return false;
}
ArraySet<String> domains = new ArraySet<>(getRemainingArgs(commandHandler));
@@ -255,8 +254,8 @@ public class DomainVerificationShell {
}
try {
- mCallback.setDomainVerificationUserSelectionInternal(userId,
- packageName, enabled, domains);
+ mCallback.setDomainVerificationUserSelectionInternal(userId, packageName, enabled,
+ domains);
} catch (NameNotFoundException e) {
commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
return false;
@@ -362,15 +361,12 @@ public class DomainVerificationShell {
private boolean runSetAppLinksAllowed(@NonNull BasicShellCommandHandler commandHandler) {
String packageName = null;
Integer userId = null;
- Boolean allowed = null;
String option;
while ((option = commandHandler.getNextOption()) != null) {
if (option.equals("--package")) {
- packageName = commandHandler.getNextArgRequired();
- } if (option.equals("--user")) {
+ packageName = commandHandler.getNextArg();
+ } else if (option.equals("--user")) {
userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
- } else if (allowed == null) {
- allowed = Boolean.valueOf(option);
} else {
commandHandler.getErrPrintWriter().println("Error: unexpected option: " + option);
return false;
@@ -389,11 +385,21 @@ public class DomainVerificationShell {
return false;
}
- if (allowed == null) {
+ String allowedArg = commandHandler.getNextArg();
+ if (TextUtils.isEmpty(allowedArg)) {
commandHandler.getErrPrintWriter().println("Error: allowed setting not specified");
return false;
}
+ boolean allowed;
+ try {
+ allowed = parseEnabled(allowedArg);
+ } catch (IllegalArgumentException e) {
+ commandHandler.getErrPrintWriter()
+ .println("Error: invalid allowed setting: " + e.getMessage());
+ return false;
+ }
+
userId = translateUserId(userId, "runSetAppLinksAllowed");
try {
@@ -422,6 +428,22 @@ public class DomainVerificationShell {
}
/**
+ * Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
+ * accidentally parsed as a boolean.
+ */
+ @NonNull
+ private boolean parseEnabled(@NonNull String arg) throws IllegalArgumentException {
+ switch (arg.toLowerCase(Locale.US)) {
+ case "true":
+ return true;
+ case "false":
+ return false;
+ default:
+ throw new IllegalArgumentException(arg + " is not a valid boolean");
+ }
+ }
+
+ /**
* Separated interface from {@link DomainVerificationManagerInternal} to hide methods that are
* even more internal, and so that testing is easier.
*/
@@ -498,7 +520,8 @@ public class DomainVerificationShell {
void verifyPackages(@Nullable List<String> packageNames, boolean reVerify);
/**
- * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer)
+ * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer,
+ * Function)
*/
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId) throws NameNotFoundException;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 883bbad1bd2d..cb3b5c9db7e7 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
import android.os.Binder;
import com.android.internal.util.CollectionUtils;
@@ -30,7 +29,6 @@ import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import java.util.List;
import java.util.Set;
public final class DomainVerificationUtils {
@@ -46,7 +44,6 @@ public final class DomainVerificationUtils {
}
public static boolean isDomainVerificationIntent(Intent intent,
- @NonNull List<ResolveInfo> candidates,
@PackageManager.ResolveInfoFlags int resolveInfoFlags) {
if (!intent.isWebIntent()) {
return false;
@@ -63,42 +60,18 @@ public final class DomainVerificationUtils {
&& intent.hasCategory(Intent.CATEGORY_BROWSABLE);
}
- // In cases where at least one browser is resolved and only one non-browser is resolved,
- // the Intent is coerced into an app links intent, under the assumption the browser can
- // be skipped if the app is approved at any level for the domain.
- boolean foundBrowser = false;
- boolean foundOneApp = false;
-
- final int candidatesSize = candidates.size();
- for (int index = 0; index < candidatesSize; index++) {
- final ResolveInfo info = candidates.get(index);
- if (info.handleAllWebDataURI) {
- foundBrowser = true;
- } else if (foundOneApp) {
- // Already true, so duplicate app
- foundOneApp = false;
- break;
- } else {
- foundOneApp = true;
- }
- }
-
boolean matchDefaultByFlags = (resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
- boolean onlyOneNonBrowser = foundBrowser && foundOneApp;
// Check if matches (BROWSABLE || none) && DEFAULT
if (categoriesSize == 0) {
- // No categories, run coerce case, matching DEFAULT by flags
- return onlyOneNonBrowser && matchDefaultByFlags;
- } else if (intent.hasCategory(Intent.CATEGORY_DEFAULT)) {
- // Run coerce case, matching by explicit DEFAULT
- return onlyOneNonBrowser;
+ // No categories, only allow matching DEFAULT by flags
+ return matchDefaultByFlags;
} else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
// Intent matches BROWSABLE, must match DEFAULT by flags
return matchDefaultByFlags;
} else {
- // Otherwise not matching any app link categories
- return false;
+ // Otherwise only needs to have DEFAULT
+ return intent.hasCategory(Intent.CATEGORY_DEFAULT);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
index a089a6022735..40c70915f3ce 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -68,6 +68,12 @@ public class DomainVerificationPkgState {
this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0));
}
+ public DomainVerificationPkgState(@NonNull DomainVerificationPkgState pkgState,
+ @NonNull UUID id, boolean hasAutoVerifyDomains) {
+ this(pkgState.getPackageName(), id, hasAutoVerifyDomains, pkgState.getStateMap(),
+ pkgState.getUserStates());
+ }
+
@Nullable
public DomainVerificationInternalUserState getUserState(@UserIdInt int userId) {
return mUserStates.get(userId);
@@ -84,10 +90,6 @@ public class DomainVerificationPkgState {
return userState;
}
- public void setId(@NonNull UUID id) {
- mId = id;
- }
-
public void removeUser(@UserIdInt int userId) {
mUserStates.remove(userId);
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index 18042af139a3..fa36683e4aff 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -207,20 +207,24 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
int callingUid = response.callingUid;
if (!successfulDomains.isEmpty()) {
try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- successfulDomains, DomainVerificationState.STATE_SUCCESS);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException e) {
+ if (mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ successfulDomains, DomainVerificationState.STATE_SUCCESS)
+ != DomainVerificationManager.STATUS_OK) {
+ Slog.e(TAG, "Failure reporting successful domains for " + packageName);
+ }
+ } catch (Exception e) {
Slog.e(TAG, "Failure reporting successful domains for " + packageName, e);
}
}
if (!failedDomains.isEmpty()) {
try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException e) {
+ if (mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE)
+ != DomainVerificationManager.STATUS_OK) {
+ Slog.e(TAG, "Failure reporting failed domains for " + packageName);
+ }
+ } catch (Exception e) {
Slog.e(TAG, "Failure reporting failed domains for " + packageName, e);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a909c6d119e8..d361a8c03fbd 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1479,7 +1479,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
if (windowSurface != null && windowSurface.isValid()) {
- Transaction transaction = getPendingTransaction();
+ Transaction transaction = getSyncTransaction();
transaction.setCornerRadius(windowSurface, cornersRadius);
}
}
@@ -1491,7 +1491,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
layoutLetterbox(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(getPendingTransaction());
+ mLetterbox.applySurfaceChanges(getSyncTransaction());
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 5ccf576e1099..9855ea50c83d 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -451,7 +451,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) {
if (organizer == null) return;
- migrateToNewSurfaceControl();
+ migrateToNewSurfaceControl(getSyncTransaction());
mOrganizerController.onDisplayAreaVanished(organizer, this);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c78f9ec21516..95b2b5d088a0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3428,7 +3428,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
- assignChildLayers(getPendingTransaction());
+ assignChildLayers(getSyncTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ad0ce5bf4b28..09a8e4f23644 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2382,11 +2382,11 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
- void migrateToNewSurfaceControl() {
- super.migrateToNewSurfaceControl();
+ void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
+ super.migrateToNewSurfaceControl(t);
mLastSurfaceSize.x = 0;
mLastSurfaceSize.y = 0;
- updateSurfaceSize(getPendingTransaction());
+ updateSurfaceSize(t);
}
void updateSurfaceSize(SurfaceControl.Transaction transaction) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 498fc5c81a4c..88e9ae9179c9 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -963,6 +963,22 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
}
+ @Override
+ void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
+ super.migrateToNewSurfaceControl(t);
+ if (mAppAnimationLayer == null) {
+ return;
+ }
+
+ // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
+ t.reparent(mAppAnimationLayer, mSurfaceControl);
+ t.reparent(mBoostedAppAnimationLayer, mSurfaceControl);
+ t.reparent(mHomeAppAnimationLayer, mSurfaceControl);
+ t.reparent(mSplitScreenDividerAnchor, mSurfaceControl);
+ reassignLayer(t);
+ scheduleAnimation();
+ }
+
void onRootTaskRemoved(Task rootTask) {
if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
Slog.v(TAG_ROOT_TASK, "onRootTaskRemoved: detaching " + rootTask + " from displayId="
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 5d22f8fde057..375b3f49be13 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -310,7 +310,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
boolean taskAppearedSent = t.mTaskAppearedSent;
if (taskAppearedSent) {
if (t.getSurfaceControl() != null) {
- t.migrateToNewSurfaceControl();
+ t.migrateToNewSurfaceControl(t.getSyncTransaction());
}
t.mTaskAppearedSent = false;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8d859584d5f5..e3679c0d8096 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -389,12 +389,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mParent.onChildAdded(this);
}
if (!mReparenting) {
+ onSyncReparent(oldParent, mParent);
if (mParent != null && mParent.mDisplayContent != null
&& mDisplayContent != mParent.mDisplayContent) {
onDisplayChanged(mParent.mDisplayContent);
}
onParentChanged(mParent, oldParent);
- onSyncReparent(oldParent, mParent);
}
}
@@ -460,8 +460,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* This is used to revoke control of the SurfaceControl from a client process that was
* previously organizing this WindowContainer.
*/
- void migrateToNewSurfaceControl() {
- SurfaceControl.Transaction t = getPendingTransaction();
+ void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
t.remove(mSurfaceControl);
// Clear the last position so the new SurfaceControl will get correct position
mLastSurfacePosition.set(0, 0);
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index e3fbeddc3a5f..db70d44d37f6 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -96,6 +96,10 @@ static const Constants& constants() {
return c;
}
+static bool isPageAligned(IncFsSize s) {
+ return (s & (Constants::blockSize - 1)) == 0;
+}
+
template <base::LogSeverity level = base::ERROR>
bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
auto cstr = path::c_str(name);
@@ -1001,25 +1005,53 @@ std::string IncrementalService::normalizePathToStorage(const IncFsMount& ifs, St
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
incfs::NewFileParams params, std::span<const uint8_t> data) {
- if (auto ifs = getIfs(storage)) {
- std::string normPath = normalizePathToStorage(*ifs, storage, path);
- if (normPath.empty()) {
- LOG(ERROR) << "Internal error: storageId " << storage
- << " failed to normalize: " << path;
+ const auto ifs = getIfs(storage);
+ if (!ifs) {
+ return -EINVAL;
+ }
+ if (data.size() > params.size) {
+ LOG(ERROR) << "Bad data size - bigger than file size";
+ return -EINVAL;
+ }
+ if (!data.empty() && data.size() != params.size) {
+ // Writing a page is an irreversible operation, and it can't be updated with additional
+ // data later. Check that the last written page is complete, or we may break the file.
+ if (!isPageAligned(data.size())) {
+ LOG(ERROR) << "Bad data size - tried to write half a page?";
return -EINVAL;
}
- if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
- LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
- return err;
+ }
+ const std::string normPath = normalizePathToStorage(*ifs, storage, path);
+ if (normPath.empty()) {
+ LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path;
+ return -EINVAL;
+ }
+ if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
+ LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
+ return err;
+ }
+ if (params.size > 0) {
+ // Only v2+ incfs supports automatically trimming file over-reserved sizes
+ if (mIncFs->features() & incfs::Features::v2) {
+ if (auto err = mIncFs->reserveSpace(ifs->control, normPath, params.size)) {
+ if (err != -EOPNOTSUPP) {
+ LOG(ERROR) << "Failed to reserve space for a new file: " << err;
+ (void)mIncFs->unlink(ifs->control, normPath);
+ return err;
+ } else {
+ LOG(WARNING) << "Reserving space for backing file isn't supported, "
+ "may run out of disk later";
+ }
+ }
}
if (!data.empty()) {
if (auto err = setFileContent(ifs, id, path, data); err) {
+ (void)mIncFs->unlink(ifs->control, normPath);
return err;
}
}
- return 0;
}
- return -EINVAL;
+ return 0;
}
int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
@@ -1708,7 +1740,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
}
const auto entryUncompressed = entry.method == kCompressStored;
- const auto entryPageAligned = (entry.offset & (constants().blockSize - 1)) == 0;
+ const auto entryPageAligned = isPageAligned(entry.offset);
if (!extractNativeLibs) {
// ensure the file is properly aligned and unpacked
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 80f409ff1c61..2a061226b713 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -209,6 +209,10 @@ public:
ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final {
return incfs::writeBlocks({blocks.data(), size_t(blocks.size())});
}
+ ErrorCode reserveSpace(const Control& control, std::string_view path,
+ IncFsSize size) const final {
+ return incfs::reserveSpace(control, path, size);
+ }
WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index d113f992de71..231b76ff1701 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -107,6 +107,8 @@ public:
virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0;
virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0;
+ virtual ErrorCode reserveSpace(const Control& control, std::string_view path,
+ IncFsSize size) const = 0;
virtual WaitResult waitForPendingReads(
const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index bf798273a8a9..45b796bf4704 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -372,6 +372,8 @@ public:
MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id));
MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
+ MOCK_CONST_METHOD3(reserveSpace,
+ ErrorCode(const Control& control, std::string_view path, IncFsSize size));
MOCK_CONST_METHOD3(waitForPendingReads,
WaitResult(const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer));
@@ -379,7 +381,10 @@ public:
ErrorCode(const Control& control,
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
- MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); }
+ MockIncFs() {
+ ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return());
+ ON_CALL(*this, reserveSpace(_, _, _)).WillByDefault(Return(0));
+ }
void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 592952354b8f..6f71e991bb96 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -390,8 +390,6 @@ public final class SystemServer implements Dumpable {
private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
private static final String BLOCK_MAP_FILE = "/cache/recovery/block.map";
- private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
-
// maximum number of binder threads used for system_server
// will be higher than the system default
private static final int sMaxBinderThreads = 31;
@@ -1662,8 +1660,7 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
- final boolean hasGsi = SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
- if (hasPdb && !hasGsi) {
+ if (hasPdb) {
t.traceBegin("StartPersistentDataBlock");
mSystemServiceManager.startService(PersistentDataBlockService.class);
t.traceEnd();
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 334e53a3aec7..988c02bfb3db 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -23,14 +23,17 @@ package {
android_test {
name: "PackageManagerServiceUnitTests",
- srcs: ["src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
static_libs: [
"androidx.test.rules",
"androidx.test.runner",
"junit",
+ "kotlin-test",
"services.core",
"servicestests-utils",
- "testng",
"truth-prebuilt",
],
platform_apis: true,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 0fa9a1def381..d5eda203e42f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -25,8 +25,8 @@ import android.os.PatternMatcher
import android.util.ArraySet
import com.android.server.SystemConfig
import com.android.server.compat.PlatformCompat
-import com.android.server.pm.verify.domain.DomainVerificationCollector
import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationCollector
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
index 8ef92393242a..9693f3bab127 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -19,6 +19,7 @@ package com.android.server.pm.test.verify.domain
import android.content.pm.verify.domain.DomainSet
import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationState
import android.content.pm.verify.domain.DomainVerificationUserState
import android.os.Parcel
import android.os.Parcelable
@@ -74,7 +75,9 @@ class DomainVerificationCoreApiTest {
DomainVerificationInfo(
UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
"com.test.pkg",
- massiveSet.withIndex().associate { it.value to it.index }
+ massiveSet.withIndex().associate {
+ it.value to DomainVerificationState.convertToInfoState(it.index)
+ }
)
},
unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) },
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 53f0ca20e787..7e25901301aa 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -20,20 +20,21 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageUserState
-import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
import android.os.Build
import android.os.Process
import android.util.ArraySet
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.verify.domain.DomainVerificationEnforcer
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
-import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.spyThrowOnUnmocked
import com.android.server.testutils.whenever
@@ -50,6 +51,8 @@ import java.io.File
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.test.assertFailsWith
+import kotlin.test.fail
@RunWith(Parameterized::class)
class DomainVerificationEnforcerTest {
@@ -81,47 +84,49 @@ class DomainVerificationEnforcerTest {
whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
true
}
+ whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
})
}
}
- val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger, DomainVerificationService> =
- {
- val callingUidInt = AtomicInteger(-1)
- val callingUserIdInt = AtomicInteger(-1)
-
- val connection: DomainVerificationManagerInternal.Connection =
- mockThrowOnUnmocked {
- whenever(callingUid) { callingUidInt.get() }
- whenever(callingUserId) { callingUserIdInt.get() }
- whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
- whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
- whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
- whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
- whenever(schedule(anyInt(), any()))
- whenever(scheduleWriteSettings())
- whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
- whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
- true
- }
+ val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger,
+ DomainVerificationService> = {
+ val callingUidInt = AtomicInteger(-1)
+ val callingUserIdInt = AtomicInteger(-1)
+
+ val connection: DomainVerificationManagerInternal.Connection =
+ mockThrowOnUnmocked {
+ whenever(callingUid) { callingUidInt.get() }
+ whenever(callingUserId) { callingUserIdInt.get() }
+ whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
+ whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
+ whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
+ whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
+ whenever(schedule(anyInt(), any()))
+ whenever(scheduleWriteSettings())
+ whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+ whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+ true
}
- val service = DomainVerificationService(
- it,
- mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
- mockThrowOnUnmocked {
- whenever(
- isChangeEnabled(
- anyLong(),
- any()
- )
- ) { true }
- }).apply {
- setConnection(connection)
+ whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
}
-
- Triple(callingUidInt, callingUserIdInt, service)
+ val service = DomainVerificationService(
+ it,
+ mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+ mockThrowOnUnmocked {
+ whenever(
+ isChangeEnabled(
+ anyLong(),
+ any()
+ )
+ ) { true }
+ }).apply {
+ setConnection(connection)
}
+ Triple(callingUidInt, callingUserIdInt, service)
+ }
+
fun enforcer(
type: Type,
name: String,
@@ -175,7 +180,7 @@ class DomainVerificationEnforcerTest {
service(Type.INTERNAL, "setStatusInternalPackageName") {
setDomainVerificationStatusInternal(
it.targetPackageName,
- DomainVerificationManager.STATE_SUCCESS,
+ DomainVerificationState.STATE_SUCCESS,
ArraySet(setOf("example.com"))
)
},
@@ -206,7 +211,7 @@ class DomainVerificationEnforcerTest {
setDomainVerificationStatus(
it.targetDomainSetId,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service(Type.VERIFIER, "setStatusInternalUid") {
@@ -214,7 +219,7 @@ class DomainVerificationEnforcerTest {
it.callingUid,
it.targetDomainSetId,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
@@ -475,24 +480,7 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// User selector makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
-
- // User selector doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
}
val callingUserId = 0
@@ -529,24 +517,7 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// User selector makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
-
- // User selector doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
}
val callingUserId = 0
@@ -590,24 +561,10 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// Legacy makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
-
- // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ // The legacy selector does a silent failure when the user IDs don't match, so it
+ // cannot verify the non-existent user ID check, as it will not throw an Exception.
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+ verifyUserIdCheck = false)
}
val callingUserId = 0
@@ -642,27 +599,29 @@ class DomainVerificationEnforcerTest {
}
val target = params.construct(context)
- fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
- // Legacy makes no distinction by UID
- val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
+ // Legacy code can return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ // as an error code. This is distinct from the class level assertFails as unfortunately
+ // the same number, 0, is used in opposite contexts, where it does represent a failure
+ // for this legacy case, but not for the modern APIs.
+ fun assertFailsLegacy(block: () -> Any?) {
+ try {
+ val value = block()
+ if ((value as? Int)
+ != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ ) {
+ throw AssertionError("Expected call to return false, was $value")
}
+ } catch (e: SecurityException) {
+ } catch (e: PackageManager.NameNotFoundException) {
+ // Any of these 2 exceptions are considered failures, which is expected
}
+ }
- // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = false, callingUserId, targetUserId)
- }
- }
+ fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+ // Legacy makes no distinction by UID
+ val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+ assertFailsMethod = ::assertFailsLegacy)
}
val callingUserId = 0
@@ -704,17 +663,8 @@ class DomainVerificationEnforcerTest {
fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
// Owner querent makes no distinction by UID
val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
- if (throws) {
- allUids.forEach {
- assertFails {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
- } else {
- allUids.forEach {
- runMethod(target, it, visible = true, callingUserId, targetUserId)
- }
- }
+ runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+ verifyInvisiblePkg = false)
}
val callingUserId = 0
@@ -782,22 +732,88 @@ class DomainVerificationEnforcerTest {
return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
}
+ private fun runCrossUserMethod(
+ allUids: Iterable<Int>,
+ target: Any,
+ callingUserId: Int,
+ targetUserId: Int,
+ throws: Boolean,
+ verifyUserIdCheck: Boolean = true,
+ verifyInvisiblePkg: Boolean = true,
+ assertFailsMethod: (() -> Any?) -> Unit = ::assertFails,
+ ) {
+ if (throws) {
+ allUids.forEach {
+ assertFailsMethod {
+ // When testing a non-user ID failure, send an invalid user ID.
+ // This ensures the failure occurs before the user ID check is run.
+ try {
+ runMethod(target, it, visible = true, callingUserId, 100)
+ } catch (e: SecurityException) {
+ if (verifyUserIdCheck) {
+ e.message?.let {
+ if (it.contains("user ID", ignoreCase = true)
+ || it.contains("100")) {
+ fail(
+ "Method should not check user existence before permissions"
+ )
+ }
+ }
+ }
+
+ // Rethrow to allow normal fail checking logic to run
+ throw e
+ }
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
+ }
+ }
+
+ if (verifyInvisiblePkg) {
+ allUids.forEach {
+ assertFailsMethod {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
+ }
+ }
+
+ if (verifyUserIdCheck) {
+ // An invalid target user ID should always fail
+ allUids.forEach {
+ assertFailsWith(SecurityException::class) {
+ runMethod(target, it, visible = true, callingUserId, 100)
+ }
+ }
+
+ // An invalid calling user ID should always fail, although this cannot happen in prod
+ allUids.forEach {
+ assertFailsWith(SecurityException::class) {
+ runMethod(target, it, visible = true, 100, targetUserId)
+ }
+ }
+ }
+ }
+
private fun assertFails(block: () -> Any?) {
try {
val value = block()
- // Some methods return false rather than throwing, so check that as well
- if ((value as? Boolean) != false) {
- // Can also return default value if it's a legacy call
- if ((value as? Int)
- != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
- ) {
- throw AssertionError("Expected call to return false, was $value")
- }
+ // Some methods return false or an error rather than throwing, so check that as well
+ val valueAsBoolean = value as? Boolean
+ if (valueAsBoolean == false) {
+ // Expected failure, do not throw
+ return
+ }
+
+ val valueAsInt = value as? Int
+ if (valueAsInt != null && valueAsInt == DomainVerificationManager.STATUS_OK) {
+ throw AssertionError("Expected call to return false, was $value")
}
} catch (e: SecurityException) {
} catch (e: PackageManager.NameNotFoundException) {
- } catch (e: DomainVerificationManager.InvalidDomainSetException) {
- // Any of these 3 exceptions are considered failures, which is expected
+ // Any of these 2 exceptions are considered failures, which is expected
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
new file mode 100644
index 000000000000..8ae4c5ae96a3
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
@@ -0,0 +1,48 @@
+/*
+ * 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.pm.test.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+
+import com.android.server.pm.verify.domain.DomainVerificationService;
+
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Proxies Kotlin calls to the Java layer such that null values can be passed for {@link NonNull}
+ * marked parameters, as Kotlin disallows this at the compiler leveling, preventing the null error
+ * codes from being tested.
+ */
+class DomainVerificationJavaUtil {
+
+ static int setStatusForceNullable(@NonNull DomainVerificationService service,
+ @Nullable UUID domainSetId, @Nullable Set<String> domains, int state)
+ throws PackageManager.NameNotFoundException {
+ return service.setDomainVerificationStatus(domainSetId, domains, state);
+ }
+
+ static int setUserSelectionForceNullable(@NonNull DomainVerificationService service,
+ @Nullable UUID domainSetId, @Nullable Set<String> domains, boolean enabled,
+ @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException {
+ return service.setDomainVerificationUserSelection(domainSetId, domains, enabled, userId);
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
index 9a3bd994eac0..509824d93512 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
@@ -19,9 +19,9 @@ package com.android.server.pm.test.verify.domain
import android.content.pm.IntentFilterVerificationInfo
import android.content.pm.PackageManager
import android.util.ArraySet
-import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.readXml
import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.writeXml
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Rule
import org.junit.Test
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
new file mode 100644
index 000000000000..0e74b65d25d5
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -0,0 +1,372 @@
+/*
+ * 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.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationUserState
+import android.os.Build
+import android.os.PatternMatcher
+import android.os.Process
+import android.util.ArraySet
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
+import java.util.UUID
+import kotlin.test.assertFailsWith
+
+class DomainVerificationManagerApiTest {
+
+ companion object {
+ private const val PKG_ONE = "com.test.one"
+ private const val PKG_TWO = "com.test.two"
+ private const val PKG_THREE = "com.test.three"
+
+ private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
+ private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
+ private val UUID_THREE = UUID.fromString("0b3260ed-07c4-4b45-840b-237f8fb8b433")
+ private val UUID_INVALID = UUID.fromString("ad33babc-490b-4965-9d78-7e91248b00f")
+
+ private val DOMAIN_BASE = DomainVerificationManagerApiTest::class.java.packageName
+ private val DOMAIN_1 = "one.$DOMAIN_BASE"
+ private val DOMAIN_2 = "two.$DOMAIN_BASE"
+ private val DOMAIN_3 = "three.$DOMAIN_BASE"
+ private val DOMAIN_4 = "four.$DOMAIN_BASE"
+ }
+
+ @Test
+ fun queryValidVerificationPackageNames() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkgWithDomains.getName())
+ }
+
+ @Test
+ fun getDomainVerificationInfoId() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ assertThat(service.getDomainVerificationInfoId(PKG_ONE)).isEqualTo(UUID_ONE)
+ assertThat(service.getDomainVerificationInfoId(PKG_TWO)).isEqualTo(UUID_TWO)
+
+ assertThat(service.getDomainVerificationInfoId("invalid.pkg.name")).isEqualTo(null)
+ }
+
+ @Test
+ fun getDomainVerificationInfo() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ val infoOne = service.getDomainVerificationInfo(pkgWithDomains.getName())
+ assertThat(infoOne).isNotNull()
+ assertThat(infoOne!!.identifier).isEqualTo(pkgWithDomains.domainSetId)
+ assertThat(infoOne.packageName).isEqualTo(pkgWithDomains.getName())
+ assertThat(infoOne.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DomainVerificationInfo.STATE_NO_RESPONSE,
+ DOMAIN_2 to DomainVerificationInfo.STATE_NO_RESPONSE,
+ ))
+
+ assertThat(service.getDomainVerificationInfo(pkgWithoutDomains.getName())).isNull()
+
+ assertFailsWith(PackageManager.NameNotFoundException::class) {
+ service.getDomainVerificationInfo("invalid.pkg.name")
+ }
+ }
+
+ @Test
+ fun setStatus() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+
+ val map = mutableMapOf(pkg1.getName() to pkg1, pkg2.getName() to pkg2)
+ val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_2), 1100))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setStatus(UUID_INVALID, setOf(DOMAIN_1), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID)
+
+ assertThat(DomainVerificationJavaUtil.setStatusForceNullable(service, null,
+ setOf(DOMAIN_1), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL)
+
+ assertThat(DomainVerificationJavaUtil.setStatusForceNullable(service, UUID_ONE, null,
+ 1100)).isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setStatus(UUID_ONE, emptySet(), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_3), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1, DOMAIN_2, DOMAIN_3), 1100))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), 15))
+ .isEqualTo(DomainVerificationManager.ERROR_INVALID_STATE_CODE)
+
+ map.clear()
+ assertFailsWith(PackageManager.NameNotFoundException::class){
+ service.setStatus(UUID_ONE, setOf(DOMAIN_1), 1100)
+ }
+ }
+
+ @Test
+ fun setDomainVerificationLinkHandlingAllowed() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+
+ val map = mutableMapOf(pkg1.getName() to pkg1, pkg2.getName() to pkg2)
+ val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
+
+ service.setDomainVerificationLinkHandlingAllowed(PKG_ONE, false, 0);
+
+ // Should edit same package, same user
+ assertThat(service.getDomainVerificationUserState(PKG_ONE, 0)
+ ?.isLinkHandlingAllowed).isEqualTo(false)
+
+ // Shouldn't edit different user
+ assertThat(service.getDomainVerificationUserState(PKG_ONE, 1)
+ ?.isLinkHandlingAllowed).isEqualTo(true)
+
+ // Shouldn't edit different package
+ assertThat(service.getDomainVerificationUserState(PKG_TWO, 0)
+ ?.isLinkHandlingAllowed).isEqualTo(true)
+
+ assertFailsWith(PackageManager.NameNotFoundException::class){
+ service.setDomainVerificationLinkHandlingAllowed("invalid.pkg.name", false, 0);
+ }
+ }
+
+ @Test
+ fun setUserSelection() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+ val pkg3 = mockPkgSetting(PKG_THREE, UUID_THREE, listOf(DOMAIN_1, DOMAIN_2))
+
+ val map = mutableMapOf(
+ pkg1.getName() to pkg1,
+ pkg2.getName() to pkg2,
+ pkg3.getName() to pkg3
+ )
+ val service = makeService(map::get).apply { addPackages(pkg1, pkg2, pkg3) }
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, 0))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_INVALID, setOf(DOMAIN_1), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID)
+
+ assertThat(DomainVerificationJavaUtil.setUserSelectionForceNullable(service, null,
+ setOf(DOMAIN_1), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL)
+
+ assertThat(DomainVerificationJavaUtil.setUserSelectionForceNullable(service, UUID_ONE, null,
+ true, 0)).isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setUserSelection(UUID_ONE, emptySet(), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_3), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_1, DOMAIN_2, DOMAIN_3), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+ service.setStatus(UUID_ONE, setOf(DOMAIN_2), DomainVerificationInfo.STATE_SUCCESS)
+
+ assertThat(service.setUserSelection(UUID_THREE, setOf(DOMAIN_2), true, 0))
+ .isEqualTo(DomainVerificationManager.ERROR_UNABLE_TO_APPROVE)
+
+ map.clear()
+ assertFailsWith(PackageManager.NameNotFoundException::class){
+ service.setUserSelection(UUID_ONE, setOf(DOMAIN_1), true, 0)
+ }
+ }
+
+ @Test
+ fun getDomainVerificationUserState() {
+ val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+ val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+ addPackages(pkgWithDomains, pkgWithoutDomains)
+ }
+
+ val infoOne = service.getDomainVerificationUserState(pkgWithDomains.getName(), 0)
+ assertThat(infoOne).isNotNull()
+ assertThat(infoOne!!.identifier).isEqualTo(pkgWithDomains.domainSetId)
+ assertThat(infoOne.packageName).isEqualTo(pkgWithDomains.getName())
+ assertThat(infoOne.isLinkHandlingAllowed).isTrue()
+ assertThat(infoOne.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DomainVerificationUserState.DOMAIN_STATE_NONE,
+ DOMAIN_2 to DomainVerificationUserState.DOMAIN_STATE_NONE,
+ ))
+
+ val infoTwo = service.getDomainVerificationUserState(pkgWithoutDomains.getName(), 0)
+ assertThat(infoTwo).isNotNull()
+ assertThat(infoTwo!!.identifier).isEqualTo(pkgWithoutDomains.domainSetId)
+ assertThat(infoTwo.packageName).isEqualTo(pkgWithoutDomains.getName())
+ assertThat(infoOne.isLinkHandlingAllowed).isTrue()
+ assertThat(infoTwo.hostToStateMap).isEmpty()
+
+ assertFailsWith(PackageManager.NameNotFoundException::class) {
+ service.getDomainVerificationUserState("invalid.pkg.name", 0)
+ }
+ }
+
+ @Test
+ fun getOwnersForDomain() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
+
+ val service = makeService(pkg1, pkg2).apply {
+ addPackages(pkg1, pkg2)
+ }
+
+ assertThat(service.getOwnersForDomain(DOMAIN_1, 0)).isEmpty()
+
+ service.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
+
+ service.setStatus(pkg2.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
+
+ service.setUserSelection(pkg1.domainSetId, setOf(DOMAIN_2), true, 0)
+
+ service.getOwnersForDomain(DOMAIN_1, 0).let {
+ assertThat(it).hasSize(2)
+ assertThat(it[0].packageName).isEqualTo(pkg1.getName())
+ assertThat(it[0].isOverrideable).isEqualTo(false)
+ assertThat(it[1].packageName).isEqualTo(pkg2.getName())
+ assertThat(it[1].isOverrideable).isEqualTo(false)
+ }
+
+ service.getOwnersForDomain(DOMAIN_2, 0).let {
+ assertThat(it).hasSize(1)
+ assertThat(it.single().packageName).isEqualTo(pkg1.getName())
+ assertThat(it.single().isOverrideable).isEqualTo(true)
+ }
+ assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
+
+ service.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0)
+ service.getOwnersForDomain(DOMAIN_2, 0).let {
+ assertThat(it).hasSize(1)
+ assertThat(it.single().packageName).isEqualTo(pkg2.getName())
+ assertThat(it.single().isOverrideable).isEqualTo(true)
+ }
+ assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
+ }
+
+ private fun makeService(vararg pkgSettings: PackageSetting) =
+ makeService { pkgName -> pkgSettings.find { pkgName == it.getName() } }
+
+ private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
+ DomainVerificationService(mockThrowOnUnmocked {
+ // Assume the test has every permission necessary
+ whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+ whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+ PackageManager.PERMISSION_GRANTED
+ }
+ }, mockThrowOnUnmocked {
+ whenever(linkedApps) { ArraySet<String>() }
+ }, mockThrowOnUnmocked {
+ whenever(isChangeEnabled(anyLong(), any())) { true }
+ }).apply {
+ setConnection(mockThrowOnUnmocked {
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(1)) { true }
+ whenever(scheduleWriteSettings())
+
+ // Need to provide an internal UID so some permission checks are ignored
+ whenever(callingUid) { Process.ROOT_UID }
+ whenever(callingUserId) { 0 }
+
+ whenever(getPackageSettingLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)
+ }
+ whenever(getPackageLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)?.getPkg()
+ }
+ })
+ }
+
+ private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
+ DOMAIN_1, DOMAIN_2
+ )) = mockThrowOnUnmocked<PackageSetting> {
+ val pkg = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { pkgName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+
+ val activityList = listOf(
+ ParsedActivity().apply {
+ domains.forEach {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority(it, null)
+ }
+ )
+ }
+ },
+ )
+
+ whenever(activities) { activityList }
+ }
+
+ whenever(getPkg()) { pkg }
+ whenever(getName()) { pkgName }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(getInstantApp(anyInt())) { false }
+ whenever(firstInstallTime) { 0L }
+ }
+
+ fun DomainVerificationService.addPackages(vararg pkgSettings: PackageSetting) =
+ pkgSettings.forEach(::addPackage)
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
index 8c31c65e1b0a..a5db3c578670 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -16,8 +16,8 @@
package com.android.server.pm.test.verify.domain
-import android.content.pm.verify.domain.DomainVerificationRequest
import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationRequest
import android.content.pm.verify.domain.DomainVerificationUserState
import com.android.server.pm.verify.domain.DomainVerificationPersistence
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
new file mode 100644
index 000000000000..fe3672d06bc0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -0,0 +1,425 @@
+/*
+ * 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.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_SUCCESS
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_UNMODIFIABLE
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_SELECTED
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_VERIFIED
+import android.os.Build
+import android.os.PatternMatcher
+import android.os.Process
+import android.util.ArraySet
+import android.util.Xml
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import java.util.UUID
+
+class DomainVerificationPackageTest {
+
+ companion object {
+ private const val PKG_ONE = "com.test.one"
+ private const val PKG_TWO = "com.test.two"
+ private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
+ private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
+
+ private val DOMAIN_BASE = DomainVerificationPackageTest::class.java.packageName
+ private val DOMAIN_1 = "one.$DOMAIN_BASE"
+ private val DOMAIN_2 = "two.$DOMAIN_BASE"
+ private val DOMAIN_3 = "three.$DOMAIN_BASE"
+ private val DOMAIN_4 = "four.$DOMAIN_BASE"
+
+ private const val USER_ID = 0
+ }
+
+ private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
+ private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
+
+ @Test
+ fun addPackageFirstTime() {
+ val service = makeService(pkg1, pkg2)
+ service.addPackage(pkg1)
+ val info = service.getInfo(pkg1.getName())
+ assertThat(info.packageName).isEqualTo(pkg1.getName())
+ assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_NO_RESPONSE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+
+ val userState = service.getUserState(pkg1.getName())
+ assertThat(userState.packageName).isEqualTo(pkg1.getName())
+ assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
+ assertThat(userState.user.identifier).isEqualTo(USER_ID)
+ assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_NONE,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkg1.getName())
+ }
+
+ @Test
+ fun addPackageActive() {
+ // language=XML
+ val xml = """
+ <?xml?>
+ <domain-verifications>
+ <active>
+ <package-state
+ packageName="${pkg1.getName()}"
+ id="${pkg1.domainSetId}"
+ >
+ <state>
+ <domain name="$DOMAIN_1" state="$STATE_SUCCESS"/>
+ </state>
+ <user-states>
+ <user-state userId="$USER_ID" allowLinkHandling="false">
+ <enabled-hosts>
+ <host name="$DOMAIN_2"/>
+ </enabled-hosts>
+ </user-state>
+ </user-states>
+ </package-state>
+ </active>
+ </domain-verifications>
+ """.trimIndent()
+
+ val service = makeService(pkg1, pkg2)
+ xml.byteInputStream().use {
+ service.readSettings(Xml.resolvePullParser(it))
+ }
+
+ service.addPackage(pkg1)
+
+ val info = service.getInfo(pkg1.getName())
+ assertThat(info.packageName).isEqualTo(pkg1.getName())
+ assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+
+ val userState = service.getUserState(pkg1.getName())
+ assertThat(userState.packageName).isEqualTo(pkg1.getName())
+ assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(userState.isLinkHandlingAllowed).isEqualTo(false)
+ assertThat(userState.user.identifier).isEqualTo(USER_ID)
+ assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkg1.getName())
+ }
+
+ @Test
+ fun migratePackageDropDomain() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
+ listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
+ listOf(DOMAIN_1, DOMAIN_2))
+
+ // Test 4 domains:
+ // 1 will be approved and preserved, 2 will be selected and preserved,
+ // 3 will be denied and dropped, 4 will be selected and dropped
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ // To test the approve/denial states, use the internal methods for this variant
+ service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_APPROVED,
+ ArraySet(setOf(DOMAIN_1)))
+ service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_DENIED,
+ ArraySet(setOf(DOMAIN_3)))
+ service.setUserSelection(
+ UUID_ONE, setOf(DOMAIN_2, DOMAIN_4), true, USER_ID)
+
+ // Check the verifier cannot change the shell approve/deny states
+ service.setStatus(UUID_ONE, setOf(DOMAIN_1, DOMAIN_3), STATE_SUCCESS)
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_UNMODIFIABLE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ DOMAIN_3 to STATE_UNMODIFIABLE,
+ DOMAIN_4 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ DOMAIN_3 to DOMAIN_STATE_NONE,
+ DOMAIN_4 to DOMAIN_STATE_SELECTED,
+ ))
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ map[pkgName] = pkgAfter
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_UNMODIFIABLE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ @Test
+ fun migratePackageDropAll() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, emptyList())
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_NO_RESPONSE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_NONE,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
+ assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
+ assertThat(service.queryValidVerificationPackageNames()).isEmpty()
+ }
+
+ @Test
+ fun migratePackageAddDomain() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
+ listOf(DOMAIN_1, DOMAIN_2))
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
+ listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
+
+ // Test 3 domains:
+ // 1 will be verified and preserved, 2 will be selected and preserved,
+ // 3 will be new and default
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS)
+ service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID)
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ DOMAIN_3 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ DOMAIN_3 to DOMAIN_STATE_NONE,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ @Test
+ fun migratePackageAddAll() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, emptyList())
+ val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
+
+ val map = mutableMapOf<String, PackageSetting>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+ .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
+ assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
+ assertThat(service.queryValidVerificationPackageNames()).isEmpty()
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_NO_RESPONSE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_NONE,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ private fun DomainVerificationService.getInfo(pkgName: String) =
+ getDomainVerificationInfo(pkgName)
+ .also { assertThat(it).isNotNull() }!!
+
+ private fun DomainVerificationService.getUserState(pkgName: String) =
+ getDomainVerificationUserState(pkgName, USER_ID)
+ .also { assertThat(it).isNotNull() }!!
+
+ private fun makeService(vararg pkgSettings: PackageSetting) =
+ makeService { pkgName -> pkgSettings.find { pkgName == it.getName()} }
+
+ private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
+ DomainVerificationService(mockThrowOnUnmocked {
+ // Assume the test has every permission necessary
+ whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+ whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+ PackageManager.PERMISSION_GRANTED
+ }
+ }, mockThrowOnUnmocked {
+ whenever(linkedApps) { ArraySet<String>() }
+ }, mockThrowOnUnmocked {
+ whenever(isChangeEnabled(ArgumentMatchers.anyLong(), any())) { true }
+ }).apply {
+ setConnection(mockThrowOnUnmocked {
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(1)) { true }
+ whenever(scheduleWriteSettings())
+
+ // Need to provide an internal UID so some permission checks are ignored
+ whenever(callingUid) { Process.ROOT_UID }
+ whenever(callingUserId) { 0 }
+
+ whenever(getPackageSettingLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)!!
+ }
+ whenever(getPackageLocked(anyString())) {
+ pkgSettingFunction(arguments[0] as String)!!.getPkg()
+ }
+ })
+ }
+
+ private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
+ DOMAIN_1, DOMAIN_2
+ )) = mockThrowOnUnmocked<PackageSetting> {
+ val pkg = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { pkgName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+
+ val activityList = listOf(
+ ParsedActivity().apply {
+ domains.forEach {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority(it, null)
+ }
+ )
+ }
+ },
+ )
+
+ whenever(activities) { activityList }
+ }
+
+ whenever(getPkg()) { pkg }
+ whenever(getName()) { pkgName }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(getInstantApp(anyInt())) { false }
+ whenever(firstInstallTime) { 0L }
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
index 6597577cf14f..f8fda12f806d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.verify.domain
-import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
import android.util.ArrayMap
import android.util.TypedXmlPullParser
import android.util.TypedXmlSerializer
@@ -117,11 +117,11 @@ class DomainVerificationPersistenceTest {
@Test
fun readMalformed() {
val stateZero = mockEmptyPkgState(0).apply {
- stateMap["example.com"] = DomainVerificationManager.STATE_SUCCESS
- stateMap["example.org"] = DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED
+ stateMap["example.com"] = DomainVerificationState.STATE_SUCCESS
+ stateMap["example.org"] = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED
// A domain without a written state falls back to default
- stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE
+ stateMap["missing-state.com"] = DomainVerificationState.STATE_NO_RESPONSE
userStates[1] = DomainVerificationInternalUserState(1).apply {
addHosts(setOf("example-user1.com", "example-user1.org"))
@@ -159,9 +159,9 @@ class DomainVerificationPersistenceTest {
>
<state>
<domain name="example.com" state="${
- DomainVerificationManager.STATE_SUCCESS}"/>
+ DomainVerificationState.STATE_SUCCESS}"/>
<domain name="example.org" state="${
- DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED}"/>
+ DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED}"/>
<not-domain name="not-domain.com" state="1"/>
<domain name="missing-state.com"/>
</state>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
index 91e5beccee09..a9b77ea6d95b 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
@@ -21,20 +21,20 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
+import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.verify.domain.DomainVerificationRequest
-import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationState
import android.os.Bundle
import android.os.UserHandle
import android.util.ArraySet
import com.android.server.DeviceIdleInternal
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.verify.domain.DomainVerificationCollector
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2
-import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
@@ -106,20 +106,22 @@ class DomainVerificationProxyTest {
when (val pkgName = arguments[0] as String) {
TEST_PKG_NAME_TARGET_ONE -> DomainVerificationInfo(
TEST_UUID_ONE, pkgName, mapOf(
- "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE,
- "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE
+ "example1.com" to DomainVerificationInfo.STATE_NO_RESPONSE,
+ "example2.com" to DomainVerificationInfo.STATE_NO_RESPONSE
)
)
TEST_PKG_NAME_TARGET_TWO -> DomainVerificationInfo(
TEST_UUID_TWO, pkgName, mapOf(
- "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE,
- "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE
+ "example3.com" to DomainVerificationInfo.STATE_NO_RESPONSE,
+ "example4.com" to DomainVerificationInfo.STATE_NO_RESPONSE
)
)
else -> throw IllegalArgumentException("Unexpected package name $pkgName")
}
}
- whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt()))
+ whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt())) {
+ DomainVerificationManager.STATUS_OK
+ }
}
collector = mockThrowOnUnmocked {
whenever(collectValidAutoVerifyDomains(any())) {
@@ -316,7 +318,7 @@ class DomainVerificationProxyTest {
eq(TEST_CALLING_UID_ACCEPT),
idCaptor.capture(),
hostCaptor.capture(),
- eq(DomainVerificationManager.STATE_SUCCESS)
+ eq(DomainVerificationState.STATE_SUCCESS)
)
assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt
new file mode 100644
index 000000000000..6859b113298d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.pm.test.verify.domain
+
+import android.annotation.UserIdInt
+import com.android.server.pm.verify.domain.DomainVerificationService
+import java.util.UUID
+
+fun DomainVerificationService.setStatus(domainSetId: UUID, domains: Set<String>, state: Int) =
+ setDomainVerificationStatus(domainSetId, domains.toMutableSet(), state)
+
+fun DomainVerificationService.setUserSelection(
+ domainSetId: UUID,
+ domains: Set<String>,
+ enabled: Boolean,
+ @UserIdInt userId: Int
+) = setDomainVerificationUserSelection(domainSetId, domains.toMutableSet(), enabled, userId)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 0d8f275be09c..377bae15e2d5 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -20,28 +20,27 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageUserState
-import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationState
import android.os.Build
import android.os.Process
import android.util.ArraySet
import android.util.SparseArray
import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
-import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.spyThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-import org.mockito.Mockito
+import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
-import org.mockito.Mockito.any
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
@@ -129,13 +128,13 @@ class DomainVerificationSettingsMutationTest {
setDomainVerificationStatus(
TEST_UUID,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service("setStatusInternalPackageName") {
setDomainVerificationStatusInternal(
TEST_PKG,
- DomainVerificationManager.STATE_SUCCESS,
+ DomainVerificationState.STATE_SUCCESS,
ArraySet(setOf("example.com"))
)
},
@@ -144,7 +143,7 @@ class DomainVerificationSettingsMutationTest {
TEST_UID,
TEST_UUID,
setOf("example.com"),
- DomainVerificationManager.STATE_SUCCESS
+ DomainVerificationState.STATE_SUCCESS
)
},
service("setLinkHandlingAllowedUserId") {
@@ -266,5 +265,7 @@ class DomainVerificationSettingsMutationTest {
// This doesn't check for visibility; that's done in the enforcer test
whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(10)) { true }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 0576125748fb..44c1b8f3fbb9 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
import android.content.pm.verify.domain.DomainVerificationUserState
import android.os.Build
import android.os.PatternMatcher
@@ -74,6 +75,8 @@ class DomainVerificationUserStateOverrideTest {
}).apply {
setConnection(mockThrowOnUnmocked {
whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(1)) { true }
whenever(scheduleWriteSettings())
// Need to provide an internal UID so some permission checks are ignored
@@ -154,19 +157,20 @@ class DomainVerificationUserStateOverrideTest {
.containsExactly(PKG_TWO)
}
- @Test(expected = IllegalArgumentException::class)
+ @Test
fun anotherPackageTakeoverFailure() {
val service = makeService()
// Verify 1 to give it a higher approval level
service.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
- DomainVerificationManager.STATE_SUCCESS)
+ DomainVerificationState.STATE_SUCCESS)
assertThat(service.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
assertThat(service.getOwnersForDomain(DOMAIN_ONE, USER_ID).map { it.packageName })
.containsExactly(PKG_ONE)
// Attempt override by package 2
- service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true, USER_ID)
+ assertThat(service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true,
+ USER_ID)).isEqualTo(DomainVerificationManager.ERROR_UNABLE_TO_APPROVE)
}
private fun DomainVerificationService.stateFor(pkgName: String, host: String) =
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
index ee0a16a70265..2e0cadf264cf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -11,7 +11,7 @@
* 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.server.pm.dex;
@@ -28,28 +28,34 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.BatteryManager;
import android.os.Build;
+import android.os.PowerManager;
import android.os.UserHandle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.server.pm.Installer;
+import com.android.server.pm.PackageManagerService;
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
import java.io.File;
@@ -69,9 +75,15 @@ public class DexManagerTests {
DelegateLastClassLoader.class.getName();
private static final String UNSUPPORTED_CLASS_LOADER_NAME = "unsupported.class_loader";
- @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+ private static final int TEST_BATTERY_LEVEL_CRITICAL = 10;
+ private static final int TEST_BATTERY_LEVEL_DEFAULT = 80;
+
+ public StaticMockitoSession mMockitoSession;
@Mock Installer mInstaller;
@Mock IPackageManager mPM;
+ @Mock BatteryManager mMockBatteryManager;
+ @Mock PowerManager mMockPowerManager;
+
private final Object mInstallLock = new Object();
private DexManager mDexManager;
@@ -117,7 +129,37 @@ public class DexManagerTests {
mSystemServerJarUpdatedContext = new TestData("android", isa, mUser0,
DELEGATE_LAST_CLASS_LOADER_NAME);
- mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
+ // Initialize Static Mocking
+
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // Mock....
+
+ mMockBatteryManager = ExtendedMockito.mock(BatteryManager.class);
+ mMockPowerManager = ExtendedMockito.mock(PowerManager.class);
+
+ setDefaultMockValues();
+
+ Resources mockResources = ExtendedMockito.mock(Resources.class);
+ ExtendedMockito.when(mockResources
+ .getInteger(com.android.internal.R.integer.config_criticalBatteryWarningLevel))
+ .thenReturn(15);
+
+ Context mockContext = ExtendedMockito.mock(Context.class);
+ ExtendedMockito.doReturn(mockResources)
+ .when(mockContext)
+ .getResources();
+ ExtendedMockito.doReturn(mMockBatteryManager)
+ .when(mockContext)
+ .getSystemService(BatteryManager.class);
+ ExtendedMockito.doReturn(mMockPowerManager)
+ .when(mockContext)
+ .getSystemService(PowerManager.class);
+
+ mDexManager = new DexManager(mockContext, mPM, /*PackageDexOptimizer*/ null,
mInstaller, mInstallLock);
// Foo and Bar are available to user0.
@@ -128,6 +170,25 @@ public class DexManagerTests {
mDexManager.load(existingPackages);
}
+ @After
+ public void teardown() throws Exception {
+ mMockitoSession.finishMocking();
+ }
+
+ private void setDefaultMockValues() {
+ ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_DISCHARGING)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+ ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_DEFAULT)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+ ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_NONE)
+ .when(mMockPowerManager)
+ .getCurrentThermalStatus();
+ }
+
@Test
public void testNotifyPrimaryUse() {
// The main dex file and splits are re-loaded by the app.
@@ -633,6 +694,114 @@ public class DexManagerTests {
assertNoDclInfo(mSystemServerJarInvalid);
}
+ @Test
+ public void testInstallScenarioToReasonDefault() {
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
+ @Test
+ public void testInstallScenarioToReasonThermal() {
+ ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_SEVERE)
+ .when(mMockPowerManager)
+ .getCurrentThermalStatus();
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
+ @Test
+ public void testInstallScenarioToReasonBatteryDischarging() {
+ ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
+ @Test
+ public void testInstallScenarioToReasonBatteryCharging() {
+ ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+ ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_CHARGING)
+ .when(mMockBatteryManager)
+ .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_FAST,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_FAST));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK));
+
+ assertEquals(
+ PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+ mDexManager.getCompilationReasonForInstallScenario(
+ PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
String[] expectedContexts) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
new file mode 100644
index 000000000000..5a4431ee8c89
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
@@ -0,0 +1,2 @@
+calin@google.com
+ngeoffray@google.com
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/OwnersTest/OWNERS b/services/tests/servicestests/assets/OwnersTest/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
new file mode 100644
index 000000000000..e95633abe79a
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index a38745f2a66e..d9af51f819c3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -42,7 +42,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.FileUtils;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.recovery.KeyChainSnapshot;
@@ -109,7 +108,7 @@ public class KeySyncTaskTest {
private RecoverySnapshotStorage mRecoverySnapshotStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
- private AndroidKeyStoreSecretKey mWrappingKey;
+ private SecretKey mWrappingKey;
private PlatformEncryptionKey mEncryptKey;
private KeySyncTask mKeySyncTask;
@@ -848,7 +847,7 @@ public class KeySyncTaskTest {
return keyGenerator.generateKey();
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ private SecretKey generateAndroidKeyStoreKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -857,7 +856,7 @@ public class KeySyncTaskTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
private static byte[] utf8Bytes(String s) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index c295177814b0..64130266b2c4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import android.content.Context;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
@@ -45,6 +44,7 @@ import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
@SmallTest
@@ -77,7 +77,7 @@ public class RecoverableKeyGeneratorTest {
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
- AndroidKeyStoreSecretKey platformKey = generatePlatformKey();
+ SecretKey platformKey = generatePlatformKey();
mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, platformKey);
mDecryptKey = new PlatformDecryptionKey(TEST_GENERATION_ID, platformKey);
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mRecoverableKeyStoreDb);
@@ -168,7 +168,7 @@ public class RecoverableKeyGeneratorTest {
assertArrayEquals(rawMaterial, unwrappedMaterial);
}
- private AndroidKeyStoreSecretKey generatePlatformKey() throws Exception {
+ private SecretKey generatePlatformKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -177,7 +177,7 @@ public class RecoverableKeyGeneratorTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
private static byte[] randomBytes(int n) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b65e4877da50..a227cd3c6f5c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -45,7 +45,6 @@ import android.content.Intent;
import android.os.Binder;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -1311,7 +1310,7 @@ public class RecoverableKeyStoreManagerTest {
mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ private SecretKey generateAndroidKeyStoreKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -1320,6 +1319,6 @@ public class RecoverableKeyStoreManagerTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
index 9813ab74721e..60052f7114b3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Pair;
@@ -117,7 +116,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_decryptsWrappedKeys_nullMetadata() throws Exception {
String alias = "karlin";
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
SecretKey appKey = generateKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -136,7 +135,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_decryptsWrappedKeys_nonNullMetadata() throws Exception {
String alias = "karlin";
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
SecretKey appKey = generateKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NON_NULL_METADATA);
@@ -155,7 +154,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception {
String alias = "karlin";
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
SecretKey appKey = generateKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -171,7 +170,7 @@ public class WrappedKeyTest {
@Test
public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey platformKey = generateAndroidKeyStoreKey();
WrappedKey wrappedKey = WrappedKey.fromSecretKey(
new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey(),
/*metadata=*/ null);
@@ -197,7 +196,7 @@ public class WrappedKeyTest {
return keyGenerator.generateKey();
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ private SecretKey generateAndroidKeyStoreKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -207,6 +206,6 @@ public class WrappedKeyTest {
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ return keyGenerator.generateKey();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
index 3ab34484ce25..e605d755183f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -106,7 +106,8 @@ public final class ArtStatsLogUtilsTest {
RESULT_CODE);
// Assert
- verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX);
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
@@ -135,7 +136,8 @@ public final class ArtStatsLogUtilsTest {
RESULT_CODE);
// Assert
- verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE);
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
@@ -164,7 +166,8 @@ public final class ArtStatsLogUtilsTest {
RESULT_CODE);
// Assert
- verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX);
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
@@ -191,7 +194,8 @@ public final class ArtStatsLogUtilsTest {
RESULT_CODE);
// Assert
- verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA);
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE);
} finally {
deleteSliently(apk);
}
@@ -219,7 +223,8 @@ public final class ArtStatsLogUtilsTest {
RESULT_CODE);
// Assert
- verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA);
+ verifyWrites(ArtStatsLog.
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index 8874e0afd716..72e1e33491ff 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -203,6 +204,28 @@ public final class TranslationManagerService
}
}
+ @Override
+ public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+ TranslationManagerServiceImpl service;
+ synchronized (mLock) {
+ service = getServiceForUserLocked(userId);
+ }
+ if (service != null) {
+ service.registerUiTranslationStateCallback(callback, Binder.getCallingUid());
+ }
+ }
+
+ @Override
+ public void unregisterUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+ TranslationManagerServiceImpl service;
+ synchronized (mLock) {
+ service = getServiceForUserLocked(userId);
+ }
+ if (service != null) {
+ service.unregisterUiTranslationStateCallback(callback);
+ }
+ }
+
/**
* Dump the service state into the given stream. You run "adb shell dumpsys translation".
*/
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index ab6ac12c90fa..1ca07cb8d928 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -17,17 +17,24 @@
package com.android.server.translation;
import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS;
+import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE;
+import static android.view.translation.UiTranslationManager.EXTRA_STATE;
+import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.service.translation.TranslationServiceInfo;
import android.util.Slog;
import android.view.autofill.AutofillId;
+import android.view.inputmethod.InputMethodInfo;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationManager.UiTranslationState;
@@ -36,6 +43,7 @@ import com.android.internal.os.IResultReceiver;
import com.android.internal.util.SyncResultReceiver;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
@@ -174,5 +182,50 @@ final class TranslationManagerServiceImpl extends
} catch (RemoteException e) {
Slog.w(TAG, "Update UiTranslationState fail: " + e);
}
+ invokeCallbacks(state, sourceSpec, destSpec);
}
+
+ private void invokeCallbacks(
+ int state, TranslationSpec sourceSpec, TranslationSpec targetSpec) {
+ Bundle res = new Bundle();
+ res.putInt(EXTRA_STATE, state);
+ // TODO(177500482): Store the locale pair so it can be sent for RESUME events.
+ if (sourceSpec != null) {
+ res.putString(EXTRA_SOURCE_LOCALE, sourceSpec.getLanguage());
+ res.putString(EXTRA_TARGET_LOCALE, targetSpec.getLanguage());
+ }
+ // TODO(177500482): Only support the *current* Input Method.
+ List<InputMethodInfo> enabledInputMethods =
+ LocalServices.getService(InputMethodManagerInternal.class)
+ .getEnabledInputMethodListAsUser(mUserId);
+ mCallbacks.broadcast((callback, uid) -> {
+ // Code here is non-optimal since it's temporary..
+ boolean isIme = false;
+ for (InputMethodInfo inputMethod : enabledInputMethods) {
+ if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) {
+ isIme = true;
+ }
+ }
+ // TODO(177500482): Invoke it for the application being translated too.
+ if (!isIme) {
+ return;
+ }
+ try {
+ callback.sendResult(res);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e);
+ }
+ });
+ }
+
+ public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) {
+ mCallbacks.register(callback, sourceUid);
+ // TODO(177500482): trigger the callback here if we're already translating the UI.
+ }
+
+ public void unregisterUiTranslationStateCallback(IRemoteCallback callback) {
+ mCallbacks.unregister(callback);
+ }
+
+ private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 45eafa45c78d..47fbe1350a79 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2974,6 +2974,18 @@ public class CarrierConfigManager {
public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
/**
+ * Indicates if the carrier supports upgrading a call that was previously an RTT call to VT.
+ */
+ public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL =
+ "vt_upgrade_supported_for_downgraded_rtt_call";
+
+ /**
+ * Indicates if the carrier supports upgrading a call that was previously a VT call to RTT.
+ */
+ public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL =
+ "rtt_upgrade_supported_for_downgraded_vt_call";
+
+ /**
* Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
*/
public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
@@ -5156,6 +5168,8 @@ public class CarrierConfigManager {
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_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL, true);
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/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b30dd2697645..96af172e489e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -54,6 +54,7 @@ import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -2374,6 +2375,41 @@ interface ITelephony {
void setDeviceUceEnabled(boolean isEnabled);
/**
+ * Add feature tags to the IMS registration being tracked by UCE and potentially
+ * generate a new PUBLISH to the network.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability addUceRegistrationOverrideShell(int subId, in List<String> featureTags);
+
+ /**
+ * Remove feature tags from the IMS registration being tracked by UCE and potentially
+ * generate a new PUBLISH to the network.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+ in List<String> featureTags);
+
+ /**
+ * Clear overridden feature tags in the IMS registration being tracked by UCE and potentially
+ * generate a new PUBLISH to the network.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability clearUceRegistrationOverrideShell(int subId);
+
+ /**
+ * Get the latest RcsContactUceCapability structure that is used in SIP PUBLISH procedures.
+ * Note: This is designed for a SHELL command only.
+ */
+ RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId);
+
+ /**
+ * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the
+ * device does not have an active PUBLISH.
+ * Note: This is designed for a SHELL command only.
+ */
+ String getLastUcePidfXmlShell(int subId);
+
+ /**
* Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
* specified thresholds.
*/
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 58d8ee512dc2..a0b13c89ff49 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,6 +23,8 @@ import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -35,11 +37,14 @@ import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
@@ -183,7 +188,7 @@ import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
import android.net.IQosCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -378,6 +383,11 @@ public class ConnectivityServiceTest {
// Set a non-zero value to verify the flow to set tcp init rwnd value.
private static final int TEST_TCP_INIT_RWND = 60;
+ // Used for testing the per-work-profile default network.
+ private static final int TEST_APP_ID = 103;
+ private static final int TEST_WORK_PROFILE_USER_ID = 2;
+ private static final int TEST_WORK_PROFILE_APP_UID =
+ UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID);
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
@@ -421,6 +431,7 @@ public class ConnectivityServiceTest {
private VpnManagerService mVpnManagerService;
private TestNetworkCallback mDefaultNetworkCallback;
private TestNetworkCallback mSystemDefaultNetworkCallback;
+ private TestNetworkCallback mProfileDefaultNetworkCallback;
// State variables required to emulate NetworkPolicyManagerService behaviour.
private int mUidRules = RULE_NONE;
@@ -542,13 +553,26 @@ public class ConnectivityServiceTest {
return super.getSystemService(name);
}
+ final HashMap<UserHandle, UserManager> mUserManagers = new HashMap<>();
@Override
public Context createContextAsUser(UserHandle user, int flags) {
final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this));
doReturn(user).when(asUser).getUser();
+ doAnswer((inv) -> {
+ final UserManager um = mUserManagers.computeIfAbsent(user,
+ u -> mock(UserManager.class, AdditionalAnswers.delegatesTo(mUserManager)));
+ return um;
+ }).when(asUser).getSystemService(Context.USER_SERVICE);
return asUser;
}
+ public void setWorkProfile(@NonNull final UserHandle userHandle, boolean value) {
+ // This relies on all contexts for a given user returning the same UM mock
+ final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */)
+ .getSystemService(UserManager.class);
+ doReturn(value).when(umMock).isManagedProfile();
+ }
+
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
@@ -1408,17 +1432,36 @@ public class ConnectivityServiceTest {
fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
}
- private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
- int uid) {
+ private <T> T doAsUid(final int uid, @NonNull final Supplier<T> what) {
when(mDeps.getCallingUid()).thenReturn(uid);
try {
- mCm.registerNetworkCallback(request, callback);
- waitForIdle();
+ return what.get();
} finally {
returnRealCallingUid();
}
}
+ private void doAsUid(final int uid, @NonNull final Runnable what) {
+ doAsUid(uid, () -> {
+ what.run(); return Void.TYPE;
+ });
+ }
+
+ private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
+ int uid) {
+ doAsUid(uid, () -> {
+ mCm.registerNetworkCallback(request, callback);
+ });
+ }
+
+ private void registerDefaultNetworkCallbackAsUid(@NonNull final NetworkCallback callback,
+ final int uid) {
+ doAsUid(uid, () -> {
+ mCm.registerDefaultNetworkCallback(callback);
+ waitForIdle();
+ });
+ }
+
private static final int PRIMARY_USER = 0;
private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
@@ -1465,6 +1508,9 @@ public class ConnectivityServiceTest {
Looper.prepare();
}
mockDefaultPackages();
+ mockHasSystemFeature(FEATURE_WIFI, true);
+ mockHasSystemFeature(FEATURE_WIFI_DIRECT, true);
+ doReturn(true).when(mTelephonyManager).isDataCapable();
FakeSettingsProvider.clearSettingsProvider();
mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
@@ -1517,10 +1563,7 @@ public class ConnectivityServiceTest {
}
private ConnectivityService.Dependencies makeDependencies() {
- doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties)
- .getInt("net.tcp.default_init_rwnd", 0);
doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
- doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
doReturn(mNetIdManager).when(deps).makeNetIdManager();
@@ -1578,6 +1621,7 @@ public class ConnectivityServiceTest {
@After
public void tearDown() throws Exception {
unregisterDefaultNetworkCallbacks();
+ maybeTearDownEnterpriseNetwork();
setAlwaysOnNetworks(false);
if (mCellNetworkAgent != null) {
mCellNetworkAgent.disconnect();
@@ -1788,7 +1832,8 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS));
- assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+ assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+ assertFalse(mCm.isNetworkSupported(TYPE_PROXY));
// Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our
// mocks, this assert exercises the ConnectivityService code path that ensures that
@@ -7997,7 +8042,6 @@ public class ConnectivityServiceTest {
// Switching default network updates TCP buffer sizes.
verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
- verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
// Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that
// the NAT64 prefix was removed because one was never discovered.
cellLp.addLinkAddress(myIpv4);
@@ -8483,14 +8527,12 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false);
networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
- verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
// Change link Properties should have updated tcp buffer size.
LinkProperties lp = new LinkProperties();
lp.setTcpBufferSizes(testTcpBufferSizes);
mCellNetworkAgent.sendLinkProperties(lp);
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verifyTcpBufferSizeChange(testTcpBufferSizes);
- verify(mSystemProperties, times(2)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
// Clean up.
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -10118,9 +10160,12 @@ public class ConnectivityServiceTest {
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
mSystemDefaultNetworkCallback = new TestNetworkCallback();
mDefaultNetworkCallback = new TestNetworkCallback();
+ mProfileDefaultNetworkCallback = new TestNetworkCallback();
mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
+ registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
+ TEST_WORK_PROFILE_APP_UID);
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
}
@@ -10132,6 +10177,9 @@ public class ConnectivityServiceTest {
if (null != mSystemDefaultNetworkCallback) {
mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback);
}
+ if (null != mProfileDefaultNetworkCallback) {
+ mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
+ }
}
private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -10187,7 +10235,7 @@ public class ConnectivityServiceTest {
oemPrefListener.expectOnComplete();
}
- private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
+ private static class TestOemListenerCallback implements IOnCompleteListener {
final CompletableFuture<Object> mDone = new CompletableFuture<>();
@Override
@@ -11220,4 +11268,400 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect();
bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
+
+ private UidRangeParcel[] uidRangeFor(final UserHandle handle) {
+ UidRange range = UidRange.createForUser(handle);
+ return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) };
+ }
+
+ private static class TestOnCompleteListener implements Runnable {
+ final class OnComplete {}
+ final ArrayTrackRecord<OnComplete>.ReadHead mHistory =
+ new ArrayTrackRecord<OnComplete>().newReadHead();
+
+ @Override
+ public void run() {
+ mHistory.add(new OnComplete());
+ }
+
+ public void expectOnComplete() {
+ assertNotNull(mHistory.poll(TIMEOUT_MS, it -> true));
+ }
+ }
+
+ private TestNetworkAgentWrapper makeEnterpriseNetworkAgent() throws Exception {
+ final NetworkCapabilities workNc = new NetworkCapabilities();
+ workNc.addCapability(NET_CAPABILITY_ENTERPRISE);
+ workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc);
+ }
+
+ private TestNetworkCallback mEnterpriseCallback;
+ private UserHandle setupEnterpriseNetwork() {
+ final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(userHandle, true);
+
+ // File a request to avoid the enterprise network being disconnected as soon as the default
+ // request goes away – it would make impossible to test that networkRemoveUidRanges
+ // is called, as the network would disconnect first for lack of a request.
+ mEnterpriseCallback = new TestNetworkCallback();
+ final NetworkRequest keepUpRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_ENTERPRISE)
+ .build();
+ mCm.requestNetwork(keepUpRequest, mEnterpriseCallback);
+ return userHandle;
+ }
+
+ private void maybeTearDownEnterpriseNetwork() {
+ if (null != mEnterpriseCallback) {
+ mCm.unregisterNetworkCallback(mEnterpriseCallback);
+ }
+ }
+
+ /**
+ * Make sure per-profile networking preference behaves as expected when the enterprise network
+ * goes up and down while the preference is active. Make sure they behave as expected whether
+ * there is a general default network or not.
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDown() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ registerDefaultNetworkCallbacks();
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+
+ // Setting a network preference for this user will create a new set of routing rules for
+ // the UID range that corresponds to this user, so as to define the default network
+ // for these apps separately. This is true because the multi-layer request relevant to
+ // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific
+ // rules to the correct network – in this case the system default network. The case where
+ // the default network for the profile happens to be the same as the system default
+ // is not handled specially, the rules are always active as long as a preference is set.
+ inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ // The enterprise network is not ready yet.
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ mProfileDefaultNetworkCallback);
+
+ final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+ workAgent.connect(false);
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
+ mSystemDefaultNetworkCallback.assertNoCallback();
+ mDefaultNetworkCallback.assertNoCallback();
+ inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM);
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ // Make sure changes to the work agent send callbacks to the app in the work profile, but
+ // not to the other apps.
+ workAgent.setNetworkValid(true /* isStrictMode */);
+ workAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent,
+ nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)
+ && nc.hasCapability(NET_CAPABILITY_ENTERPRISE));
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+ workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+ // Conversely, change a capability on the system-wide default network and make sure
+ // that only the apps outside of the work profile receive the callbacks.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ mProfileDefaultNetworkCallback.assertNoCallback();
+
+ // Disconnect and reconnect the system-wide default network and make sure that the
+ // apps on this network see the appropriate callbacks, and the app on the work profile
+ // doesn't because it continues to use the enterprise network.
+ mCellNetworkAgent.disconnect();
+ mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.assertNoCallback();
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.assertNoCallback();
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+
+ // When the agent disconnects, test that the app on the work profile falls back to the
+ // default network.
+ workAgent.disconnect();
+ mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId);
+
+ mCellNetworkAgent.disconnect();
+ mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+ // If the control comes here, callbacks seem to behave correctly in the presence of
+ // a default network when the enterprise network goes up and down. Now, make sure they
+ // also behave correctly in the absence of a system-wide default network.
+ final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent();
+ workAgent2.connect(false);
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM);
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ workAgent2.setNetworkValid(true /* isStrictMode */);
+ workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid());
+ mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2,
+ nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE)
+ && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+
+ // When the agent disconnects, test that the app on the work profile falls back to the
+ // default network.
+ workAgent2.disconnect();
+ mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ waitForIdle();
+ inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId);
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ mProfileDefaultNetworkCallback);
+
+ // Callbacks will be unregistered by tearDown()
+ }
+
+ /**
+ * Test that, in a given networking context, calling setPreferenceForUser to set per-profile
+ * defaults on then off works as expected.
+ */
+ @Test
+ public void testSetPreferenceForUserOnOff() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+
+ // Connect both a regular cell agent and an enterprise network first.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+ workAgent.connect(true);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ registerDefaultNetworkCallbacks();
+
+ mSystemDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ workAgent.disconnect();
+ mCellNetworkAgent.disconnect();
+
+ // Callbacks will be unregistered by tearDown()
+ }
+
+ /**
+ * Test per-profile default networks for two different profiles concurrently.
+ */
+ @Test
+ public void testSetPreferenceForTwoProfiles() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle2 = setupEnterpriseNetwork();
+ final UserHandle testHandle4 = UserHandle.of(TEST_WORK_PROFILE_USER_ID + 2);
+ mServiceContext.setWorkProfile(testHandle4, true);
+ registerDefaultNetworkCallbacks();
+
+ final TestNetworkCallback app4Cb = new TestNetworkCallback();
+ final int testWorkProfileAppUid4 =
+ UserHandle.getUid(testHandle4.getIdentifier(), TEST_APP_ID);
+ registerDefaultNetworkCallbackAsUid(app4Cb, testWorkProfileAppUid4);
+
+ // Connect both a regular cell agent and an enterprise network first.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+ workAgent.connect(true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle2));
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ app4Cb);
+
+ mCm.setProfileNetworkPreference(testHandle4, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle4));
+
+ app4Cb.expectAvailableCallbacksValidated(workAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ mProfileDefaultNetworkCallback);
+
+ mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+ uidRangeFor(testHandle2));
+
+ mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+ app4Cb);
+
+ workAgent.disconnect();
+ mCellNetworkAgent.disconnect();
+
+ mCm.unregisterNetworkCallback(app4Cb);
+ // Other callbacks will be unregistered by tearDown()
+ }
+
+ @Test
+ public void testProfilePreferenceRemovedUponUserRemoved() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+ INetd.PERMISSION_NONE);
+ inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+
+ final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER, testHandle);
+ processBroadcast(removedIntent);
+
+ inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle));
+ }
+
+ /**
+ * Make sure that OEM preference and per-profile preference can't be used at the same
+ * time and throw ISE if tried
+ */
+ @Test
+ public void testOemPreferenceAndProfilePreferenceExclusive() throws Exception {
+ final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(testHandle, true);
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+
+ setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY);
+ assertThrows("Should not be able to set per-profile pref while OEM prefs present",
+ IllegalStateException.class, () ->
+ mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener));
+
+ // Empty the OEM prefs
+ final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
+ final OemNetworkPreferences emptyOemPref = new OemNetworkPreferences.Builder().build();
+ mService.setOemNetworkPreference(emptyOemPref, oemPrefListener);
+ oemPrefListener.expectOnComplete();
+
+ mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ assertThrows("Should not be able to set OEM prefs while per-profile pref is on",
+ IllegalStateException.class , () ->
+ mService.setOemNetworkPreference(emptyOemPref, oemPrefListener));
+ }
+
+ /**
+ * Make sure wrong preferences for per-profile default networking are rejected.
+ */
+ @Test
+ public void testProfileNetworkPrefWrongPreference() throws Exception {
+ final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(testHandle, true);
+ assertThrows("Should not be able to set an illegal preference",
+ IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null));
+ }
+
+ /**
+ * Make sure requests for per-profile default networking for a non-work profile are
+ * rejected
+ */
+ @Test
+ public void testProfileNetworkPrefWrongProfile() throws Exception {
+ final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(testHandle, false);
+ assertThrows("Should not be able to set a user pref for a non-work profile",
+ IllegalArgumentException.class , () ->
+ mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
+ }
}
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index a10a3c81bc86..5ec111954fcc 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -21,13 +21,29 @@
package com.android.server
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_WIFI
+import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT
import android.net.ConnectivityManager.TYPE_ETHERNET
import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_MOBILE_CBS
+import android.net.ConnectivityManager.TYPE_MOBILE_DUN
+import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY
+import android.net.ConnectivityManager.TYPE_MOBILE_FOTA
+import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI
+import android.net.ConnectivityManager.TYPE_MOBILE_IA
+import android.net.ConnectivityManager.TYPE_MOBILE_IMS
+import android.net.ConnectivityManager.TYPE_MOBILE_MMS
import android.net.ConnectivityManager.TYPE_MOBILE_SUPL
+import android.net.ConnectivityManager.TYPE_VPN
import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.ConnectivityManager.TYPE_WIFI_P2P
import android.net.ConnectivityManager.TYPE_WIMAX
+import android.net.EthernetManager
import android.net.NetworkInfo.DetailedState.CONNECTED
import android.net.NetworkInfo.DetailedState.DISCONNECTED
+import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.server.ConnectivityService.LegacyTypeTracker
@@ -36,7 +52,6 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
@@ -52,88 +67,130 @@ const val UNSUPPORTED_TYPE = TYPE_WIMAX
@RunWith(AndroidJUnit4::class)
@SmallTest
class LegacyTypeTrackerTest {
- private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
+ private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE,
+ TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI,
+ TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA,
+ TYPE_MOBILE_EMERGENCY, TYPE_VPN)
private val mMockService = mock(ConnectivityService::class.java).apply {
doReturn(false).`when`(this).isDefaultNetwork(any())
}
- private val mTracker = LegacyTypeTracker(mMockService).apply {
- supportedTypes.forEach {
- addSupportedType(it)
- }
+ private val mPm = mock(PackageManager::class.java)
+ private val mContext = mock(Context::class.java).apply {
+ doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI)
+ doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+ doReturn(mPm).`when`(this).packageManager
+ doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService(
+ Context.ETHERNET_SERVICE)
+ }
+ private val mTm = mock(TelephonyManager::class.java).apply {
+ doReturn(true).`when`(this).isDataCapable
+ }
+
+ private fun makeTracker() = LegacyTypeTracker(mMockService).apply {
+ loadSupportedTypes(mContext, mTm)
}
@Test
fun testSupportedTypes() {
- try {
- mTracker.addSupportedType(supportedTypes[0])
- fail("Expected IllegalStateException")
- } catch (expected: IllegalStateException) {}
+ val tracker = makeTracker()
supportedTypes.forEach {
- assertTrue(mTracker.isTypeSupported(it))
+ assertTrue(tracker.isTypeSupported(it))
+ }
+ assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE))
+ }
+
+ @Test
+ fun testSupportedTypes_NoEthernet() {
+ doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE)
+ assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET))
+ }
+
+ @Test
+ fun testSupportedTypes_NoTelephony() {
+ doReturn(false).`when`(mTm).isDataCapable
+ val tracker = makeTracker()
+ val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN)
+ nonMobileTypes.forEach {
+ assertTrue(tracker.isTypeSupported(it))
+ }
+ supportedTypes.toSet().minus(nonMobileTypes).forEach {
+ assertFalse(tracker.isTypeSupported(it))
+ }
+ }
+
+ @Test
+ fun testSupportedTypes_NoWifiDirect() {
+ doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+ val tracker = makeTracker()
+ assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P))
+ supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach {
+ assertTrue(tracker.isTypeSupported(it))
}
- assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE))
}
@Test
fun testSupl() {
+ val tracker = makeTracker()
val mobileNai = mock(NetworkAgentInfo::class.java)
- mTracker.add(TYPE_MOBILE, mobileNai)
+ tracker.add(TYPE_MOBILE, mobileNai)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE)
reset(mMockService)
- mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+ tracker.add(TYPE_MOBILE_SUPL, mobileNai)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
reset(mMockService)
- mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
+ tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
reset(mMockService)
- mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+ tracker.add(TYPE_MOBILE_SUPL, mobileNai)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
reset(mMockService)
- mTracker.remove(mobileNai, false)
+ tracker.remove(mobileNai, false)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE)
}
@Test
fun testAddNetwork() {
+ val tracker = makeTracker()
val mobileNai = mock(NetworkAgentInfo::class.java)
val wifiNai = mock(NetworkAgentInfo::class.java)
- mTracker.add(TYPE_MOBILE, mobileNai)
- mTracker.add(TYPE_WIFI, wifiNai)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.add(TYPE_MOBILE, mobileNai)
+ tracker.add(TYPE_WIFI, wifiNai)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Make sure adding a second NAI does not change the results.
val secondMobileNai = mock(NetworkAgentInfo::class.java)
- mTracker.add(TYPE_MOBILE, secondMobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.add(TYPE_MOBILE, secondMobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Make sure removing a network that wasn't added for this type is a no-op.
- mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Remove the top network for mobile and make sure the second one becomes the network
// of record for this type.
- mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
- assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
- assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
+ assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
+ assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
// Make sure adding a network for an unsupported type does not register it.
- mTracker.add(UNSUPPORTED_TYPE, mobileNai)
- assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE))
+ tracker.add(UNSUPPORTED_TYPE, mobileNai)
+ assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE))
}
@Test
fun testBroadcastOnDisconnect() {
+ val tracker = makeTracker()
val mobileNai1 = mock(NetworkAgentInfo::class.java)
val mobileNai2 = mock(NetworkAgentInfo::class.java)
doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
- mTracker.add(TYPE_MOBILE, mobileNai1)
+ tracker.add(TYPE_MOBILE, mobileNai1)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
reset(mMockService)
doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
- mTracker.add(TYPE_MOBILE, mobileNai2)
+ tracker.add(TYPE_MOBILE, mobileNai2)
verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
- mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
+ tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE)
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index a02002752c38..814cad4ab448 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -74,7 +74,7 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker;