summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java16
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java2
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java2
-rw-r--r--services/core/java/android/os/UserManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java20
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java21
-rw-r--r--services/core/java/com/android/server/SystemService.java73
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java50
-rw-r--r--services/core/java/com/android/server/VibratorService.java17
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java8
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java21
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java3
-rw-r--r--services/core/java/com/android/server/am/UidObserverController.java196
-rw-r--r--services/core/java/com/android/server/am/UserController.java4
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java11
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java52
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java21
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java56
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/Interruptable.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/Face10.java101
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java30
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java116
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java557
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java17
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java49
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java4
-rw-r--r--services/core/java/com/android/server/gpu/GpuService.java51
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java4
-rw-r--r--services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java253
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java176
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java25
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java3
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java12
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java39
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java23
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java10
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java2
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java12
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java11
-rw-r--r--services/core/java/com/android/server/pm/Installer.java150
-rw-r--r--services/core/java/com/android/server/pm/InstantAppResolver.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java118
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java222
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java6
-rw-r--r--services/core/java/com/android/server/pm/Settings.java34
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java23
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java19
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java73
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java2
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java41
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java107
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java38
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java21
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java27
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java24
-rw-r--r--services/core/jni/com_android_server_VibratorService.cpp19
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java208
-rw-r--r--services/print/java/com/android/server/print/UserState.java5
-rw-r--r--services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java3
-rw-r--r--services/tests/PackageManagerServiceTests/host/Android.bp11
-rw-r--r--services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp19
-rw-r--r--services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt24
-rw-r--r--services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt43
-rw-r--r--services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt48
-rw-r--r--services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt48
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt3
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt14
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt3
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt3
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt1
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt3
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt413
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp28
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml38
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt62
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt160
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp48
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml94
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml34
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml32
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml33
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml33
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml34
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml34
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorServiceTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java303
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java67
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java4
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java15
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java37
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java10
124 files changed, 4047 insertions, 1223 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 833aeecc4c47..c41f4007aa59 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1530,12 +1530,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
- relevantEventTypes |= isClientInPackageWhitelist(service.getServiceInfo(), client)
+ relevantEventTypes |= isClientInPackageAllowlist(service.getServiceInfo(), client)
? service.getRelevantEventTypes()
: 0;
}
- relevantEventTypes |= isClientInPackageWhitelist(
+ relevantEventTypes |= isClientInPackageAllowlist(
mUiAutomationManager.getServiceInfo(), client)
? mUiAutomationManager.getRelevantEventTypes()
: 0;
@@ -1571,7 +1571,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private static boolean isClientInPackageWhitelist(
+ private static boolean isClientInPackageAllowlist(
@Nullable AccessibilityServiceInfo serviceInfo, Client client) {
if (serviceInfo == null) return false;
@@ -1590,7 +1590,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Slog.d(LOG_TAG, "Dropping events: "
+ Arrays.toString(clientPackages) + " -> "
+ serviceInfo.getComponentName().flattenToShortString()
- + " due to not being in package whitelist "
+ + " due to not being in package allowlist "
+ Arrays.toString(serviceInfo.packageNames));
}
}
@@ -1991,9 +1991,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) {
- // Up to JB-MR1 we had a white list with services that can enable touch
+ // Up to JB-MR1 we had a allowlist with services that can enable touch
// exploration. When a service is first started we show a dialog to the
- // use to get a permission to white list the service.
+ // use to get a permission to allowlist the service.
final int installedServiceCount = userState.mInstalledServices.size();
for (int i = 0; i < installedServiceCount; i++) {
AccessibilityServiceInfo serviceInfo = userState.mInstalledServices.get(i);
@@ -2261,9 +2261,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
if (service.getServiceInfo().getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
<= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- // Up to JB-MR1 we had a white list with services that can enable touch
+ // Up to JB-MR1 we had a allowlist with services that can enable touch
// exploration. When a service is first started we show a dialog to the
- // use to get a permission to white list the service.
+ // use to get a permission to allowlist the service.
if (userState.mTouchExplorationGrantedServices.contains(service.mComponentName)) {
return true;
} else if (mEnableTouchExplorationDialog == null
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 663fd6259fd4..ad85784ca066 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -360,7 +360,7 @@ public final class AutofillManagerService
@Override // from SystemService
public boolean isUserSupported(TargetUser user) {
- return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
+ return user.isFull() || user.isManagedProfile();
}
@Override // from SystemService
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index ea94ad0b3c20..e742015916e7 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -219,7 +219,7 @@ public final class ContentCaptureManagerService extends
@Override // from SystemService
public boolean isUserSupported(TargetUser user) {
- return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
+ return user.isFull() || user.isManagedProfile();
}
@Override // from SystemService
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index fbe8c04bd59c..61e8128bd510 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -262,8 +262,7 @@ public abstract class UserManagerInternal {
public abstract boolean hasUserRestriction(String restriction, int userId);
/**
- * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not
- * found.
+ * Gets a {@link UserInfo} for the given {@code userId}, or {@code null} if not found.
*/
public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 97f3b373f63e..ee794badad88 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,14 +20,14 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
import static android.Manifest.permission.SHUTDOWN;
-import static android.net.INetd.FIREWALL_BLACKLIST;
+import static android.net.INetd.FIREWALL_ALLOWLIST;
import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
import static android.net.INetd.FIREWALL_CHAIN_NONE;
import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_DENYLIST;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.INetd.FIREWALL_RULE_DENY;
-import static android.net.INetd.FIREWALL_WHITELIST;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -1575,7 +1575,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
enforceSystemUid();
try {
mNetdService.firewallSetFirewallType(
- enabled ? INetd.FIREWALL_WHITELIST : INetd.FIREWALL_BLACKLIST);
+ enabled ? INetd.FIREWALL_ALLOWLIST : INetd.FIREWALL_DENYLIST);
mFirewallEnabled = enabled;
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
@@ -1608,7 +1608,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
int numUids = 0;
if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
- if (getFirewallType(chain) == FIREWALL_WHITELIST) {
+ if (getFirewallType(chain) == FIREWALL_ALLOWLIST) {
// Close all sockets on all non-system UIDs...
ranges = new UidRangeParcel[] {
// TODO: is there a better way of finding all existing users? If so, we could
@@ -1714,13 +1714,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
private int getFirewallType(int chain) {
switch (chain) {
case FIREWALL_CHAIN_STANDBY:
- return FIREWALL_BLACKLIST;
+ return FIREWALL_DENYLIST;
case FIREWALL_CHAIN_DOZABLE:
- return FIREWALL_WHITELIST;
+ return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_POWERSAVE:
- return FIREWALL_WHITELIST;
+ return FIREWALL_ALLOWLIST;
default:
- return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST;
+ return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST;
}
}
@@ -1822,7 +1822,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
private @NonNull String getFirewallRuleName(int chain, int rule) {
String ruleName;
- if (getFirewallType(chain) == FIREWALL_WHITELIST) {
+ if (getFirewallType(chain) == FIREWALL_ALLOWLIST) {
if (rule == FIREWALL_RULE_ALLOW) {
ruleName = "allow";
} else {
@@ -1856,7 +1856,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
private int getFirewallRuleType(int chain, int rule) {
if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
- return getFirewallType(chain) == FIREWALL_WHITELIST
+ return getFirewallType(chain) == FIREWALL_ALLOWLIST
? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
}
return rule;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d1d9c0e3a285..b72985cc8f2c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1141,13 +1141,6 @@ class StorageManagerService extends IStorageManager.Stub
}
private void completeUnlockUser(int userId) {
- // If user 0 has completed unlock, perform a one-time migration of legacy obb data
- // to its new location. This may take time depending on the size of the data to be copied
- // so it's done on the StorageManager handler thread.
- if (userId == 0) {
- mPmInternal.migrateLegacyObbData();
- }
-
onKeyguardStateChanged(false);
// Record user as started so newly mounted volumes kick off events
@@ -1540,9 +1533,21 @@ class StorageManagerService extends IStorageManager.Stub
mFuseMountedUser.remove(vol.getMountUserId());
} else if (mVoldAppDataIsolationEnabled){
final int userId = vol.getMountUserId();
- mFuseMountedUser.add(userId);
// Async remount app storage so it won't block the main thread.
new Thread(() -> {
+
+ // If user 0 has completed unlock, perform a one-time migration of legacy
+ // obb data to its new location. This may take time depending on the size of
+ // the data to be copied so it's done on the StorageManager worker thread.
+ // This needs to be finished before start mounting obb directories.
+ if (userId == 0) {
+ mPmInternal.migrateLegacyObbData();
+ }
+
+ // Add fuse mounted user after migration to prevent ProcessList tries to
+ // create obb directory before migration is done.
+ mFuseMountedUser.add(userId);
+
Map<Integer, String> pidPkgMap = null;
// getProcessesWithPendingBindMounts() could fail when a new app process is
// starting and it's not planning to mount storage dirs in zygote, but it's
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 1496e926b95b..84d01ec3598d 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
+import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.UserInfo;
@@ -131,45 +132,89 @@ public abstract class SystemService {
*/
@SystemApi(client = Client.SYSTEM_SERVER)
public static final class TargetUser {
- @NonNull
- private final UserInfo mUserInfo;
+
+ // NOTE: attributes below must be immutable while ther user is running (i.e., from the
+ // moment it's started until after it's shutdown).
+ private final @UserIdInt int mUserId;
+ private final boolean mFull;
+ private final boolean mManagedProfile;
+ private final boolean mPreCreated;
/** @hide */
public TargetUser(@NonNull UserInfo userInfo) {
- mUserInfo = userInfo;
+ mUserId = userInfo.id;
+ mFull = userInfo.isFull();
+ mManagedProfile = userInfo.isManagedProfile();
+ mPreCreated = userInfo.preCreated;
}
/**
- * @return The information about the user. <b>NOTE: </b> this is a "live" object
- * referenced by {@link UserManagerService} and hence should not be modified.
+ * Checks if the target user is {@link UserInfo#isFull() full}.
*
* @hide
*/
- @NonNull
- public UserInfo getUserInfo() {
- return mUserInfo;
+ public boolean isFull() {
+ return mFull;
+ }
+
+ /**
+ * Checks if the target user is a managed profile.
+ *
+ * @hide
+ */
+ public boolean isManagedProfile() {
+ return mManagedProfile;
+ }
+
+ /**
+ * Checks if the target user is a pre-created user.
+ *
+ * @hide
+ */
+ public boolean isPreCreated() {
+ return mPreCreated;
}
/**
- * @return the target {@link UserHandle}.
+ * Gets the target user's {@link UserHandle}.
*/
@NonNull
public UserHandle getUserHandle() {
- return mUserInfo.getUserHandle();
+ return UserHandle.of(mUserId);
}
/**
- * @return the integer user id
+ * Gets the target user's id.
*
* @hide
*/
- public int getUserIdentifier() {
- return mUserInfo.id;
+ public @UserIdInt int getUserIdentifier() {
+ return mUserId;
}
@Override
public String toString() {
- return Integer.toString(getUserIdentifier());
+ return Integer.toString(mUserId);
+ }
+
+ /**
+ * @hide
+ */
+ public void dump(@NonNull StringBuilder builder) {
+ builder.append(getUserIdentifier());
+
+ if (!isFull() && !isManagedProfile()) return;
+
+ builder.append('(');
+ boolean addComma = false;
+ if (isFull()) {
+ builder.append("full");
+ }
+ if (isManagedProfile()) {
+ if (addComma) builder.append(',');
+ builder.append("mp");
+ }
+ builder.append(')');
}
}
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 74bb7d7e90f1..df966c1ea6ac 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -27,7 +27,10 @@ import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import com.android.server.SystemService.TargetUser;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -74,6 +77,13 @@ public class SystemServiceManager {
private UserManagerInternal mUserManagerInternal;
+ /**
+ * Map of started {@link TargetUser TargetUsers} by user id; users are added on start and
+ * removed after they're completely shut down.
+ */
+ @GuardedBy("mTargetUsers")
+ private final SparseArray<TargetUser> mTargetUsers = new SparseArray<>();
+
SystemServiceManager(Context context) {
mContext = context;
}
@@ -240,21 +250,26 @@ public class SystemServiceManager {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
}
- private @NonNull UserInfo getUserInfo(@UserIdInt int userHandle) {
- if (mUserManagerInternal == null) {
- throw new IllegalStateException("mUserManagerInternal not set yet");
+ private @NonNull TargetUser getTargetUser(@UserIdInt int userHandle) {
+ final TargetUser targetUser;
+ synchronized (mTargetUsers) {
+ targetUser = mTargetUsers.get(userHandle);
}
- final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle);
- if (userInfo == null) {
- throw new IllegalStateException("No UserInfo for " + userHandle);
- }
- return userInfo;
+ Preconditions.checkState(targetUser != null, "No TargetUser for " + userHandle);
+ return targetUser;
}
/**
* Starts the given user.
*/
public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
+ // Create cached TargetUser
+ final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle);
+ Preconditions.checkState(userInfo != null, "No UserInfo for " + userHandle);
+ synchronized (mTargetUsers) {
+ mTargetUsers.put(userHandle, new TargetUser(userInfo));
+ }
+
onUser(t, START, userHandle);
}
@@ -291,6 +306,11 @@ public class SystemServiceManager {
*/
public void cleanupUser(final @UserIdInt int userHandle) {
onUser(CLEANUP, userHandle);
+
+ // Remove cached TargetUser
+ synchronized (mTargetUsers) {
+ mTargetUsers.remove(userHandle);
+ }
}
private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) {
@@ -306,9 +326,9 @@ public class SystemServiceManager {
@UserIdInt int curUserId, @UserIdInt int prevUserId) {
t.traceBegin("ssm." + onWhat + "User-" + curUserId);
Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
- final TargetUser curUser = new TargetUser(getUserInfo(curUserId));
+ final TargetUser curUser = getTargetUser(curUserId);
final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null
- : new TargetUser(getUserInfo(prevUserId));
+ : getTargetUser(prevUserId);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -437,7 +457,7 @@ public class SystemServiceManager {
*/
public void dump() {
StringBuilder builder = new StringBuilder();
- builder.append("Current phase: ").append(mCurrentPhase).append("\n");
+ builder.append("Current phase: ").append(mCurrentPhase).append('\n');
builder.append("Services:\n");
final int startedLen = mServices.size();
for (int i = 0; i < startedLen; i++) {
@@ -447,6 +467,14 @@ public class SystemServiceManager {
.append("\n");
}
+ builder.append("Target users: ");
+ final int targetUsersSize = mTargetUsers.size();
+ for (int i = 0; i < targetUsersSize; i++) {
+ mTargetUsers.valueAt(i).dump(builder);
+ if (i != targetUsersSize - 1) builder.append(',');
+ }
+ builder.append('\n');
+
Slog.e(TAG, builder.toString());
}
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 32d02fb17983..96d973ec5272 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -127,6 +127,7 @@ public class VibratorService extends IVibratorService.Stub
private final int mPreviousVibrationsLimit;
private final boolean mAllowPriorityVibrationsInLowPowerMode;
private final List<Integer> mSupportedEffects;
+ private final List<Integer> mSupportedPrimitives;
private final long mCapabilities;
private final int mDefaultVibrationAmplitude;
private final SparseArray<VibrationEffect> mFallbackEffects;
@@ -184,6 +185,8 @@ public class VibratorService extends IVibratorService.Stub
static native int[] vibratorGetSupportedEffects(long controllerPtr);
+ static native int[] vibratorGetSupportedPrimitives(long controllerPtr);
+
static native long vibratorPerformEffect(
long controllerPtr, long effect, long strength, Vibration vibration);
@@ -397,6 +400,7 @@ public class VibratorService extends IVibratorService.Stub
mNativeWrapper.vibratorOff();
mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects());
+ mSupportedPrimitives = asList(mNativeWrapper.vibratorGetSupportedPrimitives());
mCapabilities = mNativeWrapper.vibratorGetCapabilities();
mContext = context;
@@ -647,8 +651,11 @@ public class VibratorService extends IVibratorService.Stub
@Override // Binder call
public boolean[] arePrimitivesSupported(int[] primitiveIds) {
boolean[] supported = new boolean[primitiveIds.length];
- if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
- Arrays.fill(supported, true);
+ if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) || mSupportedPrimitives == null) {
+ return supported;
+ }
+ for (int i = 0; i < primitiveIds.length; i++) {
+ supported[i] = mSupportedPrimitives.contains(primitiveIds[i]);
}
return supported;
}
@@ -1501,6 +1508,7 @@ public class VibratorService extends IVibratorService.Stub
pw.println(" mNotificationIntensity=" + mNotificationIntensity);
pw.println(" mRingIntensity=" + mRingIntensity);
pw.println(" mSupportedEffects=" + mSupportedEffects);
+ pw.println(" mSupportedPrimitives=" + mSupportedPrimitives);
pw.println();
pw.println(" Previous ring vibrations:");
for (VibrationInfo info : mPreviousRingVibrations) {
@@ -1759,6 +1767,11 @@ public class VibratorService extends IVibratorService.Stub
return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr);
}
+ /** Returns all compose primitives supported by the device vibrator. */
+ public int[] vibratorGetSupportedPrimitives() {
+ return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr);
+ }
+
/** Turns vibrator on to perform one of the supported effects. */
public long vibratorPerformEffect(long effect, long strength, Vibration vibration) {
return VibratorService.vibratorPerformEffect(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 23e9d1b552d5..6328cb60cf73 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -493,11 +493,6 @@ public final class ActiveServices {
}
ServiceRecord r = res.record;
-
- if (allowBackgroundActivityStarts) {
- r.allowBgActivityStartsOnServiceStart();
- }
-
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r,
allowBackgroundActivityStarts);
@@ -710,6 +705,9 @@ public final class ActiveServices {
"Not potential delay (user " + r.userId + " not started): " + r);
}
}
+ if (allowBackgroundActivityStarts) {
+ r.allowBgActivityStartsOnServiceStart();
+ }
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 0f2dfcc699e2..757b4e83e120 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -100,15 +100,20 @@ final class CoreSettingsObserver extends ContentObserver {
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
- sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALL_APPS, int.class);
- sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_IN_APPS, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.UPDATABLE_DRIVER_ALL_APPS, int.class);
sGlobalSettingToTypeMap.put(
- Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS, String.class);
- sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class);
- sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_DENYLIST, String.class);
- sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALLOWLIST, String.class);
- sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_DENYLISTS, String.class);
- sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class);
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, String.class);
+ sGlobalSettingToTypeMap.put(
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES, String.class);
// add other global settings here...
sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 2e8660e1317a..4673ecde7a68 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2222,7 +2222,8 @@ public final class ProcessList {
// access /mnt/user anyway.
&& app.mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
&& app.mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH
- && app.mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER;
+ && app.mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER
+ && app.mountMode != Zygote.MOUNT_EXTERNAL_NONE;
}
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index 4c4dd8b8defa..4d9260aec62e 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -86,15 +86,17 @@ public class UidObserverController {
}
mService.mUiHandler.post(this::dispatchUidsChanged);
}
- final int NA = mAvailUidChanges.size();
- if (NA > 0) {
- pendingChange = mAvailUidChanges.remove(NA-1);
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Retrieving available item: " + pendingChange);
+ final int size = mAvailUidChanges.size();
+ if (size > 0) {
+ pendingChange = mAvailUidChanges.remove(size - 1);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + pendingChange);
+ }
} else {
pendingChange = new UidRecord.ChangeItem();
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Allocating new item: " + pendingChange);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + pendingChange);
+ }
}
if (uidRec != null) {
uidRec.pendingChange = pendingChange;
@@ -134,7 +136,8 @@ public class UidObserverController {
}
}
pendingChange.change = change;
- pendingChange.processState = uidRec != null ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
+ pendingChange.processState = uidRec != null
+ ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
pendingChange.capability = uidRec != null ? uidRec.setCapability : 0;
pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
@@ -165,13 +168,13 @@ public class UidObserverController {
@VisibleForTesting
void dispatchUidsChanged() {
- int N;
+ int numUidChanges;
synchronized (mService) {
- N = mPendingUidChanges.size();
- if (mActiveUidChanges.length < N) {
- mActiveUidChanges = new UidRecord.ChangeItem[N];
+ numUidChanges = mPendingUidChanges.size();
+ if (mActiveUidChanges.length < numUidChanges) {
+ mActiveUidChanges = new UidRecord.ChangeItem[numUidChanges];
}
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < numUidChanges; i++) {
final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
mActiveUidChanges[i] = change;
if (change.uidRecord != null) {
@@ -180,21 +183,22 @@ public class UidObserverController {
}
}
mPendingUidChanges.clear();
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "*** Delivering " + N + " uid changes");
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes");
+ }
}
- mUidChangeDispatchCount += N;
+ mUidChangeDispatchCount += numUidChanges;
int i = mUidObservers.beginBroadcast();
while (i > 0) {
i--;
dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
- (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), N);
+ (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges);
}
mUidObservers.finishBroadcast();
if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
- for (int j = 0; j < N; ++j) {
+ for (int j = 0; j < numUidChanges; ++j) {
final UidRecord.ChangeItem item = mActiveUidChanges[j];
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
mValidateUids.remove(item.uid);
@@ -217,7 +221,7 @@ public class UidObserverController {
}
synchronized (mService) {
- for (int j = 0; j < N; j++) {
+ for (int j = 0; j < numUidChanges; j++) {
mAvailUidChanges.add(mActiveUidChanges[j]);
}
}
@@ -232,66 +236,72 @@ public class UidObserverController {
for (int j = 0; j < changesSize; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
final int change = item.change;
- if (change == UidRecord.CHANGE_PROCSTATE &&
- (reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
+ if (change == UidRecord.CHANGE_PROCSTATE
+ && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
// No-op common case: no significant change, the observer is not
// interested in all proc state changes.
continue;
}
final long start = SystemClock.uptimeMillis();
if ((change & UidRecord.CHANGE_IDLE) != 0) {
- if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID idle uid=" + item.uid);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID idle uid=" + item.uid);
+ }
observer.onUidIdle(item.uid, item.ephemeral);
}
} else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
- if ((reg.which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID active uid=" + item.uid);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
+ }
observer.onUidActive(item.uid);
}
}
- if ((reg.which & ActivityManager.UID_OBSERVER_CACHED) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_CACHED) != 0) {
if ((change & UidRecord.CHANGE_CACHED) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID cached uid=" + item.uid);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID cached uid=" + item.uid);
+ }
observer.onUidCachedChanged(item.uid, true);
} else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID active uid=" + item.uid);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID active uid=" + item.uid);
+ }
observer.onUidCachedChanged(item.uid, false);
}
}
if ((change & UidRecord.CHANGE_GONE) != 0) {
- if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID gone uid=" + item.uid);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID gone uid=" + item.uid);
+ }
observer.onUidGone(item.uid, item.ephemeral);
}
- if (reg.lastProcStates != null) {
- reg.lastProcStates.delete(item.uid);
+ if (reg.mLastProcStates != null) {
+ reg.mLastProcStates.delete(item.uid);
}
} else {
- if ((reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID CHANGED uid=" + item.uid
- + ": " + item.processState + ": " + item.capability);
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
+ + ": " + item.processState + ": " + item.capability);
+ }
boolean doReport = true;
- if (reg.cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
- final int lastState = reg.lastProcStates.get(item.uid,
+ if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
+ final int lastState = reg.mLastProcStates.get(item.uid,
ActivityManager.PROCESS_STATE_UNKNOWN);
if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
- final boolean lastAboveCut = lastState <= reg.cutpoint;
- final boolean newAboveCut = item.processState <= reg.cutpoint;
+ final boolean lastAboveCut = lastState <= reg.mCutpoint;
+ final boolean newAboveCut = item.processState <= reg.mCutpoint;
doReport = lastAboveCut != newAboveCut;
} else {
doReport = item.processState != PROCESS_STATE_NONEXISTENT;
}
}
if (doReport) {
- if (reg.lastProcStates != null) {
- reg.lastProcStates.put(item.uid, item.processState);
+ if (reg.mLastProcStates != null) {
+ reg.mLastProcStates.put(item.uid, item.processState);
}
observer.onUidStateChanged(item.uid, item.processState,
item.procStateSeq, item.capability);
@@ -311,7 +321,7 @@ public class UidObserverController {
}
private boolean isEphemeralLocked(int uid) {
- String packages[] = mService.mContext.getPackageManager().getPackagesForUid(uid);
+ final String[] packages = mService.mContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
return false;
}
@@ -321,41 +331,41 @@ public class UidObserverController {
@GuardedBy("mService")
void dump(PrintWriter pw, String dumpPackage) {
- final int NI = mUidObservers.getRegisteredCallbackCount();
+ final int count = mUidObservers.getRegisteredCallbackCount();
boolean printed = false;
- for (int i=0; i<NI; i++) {
+ for (int i = 0; i < count; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
if (!printed) {
pw.println(" mUidObservers:");
printed = true;
}
- pw.print(" "); UserHandle.formatUid(pw, reg.uid);
- pw.print(" "); pw.print(reg.pkg);
+ pw.print(" "); UserHandle.formatUid(pw, reg.mUid);
+ pw.print(" "); pw.print(reg.mPkg);
final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
- if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
pw.print(" IDLE");
}
- if ((reg.which&ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
- pw.print(" ACT" );
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ pw.print(" ACT");
}
- if ((reg.which&ActivityManager.UID_OBSERVER_GONE) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
pw.print(" GONE");
}
- if ((reg.which&ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
pw.print(" STATE");
- pw.print(" (cut="); pw.print(reg.cutpoint);
+ pw.print(" (cut="); pw.print(reg.mCutpoint);
pw.print(")");
}
pw.println();
- if (reg.lastProcStates != null) {
- final int NJ = reg.lastProcStates.size();
- for (int j=0; j<NJ; j++) {
+ if (reg.mLastProcStates != null) {
+ final int size = reg.mLastProcStates.size();
+ for (int j = 0; j < size; j++) {
pw.print(" Last ");
- UserHandle.formatUid(pw, reg.lastProcStates.keyAt(j));
- pw.print(": "); pw.println(reg.lastProcStates.valueAt(j));
+ UserHandle.formatUid(pw, reg.mLastProcStates.keyAt(j));
+ pw.print(": "); pw.println(reg.mLastProcStates.valueAt(j));
}
}
}
@@ -366,8 +376,8 @@ public class UidObserverController {
pw.print(mUidChangeDispatchCount);
pw.println();
pw.println(" Slow UID dispatches:");
- final int N = mUidObservers.beginBroadcast();
- for (int i = 0; i < N; i++) {
+ final int size = mUidObservers.beginBroadcast();
+ for (int i = 0; i < size; i++) {
UidObserverRegistration r =
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
pw.print(" ");
@@ -383,21 +393,21 @@ public class UidObserverController {
@GuardedBy("mService")
void dumpDebug(ProtoOutputStream proto, String dumpPackage) {
- final int NI = mUidObservers.getRegisteredCallbackCount();
- for (int i=0; i<NI; i++) {
+ final int count = mUidObservers.getRegisteredCallbackCount();
+ for (int i = 0; i < count; i++) {
final UidObserverRegistration reg = (UidObserverRegistration)
mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.pkg)) {
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
}
}
}
- static final class UidObserverRegistration {
- final int uid;
- final String pkg;
- final int which;
- final int cutpoint;
+ private static final class UidObserverRegistration {
+ final int mUid;
+ final String mPkg;
+ final int mWhich;
+ final int mCutpoint;
/**
* Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
@@ -408,49 +418,49 @@ public class UidObserverController {
/** Max time it took for each dispatch. */
int mMaxDispatchTime;
- final SparseIntArray lastProcStates;
+ final SparseIntArray mLastProcStates;
// Please keep the enum lists in sync
- private static int[] ORIG_ENUMS = new int[]{
+ private static final int[] ORIG_ENUMS = new int[]{
ActivityManager.UID_OBSERVER_IDLE,
ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.UID_OBSERVER_GONE,
ActivityManager.UID_OBSERVER_PROCSTATE,
};
- private static int[] PROTO_ENUMS = new int[]{
+ private static final int[] PROTO_ENUMS = new int[]{
ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
};
- UidObserverRegistration(int _uid, String _pkg, int _which, int _cutpoint) {
- uid = _uid;
- pkg = _pkg;
- which = _which;
- cutpoint = _cutpoint;
+ UidObserverRegistration(int uid, String pkg, int which, int cutpoint) {
+ this.mUid = uid;
+ this.mPkg = pkg;
+ this.mWhich = which;
+ this.mCutpoint = cutpoint;
if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
- lastProcStates = new SparseIntArray();
+ mLastProcStates = new SparseIntArray();
} else {
- lastProcStates = null;
+ mLastProcStates = null;
}
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(UidObserverRegistrationProto.UID, uid);
- proto.write(UidObserverRegistrationProto.PACKAGE, pkg);
+ proto.write(UidObserverRegistrationProto.UID, mUid);
+ proto.write(UidObserverRegistrationProto.PACKAGE, mPkg);
ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
- which, ORIG_ENUMS, PROTO_ENUMS);
- proto.write(UidObserverRegistrationProto.CUT_POINT, cutpoint);
- if (lastProcStates != null) {
- final int NI = lastProcStates.size();
- for (int i=0; i<NI; i++) {
+ mWhich, ORIG_ENUMS, PROTO_ENUMS);
+ proto.write(UidObserverRegistrationProto.CUT_POINT, mCutpoint);
+ if (mLastProcStates != null) {
+ final int size = mLastProcStates.size();
+ for (int i = 0; i < size; i++) {
final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES);
proto.write(UidObserverRegistrationProto.ProcState.UID,
- lastProcStates.keyAt(i));
+ mLastProcStates.keyAt(i));
proto.write(UidObserverRegistrationProto.ProcState.STATE,
- lastProcStates.valueAt(i));
+ mLastProcStates.valueAt(i));
proto.end(pToken);
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 7420e0a6e828..83bf28e2d3ba 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -988,6 +988,8 @@ class UserController implements Handler.Callback {
final ArrayList<IStopUserCallback> stopCallbacks;
final ArrayList<KeyEvictedCallback> keyEvictedCallbacks;
int userIdToLock = userId;
+ // Must get a reference to UserInfo before it's removed
+ final UserInfo userInfo = getUserInfo(userId);
synchronized (mLock) {
stopCallbacks = new ArrayList<>(uss.mStopCallbacks);
keyEvictedCallbacks = new ArrayList<>(uss.mKeyEvictedCallbacks);
@@ -1030,8 +1032,8 @@ class UserController implements Handler.Callback {
if (stopped) {
mInjector.systemServiceManagerCleanupUser(userId);
mInjector.stackSupervisorRemoveUser(userId);
+
// Remove the user if it is ephemeral.
- UserInfo userInfo = getUserInfo(userId);
if (userInfo.isEphemeral() && !userInfo.preCreated) {
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index a47904c40cc3..54c179030929 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -259,17 +259,6 @@ public class AuthService extends SystemService {
}
@Override
- public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
- checkInternalPermission();
- final long identity = Binder.clearCallingIdentity();
- try {
- mBiometricService.resetLockout(userId, hardwareAuthToken);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
public long[] getAuthenticatorIds() throws RemoteException {
// In this method, we're not checking whether the caller is permitted to use face
// API because current authenticator ID is leaked (in a more contrived way) via Android
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 382bdbb246a1..3e0a40f9c288 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -638,19 +638,6 @@ public class BiometricService extends SystemService {
}
@Override // Binder call
- public void resetLockout(int userId, byte[] hardwareAuthToken) {
- checkInternalPermission();
-
- try {
- for (BiometricSensor sensor : mSensors) {
- sensor.impl.resetLockout(userId, hardwareAuthToken);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }
-
- @Override // Binder call
public long[] getAuthenticatorIds(int callingUserId) {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index ab48fdb0fd6e..73fc17aa26f1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -45,7 +45,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
private final PowerManager mPowerManager;
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
- private boolean mShouldSendErrorToClient;
+ private boolean mShouldSendErrorToClient = true;
private boolean mAlreadyCancelled;
/**
@@ -85,11 +85,11 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
// case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint
// that do not handle lockout under the HAL. In these cases, ensure that the framework only
// sends errors once per ClientMonitor.
- if (!mShouldSendErrorToClient) {
+ if (mShouldSendErrorToClient) {
logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
try {
if (getListener() != null) {
- mShouldSendErrorToClient = true;
+ mShouldSendErrorToClient = false;
getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
}
} catch (RemoteException e) {
@@ -98,7 +98,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
}
if (finish) {
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -114,7 +114,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
}
@Override
- public void cancelWithoutStarting(@NonNull FinishCallback finishCallback) {
+ public void cancelWithoutStarting(@NonNull Callback callback) {
final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
try {
if (getListener() != null) {
@@ -123,7 +123,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendError", e);
}
- finishCallback.onClientFinished(this, true /* success */);
+ callback.onClientFinished(this, true /* success */);
}
/**
@@ -155,7 +155,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendAcquired", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index cb2321f524f6..9128359250df 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -99,6 +99,14 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
return getCookie() != 0;
}
+ public long getOperationId() {
+ return mOperationId;
+ }
+
+ public boolean isRestricted() {
+ return mIsRestricted;
+ }
+
@Override
protected boolean isCryptoOperation() {
return mOperationId != 0;
@@ -191,7 +199,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify listener, finishing", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -211,8 +219,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
* Start authentication
*/
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
final @LockoutTracker.LockoutMode int lockoutMode =
mLockoutTracker.getLockoutModeForUser(getTargetUserId());
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 4f37dccea42e..588e865112c2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -85,7 +85,7 @@ public class BiometricScheduler {
static final int STATE_WAITING_FOR_COOKIE = 4;
/**
- * The {@link ClientMonitor.FinishCallback} has been invoked and the client is finished.
+ * The {@link ClientMonitor.Callback} has been invoked and the client is finished.
*/
static final int STATE_FINISHED = 5;
@@ -99,13 +99,13 @@ public class BiometricScheduler {
@interface OperationState {}
@NonNull final ClientMonitor<?> clientMonitor;
- @Nullable final ClientMonitor.FinishCallback clientFinishCallback;
+ @Nullable final ClientMonitor.Callback mClientCallback;
@OperationState int state;
Operation(@NonNull ClientMonitor<?> clientMonitor,
- @Nullable ClientMonitor.FinishCallback finishCallback) {
+ @Nullable ClientMonitor.Callback callback) {
this.clientMonitor = clientMonitor;
- this.clientFinishCallback = finishCallback;
+ this.mClientCallback = callback;
state = STATE_WAITING_IN_QUEUE;
}
@@ -133,7 +133,7 @@ public class BiometricScheduler {
public void run() {
if (operation.state != Operation.STATE_FINISHED) {
Slog.e(tag, "[Watchdog Triggered]: " + operation);
- operation.clientMonitor.mFinishCallback
+ operation.clientMonitor.mCallback
.onClientFinished(operation.clientMonitor, false /* success */);
}
}
@@ -175,17 +175,25 @@ public class BiometricScheduler {
@Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@NonNull private final IBiometricService mBiometricService;
@NonNull private final Handler mHandler = new Handler(Looper.getMainLooper());
- @NonNull private final InternalFinishCallback mInternalFinishCallback;
+ @NonNull private final InternalCallback mInternalCallback;
@NonNull private final Queue<Operation> mPendingOperations;
@Nullable private Operation mCurrentOperation;
@NonNull private final ArrayDeque<CrashState> mCrashStates;
- // Internal finish callback, notified when an operation is complete. Notifies the requester
+ // Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
// starting the next client).
- private class InternalFinishCallback implements ClientMonitor.FinishCallback {
+ public class InternalCallback implements ClientMonitor.Callback {
@Override
- public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) {
+ public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {
+ Slog.d(getTag(), "[Started] " + clientMonitor);
+ if (mCurrentOperation.mClientCallback != null) {
+ mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
+ }
+ }
+
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
mHandler.post(() -> {
if (mCurrentOperation == null) {
Slog.e(getTag(), "[Finishing] " + clientMonitor
@@ -203,8 +211,8 @@ public class BiometricScheduler {
Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success);
mCurrentOperation.state = Operation.STATE_FINISHED;
- if (mCurrentOperation.clientFinishCallback != null) {
- mCurrentOperation.clientFinishCallback.onClientFinished(clientMonitor, success);
+ if (mCurrentOperation.mClientCallback != null) {
+ mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success);
}
if (mGestureAvailabilityDispatcher != null) {
@@ -227,7 +235,7 @@ public class BiometricScheduler {
public BiometricScheduler(@NonNull String tag,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mBiometricTag = tag;
- mInternalFinishCallback = new InternalFinishCallback();
+ mInternalCallback = new InternalCallback();
mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
mPendingOperations = new ArrayDeque<>();
mBiometricService = IBiometricService.Stub.asInterface(
@@ -235,6 +243,14 @@ public class BiometricScheduler {
mCrashStates = new ArrayDeque<>();
}
+ /**
+ * @return A reference to the internal callback that should be invoked whenever the scheduler
+ * needs to (e.g. client started, client finished).
+ */
+ @NonNull protected InternalCallback getInternalCallback() {
+ return mInternalCallback;
+ }
+
private String getTag() {
return BASE_TAG + "/" + mBiometricTag;
}
@@ -262,7 +278,7 @@ public class BiometricScheduler {
}
final Interruptable interruptable = (Interruptable) currentClient;
- interruptable.cancelWithoutStarting(mInternalFinishCallback);
+ interruptable.cancelWithoutStarting(getInternalCallback());
// Now we wait for the client to send its FinishCallback, which kicks off the next
// operation.
return;
@@ -280,7 +296,7 @@ public class BiometricScheduler {
final boolean shouldStartNow = currentClient.getCookie() == 0;
if (shouldStartNow) {
Slog.d(getTag(), "[Starting] " + mCurrentOperation);
- currentClient.start(mInternalFinishCallback);
+ currentClient.start(getInternalCallback());
mCurrentOperation.state = Operation.STATE_STARTED;
} else {
try {
@@ -324,7 +340,7 @@ public class BiometricScheduler {
Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
mCurrentOperation.state = Operation.STATE_STARTED;
- mCurrentOperation.clientMonitor.start(mInternalFinishCallback);
+ mCurrentOperation.clientMonitor.start(getInternalCallback());
}
/**
@@ -340,11 +356,11 @@ public class BiometricScheduler {
* Adds a {@link ClientMonitor} to the pending queue
*
* @param clientMonitor operation to be scheduled
- * @param clientFinishCallback optional callback, invoked when the client is finished, but
+ * @param clientCallback optional callback, invoked when the client is finished, but
* before it has been removed from the queue.
*/
public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor,
- @Nullable ClientMonitor.FinishCallback clientFinishCallback) {
+ @Nullable ClientMonitor.Callback clientCallback) {
// Mark any interruptable pending clients as canceling. Once they reach the head of the
// queue, the scheduler will send ERROR_CANCELED and skip the operation.
for (Operation operation : mPendingOperations) {
@@ -356,7 +372,7 @@ public class BiometricScheduler {
}
}
- mPendingOperations.add(new Operation(clientMonitor, clientFinishCallback));
+ mPendingOperations.add(new Operation(clientMonitor, clientCallback));
Slog.d(getTag(), "[Added] " + clientMonitor
+ ", new queue size: " + mPendingOperations.size());
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 8b27781940ac..dec40e39fb29 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -42,7 +42,15 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde
/**
* Interface that ClientMonitor holders should use to receive callbacks.
*/
- public interface FinishCallback {
+ public interface Callback {
+ /**
+ * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
+ * the queue and becomes the current operation).
+ *
+ * @param clientMonitor Reference of the ClientMonitor that is starting.
+ */
+ default void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {}
+
/**
* Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
* (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
@@ -52,7 +60,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde
* @param clientMonitor Reference of the ClientMonitor that finished.
* @param success True if the operation completed successfully.
*/
- void onClientFinished(ClientMonitor<?> clientMonitor, boolean success);
+ default void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {}
}
/**
@@ -79,7 +87,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde
private final int mCookie;
boolean mAlreadyDone;
- @NonNull protected FinishCallback mFinishCallback;
+ @NonNull protected Callback mCallback;
/**
* @param context system_server context
@@ -125,17 +133,18 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde
/**
* Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
* If such a problem is detected, the scheduler will not invoke
- * {@link #start(FinishCallback)}.
+ * {@link #start(Callback)}.
*/
public abstract void unableToStart();
/**
* Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
* keeping is complete.
- * @param finishCallback invoked when the operation is complete (succeeds, fails, etc)
+ * @param callback invoked when the operation is complete (succeeds, fails, etc)
*/
- public void start(@NonNull FinishCallback finishCallback) {
- mFinishCallback = finishCallback;
+ public void start(@NonNull Callback callback) {
+ mCallback = callback;
+ mCallback.onClientStarted(this);
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 3cef6667b644..cb7db92ee132 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -128,11 +128,11 @@ public final class ClientMonitorCallbackConverter {
}
}
- public void onChallengeGenerated(long challenge) throws RemoteException {
+ public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
if (mFaceServiceReceiver != null) {
- mFaceServiceReceiver.onChallengeGenerated(challenge);
+ mFaceServiceReceiver.onChallengeGenerated(sensorId, challenge);
} else if (mFingerprintServiceReceiver != null) {
- mFingerprintServiceReceiver.onChallengeGenerated(challenge);
+ mFingerprintServiceReceiver.onChallengeGenerated(sensorId, challenge);
}
}
@@ -142,10 +142,21 @@ public final class ClientMonitorCallbackConverter {
}
}
- public void onFeatureGet(boolean success, int feature, boolean value)
- throws RemoteException {
+ public void onFeatureGet(boolean success, int feature, boolean value) throws RemoteException {
if (mFaceServiceReceiver != null) {
mFaceServiceReceiver.onFeatureGet(success, feature, value);
}
}
+
+ public void onChallengeInterrupted(int sensorId) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onChallengeInterrupted(sensorId);
+ }
+ }
+
+ public void onChallengeInterruptFinished(int sensorId) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onChallengeInterruptFinished(sensorId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index add5829c1342..8bf9680d60cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -77,18 +77,18 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> {
mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
true /* enrollSuccessful */);
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
}
notifyUserActivity();
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
if (hasReachedEnrollmentLimit()) {
Slog.e(TAG, "Reached enrollment limit");
- finishCallback.onClientFinished(this, false /* success */);
+ callback.onClientFinished(this, false /* success */);
return;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index dad5cad1ed8d..92c498c55dbf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -40,23 +40,23 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> {
@Override
public void unableToStart() {
try {
- getListener().onChallengeGenerated(0L);
+ getListener().onChallengeGenerated(getSensorId(), 0L);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send error", e);
}
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
startHalOperation();
try {
- getListener().onChallengeGenerated(mChallenge);
- mFinishCallback.onClientFinished(this, true /* success */);
+ getListener().onChallengeGenerated(getSensorId(), mChallenge);
+ mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 6d7b0fd3d5f1..5c08bce9b44f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -62,29 +62,35 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
private final List<S> mEnrolledList;
private ClientMonitor<T> mCurrentTask;
- private final FinishCallback mEnumerateFinishCallback = (clientMonitor, success) -> {
- final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
- ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
-
- if (!unknownHALTemplates.isEmpty()) {
- Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
- }
- for (BiometricAuthenticator.Identifier unknownHALTemplate : unknownHALTemplates) {
- mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplate,
- mCurrentTask.getTargetUserId()));
- }
-
- if (mUnknownHALTemplates.isEmpty()) {
- // No unknown HAL templates. Unknown framework templates are already cleaned up in
- // InternalEnumerateClient. Finish this client.
- mFinishCallback.onClientFinished(this, success);
- } else {
- startCleanupUnknownHalTemplates();
+ private final Callback mEnumerateCallback = new Callback() {
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
+ final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
+ ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
+
+ if (!unknownHALTemplates.isEmpty()) {
+ Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
+ }
+ for (BiometricAuthenticator.Identifier unknownHALTemplate : unknownHALTemplates) {
+ mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplate,
+ mCurrentTask.getTargetUserId()));
+ }
+
+ if (mUnknownHALTemplates.isEmpty()) {
+ // No unknown HAL templates. Unknown framework templates are already cleaned up in
+ // InternalEnumerateClient. Finish this client.
+ mCallback.onClientFinished(InternalCleanupClient.this, success);
+ } else {
+ startCleanupUnknownHalTemplates();
+ }
}
};
- private final FinishCallback mRemoveFinishCallback = (clientMonitor, success) -> {
- mFinishCallback.onClientFinished(this, success);
+ private final Callback mRemoveCallback = new Callback() {
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
+ mCallback.onClientFinished(InternalCleanupClient.this, success);
+ }
};
protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
@@ -116,7 +122,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
mStatsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
- mCurrentTask.start(mRemoveFinishCallback);
+ mCurrentTask.start(mRemoveCallback);
}
@Override
@@ -125,13 +131,13 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
// Start enumeration. Removal will start if necessary, when enumeration is completed.
mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
- mCurrentTask.start(mEnumerateFinishCallback);
+ mCurrentTask.start(mEnumerateCallback);
}
@Override
@@ -147,7 +153,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
+ mCurrentTask.getClass().getSimpleName());
return;
}
- ((RemovalClient) mCurrentTask).onRemoved(identifier, remaining);
+ ((RemovalClient<T>) mCurrentTask).onRemoved(identifier, remaining);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 3f73cd56e6c3..e07f71298d13 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -62,7 +62,7 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T>
handleEnumeratedTemplate(identifier);
if (remaining == 0) {
doTemplateCleanup();
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
}
}
@@ -72,8 +72,8 @@ public abstract class InternalEnumerateClient<T> extends ClientMonitor<T>
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
// The biometric template ids will be removed when we get confirmation from the HAL
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index 35d917747574..28e0117afd36 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -37,10 +37,10 @@ public interface Interruptable {
/**
* Notifies the client that it needs to finish before
- * {@link ClientMonitor#start(ClientMonitor.FinishCallback)} was invoked. This usually happens
+ * {@link ClientMonitor#start(ClientMonitor.Callback)} was invoked. This usually happens
* if the client is still waiting in the pending queue and got notified that a subsequent
* operation is preempting it.
- * @param finishCallback invoked when the operation is completed.
+ * @param callback invoked when the operation is completed.
*/
- void cancelWithoutStarting(@NonNull ClientMonitor.FinishCallback finishCallback);
+ void cancelWithoutStarting(@NonNull ClientMonitor.Callback callback);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
index 1a4216f9d43c..d85ab25cc812 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
@@ -58,6 +58,10 @@ public abstract class LoggableMonitor {
mStatsClient = statsClient;
}
+ public int getStatsClient() {
+ return mStatsClient;
+ }
+
private boolean isAnyFieldUnknown() {
return mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
|| mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 1c49bcdbadf4..1348f797a2dc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -55,8 +55,8 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
// The biometric template ids will be removed when we get confirmation from the HAL
startHalOperation();
@@ -85,7 +85,7 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov
// cleanup).
mAuthenticatorIds.put(getTargetUserId(), 0L);
}
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index b78ee49826f2..5deb8fa26639 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -36,10 +36,10 @@ public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> {
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
startHalOperation();
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index 6c57208c1e84..7dd6217016eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Build;
@@ -98,6 +99,11 @@ class Face10 implements IHwBinder.DeathRecipient {
@Nullable private IBiometricsFace mDaemon;
private int mCurrentUserId = UserHandle.USER_NULL;
+ // If a challenge is generated, keep track of its owner. Since IBiometricsFace@1.0 only
+ // supports a single in-flight challenge, we must notify the interrupted owner that its
+ // challenge is no longer valid. The interrupted owner will be notified when the interrupter
+ // has finished.
+ @Nullable private FaceGenerateChallengeClient mCurrentChallengeOwner;
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -394,9 +400,12 @@ class Face10 implements IHwBinder.DeathRecipient {
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId,
hasEnrolled, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> {
- if (success) {
- mCurrentUserId = targetUserId;
+ mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
+ if (success) {
+ mCurrentUserId = targetUserId;
+ }
}
});
}
@@ -456,21 +465,87 @@ class Face10 implements IHwBinder.DeathRecipient {
});
}
+ /**
+ * {@link IBiometricsFace} only supports a single in-flight challenge. In cases where two
+ * callers both need challenges (e.g. resetLockout right before enrollment), we need to ensure
+ * that either:
+ * 1) generateChallenge/operation/revokeChallenge is complete before the next generateChallenge
+ * is processed by the scheduler, or
+ * 2) the generateChallenge callback provides a mechanism for notifying the caller that its
+ * challenge has been invalidated by a subsequent caller, as well as a mechanism for
+ * notifying the previous caller that the interrupting operation is complete (e.g. the
+ * interrupting client's challenge has been revoked, so that the interrupted client can
+ * start retry logic if necessary). See
+ * {@link FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)}
+ * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second
+ * option seems better as it prioritizes the new operation, which is user-facing.
+ */
void scheduleGenerateChallenge(@NonNull IBinder token, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName) {
mHandler.post(() -> {
+ if (mCurrentChallengeOwner != null) {
+ Slog.w(TAG, "Current challenge owner: " + mCurrentChallengeOwner
+ + ", interrupted by: " + opPackageName);
+ try {
+ mCurrentChallengeOwner.getListener().onChallengeInterrupted(mSensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify challenge interrupted", e);
+ }
+ }
+
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
- mSensorId);
- mScheduler.scheduleClientMonitor(client);
+ mSensorId, mCurrentChallengeOwner);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ @Override
+ public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {
+ if (client != clientMonitor) {
+ Slog.e(TAG, "scheduleGenerateChallenge, mismatched client."
+ + " Expecting: " + client + ", received: " + clientMonitor);
+ return;
+ }
+ Slog.d(TAG, "Current challenge owner: " + client);
+ mCurrentChallengeOwner = client;
+ }
+ });
});
}
void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String owner) {
mHandler.post(() -> {
+ if (!mCurrentChallengeOwner.getOwnerString().contentEquals(owner)) {
+ Slog.e(TAG, "scheduleRevokeChallenge, package: " + owner
+ + " attempting to revoke challenge owned by: "
+ + mCurrentChallengeOwner.getOwnerString());
+ return;
+ }
+
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mLazyDaemon, token, owner, mSensorId);
- mScheduler.scheduleClientMonitor(client);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+ boolean success) {
+ if (client != clientMonitor) {
+ Slog.e(TAG, "scheduleRevokeChallenge, mismatched client."
+ + "Expecting: " + client + ", received: " + clientMonitor);
+ return;
+ }
+
+ final FaceGenerateChallengeClient previousChallengeOwner =
+ mCurrentChallengeOwner.getInterruptedClient();
+ mCurrentChallengeOwner = null;
+ Slog.d(TAG, "Previous challenge owner: " + previousChallengeOwner);
+ if (previousChallengeOwner != null) {
+ try {
+ previousChallengeOwner.getListener()
+ .onChallengeInterruptFinished(mSensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify interrupt finished", e);
+ }
+ }
+ }
+ });
});
}
@@ -488,12 +563,16 @@ class Face10 implements IHwBinder.DeathRecipient {
opPackageName, FaceUtils.getInstance(), disabledFeatures, ENROLL_TIMEOUT_SEC,
surfaceHandle, mSensorId);
- mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> {
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
+ mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+ boolean success) {
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
+ }
}
- }));
+ });
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
index 21bda74bc6b9..892d6a48488d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
@@ -92,7 +92,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -103,7 +103,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -131,7 +131,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
// 1) Authenticated == true
// 2) Error occurred
// 3) Authenticated == false
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 33244b8e61a8..bbee6cde4603 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -75,11 +75,6 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
- public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
- mFaceService.resetLockout(userId, hardwareAuthToken);
- }
-
- @Override
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFaceService.getAuthenticatorId(callingUserId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index 52a822675c2f..3e843cae96fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -116,12 +116,12 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
}
if (status != Status.OK) {
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -132,7 +132,7 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
index 67f2712d0b9d..ba401f255e43 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
@@ -17,12 +17,14 @@
package com.android.server.biometrics.sensors.face;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -36,10 +38,22 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet
private static final String TAG = "FaceGenerateChallengeClient";
private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+ // If `this` FaceGenerateChallengeClient was invoked while an existing in-flight challenge
+ // was not revoked yet, store a reference to the interrupted client here. Notify the interrupted
+ // client when `this` challenge is revoked.
+ @Nullable private final FaceGenerateChallengeClient mInterruptedClient;
+
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
+ @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId,
+ @Nullable FaceGenerateChallengeClient interruptedClient) {
super(context, lazyDaemon, token, listener, owner, sensorId);
+ mInterruptedClient = interruptedClient;
+ }
+
+ @Nullable
+ public FaceGenerateChallengeClient getInterruptedClient() {
+ return mInterruptedClient;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
index ce57cb7686ed..8c7b99d6eb52 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
@@ -61,8 +61,8 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> {
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
startHalOperation();
}
@@ -71,10 +71,10 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> {
try {
final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value);
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to getFeature", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
index f25242ee9b85..32d48321428a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -52,7 +52,7 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFac
getFreshDaemon().enumerate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
index 00d5f500b241..dde5ababd6c0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -51,7 +51,7 @@ class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
getFreshDaemon().remove(mBiometricId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
index 69070da0491e..f4324bedb4c6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
@@ -56,8 +56,8 @@ public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> {
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
startHalOperation();
}
@@ -65,10 +65,10 @@ public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> {
protected void startHalOperation() {
try {
getFreshDaemon().resetLockout(mHardwareAuthToken);
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to reset lockout", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index b832a09f8f88..b689106bbc44 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -80,16 +80,27 @@ public class FaceService extends SystemService {
}
@Override // Binder call
- public void generateChallenge(IBinder token, IFaceServiceReceiver receiver,
+ public void generateChallenge(IBinder token, int sensorId, IFaceServiceReceiver receiver,
String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
- mFace10.scheduleGenerateChallenge(token, receiver, opPackageName);
+ if (sensorId == mFace10.getFaceSensorProperties().sensorId) {
+ mFace10.scheduleGenerateChallenge(token, receiver, opPackageName);
+ return;
+ }
+
+ Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
}
@Override // Binder call
- public void revokeChallenge(IBinder token, String owner) {
+ public void revokeChallenge(IBinder token, int sensorId, String owner) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
- mFace10.scheduleRevokeChallenge(token, owner);
+
+ if (sensorId == mFace10.getFaceSensorProperties().sensorId) {
+ mFace10.scheduleRevokeChallenge(token, owner);
+ return;
+ }
+
+ Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
}
@Override // Binder call
@@ -267,9 +278,16 @@ public class FaceService extends SystemService {
}
@Override // Binder call
- public void resetLockout(int userId, byte[] hardwareAuthToken) {
+ public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken,
+ String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFace10.scheduleResetLockout(userId, hardwareAuthToken);
+
+ if (sensorId == mFace10.getFaceSensorProperties().sensorId) {
+ mFace10.scheduleResetLockout(userId, hardwareAuthToken);
+ return;
+ }
+
+ Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
index e7d041a11ccb..94abb7f378df 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
@@ -70,8 +70,8 @@ public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> {
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
startHalOperation();
}
@@ -82,10 +82,10 @@ public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> {
final int result = getFreshDaemon()
.setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
getListener().onFeatureSet(result == Status.OK, mFeature);
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java
index bcf304e47816..05b176d28e28 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java
@@ -50,8 +50,8 @@ public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> {
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
if (mCurrentUserId == getTargetUserId()) {
Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
@@ -61,7 +61,7 @@ public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> {
} catch (RemoteException e) {
Slog.e(TAG, "Unable to refresh authenticatorId", e);
}
- finishCallback.onClientFinished(this, true /* success */);
+ callback.onClientFinished(this, true /* success */);
return;
}
@@ -79,16 +79,16 @@ public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> {
FACE_DATA_DIR);
if (!storePath.exists()) {
Slog.e(TAG, "vold has not created the directory?");
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
return;
}
try {
getFreshDaemon().setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveUser: " + e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
index dad038626762..c5c28227fd24 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -84,7 +84,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
private static final String TAG = "Fingerprint21";
private static final int ENROLL_TIMEOUT_SEC = 60;
- private final Context mContext;
+ final Context mContext;
private final IActivityTaskManager mActivityTaskManager;
private final FingerprintSensorProperties mSensorProperties;
private final BiometricScheduler mScheduler;
@@ -96,6 +96,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFingerprint mDaemon;
+ @NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -146,15 +147,37 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
}
};
- private final IBiometricsFingerprintClientCallback mDaemonCallback =
- new IBiometricsFingerprintClientCallback.Stub() {
+ public static class HalResultController extends IBiometricsFingerprintClientCallback.Stub {
+
+ /**
+ * Interface to sends results to the HalResultController's owner.
+ */
+ public interface Callback {
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
+ @NonNull private final Context mContext;
+ @NonNull final Handler mHandler;
+ @NonNull final BiometricScheduler mScheduler;
+ @Nullable private Callback mCallback;
+
+ HalResultController(@NonNull Context context, @NonNull Handler handler,
+ @NonNull BiometricScheduler scheduler) {
+ mContext = context;
+ mHandler = handler;
+ mScheduler = scheduler;
+ }
+
+ public void setCallback(@Nullable Callback callback) {
+ mCallback = callback;
+ }
+
@Override
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.post(() -> {
- final CharSequence name = FingerprintUtils.getInstance()
- .getUniqueName(mContext, mCurrentUserId);
- final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
-
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintEnrollClient)) {
Slog.e(TAG, "onEnrollResult for non-enroll client: "
@@ -162,6 +185,11 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
return;
}
+ final int currentUserId = client.getTargetUserId();
+ final CharSequence name = FingerprintUtils.getInstance()
+ .getUniqueName(mContext, currentUserId);
+ final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
+
final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
enrollClient.onEnrollResult(fingerprint, remaining);
});
@@ -224,8 +252,9 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
+ if (mCallback != null) {
+ mCallback.onHardwareUnavailable();
+ }
}
});
}
@@ -262,20 +291,27 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
enumerateConsumer.onEnumerationResult(fp, remaining);
});
}
- };
+ }
- Fingerprint21(@NonNull Context context, int sensorId,
+ Fingerprint21(@NonNull Context context, @NonNull BiometricScheduler scheduler,
+ @NonNull Handler handler, int sensorId,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ @NonNull HalResultController controller) {
mContext = context;
+ mScheduler = scheduler;
+ mHandler = handler;
mActivityTaskManager = ActivityTaskManager.getService();
- mHandler = new Handler(Looper.getMainLooper());
+
mTaskStackListener = new BiometricTaskStackListener();
mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
mLazyDaemon = Fingerprint21.this::getDaemon;
mLockoutResetDispatcher = lockoutResetDispatcher;
mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
- mScheduler = new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
+ mHalResultController = controller;
+ mHalResultController.setCallback(() -> {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ });
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
@@ -300,7 +336,21 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
final @FingerprintSensorProperties.SensorType int sensorType =
isUdfps ? FingerprintSensorProperties.TYPE_UDFPS
: FingerprintSensorProperties.TYPE_REAR;
- mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType);
+ // resetLockout is controlled by the framework, so hardwareAuthToken is not required
+ final boolean resetLockoutRequiresHardwareAuthToken = false;
+ mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType,
+ resetLockoutRequiresHardwareAuthToken);
+ }
+
+ static Fingerprint21 newInstance(@NonNull Context context, int sensorId,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ final Handler handler = new Handler(Looper.getMainLooper());
+ final BiometricScheduler scheduler =
+ new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
+ final HalResultController controller = new HalResultController(context, handler, scheduler);
+ return new Fingerprint21(context, scheduler, handler, sensorId, lockoutResetDispatcher,
+ controller);
}
@Override
@@ -355,7 +405,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
// successfully set.
long halId = 0;
try {
- halId = mDaemon.setNotify(mDaemonCallback);
+ halId = mDaemon.setNotify(mHalResultController);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set callback for fingerprint HAL", e);
mDaemon = null;
@@ -373,6 +423,9 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
return mDaemon;
}
+ @Nullable IUdfpsOverlayController getUdfpsOverlayController() {
+ return mUdfpsOverlayController;
+ }
@LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
return mLockoutTracker.getLockoutModeForUser(userId);
}
@@ -409,14 +462,17 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
hasEnrolled, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> {
- if (success) {
- mCurrentUserId = targetUserId;
+ mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) {
+ if (success) {
+ mCurrentUserId = targetUserId;
+ }
}
});
}
- void scheduleResetLockout(int userId, byte[] hardwareAuthToken) {
+ void scheduleResetLockout(int userId) {
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
@@ -453,12 +509,16 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(),
ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController);
- mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> {
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId());
+ mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+ boolean success) {
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId());
+ }
}
- }));
+ });
});
}
@@ -485,7 +545,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
- @Nullable Surface surface, boolean restricted, int statsClient) {
+ boolean restricted, int statsClient, boolean isKeyguard) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -493,8 +553,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mLazyDaemon, token, listener, userId, operationId, restricted,
opPackageName, cookie, false /* requireConfirmation */,
- mSensorProperties.sensorId, isStrongBiometric, surface, statsClient,
- mTaskStackListener, mLockoutTracker, mUdfpsOverlayController);
+ mSensorProperties.sensorId, isStrongBiometric, statsClient,
+ mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, isKeyguard);
mScheduler.scheduleClientMonitor(client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
new file mode 100644
index 000000000000..1a6ad46fce67
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.trust.TrustManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+/**
+ * A mockable/testable provider of the {@link android.hardware.biometrics.fingerprint.V2_3} HIDL
+ * interface. This class is intended simulate UDFPS logic for devices that do not have an actual
+ * fingerprint@2.3 HAL (where UDFPS starts to become supported)
+ *
+ * UDFPS "accept" can only happen within a set amount of time after a sensor authentication. This is
+ * specified by {@link MockHalResultController#AUTH_VALIDITY_MS}. Touches after this duration will
+ * be treated as "reject".
+ *
+ * This class provides framework logic to emulate, for testing only, the UDFPS functionalies below:
+ *
+ * 1) IF either A) the caller is keyguard, and the device is not in a trusted state (authenticated
+ * via biometric sensor or unlocked with a trust agent {@see android.app.trust.TrustManager}, OR
+ * B) the caller is not keyguard, and regardless of trusted state, AND (following applies to both
+ * (A) and (B) above) {@link FingerprintManager#onFingerDown(int, int, float, float)} is
+ * received, this class will respond with {@link AuthenticationCallback#onAuthenticationFailed()}
+ * after a tunable flat_time + variance_time.
+ *
+ * In the case above (1), callers must not receive a successful authentication event here because
+ * the sensor has not actually been authenticated.
+ *
+ * 2) IF A) the caller is keyguard and the device is not in a trusted state, OR B) the caller is not
+ * keyguard and regardless of trusted state, AND (following applies to both (A) and (B)) the
+ * sensor is touched and the fingerprint is accepted by the HAL, and then
+ * {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
+ * respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
+ * after a tunable flat_time + variance_time. Note that the authentication callback from the
+ * sensor is held until {@link FingerprintManager#onFingerDown(int, int, float, float)} is
+ * invoked.
+ *
+ * In the case above (2), callers can receive a successful authentication callback because the
+ * real sensor was authenticated. Note that even though the real sensor was touched, keyguard
+ * fingerprint authentication does not put keyguard into a trusted state because the
+ * authentication callback is held until onFingerDown was invoked. This allows callers such as
+ * keyguard to simulate a realistic path.
+ *
+ * 3) IF the caller is keyguard AND the device in a trusted state and then
+ * {@link FingerprintManager#onFingerDown(int, int, float, float)} is received, this class will
+ * respond with {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)}
+ * after a tunable flat_time + variance time.
+ *
+ * In the case above (3), since the device is already unlockable via trust agent, it's fine to
+ * simulate the successful auth path. Non-keyguard clients will fall into either (1) or (2)
+ * above.
+ *
+ * This class currently does not simulate false rejection. Conversely, this class relies on the
+ * real hardware sensor so does not affect false acceptance.
+ */
+@SuppressWarnings("deprecation")
+public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManager.TrustListener {
+
+ private static final String TAG = "Fingerprint21UdfpsMock";
+
+ // Secure setting integer. If true, the system will load this class to enable udfps testing.
+ public static final String CONFIG_ENABLE_TEST_UDFPS =
+ "com.android.server.biometrics.sensors.fingerprint.test_udfps.enable";
+ // Secure setting integer. A fixed duration intended to simulate something like the duration
+ // required for image capture.
+ private static final String CONFIG_AUTH_DELAY_PT1 =
+ "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt1";
+ // Secure setting integer. A fixed duration intended to simulate something like the duration
+ // required for template matching to complete.
+ private static final String CONFIG_AUTH_DELAY_PT2 =
+ "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_pt2";
+ // Secure setting integer. A random value between [-randomness, randomness] will be added to the
+ // capture delay above for each accept/reject.
+ private static final String CONFIG_AUTH_DELAY_RANDOMNESS =
+ "com.android.server.biometrics.sensors.fingerprint.test_udfps.auth_delay_randomness";
+
+ private static final int DEFAULT_AUTH_DELAY_PT1_MS = 300;
+ private static final int DEFAULT_AUTH_DELAY_PT2_MS = 400;
+ private static final int DEFAULT_AUTH_DELAY_RANDOMNESS_MS = 100;
+
+ @NonNull private final TestableBiometricScheduler mScheduler;
+ @NonNull private final Handler mHandler;
+ @NonNull private final FingerprintSensorProperties mSensorProperties;
+ @NonNull private final MockHalResultController mMockHalResultController;
+ @NonNull private final TrustManager mTrustManager;
+ @NonNull private final SparseBooleanArray mUserHasTrust;
+ @NonNull private final Random mRandom;
+ @NonNull private final FakeRejectRunnable mFakeRejectRunnable;
+ @NonNull private final FakeAcceptRunnable mFakeAcceptRunnable;
+ @NonNull private final RestartAuthRunnable mRestartAuthRunnable;
+
+ private static class TestableBiometricScheduler extends BiometricScheduler {
+ @NonNull private final TestableInternalCallback mInternalCallback;
+ @NonNull private Fingerprint21UdfpsMock mFingerprint21;
+
+ TestableBiometricScheduler(@NonNull String tag,
+ @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ super(tag, gestureAvailabilityDispatcher);
+ mInternalCallback = new TestableInternalCallback();
+ }
+
+ class TestableInternalCallback extends InternalCallback {
+ @Override
+ public void onClientStarted(ClientMonitor<?> clientMonitor) {
+ super.onClientStarted(clientMonitor);
+ Slog.d(TAG, "Client started: " + clientMonitor);
+ mFingerprint21.setDebugMessage("Started: " + clientMonitor);
+ }
+
+ @Override
+ public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) {
+ super.onClientFinished(clientMonitor, success);
+ Slog.d(TAG, "Client finished: " + clientMonitor);
+ mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
+ }
+ }
+
+ void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
+ mFingerprint21 = fingerprint21;
+ }
+
+ /**
+ * Expose the internal finish callback so it can be used for testing
+ */
+ @Override
+ @NonNull protected InternalCallback getInternalCallback() {
+ return mInternalCallback;
+ }
+ }
+
+ /**
+ * All of the mocking/testing should happen in here. This way we don't need to modify the
+ * {@link com.android.server.biometrics.sensors.ClientMonitor} implementations and can run the
+ * real path there.
+ */
+ private static class MockHalResultController extends HalResultController {
+
+ // Duration for which a sensor authentication can be treated as UDFPS success.
+ private static final int AUTH_VALIDITY_MS = 10 * 1000; // 10 seconds
+
+ static class LastAuthArgs {
+ @NonNull final AuthenticationConsumer lastAuthenticatedClient;
+ final long deviceId;
+ final int fingerId;
+ final int groupId;
+ @Nullable final ArrayList<Byte> token;
+
+ LastAuthArgs(@NonNull AuthenticationConsumer authenticationConsumer, long deviceId,
+ int fingerId, int groupId, @Nullable ArrayList<Byte> token) {
+ lastAuthenticatedClient = authenticationConsumer;
+ this.deviceId = deviceId;
+ this.fingerId = fingerId;
+ this.groupId = groupId;
+ if (token == null) {
+ this.token = null;
+ } else {
+ // Store a copy so the owner can be GC'd
+ this.token = new ArrayList<>(token);
+ }
+ }
+ }
+
+ // Initialized after the constructor, but before it's ever used.
+ @NonNull private RestartAuthRunnable mRestartAuthRunnable;
+ @NonNull private Fingerprint21UdfpsMock mFingerprint21;
+ @Nullable private LastAuthArgs mLastAuthArgs;
+
+ MockHalResultController(@NonNull Context context, @NonNull Handler handler,
+ @NonNull BiometricScheduler scheduler) {
+ super(context, handler, scheduler);
+ }
+
+ void init(@NonNull RestartAuthRunnable restartAuthRunnable,
+ @NonNull Fingerprint21UdfpsMock fingerprint21) {
+ mRestartAuthRunnable = restartAuthRunnable;
+ mFingerprint21 = fingerprint21;
+ }
+
+ @Nullable AuthenticationConsumer getLastAuthenticatedClient() {
+ return mLastAuthArgs != null ? mLastAuthArgs.lastAuthenticatedClient : null;
+ }
+
+ /**
+ * Intercepts the HAL authentication and holds it until the UDFPS simulation decides
+ * that authentication finished.
+ */
+ @Override
+ public void onAuthenticated(long deviceId, int fingerId, int groupId,
+ ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(TAG, "Non authentication consumer: " + client);
+ return;
+ }
+
+ final boolean accepted = fingerId != 0;
+ if (accepted) {
+ mFingerprint21.setDebugMessage("Finger accepted");
+ } else {
+ mFingerprint21.setDebugMessage("Finger rejected");
+ }
+
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ mLastAuthArgs = new LastAuthArgs(authenticationConsumer, deviceId, fingerId,
+ groupId, token);
+
+ // Remove any existing restart runnbables since auth just started, and put a new
+ // one on the queue.
+ mHandler.removeCallbacks(mRestartAuthRunnable);
+ mRestartAuthRunnable.setLastAuthReference(authenticationConsumer);
+ mHandler.postDelayed(mRestartAuthRunnable, AUTH_VALIDITY_MS);
+ });
+ }
+
+ /**
+ * Calls through to the real interface and notifies clients of accept/reject.
+ */
+ void sendAuthenticated(long deviceId, int fingerId, int groupId,
+ ArrayList<Byte> token) {
+ Slog.d(TAG, "sendAuthenticated: " + (fingerId != 0));
+ mFingerprint21.setDebugMessage("Udfps match: " + (fingerId != 0));
+ super.onAuthenticated(deviceId, fingerId, groupId, token);
+ }
+ }
+
+ static Fingerprint21UdfpsMock newInstance(@NonNull Context context, int sensorId,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ Slog.d(TAG, "Creating Fingerprint23Mock!");
+
+ final Handler handler = new Handler(Looper.getMainLooper());
+ final TestableBiometricScheduler scheduler =
+ new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
+ final MockHalResultController controller =
+ new MockHalResultController(context, handler, scheduler);
+ return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId,
+ lockoutResetDispatcher, controller);
+ }
+
+ private static abstract class FakeFingerRunnable implements Runnable {
+ private long mFingerDownTime;
+ private int mCaptureDuration;
+
+ /**
+ * @param fingerDownTime System time when onFingerDown occurred
+ * @param captureDuration Duration that the finger needs to be down for
+ */
+ void setSimulationTime(long fingerDownTime, int captureDuration) {
+ mFingerDownTime = fingerDownTime;
+ mCaptureDuration = captureDuration;
+ }
+
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ boolean isImageCaptureComplete() {
+ return System.currentTimeMillis() - mFingerDownTime > mCaptureDuration;
+ }
+ }
+
+ private final class FakeRejectRunnable extends FakeFingerRunnable {
+ @Override
+ public void run() {
+ mMockHalResultController.sendAuthenticated(0, 0, 0, null);
+ }
+ }
+
+ private final class FakeAcceptRunnable extends FakeFingerRunnable {
+ @Override
+ public void run() {
+ if (mMockHalResultController.mLastAuthArgs == null) {
+ // This can happen if the user has trust agents enabled, which make lockscreen
+ // dismissable. Send a fake non-zero (accept) finger.
+ Slog.d(TAG, "Sending fake finger");
+ mMockHalResultController.sendAuthenticated(1 /* deviceId */,
+ 1 /* fingerId */, 1 /* groupId */, null /* token */);
+ } else {
+ mMockHalResultController.sendAuthenticated(
+ mMockHalResultController.mLastAuthArgs.deviceId,
+ mMockHalResultController.mLastAuthArgs.fingerId,
+ mMockHalResultController.mLastAuthArgs.groupId,
+ mMockHalResultController.mLastAuthArgs.token);
+ }
+ }
+ }
+
+ /**
+ * The fingerprint HAL allows multiple (5) fingerprint attempts per HIDL invocation of the
+ * authenticate method. However, valid fingerprint authentications are invalidated after
+ * {@link MockHalResultController#AUTH_VALIDITY_MS}, meaning UDFPS touches will be reported as
+ * rejects if touched after that duration. However, since a valid fingerprint was detected, the
+ * HAL and FingerprintService will not look for subsequent fingerprints.
+ *
+ * In order to keep the FingerprintManager API consistent (that multiple fingerprint attempts
+ * are allowed per auth lifecycle), we internally cancel and restart authentication so that the
+ * sensor is responsive again.
+ */
+ private static final class RestartAuthRunnable implements Runnable {
+ @NonNull private final Fingerprint21UdfpsMock mFingerprint21;
+ @NonNull private final TestableBiometricScheduler mScheduler;
+
+ // Store a reference to the auth consumer that should be invalidated.
+ private AuthenticationConsumer mLastAuthConsumer;
+
+ RestartAuthRunnable(@NonNull Fingerprint21UdfpsMock fingerprint21,
+ @NonNull TestableBiometricScheduler scheduler) {
+ mFingerprint21 = fingerprint21;
+ mScheduler = scheduler;
+ }
+
+ void setLastAuthReference(AuthenticationConsumer lastAuthConsumer) {
+ mLastAuthConsumer = lastAuthConsumer;
+ }
+
+ @Override
+ public void run() {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+
+ // We don't care about FingerprintDetectClient, since accept/rejects are both OK. UDFPS
+ // rejects will just simulate the path where non-enrolled fingers are presented.
+ if (!(client instanceof FingerprintAuthenticationClient)) {
+ Slog.e(TAG, "Non-FingerprintAuthenticationClient client: " + client);
+ return;
+ }
+
+ // Perhaps the runnable is stale, or the user stopped/started auth manually. Do not
+ // restart auth in this case.
+ if (client != mLastAuthConsumer) {
+ Slog.e(TAG, "Current client: " + client
+ + " does not match mLastAuthConsumer: " + mLastAuthConsumer);
+ return;
+ }
+
+ Slog.d(TAG, "Restarting auth, current: " + client);
+ mFingerprint21.setDebugMessage("Auth timed out");
+
+ final FingerprintAuthenticationClient authClient =
+ (FingerprintAuthenticationClient) client;
+ // Store the authClient parameters so it can be rescheduled
+ final IBinder token = client.getToken();
+ final long operationId = authClient.getOperationId();
+ final int user = client.getTargetUserId();
+ final int cookie = client.getCookie();
+ final ClientMonitorCallbackConverter listener = client.getListener();
+ final String opPackageName = client.getOwnerString();
+ final boolean restricted = authClient.isRestricted();
+ final int statsClient = client.getStatsClient();
+ final boolean isKeyguard = authClient.isKeyguard();
+
+ // Don't actually send cancel() to the HAL, since successful auth already finishes
+ // HAL authenticate() lifecycle. Just
+ mScheduler.getInternalCallback().onClientFinished(client, true /* success */);
+
+ // Schedule this only after we invoke onClientFinished for the previous client, so that
+ // internal preemption logic is not run.
+ mFingerprint21.scheduleAuthenticate(token, operationId, user, cookie,
+ listener, opPackageName, restricted, statsClient, isKeyguard);
+ }
+ }
+
+ private Fingerprint21UdfpsMock(@NonNull Context context,
+ @NonNull TestableBiometricScheduler scheduler,
+ @NonNull Handler handler, int sensorId,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull MockHalResultController controller) {
+ super(context, scheduler, handler, sensorId, lockoutResetDispatcher, controller);
+ mScheduler = scheduler;
+ mScheduler.init(this);
+ mHandler = handler;
+ // resetLockout is controlled by the framework, so hardwareAuthToken is not required
+ final boolean resetLockoutRequiresHardwareAuthToken = false;
+ mSensorProperties = new FingerprintSensorProperties(sensorId,
+ FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken);
+ mMockHalResultController = controller;
+ mUserHasTrust = new SparseBooleanArray();
+ mTrustManager = context.getSystemService(TrustManager.class);
+ mTrustManager.registerTrustListener(this);
+ mRandom = new Random();
+ mFakeRejectRunnable = new FakeRejectRunnable();
+ mFakeAcceptRunnable = new FakeAcceptRunnable();
+ mRestartAuthRunnable = new RestartAuthRunnable(this, mScheduler);
+
+ // We can't initialize this during MockHalresultController's constructor due to a circular
+ // dependency.
+ mMockHalResultController.init(mRestartAuthRunnable, this);
+ }
+
+ @Override
+ public void onTrustChanged(boolean enabled, int userId, int flags) {
+ mUserHasTrust.put(userId, enabled);
+ }
+
+ @Override
+ public void onTrustManagedChanged(boolean enabled, int userId) {
+
+ }
+
+ @Override
+ public void onTrustError(CharSequence message) {
+
+ }
+
+ @Override
+ @NonNull
+ FingerprintSensorProperties getFingerprintSensorProperties() {
+ return mSensorProperties;
+ }
+
+ @Override
+ void onFingerDown(int x, int y, float minor, float major) {
+ mHandler.post(() -> {
+ Slog.d(TAG, "onFingerDown");
+ final AuthenticationConsumer lastAuthenticatedConsumer =
+ mMockHalResultController.getLastAuthenticatedClient();
+ final ClientMonitor<?> currentScheduledClient = mScheduler.getCurrentClient();
+
+ if (currentScheduledClient == null) {
+ Slog.d(TAG, "Not authenticating");
+ return;
+ }
+
+ mHandler.removeCallbacks(mFakeRejectRunnable);
+ mHandler.removeCallbacks(mFakeAcceptRunnable);
+
+ // The sensor was authenticated, is still the currently scheduled client, and the
+ // user touched the UDFPS affordance. Pretend that auth succeeded.
+ final boolean authenticatedClientIsCurrent = lastAuthenticatedConsumer != null
+ && lastAuthenticatedConsumer == currentScheduledClient;
+ // User is unlocked on keyguard via Trust Agent
+ final boolean keyguardAndTrusted;
+ if (currentScheduledClient instanceof FingerprintAuthenticationClient) {
+ keyguardAndTrusted = ((FingerprintAuthenticationClient) currentScheduledClient)
+ .isKeyguard()
+ && mUserHasTrust.get(currentScheduledClient.getTargetUserId(), false);
+ } else {
+ keyguardAndTrusted = false;
+ }
+
+ final int captureDuration = getNewCaptureDuration();
+ final int matchingDuration = getMatchingDuration();
+ final int totalDuration = captureDuration + matchingDuration;
+ setDebugMessage("Duration: " + totalDuration
+ + " (" + captureDuration + " + " + matchingDuration + ")");
+ if (authenticatedClientIsCurrent || keyguardAndTrusted) {
+ mFakeAcceptRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
+ mHandler.postDelayed(mFakeAcceptRunnable, totalDuration);
+ } else if (currentScheduledClient instanceof AuthenticationConsumer) {
+ // Something is authenticating but authentication has not succeeded yet. Pretend
+ // that auth rejected.
+ mFakeRejectRunnable.setSimulationTime(System.currentTimeMillis(), captureDuration);
+ mHandler.postDelayed(mFakeRejectRunnable, totalDuration);
+ }
+ });
+ }
+
+ @Override
+ void onFingerUp() {
+ mHandler.post(() -> {
+ Slog.d(TAG, "onFingerUp");
+
+ // Only one of these can be on the handler at any given time (see onFingerDown). If
+ // image capture is not complete, send ACQUIRED_TOO_FAST and remove the runnable from
+ // the handler. Image capture (onFingerDown) needs to happen again.
+ if (mHandler.hasCallbacks(mFakeRejectRunnable)
+ && !mFakeRejectRunnable.isImageCaptureComplete()) {
+ mHandler.removeCallbacks(mFakeRejectRunnable);
+ mMockHalResultController.onAcquired(0 /* deviceId */,
+ FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
+ 0 /* vendorCode */);
+ } else if (mHandler.hasCallbacks(mFakeAcceptRunnable)
+ && !mFakeAcceptRunnable.isImageCaptureComplete()) {
+ mHandler.removeCallbacks(mFakeAcceptRunnable);
+ mMockHalResultController.onAcquired(0 /* deviceId */,
+ FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST,
+ 0 /* vendorCode */);
+ }
+ });
+ }
+
+ private int getNewCaptureDuration() {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ final int captureTime = Settings.Secure.getIntForUser(contentResolver,
+ CONFIG_AUTH_DELAY_PT1,
+ DEFAULT_AUTH_DELAY_PT1_MS,
+ UserHandle.USER_CURRENT);
+ final int randomDelayRange = Settings.Secure.getIntForUser(contentResolver,
+ CONFIG_AUTH_DELAY_RANDOMNESS,
+ DEFAULT_AUTH_DELAY_RANDOMNESS_MS,
+ UserHandle.USER_CURRENT);
+ final int randomDelay = mRandom.nextInt(randomDelayRange * 2) - randomDelayRange;
+
+ // Must be at least 0
+ return Math.max(captureTime + randomDelay, 0);
+ }
+
+ private int getMatchingDuration() {
+ final int matchingTime = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ CONFIG_AUTH_DELAY_PT2,
+ DEFAULT_AUTH_DELAY_PT2_MS,
+ UserHandle.USER_CURRENT);
+
+ // Must be at least 0
+ return Math.max(matchingTime, 0);
+ }
+
+ private void setDebugMessage(String message) {
+ try {
+ final IUdfpsOverlayController controller = getUdfpsOverlayController();
+ // Things can happen before SysUI loads and sets the controller.
+ if (controller != null) {
+ Slog.d(TAG, "setDebugMessage: " + message);
+ controller.setDebugMessage(message);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending message: " + message, e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
index 1564056cfdbd..99d348a780f3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
@@ -29,7 +29,6 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.Surface;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -47,6 +46,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
private static final String TAG = "Biometrics/FingerprintAuthClient";
+ private final boolean mIsKeyguard;
private final LockoutFrameworkImpl mLockoutFrameworkImpl;
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
@@ -54,16 +54,17 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient,
+ int sensorId, boolean isStrongBiometric, int statsClient,
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
+ @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isKeyguard) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
lockoutTracker);
mLockoutFrameworkImpl = lockoutTracker;
mUdfpsOverlayController = udfpsOverlayController;
+ mIsKeyguard = isKeyguard;
}
@Override
@@ -79,7 +80,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
if (authenticated) {
resetFailedAttempts(getTargetUserId());
UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
} else {
final @LockoutTracker.LockoutMode int lockoutMode =
mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
@@ -119,7 +120,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -132,7 +133,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -145,4 +146,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
public void onFingerUp() {
UdfpsHelper.onFingerUp(getFreshDaemon());
}
+
+ public boolean isKeyguard() {
+ return mIsKeyguard;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 3418c466aa69..21a46d58a3b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -75,11 +75,6 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
}
@Override
- public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
- mFingerprintService.resetLockout(userId, hardwareAuthToken);
- }
-
- @Override
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFingerprintService.getAuthenticatorId(callingUserId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java
index 8b295f8f4931..8652ee403089 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java
@@ -68,13 +68,13 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
startHalOperation();
}
@@ -88,7 +88,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
index 32f8b8fe8b26..d5db6e411b95 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
@@ -79,7 +79,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
@@ -92,7 +92,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
index 240c3c56f75e..834bf42eba3f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
@@ -52,7 +52,7 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiomet
getFreshDaemon().enumerate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
index a9336ef6a6c2..9f5456300884 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
@@ -54,7 +54,7 @@ class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
getFreshDaemon().remove(getTargetUserId(), mBiometricId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index e4387c9e2b81..7c7da118df10 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -38,14 +39,17 @@ import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.NativeHandle;
import android.os.Process;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
import android.view.Surface;
+import com.android.internal.R;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -94,10 +98,16 @@ public class FingerprintService extends SystemService {
}
@Override // Binder call
- public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver,
- String opPackageName) {
+ public void generateChallenge(IBinder token, int sensorId,
+ IFingerprintServiceReceiver receiver, String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
+
+ if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
+ mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
+ return;
+ }
+
+ Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
}
@Override // Binder call
@@ -158,8 +168,8 @@ public class FingerprintService extends SystemService {
final int statsClient = isKeyguard ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
- new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
- restricted, statsClient);
+ new ClientMonitorCallbackConverter(receiver), opPackageName,
+ restricted, statsClient, isKeyguard);
}
@Override
@@ -193,8 +203,8 @@ public class FingerprintService extends SystemService {
final boolean restricted = true; // BiometricPrompt is always restricted
mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie,
- new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, surface,
- restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT);
+ new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
+ BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, false /* isKeyguard */);
}
@Override // Binder call
@@ -343,9 +353,16 @@ public class FingerprintService extends SystemService {
}
@Override // Binder call
- public void resetLockout(int userId, byte [] hardwareAuthToken) {
+ public void resetLockout(IBinder token, int sensorId, int userId,
+ @Nullable byte [] hardwareAuthToken, String opPackageName) {
Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT);
- mFingerprint21.scheduleResetLockout(userId, hardwareAuthToken);
+
+ if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
+ mFingerprint21.scheduleResetLockout(userId);
+ return;
+ }
+
+ Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId);
}
@Override
@@ -369,8 +386,18 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void initializeConfiguration(int sensorId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher);
+
+ if ((Build.IS_USERDEBUG || Build.IS_ENG)
+ && getContext().getResources().getBoolean(R.bool.allow_test_udfps)
+ && Settings.Secure.getIntForUser(getContext().getContentResolver(),
+ Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
+ UserHandle.USER_CURRENT) != 0) {
+ mFingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
+ mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ } else {
+ mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId,
+ mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
index e1082ae51575..c1c3593db564 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
@@ -22,7 +22,6 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.os.Build;
import android.os.Environment;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.SELinux;
import android.util.Slog;
@@ -58,8 +57,8 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics
}
@Override
- public void start(@NonNull FinishCallback finishCallback) {
- super.start(finishCallback);
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
if (mCurrentUserId == getTargetUserId()) {
Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
@@ -69,7 +68,7 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics
} catch (RemoteException e) {
Slog.e(TAG, "Unable to refresh authenticatorId", e);
}
- finishCallback.onClientFinished(this, true /* success */);
+ callback.onClientFinished(this, true /* success */);
return;
}
@@ -89,7 +88,7 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics
if (!mDirectory.exists()) {
if (!mDirectory.mkdir()) {
Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath());
- finishCallback.onClientFinished(this, false /* success */);
+ callback.onClientFinished(this, false /* success */);
return;
}
// Calling mkdir() from this process will create a directory with our
@@ -97,7 +96,7 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics
// the label.
if (!SELinux.restorecon(mDirectory)) {
Slog.e(TAG, "Restorecons failed. Directory will have wrong label.");
- finishCallback.onClientFinished(this, false /* success */);
+ callback.onClientFinished(this, false /* success */);
return;
}
}
@@ -116,10 +115,10 @@ public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometrics
getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath());
mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
? getFreshDaemon().getAuthenticatorId() : 0L);
- mFinishCallback.onClientFinished(this, true /* success */);
+ mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveGroup: " + e);
- mFinishCallback.onClientFinished(this, false /* success */);
+ mCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index 9e0405792746..0400ef522142 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -70,10 +70,6 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
- public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
- }
-
- @Override
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return 0;
}
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
index c0617ca95f9f..54794fecbf5b 100644
--- a/services/core/java/com/android/server/gpu/GpuService.java
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -65,7 +65,7 @@ public class GpuService extends SystemService {
private static final String PROD_DRIVER_PROPERTY = "ro.gfx.driver.0";
private static final String DEV_DRIVER_PROPERTY = "ro.gfx.driver.1";
- private static final String GAME_DRIVER_ALLOWLIST_FILENAME = "allowlist.txt";
+ private static final String UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST_FILENAME = "allowlist.txt";
private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
private final Context mContext;
@@ -77,7 +77,7 @@ public class GpuService extends SystemService {
private final boolean mHasProdDriver;
private final boolean mHasDevDriver;
private ContentResolver mContentResolver;
- private long mGameDriverVersionCode;
+ private long mProdDriverVersionCode;
private SettingsObserver mSettingsObserver;
private DeviceConfigListener mDeviceConfigListener;
@GuardedBy("mLock")
@@ -88,7 +88,7 @@ public class GpuService extends SystemService {
mContext = context;
mProdDriverPackageName = SystemProperties.get(PROD_DRIVER_PROPERTY);
- mGameDriverVersionCode = -1;
+ mProdDriverVersionCode = -1;
mDevDriverPackageName = SystemProperties.get(DEV_DRIVER_PROPERTY);
mPackageManager = context.getPackageManager();
mHasProdDriver = !TextUtils.isEmpty(mProdDriverPackageName);
@@ -117,20 +117,20 @@ public class GpuService extends SystemService {
}
mSettingsObserver = new SettingsObserver();
mDeviceConfigListener = new DeviceConfigListener();
- fetchGameDriverPackageProperties();
+ fetchProductionDriverPackageProperties();
processDenylists();
setDenylist();
- fetchDeveloperDriverPackageProperties();
+ fetchPrereleaseDriverPackageProperties();
}
}
private final class SettingsObserver extends ContentObserver {
- private final Uri mGameDriverDenylistsUri =
- Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_DENYLISTS);
+ private final Uri mProdDriverDenylistsUri =
+ Settings.Global.getUriFor(Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS);
SettingsObserver() {
super(new Handler());
- mContentResolver.registerContentObserver(mGameDriverDenylistsUri, false, this,
+ mContentResolver.registerContentObserver(mProdDriverDenylistsUri, false, this,
UserHandle.USER_ALL);
}
@@ -140,7 +140,7 @@ public class GpuService extends SystemService {
return;
}
- if (mGameDriverDenylistsUri.equals(uri)) {
+ if (mProdDriverDenylistsUri.equals(uri)) {
processDenylists();
setDenylist();
}
@@ -157,9 +157,11 @@ public class GpuService extends SystemService {
@Override
public void onPropertiesChanged(Properties properties) {
synchronized (mDeviceConfigLock) {
- if (properties.getKeyset().contains(Settings.Global.GAME_DRIVER_DENYLISTS)) {
+ if (properties.getKeyset().contains(
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS)) {
parseDenylists(
- properties.getString(Settings.Global.GAME_DRIVER_DENYLISTS, ""));
+ properties.getString(
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS, ""));
setDenylist();
}
}
@@ -186,10 +188,10 @@ public class GpuService extends SystemService {
case ACTION_PACKAGE_CHANGED:
case ACTION_PACKAGE_REMOVED:
if (isProdDriver) {
- fetchGameDriverPackageProperties();
+ fetchProductionDriverPackageProperties();
setDenylist();
} else if (isDevDriver) {
- fetchDeveloperDriverPackageProperties();
+ fetchPrereleaseDriverPackageProperties();
}
break;
default:
@@ -218,7 +220,7 @@ public class GpuService extends SystemService {
}
}
- private void fetchGameDriverPackageProperties() {
+ private void fetchProductionDriverPackageProperties() {
final ApplicationInfo driverInfo;
try {
driverInfo = mPackageManager.getApplicationInfo(mProdDriverPackageName,
@@ -241,15 +243,16 @@ public class GpuService extends SystemService {
// Reset the allowlist.
Settings.Global.putString(mContentResolver,
- Settings.Global.GAME_DRIVER_ALLOWLIST, "");
- mGameDriverVersionCode = driverInfo.longVersionCode;
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, "");
+ mProdDriverVersionCode = driverInfo.longVersionCode;
try {
final Context driverContext = mContext.createPackageContext(mProdDriverPackageName,
Context.CONTEXT_RESTRICTED);
- assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_ALLOWLIST_FILENAME,
- Settings.Global.GAME_DRIVER_ALLOWLIST, ",");
+ assetToSettingsGlobal(mContext, driverContext,
+ UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST_FILENAME,
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, ",");
} catch (PackageManager.NameNotFoundException e) {
if (DEBUG) {
Slog.w(TAG, "driver package '" + mProdDriverPackageName + "' not installed");
@@ -259,11 +262,11 @@ public class GpuService extends SystemService {
private void processDenylists() {
String base64String = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_GAME_DRIVER,
- Settings.Global.GAME_DRIVER_DENYLISTS);
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS);
if (base64String == null) {
base64String =
Settings.Global.getString(mContentResolver,
- Settings.Global.GAME_DRIVER_DENYLISTS);
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLISTS);
}
parseDenylists(base64String != null ? base64String : "");
}
@@ -288,16 +291,16 @@ public class GpuService extends SystemService {
private void setDenylist() {
Settings.Global.putString(mContentResolver,
- Settings.Global.GAME_DRIVER_DENYLIST, "");
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, "");
synchronized (mLock) {
if (mDenylists == null) {
return;
}
List<Denylist> denylists = mDenylists.getDenylistsList();
for (Denylist denylist : denylists) {
- if (denylist.getVersionCode() == mGameDriverVersionCode) {
+ if (denylist.getVersionCode() == mProdDriverVersionCode) {
Settings.Global.putString(mContentResolver,
- Settings.Global.GAME_DRIVER_DENYLIST,
+ Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST,
String.join(",", denylist.getPackageNamesList()));
return;
}
@@ -305,7 +308,7 @@ public class GpuService extends SystemService {
}
}
- private void fetchDeveloperDriverPackageProperties() {
+ private void fetchPrereleaseDriverPackageProperties() {
final ApplicationInfo driverInfo;
try {
driverInfo = mPackageManager.getApplicationInfo(mDevDriverPackageName,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 85a4515d46a0..9ab410d258cc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3450,9 +3450,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return InputBindResult.USER_SWITCHING;
}
- // Master feature flag that overrides other conditions and forces IME preRendering.
+ // Main feature flag that overrides other conditions and forces IME preRendering.
if (DEBUG) {
- Slog.v(TAG, "IME PreRendering MASTER flag: "
+ Slog.v(TAG, "IME PreRendering main flag: "
+ DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() + ", LowRam: " + mIsLowRam);
}
// pre-rendering not supported on low-ram devices.
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
new file mode 100644
index 000000000000..e3074dba26ef
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.service.gatekeeper.IGateKeeperService;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.widget.VerifyCredentialResponse;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class that handles biometric-related work in the {@link LockSettingsService} area, for example
+ * resetLockout.
+ */
+@SuppressWarnings("deprecation")
+public class BiometricDeferredQueue {
+ private static final String TAG = "BiometricDeferredQueue";
+
+ @NonNull private final Context mContext;
+ @NonNull private final SyntheticPasswordManager mSpManager;
+ @NonNull private final Handler mHandler;
+ @Nullable private FingerprintManager mFingerprintManager;
+ @Nullable private FaceManager mFaceManager;
+
+ // Entries added by LockSettingsService once a user's synthetic password is known. At this point
+ // things are still keyed by userId.
+ @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts;
+
+ /**
+ * Authentication info for a successful user unlock via Synthetic Password. This can be used to
+ * perform multiple operations (e.g. resetLockout for multiple HALs/Sensors) by sending the
+ * Gatekeeper Password to Gatekeer multiple times, each with a sensor-specific challenge.
+ */
+ private static class UserAuthInfo {
+ final int userId;
+ @NonNull final byte[] gatekeeperPassword;
+
+ UserAuthInfo(int userId, @NonNull byte[] gatekeeperPassword) {
+ this.userId = userId;
+ this.gatekeeperPassword = gatekeeperPassword;
+ }
+ }
+
+ /**
+ * Per-authentication callback.
+ */
+ private static class FaceResetLockoutTask implements FaceManager.GenerateChallengeCallback {
+ interface FinishCallback {
+ void onFinished();
+ }
+
+ @NonNull FinishCallback finishCallback;
+ @NonNull FaceManager faceManager;
+ @NonNull SyntheticPasswordManager spManager;
+ @NonNull Set<Integer> sensorIds; // IDs of sensors waiting for challenge
+ @NonNull List<UserAuthInfo> pendingResetLockuts;
+
+ FaceResetLockoutTask(
+ @NonNull FinishCallback finishCallback,
+ @NonNull FaceManager faceManager,
+ @NonNull SyntheticPasswordManager spManager,
+ @NonNull Set<Integer> sensorIds,
+ @NonNull List<UserAuthInfo> pendingResetLockouts) {
+ this.finishCallback = finishCallback;
+ this.faceManager = faceManager;
+ this.spManager = spManager;
+ this.sensorIds = sensorIds;
+ this.pendingResetLockuts = pendingResetLockouts;
+ }
+
+ @Override
+ public void onChallengeInterrupted(int sensorId) {
+ Slog.w(TAG, "Challenge interrupted, sensor: " + sensorId);
+ // Consider re-attempting generateChallenge/resetLockout/revokeChallenge
+ // when onChallengeInterruptFinished is invoked
+ }
+
+ @Override
+ public void onChallengeInterruptFinished(int sensorId) {
+ Slog.w(TAG, "Challenge interrupt finished, sensor: " + sensorId);
+ }
+
+ @Override
+ public void onGenerateChallengeResult(int sensorId, long challenge) {
+ if (!sensorIds.contains(sensorId)) {
+ Slog.e(TAG, "Unknown sensorId received: " + sensorId);
+ return;
+ }
+
+ // Challenge received for a sensor. For each sensor, reset lockout for all users.
+ for (UserAuthInfo userAuthInfo : pendingResetLockuts) {
+ Slog.d(TAG, "Resetting face lockout for sensor: " + sensorId
+ + ", user: " + userAuthInfo.userId);
+ final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
+ getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
+ userAuthInfo.userId);
+ if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+ Slog.wtf(TAG, "VerifyChallenge failed, response: "
+ + response.getResponseCode());
+ }
+ faceManager.resetLockout(sensorId, userAuthInfo.userId,
+ response.getGatekeeperHAT());
+ }
+
+ sensorIds.remove(sensorId);
+ faceManager.revokeChallenge(sensorId);
+
+ if (sensorIds.isEmpty()) {
+ Slog.d(TAG, "Done requesting resetLockout for all face sensors");
+ finishCallback.onFinished();
+ }
+ }
+
+ synchronized IGateKeeperService getGatekeeperService() {
+ final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
+ if (service == null) {
+ Slog.e(TAG, "Unable to acquire GateKeeperService");
+ return null;
+ }
+ return IGateKeeperService.Stub.asInterface(service);
+ }
+ }
+
+ @Nullable private FaceResetLockoutTask mFaceResetLockoutTask;
+
+ private final FaceResetLockoutTask.FinishCallback mFaceFinishCallback = () -> {
+ mFaceResetLockoutTask = null;
+ };
+
+ BiometricDeferredQueue(@NonNull Context context, @NonNull SyntheticPasswordManager spManager,
+ @NonNull Handler handler) {
+ mContext = context;
+ mSpManager = spManager;
+ mHandler = handler;
+ mPendingResetLockouts = new ArrayList<>();
+ }
+
+ public void systemReady(@Nullable FingerprintManager fingerprintManager,
+ @Nullable FaceManager faceManager) {
+ mFingerprintManager = fingerprintManager;
+ mFaceManager = faceManager;
+ }
+
+ /**
+ * Adds a request for resetLockout on all biometric sensors for the user specified. The queue
+ * owner must invoke {@link #processPendingLockoutResets()} at some point to kick off the
+ * operations.
+ *
+ * Note that this should only ever be invoked for successful authentications, otherwise it will
+ * consume a Gatekeeper authentication attempt and potentially wipe the user/device.
+ *
+ * @param userId The user that the operation will apply for.
+ * @param gatekeeperPassword The Gatekeeper Password
+ */
+ void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) {
+ mHandler.post(() -> {
+ Slog.d(TAG, "addPendingLockoutResetForUser: " + userId);
+ mPendingResetLockouts.add(new UserAuthInfo(userId, gatekeeperPassword));
+ });
+ }
+
+ void processPendingLockoutResets() {
+ mHandler.post(() -> {
+ Slog.d(TAG, "processPendingLockoutResets: " + mPendingResetLockouts.size());
+ processPendingLockoutsForFingerprint(new ArrayList<>(mPendingResetLockouts));
+ processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockouts));
+ mPendingResetLockouts.clear();
+ });
+ }
+
+ private void processPendingLockoutsForFingerprint(List<UserAuthInfo> pendingResetLockouts) {
+ if (mFingerprintManager != null) {
+ final List<FingerprintSensorProperties> fingerprintSensorProperties =
+ mFingerprintManager.getSensorProperties();
+ for (FingerprintSensorProperties prop : fingerprintSensorProperties) {
+ if (!prop.resetLockoutRequiresHardwareAuthToken) {
+ for (UserAuthInfo user : pendingResetLockouts) {
+ mFingerprintManager.resetLockout(prop.sensorId, user.userId,
+ null /* hardwareAuthToken */);
+ }
+ } else {
+ Slog.e(TAG, "Fingerprint resetLockout with HAT not supported yet");
+ // TODO(b/152414803): Implement this when resetLockout is implemented below
+ // the framework.
+ }
+ }
+ }
+ }
+
+ /**
+ * For devices on {@link android.hardware.biometrics.face.V1_0} which only support a single
+ * in-flight challenge, we generate a single challenge to reset lockout for all profiles. This
+ * hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked
+ * challenge, or other race conditions.
+ *
+ * TODO(b/162965646) This logic can be avoided if multiple in-flight challenges are supported.
+ * Though it will need to continue to exist to support existing HIDLs, each profile that
+ * requires resetLockout could have its own challenge, and the `mPendingResetLockouts` queue
+ * can be avoided.
+ */
+ private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
+ if (mFaceManager != null) {
+ if (mFaceResetLockoutTask != null) {
+ // This code will need to be updated if this problem ever occurs.
+ Slog.w(TAG, "mFaceGenerateChallengeCallback not null, previous operation may be"
+ + " stuck");
+ }
+ final List<FaceSensorProperties> faceSensorProperties =
+ mFaceManager.getSensorProperties();
+ final Set<Integer> sensorIds = new ArraySet<>();
+ for (FaceSensorProperties prop : faceSensorProperties) {
+ sensorIds.add(prop.sensorId);
+ }
+
+ mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager,
+ mSpManager, sensorIds, pendingResetLockouts);
+ for (final FaceSensorProperties prop : faceSensorProperties) {
+ // Generate a challenge for each sensor. The challenge does not need to be
+ // per-user, since the HAT returned by gatekeeper contains userId.
+ mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index d6e37bacdba8..0044d8936841 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -37,7 +37,6 @@ import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_RETURN_GK
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -187,23 +186,6 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
- // TODO (b/145978626) LockSettingsService no longer accepts challenges in the verifyCredential
- // paths. These are temporarily left around to ensure that resetLockout works. It will be
- // removed once resetLockout is compartmentalized.
- // No challenge provided
- private static final int CHALLENGE_NONE = 0;
- // Challenge was provided from the external caller (non-LockSettingsService)
- private static final int CHALLENGE_FROM_CALLER = 1;
- // Challenge was generated from within LockSettingsService, for resetLockout. When challenge
- // type is set to internal, LSS will revokeChallenge after all profiles for that user are
- // unlocked.
- private static final int CHALLENGE_INTERNAL = 2;
-
- @IntDef({CHALLENGE_NONE,
- CHALLENGE_FROM_CALLER,
- CHALLENGE_INTERNAL})
- @interface ChallengeType {}
-
// Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
// Do not call into ActivityManager while holding mSpManager lock.
private final Object mSeparateChallengeLock = new Object();
@@ -219,6 +201,7 @@ public class LockSettingsService extends ILockSettings.Stub {
protected final LockSettingsStorage mStorage;
private final LockSettingsStrongAuth mStrongAuth;
private final SynchronizedStrongAuthTracker mStrongAuthTracker;
+ private final BiometricDeferredQueue mBiometricDeferredQueue;
private final NotificationManager mNotificationManager;
private final UserManager mUserManager;
@@ -321,15 +304,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private class PendingResetLockout {
- final int mUserId;
- final byte[] mHAT;
- PendingResetLockout(int userId, byte[] hat) {
- mUserId = userId;
- mHAT = hat;
- }
- }
-
private LockscreenCredential generateRandomProfilePassword() {
byte[] randomLockSeed = new byte[] {};
try {
@@ -588,6 +562,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mSpManager = injector.getSyntheticPasswordManager(mStorage);
mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache();
+ mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
mStorage);
@@ -740,8 +715,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// If boot took too long and the password in vold got expired, parent keystore will
// be still locked, we ignore this case since the user will be prompted to unlock
// the device after boot.
- unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */,
- CHALLENGE_NONE, 0 /* challenge */, null /* resetLockouts */);
+ unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */);
}
}
@@ -830,6 +804,8 @@ public class LockSettingsService extends ILockSettings.Stub {
mRebootEscrowManager.loadRebootEscrowDataIfAvailable();
// TODO: maybe skip this for split system user mode.
mStorage.prefetchUser(UserHandle.USER_SYSTEM);
+ mBiometricDeferredQueue.systemReady(mInjector.getFingerprintManager(),
+ mInjector.getFaceManager());
}
private void getAuthSecretHal() {
@@ -1306,13 +1282,10 @@ public class LockSettingsService extends ILockSettings.Stub {
return credential;
}
- private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated,
- @ChallengeType int challengeType, long challenge,
- @Nullable ArrayList<PendingResetLockout> resetLockouts) {
+ private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated) {
try {
doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
- challengeType, challenge, profileHandle, null /* progressCallback */,
- resetLockouts, 0 /* flags */);
+ profileHandle, null /* progressCallback */, 0 /* flags */);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
| NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -1327,10 +1300,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private void unlockUser(int userId, byte[] token, byte[] secret) {
- unlockUser(userId, token, secret, CHALLENGE_NONE, 0 /* challenge */, null);
- }
-
/**
* Unlock the user (both storage and user state) and its associated managed profiles
* synchronously.
@@ -1339,9 +1308,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* can end up calling into other system services to process user unlock request (via
* {@link com.android.server.SystemServiceManager#unlockUser} </em>
*/
- private void unlockUser(int userId, byte[] token, byte[] secret,
- @ChallengeType int challengeType, long challenge,
- @Nullable ArrayList<PendingResetLockout> resetLockouts) {
+ private void unlockUser(int userId, byte[] token, byte[] secret) {
Slog.i(TAG, "Unlocking user " + userId + " with secret only, length "
+ (secret != null ? secret.length : 0));
// TODO: make this method fully async so we can update UI with progress strings
@@ -1378,6 +1345,9 @@ public class LockSettingsService extends ILockSettings.Stub {
}
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+ if (!hasUnifiedChallenge(userId)) {
+ mBiometricDeferredQueue.processPendingLockoutResets();
+ }
return;
}
@@ -1388,10 +1358,7 @@ public class LockSettingsService extends ILockSettings.Stub {
if (hasUnifiedChallenge(profile.id)) {
if (mUserManager.isUserRunning(profile.id)) {
// Unlock managed profile with unified lock
- // Must pass the challenge on for resetLockout, so it's not over-written, which
- // causes LockSettingsService to revokeChallenge inappropriately.
- unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */,
- challengeType, challenge, resetLockouts);
+ unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */);
} else {
try {
// Profile not ready for unlock yet, but decrypt the unified challenge now
@@ -1412,22 +1379,9 @@ public class LockSettingsService extends ILockSettings.Stub {
restoreCallingIdentity(ident);
}
}
-
}
- if (resetLockouts != null && !resetLockouts.isEmpty()) {
- mHandler.post(() -> {
- final BiometricManager bm = mContext.getSystemService(BiometricManager.class);
- final PackageManager pm = mContext.getPackageManager();
- for (int i = 0; i < resetLockouts.size(); i++) {
- bm.resetLockout(resetLockouts.get(i).mUserId, resetLockouts.get(i).mHAT);
- }
- if (challengeType == CHALLENGE_INTERNAL
- && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
- mContext.getSystemService(FaceManager.class).revokeChallenge();
- }
- });
- }
+ mBiometricDeferredQueue.processPendingLockoutResets();
}
private boolean hasUnifiedChallenge(int userId) {
@@ -1727,8 +1681,7 @@ public class LockSettingsService extends ILockSettings.Stub {
setUserKeyProtection(userId, credential, convertResponse(gkResponse));
fixateNewestUserKeyAuth(userId);
// Refresh the auth token
- doVerifyCredential(credential, CHALLENGE_FROM_CALLER, 0, userId,
- null /* progressCallback */, 0 /* flags */);
+ doVerifyCredential(credential, userId, null /* progressCallback */, 0 /* flags */);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
@@ -1972,8 +1925,7 @@ public class LockSettingsService extends ILockSettings.Stub {
ICheckCredentialProgressCallback progressCallback) {
checkPasswordReadPermission(userId);
try {
- return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId, progressCallback,
- 0 /* flags */);
+ return doVerifyCredential(credential, userId, progressCallback, 0 /* flags */);
} finally {
scheduleGc();
}
@@ -1984,10 +1936,8 @@ public class LockSettingsService extends ILockSettings.Stub {
public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
int userId, int flags) {
checkPasswordReadPermission(userId);
-
try {
- return doVerifyCredential(credential, CHALLENGE_NONE, 0L, userId,
- null /* progressCallback */, flags);
+ return doVerifyCredential(credential, userId, null /* progressCallback */, flags);
} finally {
scheduleGc();
}
@@ -2006,32 +1956,17 @@ public class LockSettingsService extends ILockSettings.Stub {
return response;
}
- /**
+ /*
+ * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
+ * format.
* @param credential User's lockscreen credential
- * @param challengeType Owner of the challenge
- * @param challenge Challenge to be wrapped within Gatekeeper's HAT, if the credential is
- * verified
* @param userId User to verify the credential for
* @param progressCallback Receive progress callbacks
* @param flags See {@link LockPatternUtils.VerifyFlag}
* @return See {@link VerifyCredentialResponse}
*/
private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
- @ChallengeType int challengeType, long challenge, int userId,
- ICheckCredentialProgressCallback progressCallback,
- @LockPatternUtils.VerifyFlag int flags) {
- return doVerifyCredential(credential, challengeType, challenge, userId,
- progressCallback, null /* resetLockouts */, flags);
- }
-
- /**
- * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
- * format.
- */
- private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
- @ChallengeType int challengeType, long challenge, int userId,
- ICheckCredentialProgressCallback progressCallback,
- @Nullable ArrayList<PendingResetLockout> resetLockouts,
+ int userId, ICheckCredentialProgressCallback progressCallback,
@LockPatternUtils.VerifyFlag int flags) {
if (credential == null || credential.isNone()) {
throw new IllegalArgumentException("Credential can't be null or empty");
@@ -2041,9 +1976,10 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
return VerifyCredentialResponse.ERROR;
}
- VerifyCredentialResponse response = null;
- response = spBasedDoVerifyCredential(credential, challengeType, challenge,
- userId, progressCallback, resetLockouts, flags);
+
+ VerifyCredentialResponse response = spBasedDoVerifyCredential(credential, userId,
+ progressCallback, flags);
+
// The user employs synthetic password based credential.
if (response != null) {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -2064,8 +2000,7 @@ public class LockSettingsService extends ILockSettings.Stub {
return VerifyCredentialResponse.ERROR;
}
- response = verifyCredential(userId, storedHash, credential,
- challengeType, challenge, progressCallback);
+ response = verifyCredential(userId, storedHash, credential, progressCallback);
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
@@ -2085,8 +2020,6 @@ public class LockSettingsService extends ILockSettings.Stub {
// Unlock parent by using parent's challenge
final VerifyCredentialResponse parentResponse = doVerifyCredential(
credential,
- CHALLENGE_NONE,
- 0L,
parentProfileId,
null /* progressCallback */,
flags);
@@ -2098,10 +2031,7 @@ public class LockSettingsService extends ILockSettings.Stub {
try {
// Unlock work profile, and work profile with unified lock must use password only
return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
- CHALLENGE_NONE,
- 0L,
- userId, null /* progressCallback */,
- flags);
+ userId, null /* progressCallback */, flags);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
| NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
@@ -2119,8 +2049,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* hash to GK.
*/
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
- LockscreenCredential credential, @ChallengeType int challengeType, long challenge,
- ICheckCredentialProgressCallback progressCallback) {
+ LockscreenCredential credential, ICheckCredentialProgressCallback progressCallback) {
if ((storedHash == null || storedHash.hash.length == 0) && credential.isNone()) {
// don't need to pass empty credentials to GateKeeper
return VerifyCredentialResponse.OK;
@@ -2137,7 +2066,7 @@ public class LockSettingsService extends ILockSettings.Stub {
GateKeeperResponse gateKeeperResponse;
try {
gateKeeperResponse = getGateKeeperService().verifyChallenge(
- userId, challenge, storedHash.hash, credential.getCredential());
+ userId, 0L /* challenge */, storedHash.hash, credential.getCredential());
} catch (RemoteException e) {
Slog.e(TAG, "gatekeeper verify failed", e);
gateKeeperResponse = GateKeeperResponse.ERROR;
@@ -2710,28 +2639,13 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
- @ChallengeType int challengeType, long challenge,
int userId, ICheckCredentialProgressCallback progressCallback,
- @Nullable ArrayList<PendingResetLockout> resetLockouts,
@LockPatternUtils.VerifyFlag int flags) {
-
final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);
- Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " challengeType=" + challengeType
+ Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId
+ " hasEnrolledBiometrics=" + hasEnrolledBiometrics);
- final PackageManager pm = mContext.getPackageManager();
- // TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
- // we need to generate challenge for each one, have it signed by GK and reset lockout
- // for each modality.
- if (challengeType == CHALLENGE_NONE && pm.hasSystemFeature(PackageManager.FEATURE_FACE)
- && hasEnrolledBiometrics) {
- // If there are multiple profiles in the same account, ensure we only generate the
- // challenge once.
- challengeType = CHALLENGE_INTERNAL;
- challenge = mContext.getSystemService(FaceManager.class).generateChallengeBlocking();
- }
-
final AuthenticationResult authResult;
VerifyCredentialResponse response;
final boolean returnGkPw = (flags & VERIFY_FLAG_RETURN_GK_PW) != 0;
@@ -2752,10 +2666,13 @@ public class LockSettingsService extends ILockSettings.Stub {
// credential has matched
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
+ authResult.authToken.deriveGkPassword());
+
// perform verifyChallenge with synthetic password which generates the real GK auth
// token and response for the current user
response = mSpManager.verifyChallenge(getGateKeeperService(), authResult.authToken,
- challenge, userId);
+ 0L /* challenge */, userId);
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
// This shouldn't really happen: the unwrapping of SP succeeds, but SP doesn't
// match the recorded GK password handle.
@@ -2765,15 +2682,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
- // Do resetLockout / revokeChallenge when all profiles are unlocked
- if (hasEnrolledBiometrics) {
- if (resetLockouts == null) {
- resetLockouts = new ArrayList<>();
- }
- resetLockouts.add(new PendingResetLockout(userId, response.getGatekeeperHAT()));
- }
-
- onCredentialVerified(authResult.authToken, challengeType, challenge, resetLockouts,
+ onCredentialVerified(authResult.authToken,
PasswordMetrics.computeForCredential(userCredential), userId);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
@@ -2789,9 +2698,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private void onCredentialVerified(AuthenticationToken authToken,
- @ChallengeType int challengeType, long challenge,
- @Nullable ArrayList<PendingResetLockout> resetLockouts, PasswordMetrics metrics,
+ private void onCredentialVerified(AuthenticationToken authToken, PasswordMetrics metrics,
int userId) {
if (metrics != null) {
@@ -2806,7 +2713,7 @@ public class LockSettingsService extends ILockSettings.Stub {
{
final byte[] secret = authToken.deriveDiskEncryptionKey();
- unlockUser(userId, null, secret, challengeType, challenge, resetLockouts);
+ unlockUser(userId, null, secret);
Arrays.fill(secret, (byte) 0);
}
activateEscrowTokens(authToken, userId);
@@ -3193,11 +3100,8 @@ public class LockSettingsService extends ILockSettings.Stub {
return false;
}
}
- // TODO: Reset biometrics lockout here. Ideally that should be self-contained inside
- // onCredentialVerified(), which will require some refactoring on the current lockout
- // reset logic.
- onCredentialVerified(authResult.authToken, CHALLENGE_NONE, 0, null,
+ onCredentialVerified(authResult.authToken,
loadPasswordMetrics(authResult.authToken, userId), userId);
return true;
}
@@ -3208,8 +3112,7 @@ public class LockSettingsService extends ILockSettings.Stub {
if (cred == null) {
return false;
}
- return doVerifyCredential(cred, CHALLENGE_NONE, 0, userId,
- null /* progressCallback */, 0 /* flags */)
+ return doVerifyCredential(cred, userId, null /* progressCallback */, 0 /* flags */)
.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
}
}
@@ -3532,8 +3435,7 @@ public class LockSettingsService extends ILockSettings.Stub {
SyntheticPasswordManager.AuthenticationToken
authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion);
authToken.recreateDirectly(syntheticPassword);
- onCredentialVerified(authToken, CHALLENGE_NONE, 0, null,
- loadPasswordMetrics(authToken, userId), userId);
+ onCredentialVerified(authToken, loadPasswordMetrics(authToken, userId), userId);
}
}
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 3a4dfaf9bfcd..0b3cdae9231e 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -34,6 +34,7 @@ import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.MediaRoute2Info;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -55,7 +56,6 @@ class BluetoothRouteProvider {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
- private static BluetoothRouteProvider sInstance;
@SuppressWarnings("WeakerAccess") /* synthetic access */
// Maps hardware address to BluetoothRouteInfo
@@ -79,19 +79,21 @@ class BluetoothRouteProvider {
private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
- static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
+ /**
+ * Create an instance of {@link BluetoothRouteProvider}.
+ * It may return {@code null} if Bluetooth is not supported on this hardware platform.
+ */
+ @Nullable
+ static BluetoothRouteProvider createInstance(@NonNull Context context,
@NonNull BluetoothRoutesUpdatedListener listener) {
Objects.requireNonNull(context);
Objects.requireNonNull(listener);
- if (sInstance == null) {
- BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
- if (btAdapter == null) {
- return null;
- }
- sInstance = new BluetoothRouteProvider(context, btAdapter, listener);
+ BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (btAdapter == null) {
+ return null;
}
- return sInstance;
+ return new BluetoothRouteProvider(context, btAdapter, listener);
}
private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter,
@@ -103,7 +105,7 @@ class BluetoothRouteProvider {
buildBluetoothRoutes();
}
- public void start() {
+ public void start(UserHandle user) {
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
@@ -118,7 +120,8 @@ class BluetoothRouteProvider {
addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED,
deviceStateChangedReceiver);
- mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, user,
+ mIntentFilter, null, null);
}
/**
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 875bfdffafcd..1114fe0d9bf8 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1176,7 +1176,8 @@ class MediaRouter2ServiceImpl {
super(Looper.getMainLooper(), null, true);
mServiceRef = new WeakReference<>(service);
mUserRecord = userRecord;
- mSystemProvider = new SystemMediaRoute2Provider(service.mContext);
+ mSystemProvider = new SystemMediaRoute2Provider(service.mContext,
+ UserHandle.of(userRecord.mUserId));
mRouteProviders.add(mSystemProvider);
mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
this, mUserRecord.mUserId);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 0f6748366e16..8777ceacf884 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -589,11 +589,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
if (mDestroyed) {
return;
}
+ ParceledListSlice<QueueItem> parcelableQueue;
+ if (mQueue == null) {
+ parcelableQueue = null;
+ } else {
+ parcelableQueue = new ParceledListSlice<>(mQueue);
+ // Limit the size of initial Parcel to prevent binder buffer overflow
+ // as onQueueChanged is an async binder call.
+ parcelableQueue.setInlineCountLimit(1);
+ }
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
- holder.mCallback.onQueueChanged(mQueue == null ? null :
- new ParceledListSlice<>(mQueue));
+ holder.mCallback.onQueueChanged(parcelableQueue);
} catch (DeadObjectException e) {
mControllerCallbackHolders.remove(i);
logCallbackException("Removing dead callback in pushQueueUpdate", holder, e);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index eb4ab1ceac28..a8a0e2e937d0 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -32,14 +32,15 @@ import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
-import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioPlaybackConfiguration;
@@ -58,7 +59,6 @@ import android.media.session.ISessionManager;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -138,7 +138,6 @@ public class MediaSessionService extends SystemService implements Monitor {
private KeyguardManager mKeyguardManager;
private AudioManagerInternal mAudioManagerInternal;
private ContentResolver mContentResolver;
- private SettingsObserver mSettingsObserver;
private boolean mHasFeatureLeanback;
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
@@ -192,8 +191,6 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}, null /* handler */);
mContentResolver = mContext.getContentResolver();
- mSettingsObserver = new SettingsObserver();
- mSettingsObserver.observe();
mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
@@ -202,8 +199,20 @@ public class MediaSessionService extends SystemService implements Monitor {
instantiateCustomProvider(null);
instantiateCustomDispatcher(null);
mRecordThread.start();
+
+ final IntentFilter filter = new IntentFilter(
+ NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED);
+ mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter);
}
+ private final BroadcastReceiver mNotificationListenerEnabledChangedReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateActiveSessionListeners();
+ }
+ };
+
private boolean isGlobalPriorityActiveLocked() {
return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
}
@@ -1082,25 +1091,6 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
- final class SettingsObserver extends ContentObserver {
- private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
-
- private SettingsObserver() {
- super(null);
- }
-
- private void observe() {
- mContentResolver.registerContentObserver(mSecureSettingsUri,
- false, this, ALL.getIdentifier());
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- updateActiveSessionListeners();
- }
- }
-
class SessionManagerImpl extends ISessionManager.Stub {
private static final String EXTRA_WAKELOCK_ACQUIRED =
"android.media.AudioService.WAKELOCK_ACQUIRED";
@@ -2710,5 +2700,4 @@ public class MediaSessionService extends SystemService implements Monitor {
obtainMessage(msg, userIdInteger).sendToTarget();
}
}
-
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 953aae44d6a7..d9b5b6d41c11 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -20,7 +20,6 @@ import static com.android.server.media.SessionPolicyProvider.SESSION_POLICY_IGNO
import android.media.Session2Token;
import android.media.session.MediaSession;
-import android.os.Debug;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
@@ -187,7 +186,7 @@ class MediaSessionStack {
*/
public void updateMediaButtonSessionIfNeeded() {
if (DEBUG) {
- Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
+ Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + getCallers(2));
}
List<Integer> audioPlaybackUids =
mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
@@ -413,4 +412,24 @@ class MediaSessionStack {
// so they also need to be cleared.
mCachedActiveLists.remove(UserHandle.USER_ALL);
}
+
+ // Code copied from android.os.Debug#getCallers(int)
+ private static String getCallers(final int depth) {
+ final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < depth; i++) {
+ sb.append(getCaller(callStack, i)).append(" ");
+ }
+ return sb.toString();
+ }
+
+ // Code copied from android.os.Debug#getCaller(StackTraceElement[], int)
+ private static String getCaller(StackTraceElement[] callStack, int depth) {
+ // callStack[4] is the caller of the method that called getCallers()
+ if (4 + depth >= callStack.length) {
+ return "<bottom of call stack>";
+ }
+ StackTraceElement caller = callStack[4 + depth];
+ return caller.getClassName() + "." + caller.getMethodName() + ":" + caller.getLineNumber();
+ }
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 2c089ca8300e..4f7af9469668 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -45,6 +45,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -99,7 +100,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
};
- SystemMediaRoute2Provider(Context context) {
+ SystemMediaRoute2Provider(Context context, UserHandle user) {
super(sComponentName);
mIsSystemRouteProvider = true;
@@ -117,7 +118,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
updateDeviceRoute(newAudioRoutes);
// .getInstance returns null if there is no bt adapter available
- mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
+ mBtRouteProvider = BluetoothRouteProvider.createInstance(context, (routes) -> {
publishProviderState();
boolean sessionInfoChanged;
@@ -130,11 +131,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
IntentFilter intentFilter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
- mContext.registerReceiver(new AudioManagerBroadcastReceiver(), intentFilter);
+ mContext.registerReceiverAsUser(new AudioManagerBroadcastReceiver(), user,
+ intentFilter, null, null);
if (mBtRouteProvider != null) {
mHandler.post(() -> {
- mBtRouteProvider.start();
+ mBtRouteProvider.start(user);
notifyProviderState();
});
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 295143e76235..29ee8eb13564 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4367,7 +4367,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// Flip state because app was explicitly added or removed to denylist.
setMeteredNetworkDenylist(uid, (isDenylisted || isRestrictedByAdmin));
if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowlisted) {
- // Since dneylist prevails over allowlist, we need to handle the special case
+ // Since denylist prevails over allowlist, we need to handle the special case
// where app is allowlisted and denylisted at the same time (although such
// scenario should be blocked by the UI), then denylist is removed.
setMeteredNetworkAllowlist(uid, isAllowlisted);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 52928dc45131..9f9235dc852f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -31,6 +31,7 @@ import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
+import static android.app.NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID;
@@ -9180,6 +9181,17 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
+ boolean isPrimary, boolean enabled) {
+ super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled);
+
+ getContext().sendBroadcastAsUser(
+ new Intent(ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.ALL, null);
+ }
+
+ @Override
protected void loadDefaultsFromConfig() {
String defaultListenerAccess = mContext.getResources().getString(
R.string.config_defaultListenerAccessPackages);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index def9c78f98c7..3d7c978ca625 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -35,9 +35,6 @@ import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedProvider;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
@@ -353,13 +350,9 @@ public class AppsFilter {
injector.getUserManagerInternal().getUserInfos());
}
};
- HandlerThread appsFilterThread = new HandlerThread("appsFilter");
- appsFilterThread.start();
- Handler appsFilterHandler = new Handler(appsFilterThread.getLooper());
- Executor executor = new HandlerExecutor(appsFilterHandler);
-
AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig,
- forcedQueryablePackageNames, forceSystemAppsQueryable, null, executor);
+ forcedQueryablePackageNames, forceSystemAppsQueryable, null,
+ injector.getBackgroundExecutor());
featureConfig.setAppsFilter(appsFilter);
return appsFilter;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 672ad5e59428..cd383b9d1d7a 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -23,6 +23,8 @@ import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.Build;
+import android.os.CreateAppDataArgs;
+import android.os.CreateAppDataResult;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.IInstalld;
@@ -39,7 +41,10 @@ import dalvik.system.BlockGuard;
import dalvik.system.VMRuntime;
import java.io.FileDescriptor;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
public class Installer extends SystemService {
private static final String TAG = "Installer";
@@ -176,37 +181,140 @@ public class Installer extends SystemService {
}
}
+ private static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName,
+ int userId, int flags, int appId, String seInfo, int targetSdkVersion) {
+ final CreateAppDataArgs args = new CreateAppDataArgs();
+ args.uuid = uuid;
+ args.packageName = packageName;
+ args.userId = userId;
+ args.flags = flags;
+ args.appId = appId;
+ args.seInfo = seInfo;
+ args.targetSdkVersion = targetSdkVersion;
+ return args;
+ }
+
+ private static CreateAppDataResult buildPlaceholderCreateAppDataResult() {
+ final CreateAppDataResult result = new CreateAppDataResult();
+ result.ceDataInode = -1;
+ result.exceptionCode = 0;
+ result.exceptionMessage = null;
+ return result;
+ }
+
+ /**
+ * @deprecated callers are encouraged to migrate to using {@link Batch} to
+ * more efficiently handle operations in bulk.
+ */
+ @Deprecated
public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
String seInfo, int targetSdkVersion) throws InstallerException {
- if (!checkBeforeRemote()) return -1;
+ final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
+ appId, seInfo, targetSdkVersion);
+ final CreateAppDataResult result = createAppData(args);
+ if (result.exceptionCode == 0) {
+ return result.ceDataInode;
+ } else {
+ throw new InstallerException(result.exceptionMessage);
+ }
+ }
+
+ public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args)
+ throws InstallerException {
+ if (!checkBeforeRemote()) {
+ return buildPlaceholderCreateAppDataResult();
+ }
try {
- return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
- targetSdkVersion);
+ return mInstalld.createAppData(args);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ public @NonNull CreateAppDataResult[] createAppDataBatched(@NonNull CreateAppDataArgs[] args)
+ throws InstallerException {
+ if (!checkBeforeRemote()) {
+ final CreateAppDataResult[] results = new CreateAppDataResult[args.length];
+ Arrays.fill(results, buildPlaceholderCreateAppDataResult());
+ return results;
+ }
+ try {
+ return mInstalld.createAppDataBatched(args);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
/**
- * Batched version of createAppData for use with multiple packages.
+ * Class that collects multiple {@code installd} operations together in an
+ * attempt to more efficiently execute them in bulk.
+ * <p>
+ * Instead of returning results immediately, {@link CompletableFuture}
+ * instances are returned which can be used to chain follow-up work for each
+ * request.
+ * <p>
+ * The creator of this object <em>must</em> invoke {@link #execute()}
+ * exactly once to begin execution of all pending operations. Once execution
+ * has been kicked off, no additional events can be enqueued into this
+ * instance, but multiple instances can safely exist in parallel.
*/
- public void createAppDataBatched(String[] uuids, String[] packageNames, int userId, int flags,
- int[] appIds, String[] seInfos, int[] targetSdkVersions) throws InstallerException {
- if (!checkBeforeRemote()) return;
- final int batchSize = 256;
- for (int i = 0; i < uuids.length; i += batchSize) {
- int to = i + batchSize;
- if (to > uuids.length) {
- to = uuids.length;
- }
-
- try {
- mInstalld.createAppDataBatched(Arrays.copyOfRange(uuids, i, to),
- Arrays.copyOfRange(packageNames, i, to), userId, flags,
- Arrays.copyOfRange(appIds, i, to), Arrays.copyOfRange(seInfos, i, to),
- Arrays.copyOfRange(targetSdkVersions, i, to));
- } catch (Exception e) {
- throw InstallerException.from(e);
+ public static class Batch {
+ private static final int CREATE_APP_DATA_BATCH_SIZE = 256;
+
+ private boolean mExecuted;
+
+ private final List<CreateAppDataArgs> mArgs = new ArrayList<>();
+ private final List<CompletableFuture<Long>> mFutures = new ArrayList<>();
+
+ /**
+ * Enqueue the given {@code installd} operation to be executed in the
+ * future when {@link #execute(Installer)} is invoked.
+ * <p>
+ * Callers of this method are not required to hold a monitor lock on an
+ * {@link Installer} object.
+ */
+ public synchronized @NonNull CompletableFuture<Long> createAppData(String uuid,
+ String packageName, int userId, int flags, int appId, String seInfo,
+ int targetSdkVersion) {
+ if (mExecuted) throw new IllegalStateException();
+
+ final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
+ appId, seInfo, targetSdkVersion);
+ final CompletableFuture<Long> future = new CompletableFuture<>();
+ mArgs.add(args);
+ mFutures.add(future);
+ return future;
+ }
+
+ /**
+ * Execute all pending {@code installd} operations that have been
+ * collected by this batch in a blocking fashion.
+ * <p>
+ * Callers of this method <em>must</em> hold a monitor lock on the given
+ * {@link Installer} object.
+ */
+ public synchronized void execute(@NonNull Installer installer) throws InstallerException {
+ if (mExecuted) throw new IllegalStateException();
+ mExecuted = true;
+
+ final int size = mArgs.size();
+ for (int i = 0; i < size; i += CREATE_APP_DATA_BATCH_SIZE) {
+ final CreateAppDataArgs[] args = new CreateAppDataArgs[Math.min(size - i,
+ CREATE_APP_DATA_BATCH_SIZE)];
+ for (int j = 0; j < args.length; j++) {
+ args[j] = mArgs.get(i + j);
+ }
+ final CreateAppDataResult[] results = installer.createAppDataBatched(args);
+ for (int j = 0; j < args.length; j++) {
+ final CreateAppDataResult result = results[j];
+ final CompletableFuture<Long> future = mFutures.get(i + j);
+ if (result.exceptionCode == 0) {
+ future.complete(result.ceDataInode);
+ } else {
+ future.completeExceptionally(
+ new InstallerException(result.exceptionMessage));
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 0b0f13929b2a..79f8dc1c9a1e 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -380,7 +380,7 @@ public abstract class InstantAppResolver {
sanitizeIntent(request.origIntent),
// This must only expose the secured version of the host
request.hostDigestPrefixSecure,
- UserHandle.getUserHandleForUid(request.userId),
+ UserHandle.of(request.userId),
request.isRequesterInstantApp,
request.token
);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2807a1e4c081..28c5e964fe27 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1328,12 +1328,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (PackageInstaller.STATUS_SUCCESS == status) {
mChildSessionsRemaining.removeAt(sessionIndex);
if (mChildSessionsRemaining.size() == 0) {
- try {
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
- PackageInstallerSession.this.sessionId);
- mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
- } catch (IntentSender.SendIntentException ignore) {
- }
+ destroyInternal();
+ dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED,
+ "Session installed", null);
}
} else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) {
try {
@@ -1492,53 +1489,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
- * Assert multipackage install has consistent sessions.
- *
- * @throws PackageManagerException if child sessions don't match parent session
- * in respect to staged and enable rollback parameters.
- */
- @GuardedBy("mLock")
- private void assertMultiPackageConsistencyLocked(
- @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException {
- for (PackageInstallerSession childSession : childSessions) {
- // It might be that the parent session is loaded before all of it's child sessions are,
- // e.g. when reading sessions from XML. Those sessions will be null here, and their
- // conformance with the multipackage params will be checked when they're loaded.
- if (childSession == null) {
- continue;
- }
- assertConsistencyWithLocked(childSession);
- }
- }
-
- /**
- * Assert consistency with the given session.
- *
- * @throws PackageManagerException if other sessions doesn't match this session
- * in respect to staged and enable rollback parameters.
- */
- @GuardedBy("mLock")
- private void assertConsistencyWithLocked(PackageInstallerSession other)
- throws PackageManagerException {
- // Session groups must be consistent wrt to isStaged parameter. Non-staging session
- // cannot be grouped with staging sessions.
- if (this.params.isStaged != other.params.isStaged) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
- "Multipackage Inconsistency: session " + other.sessionId
- + " and session " + sessionId
- + " have inconsistent staged settings");
- }
- if (this.params.getEnableRollback() != other.params.getEnableRollback()) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
- "Multipackage Inconsistency: session " + other.sessionId
- + " and session " + sessionId
- + " have inconsistent rollback settings");
- }
- }
-
- /**
* Seal the session to prevent further modification.
*
* <p>The session will be sealed after calling this method even if it failed.
@@ -1552,14 +1502,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
assertNoWriteFileTransfersOpenLocked();
assertPreparedAndNotDestroyedLocked("sealing of session");
-
mSealed = true;
- List<PackageInstallerSession> childSessions = getChildSessionsLocked();
- if (childSessions != null) {
- assertMultiPackageConsistencyLocked(childSessions);
- }
- } catch (PackageManagerException e) {
- throw onSessionValidationFailure(e);
} catch (Throwable e) {
// Convert all exceptions into package manager exceptions as only those are handled
// in the code above.
@@ -2791,23 +2734,27 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- @Override
- public void abandon() {
- if (hasParentSessionId()) {
- throw new IllegalStateException(
- "Session " + sessionId + " is a child of multi-package session "
- + getParentSessionId() + " and may not be abandoned directly.");
+ private void abandonNonStaged() {
+ synchronized (mLock) {
+ assertCallerIsOwnerOrRootLocked();
+ if (mRelinquished) {
+ if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control");
+ return;
+ }
+ destroyInternal();
}
+ dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
+ }
+ private void abandonStaged() {
synchronized (mLock) {
- if (params.isStaged && mDestroyed) {
+ if (mDestroyed) {
// If a user abandons staged session in an unsafe state, then system will try to
// abandon the destroyed staged session when it is safe on behalf of the user.
assertCallerIsOwnerOrRootOrSystemLocked();
} else {
assertCallerIsOwnerOrRootLocked();
}
-
if (isStagedAndInTerminalState()) {
// We keep the session in the database if it's in a finalized state. It will be
// removed by PackageInstallerService when the last update time is old enough.
@@ -2815,26 +2762,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// do it now.
return;
}
- if (mCommitted && params.isStaged) {
- mDestroyed = true;
+ mDestroyed = true;
+ if (mCommitted) {
if (!mStagingManager.abortCommittedSessionLocked(this)) {
// Do not clean up the staged session from system. It is not safe yet.
mCallback.onStagedSessionChanged(this);
return;
}
- cleanStageDir(getChildSessionsLocked());
- }
-
- if (mRelinquished) {
- Slog.d(TAG, "Ignoring abandon after commit relinquished control");
- return;
}
+ cleanStageDir(getChildSessionsLocked());
destroyInternal();
}
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
}
@Override
+ public void abandon() {
+ if (hasParentSessionId()) {
+ throw new IllegalStateException(
+ "Session " + sessionId + " is a child of multi-package session "
+ + getParentSessionId() + " and may not be abandoned directly.");
+ }
+ if (params.isStaged) {
+ abandonStaged();
+ } else {
+ abandonNonStaged();
+ }
+ }
+
+ @Override
public boolean isMultiPackage() {
return params.isMultiPackage;
}
@@ -3193,6 +3149,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new IllegalStateException("Multi-session " + childSessionId
+ " can't be a child.");
}
+ if (params.isStaged != childSession.params.isStaged) {
+ throw new IllegalStateException("Multipackage Inconsistency: session "
+ + childSession.sessionId + " and session " + sessionId
+ + " have inconsistent staged settings");
+ }
+ if (params.getEnableRollback() != childSession.params.getEnableRollback()) {
+ throw new IllegalStateException("Multipackage Inconsistency: session "
+ + childSession.sessionId + " and session " + sessionId
+ + " have inconsistent rollback settings");
+ }
try {
acquireTransactionLock();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0d1c00dfe035..9c8b972985eb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -248,6 +248,8 @@ import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -418,7 +420,9 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -923,6 +927,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final Object mLock;
private final Installer mInstaller;
private final Object mInstallLock;
+ private final Executor mBackgroundExecutor;
// ----- producers -----
private final Singleton<ComponentResolver> mComponentResolverProducer;
@@ -944,6 +949,7 @@ public class PackageManagerService extends IPackageManager.Stub
Injector(Context context, Object lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
+ Executor backgroundExecutor,
Producer<ComponentResolver> componentResolverProducer,
Producer<PermissionManagerServiceInternal> permissionManagerProducer,
Producer<UserManagerService> userManagerProducer,
@@ -965,6 +971,7 @@ public class PackageManagerService extends IPackageManager.Stub
mInstaller = installer;
mAbiHelper = abiHelper;
mInstallLock = installLock;
+ mBackgroundExecutor = backgroundExecutor;
mComponentResolverProducer = new Singleton<>(componentResolverProducer);
mPermissionManagerProducer = new Singleton<>(permissionManagerProducer);
mUserManagerProducer = new Singleton<>(userManagerProducer);
@@ -1078,6 +1085,10 @@ public class PackageManagerService extends IPackageManager.Stub
public PlatformCompat getCompatibility() {
return mPlatformCompatProducer.get(this, mPackageManager);
}
+
+ public Executor getBackgroundExecutor() {
+ return mBackgroundExecutor;
+ }
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -2580,17 +2591,21 @@ public class PackageManagerService extends IPackageManager.Stub
t.traceBegin("create package manager");
final Object lock = new Object();
final Object installLock = new Object();
+ HandlerThread backgroundThread = new HandlerThread("PackageManagerBg");
+ backgroundThread.start();
+ Handler backgroundHandler = new Handler(backgroundThread.getLooper());
Injector injector = new Injector(
context, lock, installer, installLock, new PackageAbiHelperImpl(),
+ new HandlerExecutor(backgroundHandler),
(i, pm) ->
new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
(i, pm) ->
- PermissionManagerService.create(context, lock),
+ PermissionManagerService.create(context, lock),
(i, pm) ->
- new UserManagerService(context, pm,
- new UserDataPreparer(installer, installLock, context, onlyCore),
- lock),
+ new UserManagerService(context, pm,
+ new UserDataPreparer(installer, installLock, context, onlyCore),
+ lock),
(i, pm) ->
new Settings(Environment.getDataDirectory(),
i.getPermissionManagerServiceInternal().getPermissionSettings(),
@@ -3481,6 +3496,7 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
int count = 0;
+ final Installer.Batch batch = new Installer.Batch();
for (String pkgName : deferPackages) {
AndroidPackage pkg = null;
synchronized (mLock) {
@@ -3490,13 +3506,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (pkg != null) {
- synchronized (mInstallLock) {
- prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags,
- true /* maybeMigrateAppData */);
- }
+ prepareAppDataAndMigrate(batch, pkg, UserHandle.USER_SYSTEM, storageFlags,
+ true /* maybeMigrateAppData */);
count++;
}
}
+ synchronized (mInstallLock) {
+ executeBatchLI(batch);
+ }
traceLog.traceEnd();
Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages");
}, "prepareAppData");
@@ -7819,7 +7836,7 @@ public class PackageManagerService extends IPackageManager.Stub
// low 'int'-sized word: relative priority among 'always' results.
private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
long result = ps.getDomainVerificationStatusForUser(userId);
- // if none available, get the master status
+ // if none available, get the status
if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
if (ps.getIntentFilterVerificationInfo() != null) {
result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
@@ -17279,7 +17296,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
- // Sanity check
+ // Validity check
if (instantApp && onExternal) {
Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);
@@ -17423,7 +17440,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- // Quick sanity check that we're signed correctly if updating;
+ // Quick validity check that we're signed correctly if updating;
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -17591,9 +17608,6 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
pkgSetting = mSettings.getPackageLPr(pkgName);
}
- String abiOverride =
- (pkgSetting == null || TextUtils.isEmpty(pkgSetting.cpuAbiOverrideString)
- ? args.abiOverride : pkgSetting.cpuAbiOverrideString);
boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
&& pkgSetting.getPkgState().isUpdatedSystemApp();
AndroidPackage oldPackage = mPackages.get(pkgName);
@@ -17601,7 +17615,7 @@ public class PackageManagerService extends IPackageManager.Stub
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride);
+ args.abiOverride);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -21561,7 +21575,7 @@ public class PackageManagerService extends IPackageManager.Stub
// had been set as a preferred activity. We try to clean this up
// the next time we encounter that preferred activity, but it is
// possible for the user flow to never be able to return to that
- // situation so here we do a sanity check to make sure we haven't
+ // situation so here we do a validity check to make sure we haven't
// left any junk around.
ArrayList<PreferredActivity> removed = new ArrayList<>();
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
@@ -22746,6 +22760,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ private void executeBatchLI(@NonNull Installer.Batch batch) {
+ try {
+ batch.execute(mInstaller);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to execute pending operations", e);
+ }
+ }
+
/**
* Examine all apps present on given mounted volume, and destroy apps that
* aren't expected, either due to uninstallation or reinstallation on
@@ -22885,6 +22907,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Ensure that data directories are ready to roll for all packages
// installed for this volume and user
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "prepareAppDataAndMigrate");
+ Installer.Batch batch = new Installer.Batch();
final List<PackageSetting> packages;
synchronized (mLock) {
packages = mSettings.getVolumePackagesLPr(volumeUuid);
@@ -22905,10 +22929,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (ps.getInstalled(userId)) {
- prepareAppDataAndMigrateLIF(ps.pkg, userId, flags, migrateAppData);
+ prepareAppDataAndMigrate(batch, ps.pkg, userId, flags, migrateAppData);
preparedCount++;
}
}
+ executeBatchLI(batch);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
Slog.v(TAG, "reconcileAppsData finished " + preparedCount + " packages");
return result;
@@ -22933,6 +22959,7 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.writeKernelMappingLPr(ps);
}
+ Installer.Batch batch = new Installer.Batch();
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
@@ -22947,16 +22974,20 @@ public class PackageManagerService extends IPackageManager.Stub
if (ps.getInstalled(user.id)) {
// TODO: when user data is locked, mark that we're still dirty
- prepareAppDataLIF(pkg, user.id, flags);
-
- if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
- // Prepare app data on external storage; currently this is used to
- // setup any OBB dirs that were created by the installer correctly.
- int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
- smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
- }
+ prepareAppData(batch, pkg, user.id, flags).thenRun(() -> {
+ // Note: this code block is executed with the Installer lock
+ // already held, since it's invoked as a side-effect of
+ // executeBatchLI()
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ // Prepare app data on external storage; currently this is used to
+ // setup any OBB dirs that were created by the installer correctly.
+ int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
+ smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
+ }
+ });
}
}
+ executeBatchLI(batch);
}
/**
@@ -22967,26 +22998,33 @@ public class PackageManagerService extends IPackageManager.Stub
* will try recovering system apps by wiping data; third-party app data is
* left intact.
*/
- private void prepareAppDataLIF(AndroidPackage pkg, int userId, int flags) {
+ private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,
+ @Nullable AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
- return;
+ return CompletableFuture.completedFuture(null);
}
- prepareAppDataLeafLIF(pkg, userId, flags);
+ return prepareAppDataLeaf(batch, pkg, userId, flags);
}
- private void prepareAppDataAndMigrateLIF(AndroidPackage pkg, int userId, int flags,
- boolean maybeMigrateAppData) {
- prepareAppDataLIF(pkg, userId, flags);
-
- if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) {
- // We may have just shuffled around app data directories, so
- // prepare them one more time
- prepareAppDataLIF(pkg, userId, flags);
- }
+ private @NonNull CompletableFuture<?> prepareAppDataAndMigrate(@NonNull Installer.Batch batch,
+ @NonNull AndroidPackage pkg, int userId, int flags, boolean maybeMigrateAppData) {
+ return prepareAppData(batch, pkg, userId, flags).thenRun(() -> {
+ // Note: this code block is executed with the Installer lock
+ // already held, since it's invoked as a side-effect of
+ // executeBatchLI()
+ if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) {
+ // We may have just shuffled around app data directories, so
+ // prepare them one more time
+ final Installer.Batch batchInner = new Installer.Batch();
+ prepareAppData(batchInner, pkg, userId, flags);
+ executeBatchLI(batchInner);
+ }
+ });
}
- private void prepareAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
+ private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch,
+ @NonNull AndroidPackage pkg, int userId, int flags) {
if (DEBUG_APP_DATA) {
Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x"
+ Integer.toHexString(flags));
@@ -23006,57 +23044,67 @@ public class PackageManagerService extends IPackageManager.Stub
Preconditions.checkNotNull(pkgSeInfo);
final String seInfo = pkgSeInfo + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : "");
- long ceDataInode = -1;
- try {
- ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
- appId, seInfo, pkg.getTargetSdkVersion());
- } catch (InstallerException e) {
- if (pkg.isSystem()) {
- logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
- + ", but trying to recover: " + e);
- destroyAppDataLeafLIF(pkg, userId, flags);
- try {
- ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
- appId, seInfo, pkg.getTargetSdkVersion());
- logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
- } catch (InstallerException e2) {
- logCriticalInfo(Log.DEBUG, "Recovery failed!");
- }
- } else {
- Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
- }
- }
- // Prepare the application profiles only for upgrades and first boot (so that we don't
- // repeat the same operation at each boot).
- // We only have to cover the upgrade and first boot here because for app installs we
- // prepare the profiles before invoking dexopt (in installPackageLI).
- //
- // We also have to cover non system users because we do not call the usual install package
- // methods for them.
- //
- // NOTE: in order to speed up first boot time we only create the current profile and do not
- // update the content of the reference profile. A system image should already be configured
- // with the right profile keys and the profiles for the speed-profile prebuilds should
- // already be copied. That's done in #performDexOptUpgrade.
- //
- // TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of
- // manually copying them in #performDexOptUpgrade. When we do that we should have a more
- // granular check here and only update the existing profiles.
- if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
- mArtManagerService.prepareAppProfiles(pkg, userId,
- /* updateReferenceProfileContent= */ false);
- }
+ final int targetSdkVersion = pkg.getTargetSdkVersion();
+
+ return batch.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo,
+ targetSdkVersion).whenComplete((ceDataInode, e) -> {
+ // Note: this code block is executed with the Installer lock
+ // already held, since it's invoked as a side-effect of
+ // executeBatchLI()
+ if ((e != null) && pkg.isSystem()) {
+ logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
+ + ", but trying to recover: " + e);
+ destroyAppDataLeafLIF(pkg, userId, flags);
+ try {
+ ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId,
+ flags, appId, seInfo, pkg.getTargetSdkVersion());
+ logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
+ } catch (InstallerException e2) {
+ logCriticalInfo(Log.DEBUG, "Recovery failed!");
+ }
+ } else if (e != null) {
+ Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
+ }
- if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
- // TODO: mark this structure as dirty so we persist it!
- synchronized (mLock) {
- if (ps != null) {
- ps.setCeDataInode(ceDataInode, userId);
- }
- }
- }
+ // Prepare the application profiles only for upgrades and
+ // first boot (so that we don't repeat the same operation at
+ // each boot).
+ //
+ // We only have to cover the upgrade and first boot here
+ // because for app installs we prepare the profiles before
+ // invoking dexopt (in installPackageLI).
+ //
+ // We also have to cover non system users because we do not
+ // call the usual install package methods for them.
+ //
+ // NOTE: in order to speed up first boot time we only create
+ // the current profile and do not update the content of the
+ // reference profile. A system image should already be
+ // configured with the right profile keys and the profiles
+ // for the speed-profile prebuilds should already be copied.
+ // That's done in #performDexOptUpgrade.
+ //
+ // TODO(calin, mathieuc): We should use .dm files for
+ // prebuilds profiles instead of manually copying them in
+ // #performDexOptUpgrade. When we do that we should have a
+ // more granular check here and only update the existing
+ // profiles.
+ if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
+ mArtManagerService.prepareAppProfiles(pkg, userId,
+ /* updateReferenceProfileContent= */ false);
+ }
+
+ if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
+ // TODO: mark this structure as dirty so we persist it!
+ synchronized (mLock) {
+ if (ps != null) {
+ ps.setCeDataInode(ceDataInode, userId);
+ }
+ }
+ }
- prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
+ prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
+ });
}
private void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7aeec6d68d26..32ec052a1fe0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1043,7 +1043,9 @@ class PackageManagerShellCommand extends ShellCommand {
+ "; isStaged = " + session.isStaged()
+ "; isReady = " + session.isStagedSessionReady()
+ "; isApplied = " + session.isStagedSessionApplied()
- + "; isFailed = " + session.isStagedSessionFailed() + ";");
+ + "; isFailed = " + session.isStagedSessionFailed()
+ + "; errorMsg = " + session.getStagedSessionErrorMessage()
+ + ";");
}
private Intent parseIntentAndUser() throws URISyntaxException {
@@ -3338,7 +3340,7 @@ class PackageManagerShellCommand extends ShellCommand {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
if (!session.isMultiPackage() && !session.isStaged()) {
- // Sanity check that all .dm files match an apk.
+ // Validity check that all .dm files match an apk.
// (The installer does not support standalone .dm files and will not process them.)
try {
DexMetadataHelper.validateDexPaths(session.getNames());
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3e253673ad31..acb149b9ec3d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -102,6 +102,7 @@ import com.android.internal.util.XmlUtils;
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.LocalServices;
+import com.android.server.pm.Installer.Batch;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -4148,24 +4149,12 @@ public final class Settings {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
t.traceBegin("createNewUser-" + userHandle);
- String[] volumeUuids;
- String[] names;
- int[] appIds;
- String[] seinfos;
- int[] targetSdkVersions;
- int packagesCount;
+ Installer.Batch batch = new Installer.Batch();
final boolean skipPackageWhitelist = userTypeInstallablePackages == null;
synchronized (mLock) {
- Collection<PackageSetting> packages = mPackages.values();
- packagesCount = packages.size();
- volumeUuids = new String[packagesCount];
- names = new String[packagesCount];
- appIds = new int[packagesCount];
- seinfos = new String[packagesCount];
- targetSdkVersions = new int[packagesCount];
- Iterator<PackageSetting> packagesIterator = packages.iterator();
- for (int i = 0; i < packagesCount; i++) {
- PackageSetting ps = packagesIterator.next();
+ final int size = mPackages.size();
+ for (int i = 0; i < size; i++) {
+ final PackageSetting ps = mPackages.valueAt(i);
if (ps.pkg == null) {
continue;
}
@@ -4187,18 +4176,15 @@ public final class Settings {
}
// Need to create a data directory for all apps under this user. Accumulate all
// required args and call the installer after mPackages lock has been released
- volumeUuids[i] = ps.volumeUuid;
- names[i] = ps.name;
- appIds[i] = ps.appId;
- seinfos[i] = AndroidPackageUtils.getSeInfo(ps.pkg, ps);
- targetSdkVersions[i] = ps.pkg.getTargetSdkVersion();
+ final String seInfo = AndroidPackageUtils.getSeInfo(ps.pkg, ps);
+ batch.createAppData(ps.volumeUuid, ps.name, userHandle,
+ StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE, ps.appId,
+ seInfo, ps.pkg.getTargetSdkVersion());
}
}
t.traceBegin("createAppData");
- final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
try {
- installer.createAppDataBatched(volumeUuids, names, userHandle, flags, appIds, seinfos,
- targetSdkVersions);
+ batch.execute(installer);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to prepare app data", e);
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index ac05aabf998f..6d80f08ecbf6 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -591,13 +591,14 @@ public class StagingManager {
// If checkpoint is supported, then we only resume sessions if we are in checkpointing
// mode. If not, we fail all sessions.
if (supportsCheckpoint() && !needsCheckpoint()) {
- String errorMsg = "Reverting back to safe state. Marking " + session.sessionId
- + " as failed";
- if (!TextUtils.isEmpty(mFailureReason)) {
- errorMsg = errorMsg + ": " + mFailureReason;
+ String revertMsg = "Reverting back to safe state. Marking "
+ + session.sessionId + " as failed.";
+ final String reasonForRevert = getReasonForRevert();
+ if (!TextUtils.isEmpty(reasonForRevert)) {
+ revertMsg += " Reason for revert: " + reasonForRevert;
}
- Slog.d(TAG, errorMsg);
- session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, errorMsg);
+ Slog.d(TAG, revertMsg);
+ session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
return;
}
} catch (RemoteException e) {
@@ -702,6 +703,16 @@ public class StagingManager {
}
}
+ private String getReasonForRevert() {
+ if (!TextUtils.isEmpty(mFailureReason)) {
+ return mFailureReason;
+ }
+ if (!TextUtils.isEmpty(mNativeFailureReason)) {
+ return "Session reverted due to crashing native process: " + mNativeFailureReason;
+ }
+ return "";
+ }
+
private List<String> findAPKsInDir(File stageDir) {
List<String> ret = new ArrayList<>();
if (stageDir != null && stageDir.exists()) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f66b4ee5a1a7..d137fd05f793 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -499,20 +499,23 @@ public class UserManagerService extends IUserManager.Stub {
states = new SparseIntArray();
invalidateIsUserUnlockedCache();
}
- public int get(int userId) {
+ public int get(@UserIdInt int userId) {
return states.get(userId);
}
- public int get(int userId, int fallback) {
+ public int get(@UserIdInt int userId, int fallback) {
return states.indexOfKey(userId) >= 0 ? states.get(userId) : fallback;
}
- public void put(int userId, int state) {
+ public void put(@UserIdInt int userId, int state) {
states.put(userId, state);
invalidateIsUserUnlockedCache();
}
- public void delete(int userId) {
+ public void delete(@UserIdInt int userId) {
states.delete(userId);
invalidateIsUserUnlockedCache();
}
+ public boolean has(@UserIdInt int userId) {
+ return states.get(userId, UserHandle.USER_NULL) != UserHandle.USER_NULL;
+ }
@Override
public String toString() {
return states.toString();
@@ -3561,6 +3564,13 @@ public class UserManagerService extends IUserManager.Stub {
if (preCreatedUserData == null) {
return null;
}
+ synchronized (mUserStates) {
+ if (mUserStates.has(preCreatedUserData.info.id)) {
+ Slog.w(LOG_TAG, "Cannot reuse pre-created user "
+ + preCreatedUserData.info.id + " because it didn't stop yet");
+ return null;
+ }
+ }
final UserInfo preCreatedUser = preCreatedUserData.info;
final int newFlags = preCreatedUser.flags | flags;
if (!checkUserTypeConsistency(newFlags)) {
@@ -5225,6 +5235,7 @@ public class UserManagerService extends IUserManager.Stub {
return userData == null ? null : userData.info;
}
+ @Override
public @NonNull UserInfo[] getUserInfos() {
synchronized (mUsersLock) {
int userSize = mUsers.size();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 3d1570476b48..1be74154b53a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2464,7 +2464,28 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return null;
}
final PermissionsState permissionsState = ps.getPermissionsState();
- return permissionsState.getPermissions(userId);
+ if (!ps.getInstantApp(userId)) {
+ return permissionsState.getPermissions(userId);
+ } else {
+ // Install permission state is shared among all users, but instant app state is
+ // per-user, so we can only filter it here unless we make install permission state
+ // per-user as well.
+ final Set<String> instantPermissions = new ArraySet<>(permissionsState.getPermissions(
+ userId));
+ instantPermissions.removeIf(permissionName -> {
+ BasePermission permission = mSettings.getPermission(permissionName);
+ if (permission == null) {
+ return true;
+ }
+ if (!permission.isInstant()) {
+ EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId,
+ ps.getAppId()), permissionName);
+ return true;
+ }
+ return false;
+ });
+ return instantPermissions;
+ }
}
@Nullable
@@ -4427,7 +4448,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
- private void enforceCrossUserPermission(int callingUid, int userId,
+ private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message) {
if (userId < 0) {
@@ -4444,7 +4465,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
String errorMessage = buildInvalidCrossUserPermissionMessage(
- message, requireFullPermission);
+ callingUid, userId, message, requireFullPermission);
Slog.w(TAG, errorMessage);
throw new SecurityException(errorMessage);
}
@@ -4463,7 +4484,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
- private void enforceCrossUserOrProfilePermission(int callingUid, int userId,
+ private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
String message) {
if (userId < 0) {
@@ -4489,7 +4510,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
- message, requireFullPermission, isSameProfileGroup);
+ callingUid, userId, message, requireFullPermission, isSameProfileGroup);
Slog.w(TAG, errorMessage);
throw new SecurityException(errorMessage);
}
@@ -4524,44 +4545,48 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- private static String buildInvalidCrossUserPermissionMessage(
- String message, boolean requireFullPermission) {
+ private static String buildInvalidCrossUserPermissionMessage(int callingUid,
+ @UserIdInt int userId, String message, boolean requireFullPermission) {
StringBuilder builder = new StringBuilder();
if (message != null) {
builder.append(message);
builder.append(": ");
}
- builder.append("Requires ");
+ builder.append("UID ");
+ builder.append(callingUid);
+ builder.append(" requires ");
builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- if (requireFullPermission) {
- builder.append(".");
- return builder.toString();
+ if (!requireFullPermission) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
}
- builder.append(" or ");
- builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ builder.append(" to access user ");
+ builder.append(userId);
builder.append(".");
return builder.toString();
}
- private static String buildInvalidCrossUserOrProfilePermissionMessage(
- String message, boolean requireFullPermission, boolean isSameProfileGroup) {
+ private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid,
+ @UserIdInt int userId, String message, boolean requireFullPermission,
+ boolean isSameProfileGroup) {
StringBuilder builder = new StringBuilder();
if (message != null) {
builder.append(message);
builder.append(": ");
}
- builder.append("Requires ");
+ builder.append("UID ");
+ builder.append(callingUid);
+ builder.append(" requires ");
builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- if (requireFullPermission) {
- builder.append(".");
- return builder.toString();
- }
- builder.append(" or ");
- builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
- if (isSameProfileGroup) {
+ if (!requireFullPermission) {
builder.append(" or ");
- builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ if (isSameProfileGroup) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
+ }
}
+ builder.append(" to access user ");
builder.append(".");
return builder.toString();
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 6adff0d32d88..0c85387be695 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1156,7 +1156,7 @@ public class TrustManagerService extends SystemService {
}
private void enforceListenerPermission() {
- mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.TRUST_LISTENER,
"register trust listener");
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 2b0fe8a2602b..2fc17fe65775 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -68,6 +68,11 @@ public final class ClientProfile {
private Set<Integer> mUsingFrontendIds = new HashSet<>();
/**
+ * List of the client ids that share frontend with the current client.
+ */
+ private Set<Integer> mShareFeClientIds = new HashSet<>();
+
+ /**
* List of the Lnb ids that are used by the current client.
*/
private Set<Integer> mUsingLnbIds = new HashSet<>();
@@ -113,11 +118,7 @@ public final class ClientProfile {
}
public int getPriority() {
- return mPriority;
- }
-
- public int getNiceValue() {
- return mNiceValue;
+ return mPriority - mNiceValue;
}
public void setGroupId(int groupId) {
@@ -141,17 +142,38 @@ public final class ClientProfile {
mUsingFrontendIds.add(frontendId);
}
+ /**
+ * Update the set of client that share frontend with the current client.
+ *
+ * @param clientId the client to share the fe with the current client.
+ */
+ public void shareFrontend(int clientId) {
+ mShareFeClientIds.add(clientId);
+ }
+
+ /**
+ * Remove the given client id from the share frontend client id set.
+ *
+ * @param clientId the client to stop sharing the fe with the current client.
+ */
+ public void stopSharingFrontend(int clientId) {
+ mShareFeClientIds.remove(clientId);
+ }
+
public Set<Integer> getInUseFrontendIds() {
return mUsingFrontendIds;
}
+ public Set<Integer> getShareFeClientIds() {
+ return mShareFeClientIds;
+ }
+
/**
* Called when the client released a frontend.
- *
- * @param frontendId being released.
*/
- public void releaseFrontend(int frontendId) {
- mUsingFrontendIds.remove(frontendId);
+ public void releaseFrontend() {
+ mUsingFrontendIds.clear();
+ mShareFeClientIds.clear();
}
/**
@@ -201,6 +223,7 @@ public final class ClientProfile {
*/
public void reclaimAllResources() {
mUsingFrontendIds.clear();
+ mShareFeClientIds.clear();
mUsingLnbIds.clear();
mUsingCasSystemId = INVALID_RESOURCE_ID;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 7cb59dcbfb9b..fb2347e8e133 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -210,19 +210,36 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
synchronized (mLock) {
if (!checkClientExists(request.getClientId())) {
- throw new RemoteException("Request frontend from unregistered client:"
+ throw new RemoteException("Request frontend from unregistered client: "
+ request.getClientId());
}
+ // If the request client is holding or sharing a frontend, throw an exception.
+ if (!getClientProfile(request.getClientId()).getInUseFrontendIds().isEmpty()) {
+ throw new RemoteException("Release frontend before requesting another one. "
+ + "Client id: " + request.getClientId());
+ }
return requestFrontendInternal(request, frontendHandle);
}
}
@Override
- public void shareFrontend(int selfClientId, int targetClientId) {
+ public void shareFrontend(int selfClientId, int targetClientId) throws RemoteException {
enforceTunerAccessPermission("shareFrontend");
enforceTrmAccessPermission("shareFrontend");
- if (DEBUG) {
- Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
+ synchronized (mLock) {
+ if (!checkClientExists(selfClientId)) {
+ throw new RemoteException("Share frontend request from an unregistered client:"
+ + selfClientId);
+ }
+ if (!checkClientExists(targetClientId)) {
+ throw new RemoteException("Request to share frontend with an unregistered "
+ + "client:" + targetClientId);
+ }
+ if (getClientProfile(targetClientId).getInUseFrontendIds().isEmpty()) {
+ throw new RemoteException("Request to share frontend with a client that has no "
+ + "frontend resources. Target client id:" + targetClientId);
+ }
+ shareFrontendInternal(selfClientId, targetClientId);
}
}
@@ -315,7 +332,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
throw new RemoteException(
"Client is not the current owner of the releasing fe.");
}
- releaseFrontendInternal(fe);
+ releaseFrontendInternal(fe, clientId);
}
}
@@ -649,6 +666,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
+ protected void shareFrontendInternal(int selfClientId, int targetClientId) {
+ if (DEBUG) {
+ Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
+ }
+ for (int feId : getClientProfile(targetClientId).getInUseFrontendIds()) {
+ getClientProfile(selfClientId).useFrontend(feId);
+ }
+ getClientProfile(targetClientId).shareFrontend(selfClientId);
+ }
+
+ @VisibleForTesting
protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
if (DEBUG) {
Slog.d(TAG, "requestLnb(request=" + request + ")");
@@ -777,11 +805,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
- protected void releaseFrontendInternal(FrontendResource fe) {
+ protected void releaseFrontendInternal(FrontendResource fe, int clientId) {
if (DEBUG) {
- Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ")");
+ Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ", clientId=" + clientId + " )");
+ }
+ if (clientId == fe.getOwnerClientId()) {
+ ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
+ for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
+ clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+ }
}
- updateFrontendClientMappingOnRelease(fe);
+ clearFrontendAndClientMapping(getClientProfile(clientId));
}
@VisibleForTesting
@@ -882,8 +916,21 @@ public class TunerResourceManagerService extends SystemService implements IBinde
Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
return false;
}
+
+ // Reclaim all the resources of the share owners of the frontend that is used by the current
+ // resource reclaimed client.
ClientProfile profile = getClientProfile(reclaimingClientId);
- reclaimingResourcesFromClient(profile);
+ Set<Integer> shareFeClientIds = profile.getShareFeClientIds();
+ for (int clientId : shareFeClientIds) {
+ try {
+ mListeners.get(clientId).getListener().onReclaimResources();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e);
+ return false;
+ }
+ clearAllResourcesAndClientMapping(getClientProfile(clientId));
+ }
+ clearAllResourcesAndClientMapping(profile);
return true;
}
@@ -929,16 +976,6 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
}
- private void updateFrontendClientMappingOnRelease(@NonNull FrontendResource releasingFrontend) {
- ClientProfile ownerProfile = getClientProfile(releasingFrontend.getOwnerClientId());
- releasingFrontend.removeOwner();
- ownerProfile.releaseFrontend(releasingFrontend.getId());
- for (int exclusiveGroupMember : releasingFrontend.getExclusiveGroupMemberFeIds()) {
- getFrontendResource(exclusiveGroupMember).removeOwner();
- ownerProfile.releaseFrontend(exclusiveGroupMember);
- }
- }
-
private void updateLnbClientMappingOnNewGrant(int grantingId, int ownerClientId) {
LnbResource grantingLnb = getLnbResource(grantingId);
ClientProfile ownerProfile = getClientProfile(ownerClientId);
@@ -967,10 +1004,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
/**
- * Get the owner client's priority from the resource id.
+ * Get the owner client's priority.
*
* @param clientId the owner client id.
- * @return the priority of the owner client of the resource.
+ * @return the priority of the owner client.
*/
private int getOwnerClientPriority(int clientId) {
return getClientProfile(clientId).getPriority();
@@ -1011,7 +1048,11 @@ public class TunerResourceManagerService extends SystemService implements IBinde
return;
}
if (fe.isInUse()) {
- releaseFrontendInternal(fe);
+ ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
+ for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
+ clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+ }
+ clearFrontendAndClientMapping(ownerClient);
}
for (int excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
getFrontendResource(excGroupmemberFeId)
@@ -1093,21 +1134,37 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
private void removeClientProfile(int clientId) {
- reclaimingResourcesFromClient(getClientProfile(clientId));
+ for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) {
+ clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+ }
+ clearAllResourcesAndClientMapping(getClientProfile(clientId));
mClientProfiles.remove(clientId);
mListeners.remove(clientId);
}
- private void reclaimingResourcesFromClient(ClientProfile profile) {
+ private void clearFrontendAndClientMapping(ClientProfile profile) {
for (Integer feId : profile.getInUseFrontendIds()) {
- getFrontendResource(feId).removeOwner();
+ FrontendResource fe = getFrontendResource(feId);
+ if (fe.getOwnerClientId() == profile.getId()) {
+ fe.removeOwner();
+ continue;
+ }
+ getClientProfile(fe.getOwnerClientId()).stopSharingFrontend(profile.getId());
}
+ profile.releaseFrontend();
+ }
+
+ private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+ // Clear Lnb
for (Integer lnbId : profile.getInUseLnbIds()) {
getLnbResource(lnbId).removeOwner();
}
+ // Clear Cas
if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) {
getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId());
}
+ // Clear Frontend
+ clearFrontendAndClientMapping(profile);
profile.reclaimAllResources();
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 7565d8f9647c..6e9526afa962 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -542,7 +542,7 @@ class ActivityMetricsLogger {
+ " processSwitch=" + processSwitch + " info=" + info);
}
- if (launchedActivity.mDrawn) {
+ if (launchedActivity.isReportedDrawn()) {
// Launched activity is already visible. We cannot measure windows drawn delay.
abort(info, "launched activity already visible");
return;
@@ -681,7 +681,7 @@ class ActivityMetricsLogger {
/** @return {@code true} if the given task has an activity will be drawn. */
private static boolean hasActivityToBeDrawn(Task t) {
- return t.forAllActivities((r) -> r.mVisibleRequested && !r.mDrawn && !r.finishing);
+ return t.forAllActivities(r -> r.mVisibleRequested && !r.isReportedDrawn() && !r.finishing);
}
private void checkVisibility(Task t, ActivityRecord r) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e4f28546a5fd..64fa6ca590d2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -499,7 +499,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// and reporting to the client that it is hidden.
private boolean mSetToSleep; // have we told the activity to sleep?
boolean nowVisible; // is this activity's window visible?
- boolean mDrawn; // is this activity's window drawn?
boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
boolean idle; // has the activity gone idle?
boolean hasBeenLaunched;// has this activity ever been launched?
@@ -564,8 +563,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private boolean mClientVisible;
boolean firstWindowDrawn;
- // Last drawn state we reported to the app token.
- private boolean reportedDrawn;
+ /** Whether the visible window(s) of this activity is drawn. */
+ private boolean mReportedDrawn;
private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
new WindowState.UpdateReportedVisibilityResults();
@@ -927,7 +926,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
+ " mVisible=" + mVisible + " mClientVisible=" + mClientVisible
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
- + " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible);
+ + " reportedDrawn=" + mReportedDrawn + " reportedVisible=" + reportedVisible);
if (paused) {
pw.print(prefix); pw.print("paused="); pw.println(paused);
}
@@ -1213,14 +1212,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return task;
}
- /**
- * Sets the Task on this activity for the purposes of re-use during launch where we will
- * re-use another activity instead of this one for the launch.
- */
- void setTaskForReuse(Task task) {
- this.task = task;
- }
-
Task getStack() {
return task != null ? task.getRootTask() : null;
}
@@ -1591,7 +1582,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
keysPaused = false;
inHistory = false;
nowVisible = false;
- mDrawn = false;
mClientVisible = true;
idle = false;
hasBeenLaunched = false;
@@ -5404,11 +5394,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/** Called when the windows associated app window container are drawn. */
- void onWindowsDrawn(boolean drawn, long timestampNs) {
- mDrawn = drawn;
- if (!drawn) {
- return;
- }
+ private void onWindowsDrawn(long timestampNs) {
final TransitionInfoSnapshot info = mStackSupervisor
.getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
final boolean validInfo = info != null;
@@ -5514,7 +5500,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!nowGone) {
// If the app is not yet gone, then it can only become visible/drawn.
if (!nowDrawn) {
- nowDrawn = reportedDrawn;
+ nowDrawn = mReportedDrawn;
}
if (!nowVisible) {
nowVisible = reportedVisible;
@@ -5522,9 +5508,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
+ numInteresting + " visible=" + numVisible);
- if (nowDrawn != reportedDrawn) {
- onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos());
- reportedDrawn = nowDrawn;
+ if (nowDrawn != mReportedDrawn) {
+ if (nowDrawn) {
+ onWindowsDrawn(SystemClock.elapsedRealtimeNanos());
+ }
+ mReportedDrawn = nowDrawn;
}
if (nowVisible != reportedVisible) {
if (DEBUG_VISIBILITY) Slog.v(TAG,
@@ -5538,6 +5526,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ boolean isReportedDrawn() {
+ return mReportedDrawn;
+ }
+
boolean isClientVisible() {
return mClientVisible;
}
@@ -7677,7 +7669,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
proto.write(VISIBLE_REQUESTED, mVisibleRequested);
proto.write(CLIENT_VISIBLE, mClientVisible);
proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
- proto.write(REPORTED_DRAWN, reportedDrawn);
+ proto.write(REPORTED_DRAWN, mReportedDrawn);
proto.write(REPORTED_VISIBLE, reportedVisible);
proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows);
proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 15e88fc44746..f6158383d28a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1766,8 +1766,8 @@ class ActivityStarter {
} else if (mInTask != null) {
return mInTask;
} else {
- final Task stack = getLaunchStack(mStartActivity, mLaunchFlags,
- null /* task */, mOptions);
+ final Task stack = getLaunchStack(mStartActivity, mLaunchFlags, null /* task */,
+ mOptions);
final ActivityRecord top = stack.getTopNonFinishingActivity();
if (top != null) {
return top.getTask();
@@ -1870,13 +1870,7 @@ class ActivityStarter {
return START_SUCCESS;
}
- boolean clearTaskForReuse = false;
if (reusedTask != null) {
- if (mStartActivity.getTask() == null) {
- mStartActivity.setTaskForReuse(reusedTask);
- clearTaskForReuse = true;
- }
-
if (targetTask.intent == null) {
// This task was started because of movement of the activity based on
// affinity...
@@ -1923,13 +1917,6 @@ class ActivityStarter {
complyActivityFlags(targetTask,
reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants);
- if (clearTaskForReuse) {
- // Clear task for re-use so later code to methods
- // {@link #setTaskFromReuseOrCreateNewTask}, {@link #setTaskFromSourceRecord}, or
- // {@link #setTaskFromInTask} can parent it to the task.
- mStartActivity.setTaskForReuse(null);
- }
-
if (mAddingToTask) {
return START_SUCCESS;
}
@@ -2515,8 +2502,8 @@ class ActivityStarter {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
- final Task launchStack =
- getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
+ final Task launchStack = getLaunchStack(mStartActivity, mLaunchFlags, intentTask,
+ mOptions);
if (launchStack == null || launchStack == mTargetStack) {
// Do not set mMovedToFront to true below for split-screen-top stack, or
// START_TASK_TO_FRONT will be returned and trigger unexpected animations when a
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index affbafa095cc..2e879810c085 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -117,7 +117,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
-import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
@@ -469,12 +468,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
WindowState mLastFocus = null;
/**
- * Windows that have lost input focus and are waiting for the new focus window to be displayed
- * before they are told about this.
- */
- ArrayList<WindowState> mLosingFocus = new ArrayList<>();
-
- /**
* The foreground app of this display. Windows below this app cannot be the focused window. If
* the user taps on the area outside of the task of the focused app, we will notify AM about the
* new task the user wants to interact with.
@@ -899,10 +892,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) {
- mWmService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
- }
-
w.updateResizingWindowIfNeeded();
};
@@ -2924,21 +2913,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (mLastFocus != mCurrentFocus) {
pw.print(" mLastFocus="); pw.println(mLastFocus);
}
- if (mLosingFocus.size() > 0) {
- pw.println();
- pw.println(" Windows losing focus:");
- for (int i = mLosingFocus.size() - 1; i >= 0; i--) {
- final WindowState w = mLosingFocus.get(i);
- pw.print(" Losing #"); pw.print(i); pw.print(' ');
- pw.print(w);
- if (dumpAll) {
- pw.println(":");
- w.dump(pw, " ", true);
- } else {
- pw.println();
- }
- }
- }
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
if (mLastStatusBarVisibility != 0) {
pw.print(" mLastStatusBarVisibility=0x");
@@ -3157,7 +3131,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
- mLosingFocus.remove(newFocus);
if (newFocus != null) {
mWinAddedSinceNullFocus.clear();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index fe2d08f6a4c2..aeaffd98f820 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2816,7 +2816,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @param launchParams The resolved launch params to use.
* @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
* @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
- *
* @return The stack to use for the launch or INVALID_STACK_ID.
*/
Task getLaunchStack(@Nullable ActivityRecord r,
@@ -2965,10 +2964,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// If {@code r} is already in target display area and its task is the same as the candidate
// task, the intention should be getting a launch stack for the reusable activity, so we can
// use the existing stack.
- if (candidateTask != null && (r.getTask() == null || r.getTask() == candidateTask)) {
- // TODO(b/153920825): Fix incorrect evaluation of attached state
- final TaskDisplayArea attachedTaskDisplayArea = r.getTask() != null
- ? r.getTask().getDisplayArea() : r.getDisplayArea();
+ if (candidateTask != null) {
+ final TaskDisplayArea attachedTaskDisplayArea = candidateTask.getDisplayArea();
if (attachedTaskDisplayArea == null || attachedTaskDisplayArea == taskDisplayArea) {
return candidateTask.getRootTask();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7624d4c1ea36..cd222a97f4d9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4740,7 +4740,6 @@ public class WindowManagerService extends IWindowManager.Stub
final class H extends android.os.Handler {
public static final int REPORT_FOCUS_CHANGE = 2;
- public static final int REPORT_LOSING_FOCUS = 3;
public static final int WINDOW_FREEZE_TIMEOUT = 11;
public static final int PERSIST_ANIMATION_SCALE = 14;
@@ -4815,11 +4814,6 @@ public class WindowManagerService extends IWindowManager.Stub
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus moving from %s"
+ " to %s displayId=%d", lastFocus, newFocus,
displayContent.getDisplayId());
- if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) {
- ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Delaying loss of focus...");
- displayContent.mLosingFocus.add(lastFocus);
- lastFocus = null;
- }
}
// First notify the accessibility manager for the change so it has
@@ -4842,24 +4836,6 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case REPORT_LOSING_FOCUS: {
- final DisplayContent displayContent = (DisplayContent) msg.obj;
- ArrayList<WindowState> losers;
-
- synchronized (mGlobalLock) {
- losers = displayContent.mLosingFocus;
- displayContent.mLosingFocus = new ArrayList<>();
- }
-
- final int N = losers.size();
- for (int i = 0; i < N; i++) {
- ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing delayed focus: %s",
- losers.get(i));
- losers.get(i).reportFocusChangedSerialized(false);
- }
- break;
- }
-
case WINDOW_FREEZE_TIMEOUT: {
final DisplayContent displayContent = (DisplayContent) msg.obj;
synchronized (mGlobalLock) {
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 529fb8838aa1..b3f3a5e1ff72 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -181,6 +181,24 @@ static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jl
return effects;
}
+static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */,
+ jlong controllerPtr) {
+ vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+ if (controller == nullptr) {
+ ALOGE("vibratorGetSupportedPrimitives failed because controller was not initialized");
+ return nullptr;
+ }
+ auto result = controller->getSupportedPrimitives();
+ if (!result.isOk()) {
+ return nullptr;
+ }
+ std::vector<aidl::CompositePrimitive> supportedPrimitives = result.value();
+ jintArray primitives = env->NewIntArray(supportedPrimitives.size());
+ env->SetIntArrayRegion(primitives, 0, supportedPrimitives.size(),
+ reinterpret_cast<jint*>(supportedPrimitives.data()));
+ return primitives;
+}
+
static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
jlong effect, jlong strength, jobject vibration) {
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
@@ -259,6 +277,7 @@ static const JNINativeMethod method_table[] = {
"VibratorService$Vibration;)V",
(void*)vibratorPerformComposedEffect},
{"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
+ {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
{"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
{"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities},
{"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2ab629bf725a..6154bef2bda3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1579,21 +1579,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Creates a new {@link CallerIdentity} object to represent the caller's identity.
+ * The component name should be an active admin for the calling user.
*/
- private CallerIdentity getCallerIdentity(@NonNull ComponentName componentName) {
+ private CallerIdentity getCallerIdentity(@NonNull ComponentName adminComponent) {
final int callerUid = mInjector.binderGetCallingUid();
final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid));
- ActiveAdmin admin = policy.mAdminMap.get(componentName);
+ ActiveAdmin admin = policy.mAdminMap.get(adminComponent);
if (admin == null) {
- throw new SecurityException(String.format("No active admin for %s", componentName));
+ throw new SecurityException(String.format("No active admin for %s", adminComponent));
}
if (admin.getUid() != callerUid) {
throw new SecurityException(
- String.format("Admin %s is not owned by uid %d", componentName, callerUid));
+ String.format("Admin %s is not owned by uid %d", adminComponent, callerUid));
}
- return new CallerIdentity(callerUid, componentName.getPackageName(), componentName);
+ return new CallerIdentity(callerUid, adminComponent.getPackageName(), adminComponent);
}
/**
@@ -4589,12 +4590,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void enforceDeviceOwner(ComponentName who) {
- synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- }
- }
-
private void enforceProfileOrDeviceOwner(ComponentName who) {
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -5194,20 +5189,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty");
Preconditions.checkCollectionElementsNotNull(scopeList, "Scopes");
+ final CallerIdentity identity = getCallerIdentity(who);
+
// Remove possible duplicates.
final ArrayList<String> scopes = new ArrayList(new ArraySet(scopeList));
// Ensure given scopes are valid.
if (scopes.retainAll(Arrays.asList(DELEGATIONS))) {
throw new IllegalArgumentException("Unexpected delegation scopes");
}
- final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
// Retrieve the user ID of the calling process.
- final int userId = mInjector.userHandleGetCallingUserId();
+ final int userId = identity.getUserId();
+ final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
synchronized (getLockObject()) {
// Ensure calling process is device/profile owner.
if (hasDoDelegation) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
} else {
+ // TODO move whole condition out of synchronized block
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
}
// Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
@@ -6199,7 +6197,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) {
- enforceDeviceOwner(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
mInjector.binderWithCleanCallingIdentity(
() -> mInjector.getConnectivityManager().setGlobalProxy(proxyInfo));
}
@@ -6620,6 +6620,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+
// Allow setting this policy to true only if there is a split system user.
if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) {
throw new UnsupportedOperationException(
@@ -6627,11 +6630,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
boolean removeAllUsers = false;
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (deviceOwner.forceEphemeralUsers != forceEphemeralUsers) {
deviceOwner.forceEphemeralUsers = forceEphemeralUsers;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
mUserManagerInternal.setForceEphemeralUsers(forceEphemeralUsers);
removeAllUsers = forceEphemeralUsers;
}
@@ -6647,19 +6649,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- return deviceOwner.forceEphemeralUsers;
- }
- }
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
- private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who)
- throws SecurityException {
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ return deviceOwner.forceEphemeralUsers;
}
- ensureAllUsersAffiliated();
}
private void ensureAllUsersAffiliated() throws SecurityException {
@@ -6676,11 +6672,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
-
// TODO: If an unaffiliated user is removed, the admin will be able to request a bugreport
// which could still contain data related to that user. Should we disallow that, e.g. until
// next boot? Might not be needed given that this still requires user consent.
- ensureDeviceOwnerAndAllUsersAffiliated(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+ ensureAllUsersAffiliated();
if (mRemoteBugreportServiceIsActive.get()
|| (getDeviceOwnerRemoteBugreportUri() != null)) {
@@ -8489,6 +8486,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setDefaultSmsApplication(ComponentName admin, String packageName, boolean parent) {
Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
if (parent) {
ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
@@ -8497,7 +8495,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(
packageName, getProfileParentId(mInjector.userHandleGetCallingUserId())));
} else {
- enforceDeviceOwner(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
}
mInjector.binderWithCleanCallingIdentity(() ->
@@ -9259,14 +9257,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean removeUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
- final int callingUserId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(() -> {
String restriction = isManagedProfile(userHandle.getIdentifier())
? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
: UserManager.DISALLOW_REMOVE_USER;
- if (isAdminAffectedByRestriction(who, restriction, callingUserId)) {
+ if (isAdminAffectedByRestriction(who, restriction, identity.getUserId())) {
Log.w(LOG_TAG, "The device owner cannot remove a user because "
+ restriction + " is enabled, and was not set by the device owner");
return false;
@@ -9292,10 +9290,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean switchUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
long id = mInjector.binderClearCallingIdentity();
try {
int userId = UserHandle.USER_SYSTEM;
@@ -9316,7 +9314,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public int startUserInBackground(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
@@ -9348,7 +9347,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public int stopUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final int userId = userHandle.getIdentifier();
if (isManagedProfile(userId)) {
@@ -9416,7 +9416,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<UserHandle> getSecondaryUsers(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return mInjector.binderWithCleanCallingIdentity(() -> {
final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true
@@ -10378,6 +10379,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setGlobalSetting(ComponentName who, String setting, String value) {
Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING)
@@ -10386,8 +10389,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
// Some settings are no supported any more. However we do not want to throw a
// SecurityException to avoid breaking apps.
if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) {
@@ -10468,7 +10469,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
- CallerIdentity identity = getCallerIdentity(who);
+ final CallerIdentity identity = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(identity));
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -12008,16 +12009,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isSystemOnlyUser(ComponentName admin) {
- enforceDeviceOwner(admin);
- final int callingUserId = mInjector.userHandleGetCallingUserId();
- return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM;
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+ return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM;
}
@Override
public void reboot(ComponentName admin) {
- Objects.requireNonNull(admin);
- // Make sure caller has DO.
- enforceDeviceOwner(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+
mInjector.binderWithCleanCallingIdentity(() -> {
// Make sure there are no ongoing calls on the device.
if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
@@ -13523,18 +13526,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (deviceOwner.isLogoutEnabled == enabled) {
// already in the requested state
return;
}
deviceOwner.isLogoutEnabled = enabled;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
}
@@ -13700,20 +13703,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final String startUserSessionMessageString =
startUserSessionMessage != null ? startUserSessionMessage.toString() : null;
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (TextUtils.equals(deviceOwner.startUserSessionMessage, startUserSessionMessage)) {
return;
}
deviceOwner.startUserSessionMessage = startUserSessionMessageString;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
mInjector.getActivityManagerInternal()
@@ -13725,20 +13728,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
final String endUserSessionMessageString =
endUserSessionMessage != null ? endUserSessionMessage.toString() : null;
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
if (TextUtils.equals(deviceOwner.endUserSessionMessage, endUserSessionMessage)) {
return;
}
deviceOwner.endUserSessionMessage = endUserSessionMessageString;
- saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ saveSettingsLocked(identity.getUserId());
}
mInjector.getActivityManagerInternal()
@@ -13750,11 +13753,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return deviceOwner.startUserSessionMessage;
}
}
@@ -13764,11 +13768,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Objects.requireNonNull(admin);
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
synchronized (getLockObject()) {
- final ActiveAdmin deviceOwner =
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return deviceOwner.endUserSessionMessage;
}
}
@@ -13807,9 +13812,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mHasTelephonyFeature) {
return -1;
}
- Objects.requireNonNull(who, "ComponentName is null in addOverrideApn");
+ Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
if (tm != null) {
@@ -13827,9 +13833,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
- Objects.requireNonNull(who, "ComponentName is null in updateOverrideApn");
+ Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
if (apnId < 0) {
return false;
@@ -13849,9 +13856,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
- Objects.requireNonNull(who, "ComponentName is null in removeOverrideApn");
- enforceDeviceOwner(who);
-
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return removeOverrideApnUnchecked(apnId);
}
@@ -13870,9 +13877,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mHasTelephonyFeature) {
return Collections.emptyList();
}
- Objects.requireNonNull(who, "ComponentName is null in getOverrideApns");
- enforceDeviceOwner(who);
-
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return getOverrideApnsUnchecked();
}
@@ -13891,9 +13898,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mHasTelephonyFeature) {
return;
}
- Objects.requireNonNull(who, "ComponentName is null in setOverrideApnEnabled");
- enforceDeviceOwner(who);
-
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
setOverrideApnsEnabledUnchecked(enabled);
}
@@ -13909,8 +13916,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
- Objects.requireNonNull(who, "ComponentName is null in isOverrideApnEnabled");
- enforceDeviceOwner(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
Cursor enforceCursor = mInjector.binderWithCleanCallingIdentity(
() -> mContext.getContentResolver().query(
@@ -13992,11 +14000,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
}
-
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
-
- final int returnCode;
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
switch (mode) {
case PRIVATE_DNS_MODE_OPPORTUNISTIC:
@@ -14030,9 +14036,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return PRIVATE_DNS_MODE_UNKNOWN;
}
-
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
+
String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
if (currentMode == null) {
currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
@@ -14054,10 +14061,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
-
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
-
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
}
@@ -14402,13 +14408,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setUserControlDisabledPackages(ComponentName who, List<String> packages) {
- Preconditions.checkNotNull(who, "ComponentName is null");
+ Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkNotNull(packages, "packages is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
- enforceDeviceOwner(who);
synchronized (getLockObject()) {
- final int userHandle = mInjector.userHandleGetCallingUserId();
- setUserControlDisabledPackagesLocked(userHandle, packages);
+ setUserControlDisabledPackagesLocked(identity.getUserId(), packages);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
.setAdmin(who)
@@ -14428,12 +14434,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<String> getUserControlDisabledPackages(ComponentName who) {
- Preconditions.checkNotNull(who, "ComponentName is null");
+ final CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
- enforceDeviceOwner(who);
- final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
synchronized (getLockObject()) {
- final List<String> packages = getUserData(userHandle).mUserControlDisabledPackages;
+ final List<String> packages =
+ getUserData(identity.getUserId()).mUserControlDisabledPackages;
return packages == null ? Collections.EMPTY_LIST : packages;
}
}
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index e8266a574bf5..b93c519ec8e4 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -30,6 +30,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -132,7 +133,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
private final Context mContext;
- private final int mUserId;
+ private final @UserIdInt int mUserId;
private final RemotePrintSpooler mSpooler;
@@ -650,7 +651,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
mPrintServiceRecommendationsService =
new RemotePrintServiceRecommendationService(mContext,
- UserHandle.getUserHandleForUid(mUserId), this);
+ UserHandle.of(mUserId), this);
}
mPrintServiceRecommendationsChangeListenerRecords.add(
new ListenerRecord<IRecommendationsChangeListener>(listener) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index bc75dcd91813..12c69eaa0f8d 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -150,7 +150,8 @@ public final class ProfcollectForwardingService extends SystemService {
}
// Sample for a fraction of app launches.
- int traceFrequency = SystemProperties.getInt("profcollectd.applaunch_trace_freq", 2);
+ int traceFrequency =
+ SystemProperties.getInt("persist.profcollectd.applaunch_trace_freq", 2);
int randomNum = ThreadLocalRandom.current().nextInt(100);
if (randomNum < traceFrequency) {
try {
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index e4e7e2288590..4f636efc7c06 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -22,6 +22,7 @@ java_test_host {
],
static_libs: [
"frameworks-base-hostutils",
+ "PackageManagerServiceHostTestsIntentVerifyUtils",
],
test_suites: ["general-tests"],
java_resources: [
@@ -33,7 +34,15 @@ java_test_host {
":PackageManagerTestAppVersion4",
":PackageManagerTestAppOriginalOverride",
":PackageManagerServiceDeviceSideTests",
- ],
+ ":PackageManagerTestIntentVerifier",
+ ":PackageManagerTestIntentVerifierTarget1",
+ ":PackageManagerTestIntentVerifierTarget2",
+ ":PackageManagerTestIntentVerifierTarget3",
+ ":PackageManagerTestIntentVerifierTarget4Base",
+ ":PackageManagerTestIntentVerifierTarget4NoAutoVerify",
+ ":PackageManagerTestIntentVerifierTarget4Wildcard",
+ ":PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify",
+ ]
}
genrule {
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
new file mode 100644
index 000000000000..b7a0624e02b8
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+java_library {
+ name: "PackageManagerServiceHostTestsIntentVerifyUtils",
+ srcs: ["src/**/*.kt"],
+ host_supported: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt
new file mode 100644
index 000000000000..48119e0088c4
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/IntentVerifyTestParams.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.intent.verify
+
+interface IntentVerifyTestParams {
+
+ val methodName: String
+
+ fun toArgsMap(): Map<String, String>
+}
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt
new file mode 100644
index 000000000000..26c3903b20d6
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/SetActivityAsAlwaysParams.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.intent.verify
+
+data class SetActivityAsAlwaysParams(
+ val uri: String,
+ val packageName: String,
+ val activityName: String,
+ override val methodName: String = "setActivityAsAlways"
+) : IntentVerifyTestParams {
+
+ companion object {
+ private const val KEY_URI = "uri"
+ private const val KEY_PACKAGE_NAME = "packageName"
+ private const val KEY_ACTIVITY_NAME = "activityName"
+
+ fun fromArgs(args: Map<String, String>) = SetActivityAsAlwaysParams(
+ args.getValue(KEY_URI),
+ args.getValue(KEY_PACKAGE_NAME),
+ args.getValue(KEY_ACTIVITY_NAME)
+ )
+ }
+
+ override fun toArgsMap() = mapOf(
+ KEY_URI to uri,
+ KEY_PACKAGE_NAME to packageName,
+ KEY_ACTIVITY_NAME to activityName
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt
new file mode 100644
index 000000000000..7eddcfb621c4
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/StartActivityParams.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.intent.verify
+
+data class StartActivityParams(
+ val uri: String,
+ val expected: List<String>,
+ val withBrowsers: Boolean = false,
+ override val methodName: String = "verifyActivityStart"
+) : IntentVerifyTestParams {
+ companion object {
+ private const val KEY_URI = "uri"
+ private const val KEY_EXPECTED = "expected"
+ private const val KEY_BROWSER = "browser"
+
+ fun fromArgs(args: Map<String, String>) = StartActivityParams(
+ args.getValue(KEY_URI),
+ args.getValue(KEY_EXPECTED).split(","),
+ args.getValue(KEY_BROWSER).toBoolean()
+ )
+ }
+
+ constructor(
+ uri: String,
+ expected: String,
+ withBrowsers: Boolean = false
+ ) : this(uri, listOf(expected), withBrowsers)
+
+ override fun toArgsMap() = mapOf(
+ KEY_URI to uri,
+ KEY_EXPECTED to expected.joinToString(separator = ","),
+ KEY_BROWSER to withBrowsers.toString()
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt
new file mode 100644
index 000000000000..f93b1e0e7e37
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/src/com/android/server/pm/test/intent/verify/VerifyRequest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.intent.verify
+
+data class VerifyRequest(
+ val id: Int = -1,
+ val scheme: String,
+ val hosts: List<String>,
+ val packageName: String
+) {
+
+ companion object {
+ fun deserialize(value: String?): VerifyRequest {
+ val lines = value?.trim()?.lines()
+ ?: return VerifyRequest(scheme = "", hosts = emptyList(), packageName = "")
+ return VerifyRequest(
+ lines[0].removePrefix("id=").toInt(),
+ lines[1].removePrefix("scheme="),
+ lines[2].removePrefix("hosts=").split(","),
+ lines[3].removePrefix("packageName=")
+ )
+ }
+ }
+
+ constructor(id: Int = -1, scheme: String, host: String, packageName: String) :
+ this(id, scheme, listOf(host), packageName)
+
+ fun serializeToString() = """
+ id=$id
+ scheme=$scheme
+ hosts=${hosts.joinToString(separator = ",")}
+ packageName=$packageName
+ """.trimIndent() + "\n"
+}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
index 3847658def6a..e17358d38d8c 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
@@ -31,7 +31,8 @@ class FactoryPackageTest : BaseHostJUnit4Test() {
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
private val filePath =
HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.SYSTEM)
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 8dfefaf9750f..24c714c0d5f2 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -18,6 +18,7 @@ package com.android.server.pm.test
import com.android.internal.util.test.SystemPreparer
import com.android.tradefed.device.ITestDevice
+import org.junit.rules.TemporaryFolder
import java.io.File
import java.io.FileOutputStream
@@ -34,6 +35,19 @@ internal fun SystemPreparer.deleteApkFolders(
}
}
+internal fun ITestDevice.installJavaResourceApk(
+ tempFolder: TemporaryFolder,
+ javaResource: String,
+ reinstall: Boolean = true,
+ extraArgs: Array<String> = emptyArray()
+): String? {
+ val file = HostUtils.copyResourceToHostFile(javaResource, tempFolder.newFile())
+ return installPackage(file, reinstall, *extraArgs)
+}
+
+internal fun ITestDevice.uninstallPackages(vararg pkgNames: String) =
+ pkgNames.forEach { uninstallPackage(it) }
+
internal object HostUtils {
fun getDataDir(device: ITestDevice, pkgName: String) =
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index b7d135991ccd..37c999cbee68 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -47,7 +47,8 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT)
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index 4ae3ca5f7263..4becae66633f 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -47,7 +47,8 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
@Before
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
index 654c11c5bf81..6479f584324f 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
@@ -22,6 +22,7 @@ import java.nio.file.Paths
// Unfortunately no easy way to access PMS SystemPartitions, so mock them here
internal enum class Partition(val baseAppFolder: Path) {
SYSTEM("/system/app"),
+ SYSTEM_PRIVILEGED("/system/priv-app"),
VENDOR("/vendor/app"),
PRODUCT("/product/app"),
SYSTEM_EXT("/system_ext/app")
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 207f10a3027b..46120af06550 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -110,7 +110,8 @@ class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() {
private val preparer: SystemPreparer = SystemPreparer(tempFolder,
SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
- @get:Rule
+ @Rule
+ @JvmField
val rules = RuleChain.outerRule(tempFolder).let {
if (DEBUG_NO_REBOOT) {
it!!
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt
new file mode 100644
index 000000000000..fffda8ebd36c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.intent.verify
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.server.pm.test.Partition
+import com.android.server.pm.test.deleteApkFolders
+import com.android.server.pm.test.installJavaResourceApk
+import com.android.server.pm.test.pushApk
+import com.android.server.pm.test.uninstallPackages
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+import java.util.concurrent.TimeUnit
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class IntentFilterVerificationTest : BaseHostJUnit4Test() {
+
+ companion object {
+ private const val VERIFIER = "PackageManagerTestIntentVerifier.apk"
+ private const val VERIFIER_PKG_NAME = "com.android.server.pm.test.intent.verifier"
+ private const val TARGET_PKG_PREFIX = "$VERIFIER_PKG_NAME.target"
+ private const val TARGET_APK_PREFIX = "PackageManagerTestIntentVerifierTarget"
+ private const val TARGET_ONE = "${TARGET_APK_PREFIX}1.apk"
+ private const val TARGET_ONE_PKG_NAME = "$TARGET_PKG_PREFIX.one"
+ private const val TARGET_TWO = "${TARGET_APK_PREFIX}2.apk"
+ private const val TARGET_TWO_PKG_NAME = "$TARGET_PKG_PREFIX.two"
+ private const val TARGET_THREE = "${TARGET_APK_PREFIX}3.apk"
+ private const val TARGET_THREE_PKG_NAME = "$TARGET_PKG_PREFIX.three"
+ private const val TARGET_FOUR_BASE = "${TARGET_APK_PREFIX}4Base.apk"
+ private const val TARGET_FOUR_PKG_NAME = "$TARGET_PKG_PREFIX.four"
+ private const val TARGET_FOUR_NO_AUTO_VERIFY = "${TARGET_APK_PREFIX}4NoAutoVerify.apk"
+ private const val TARGET_FOUR_WILDCARD = "${TARGET_APK_PREFIX}4Wildcard.apk"
+ private const val TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY =
+ "${TARGET_APK_PREFIX}4WildcardNoAutoVerify.apk"
+
+ @get:ClassRule
+ val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+ }
+
+ private val tempFolder = TemporaryFolder()
+ private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+ SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
+
+ @Rule
+ @JvmField
+ val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+
+ private val permissionsFile = File("/system/etc/permissions" +
+ "/privapp-PackageManagerIntentFilterVerificationTest-permissions.xml")
+
+ @Before
+ fun cleanupAndPushPermissionsFile() {
+ // In order for the test app to be the verification agent, it needs a permission file
+ // which can be pushed onto the system and removed afterwards.
+ val file = tempFolder.newFile().apply {
+ """
+ <permissions>
+ <privapp-permissions package="$VERIFIER_PKG_NAME">
+ <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+ </privapp-permissions>
+ </permissions>
+ """
+ .trimIndent()
+ .let { writeText(it) }
+ }
+ device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME,
+ TARGET_FOUR_PKG_NAME)
+ preparer.pushApk(VERIFIER, Partition.SYSTEM_PRIVILEGED)
+ .pushFile(file, permissionsFile.toString())
+ .reboot()
+ runTest("clearResponse")
+ }
+
+ @After
+ fun cleanupAndDeletePermissionsFile() {
+ device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME,
+ TARGET_FOUR_PKG_NAME)
+ preparer.deleteApkFolders(Partition.SYSTEM_PRIVILEGED, VERIFIER)
+ .deleteFile(permissionsFile.toString())
+ device.reboot()
+ }
+
+ @Test
+ fun verifyOne() {
+ installPackage(TARGET_ONE)
+
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "https_only.pm.server.android.com",
+ "other_activity.pm.server.android.com",
+ "http_only.pm.server.android.com",
+ "verify.pm.server.android.com",
+ "https_plus_non_web_scheme.pm.server.android.com",
+ "multiple.pm.server.android.com",
+ // TODO(b/159952358): the following domain should not be
+ // verified, this is because the verifier tries to verify all web domains,
+ // even in intent filters not marked for auto verify
+ "no_verify.pm.server.android.com"
+ ),
+ packageName = TARGET_ONE_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://https_only.pm.server.android.com",
+ expected = "$TARGET_ONE_PKG_NAME.TargetActivity"
+ ))
+ }
+
+ @Test
+ fun nonWebScheme() {
+ installPackage(TARGET_TWO)
+ assertReceivedRequests(null)
+ }
+
+ @Test
+ fun verifyHttpNonSecureOnly() {
+ installPackage(TARGET_THREE)
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "multiple.pm.server.android.com"
+ ),
+ packageName = TARGET_THREE_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "http://multiple.pm.server.android.com",
+ expected = "$TARGET_THREE_PKG_NAME.TargetActivity"
+ ))
+ }
+
+ @Test
+ fun multipleResults() {
+ installPackage(TARGET_ONE)
+ installPackage(TARGET_THREE)
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "https_only.pm.server.android.com",
+ "other_activity.pm.server.android.com",
+ "http_only.pm.server.android.com",
+ "verify.pm.server.android.com",
+ "https_plus_non_web_scheme.pm.server.android.com",
+ "multiple.pm.server.android.com",
+ // TODO(b/159952358): the following domain should not be
+ // verified, this is because the verifier tries to verify all web domains,
+ // even in intent filters not marked for auto verify
+ "no_verify.pm.server.android.com"
+ ),
+ packageName = TARGET_ONE_PKG_NAME
+ ), VerifyRequest(
+ scheme = "https",
+ hosts = listOf(
+ "multiple.pm.server.android.com"
+ ),
+ packageName = TARGET_THREE_PKG_NAME
+ ))
+
+ // Target3 declares http non-s, so it should be included in the set here
+ runTest(StartActivityParams(
+ uri = "http://multiple.pm.server.android.com",
+ expected = listOf(
+ "$TARGET_ONE_PKG_NAME.TargetActivity2",
+ "$TARGET_THREE_PKG_NAME.TargetActivity"
+ )
+ ))
+
+ // But it excludes https, so it shouldn't resolve here
+ runTest(StartActivityParams(
+ uri = "https://multiple.pm.server.android.com",
+ expected = "$TARGET_ONE_PKG_NAME.TargetActivity2"
+ ))
+
+ // Remove Target3 and return to single verified Target1 app for http non-s
+ device.uninstallPackage(TARGET_THREE_PKG_NAME)
+ runTest(StartActivityParams(
+ uri = "http://multiple.pm.server.android.com",
+ expected = "$TARGET_ONE_PKG_NAME.TargetActivity2"
+ ))
+ }
+
+ @Test
+ fun demoteAlways() {
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ runTest(SetActivityAsAlwaysParams(
+ uri = "https://failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME,
+ activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Re-installing with same host/verify set will maintain always setting
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(null)
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Installing with new wildcard host will downgrade out of always, re-including browsers
+ installPackage(TARGET_FOUR_WILDCARD)
+
+ // TODO(b/159952358): The first request without the wildcard should not be sent. This is
+ // caused by the request being queued even if it should be dropped from the previous
+ // install case since the host set didn't change.
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ), VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ }
+
+ @Test
+ fun unverifiedReinstallResendRequest() {
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ installPackage(TARGET_FOUR_BASE)
+
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+ }
+
+ @Test
+ fun unverifiedUpdateRemovingDomainNoRequestDemoteAlways() {
+ installPackage(TARGET_FOUR_WILDCARD)
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(SetActivityAsAlwaysParams(
+ uri = "https://failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME,
+ activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Re-installing with a smaller host/verify set will not request re-verification
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(null)
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ // Re-installing with a (now) larger host/verify set will re-request and demote
+ installPackage(TARGET_FOUR_WILDCARD)
+ // TODO(b/159952358): The first request should not be sent. This is caused by the request
+ // being queued even if it should be dropped from the previous install case.
+ assertReceivedRequests(false, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ), VerifyRequest(
+ scheme = "https",
+ hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ }
+
+ // TODO(b/159952358): I would expect this to demote
+ // TODO(b/32810168)
+ @Test
+ fun verifiedUpdateRemovingAutoVerifyMaintainsAlways() {
+ installPackage(TARGET_FOUR_BASE)
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ installPackage(TARGET_FOUR_NO_AUTO_VERIFY)
+ assertReceivedRequests(null)
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+ }
+
+ @Test
+ fun verifiedUpdateRemovingAutoVerifyAddingDomainDemotesAlways() {
+ installPackage(TARGET_FOUR_BASE)
+
+ assertReceivedRequests(true, VerifyRequest(
+ scheme = "https",
+ host = "failing.pm.server.android.com",
+ packageName = TARGET_FOUR_PKG_NAME
+ ))
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
+ ))
+
+ installPackage(TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY)
+ assertReceivedRequests(null)
+
+ runTest(StartActivityParams(
+ uri = "https://failing.pm.server.android.com",
+ expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
+ withBrowsers = true
+ ))
+ }
+
+ private fun installPackage(javaResourceName: String) {
+ // Need to pass --user as verification is not currently run for all user installs
+ assertThat(device.installJavaResourceApk(tempFolder, javaResourceName,
+ extraArgs = arrayOf("--user", device.currentUser.toString()))).isNull()
+ }
+
+ private fun assertReceivedRequests(success: Boolean?, vararg expected: VerifyRequest?) {
+ // TODO(b/159952358): This can probably be less than 10
+ // Because tests have to assert that multiple broadcasts aren't received, there's no real
+ // better way to await for a value than sleeping for a long enough time.
+ TimeUnit.SECONDS.sleep(10)
+
+ val params = mutableMapOf<String, String>()
+ if (expected.any { it != null }) {
+ params["expected"] = expected.filterNotNull()
+ .joinToString(separator = "") { it.serializeToString() }
+ }
+ runTest("compareLastReceived", params)
+
+ if (success != null) {
+ if (success) {
+ runTest("verifyPreviousReceivedSuccess")
+ } else {
+ runTest("verifyPreviousReceivedFailure")
+ }
+ runTest("clearResponse")
+ }
+ }
+
+ private fun runTest(params: IntentVerifyTestParams) =
+ runTest(params.methodName, params.toArgsMap())
+
+ private fun runTest(testName: String, args: Map<String, String> = emptyMap()) {
+ val escapedArgs = args.mapValues {
+ // Need to escape strings so that args are passed properly through the shell command
+ "\"${it.value.trim('"')}\""
+ }
+ runDeviceTests(device, null, VERIFIER_PKG_NAME, "$VERIFIER_PKG_NAME.VerifyReceiverTest",
+ testName, null, 10 * 60 * 1000L, 10 * 60 * 1000L, 0L, true, false, escapedArgs)
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
new file mode 100644
index 000000000000..e82f57d20fcc
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifier",
+ srcs: [ "src/**/*.kt" ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.espresso.core",
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "junit",
+ "truth-prebuilt",
+ "PackageManagerServiceHostTestsIntentVerifyUtils",
+ ],
+ platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml
new file mode 100644
index 000000000000..17b50b0ec949
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier"
+ >
+
+ <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.pm.test.intent.verifier"
+ />
+
+ <application>
+ <receiver android:name=".VerifyReceiver" android:exported="true">
+ <intent-filter android:priority="999">
+ <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"/>
+ <data android:mimeType="application/vnd.android.package-archive"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt
new file mode 100644
index 000000000000..073c2be75424
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.intent.verifier
+
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import com.android.server.pm.test.intent.verify.VerifyRequest
+
+class VerifyReceiver : BroadcastReceiver() {
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION) return
+ val params = intent.toVerifyParams()
+
+ // If the receiver is called for a normal request, proxy it to the real verifier on device
+ if (params.hosts.none { it.contains("pm.server.android.com") }) {
+ sendToRealVerifier(context, Intent(intent))
+ return
+ }
+
+ // When the receiver is invoked for a test install, there is no direct connection to host,
+ // so store the result in a file to read and assert on later. Append is intentional so that
+ // amount of invocations and clean up can be verified.
+ context.filesDir.resolve("test.txt")
+ .appendText(params.serializeToString())
+ }
+
+ private fun sendToRealVerifier(context: Context, intent: Intent) {
+ context.packageManager.queryBroadcastReceivers(intent, 0)
+ .first { it.activityInfo?.packageName != context.packageName }
+ .let { it.activityInfo!! }
+ .let { intent.setComponent(ComponentName(it.packageName, it.name)) }
+ .run { context.sendBroadcast(intent) }
+ }
+
+ private fun Intent.toVerifyParams() = VerifyRequest(
+ id = getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1),
+ scheme = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME)!!,
+ hosts = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS)!!
+ .split(' '),
+ packageName = getStringExtra(
+ PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME)!!
+
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
new file mode 100644
index 000000000000..6de3d4e160ec
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.intent.verifier
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.os.UserHandle
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.ShellIdentityUtils
+import com.android.server.pm.test.intent.verify.SetActivityAsAlwaysParams
+import com.android.server.pm.test.intent.verify.StartActivityParams
+import com.android.server.pm.test.intent.verify.VerifyRequest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.File
+
+@RunWith(AndroidJUnit4::class)
+class VerifyReceiverTest {
+
+ val args: Bundle = InstrumentationRegistry.getArguments()
+ val context: Context = InstrumentationRegistry.getContext()
+
+ private val file = context.filesDir.resolve("test.txt")
+
+ @Test
+ fun clearResponse() {
+ file.delete()
+ }
+
+ @Test
+ fun compareLastReceived() {
+ val lastReceivedText = file.readTextIfExists()
+ val expectedText = args.getString("expected")
+ if (expectedText.isNullOrEmpty()) {
+ assertThat(lastReceivedText).isEmpty()
+ return
+ }
+
+ val expectedParams = expectedText.parseParams()
+ val lastReceivedParams = lastReceivedText.parseParams()
+
+ assertThat(lastReceivedParams).hasSize(expectedParams.size)
+
+ lastReceivedParams.zip(expectedParams).forEach { (actual, expected) ->
+ assertThat(actual.hosts).containsExactlyElementsIn(expected.hosts)
+ assertThat(actual.packageName).isEqualTo(expected.packageName)
+ assertThat(actual.scheme).isEqualTo(expected.scheme)
+ }
+ }
+
+ @Test
+ fun setActivityAsAlways() {
+ val params = SetActivityAsAlwaysParams.fromArgs(
+ args.keySet().associateWith { args.getString(it)!! })
+ val uri = Uri.parse(params.uri)
+ val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme(uri.scheme)
+ addDataAuthority(uri.authority, null)
+ }
+
+ val intent = Intent(Intent.ACTION_VIEW, uri).apply {
+ addCategory(Intent.CATEGORY_DEFAULT)
+ }
+ val allResults = context.packageManager.queryIntentActivities(intent, 0)
+ val allComponents = allResults
+ .map { ComponentName(it.activityInfo.packageName, it.activityInfo.name) }
+ .toTypedArray()
+ val matchingInfo = allResults.first {
+ it.activityInfo.packageName == params.packageName &&
+ it.activityInfo.name == params.activityName
+ }
+
+ ShellIdentityUtils.invokeMethodWithShellPermissions(context.packageManager,
+ ShellIdentityUtils.ShellPermissionMethodHelper<Unit, PackageManager> {
+ it.addUniquePreferredActivity(filter, matchingInfo.match, allComponents,
+ ComponentName(matchingInfo.activityInfo.packageName,
+ matchingInfo.activityInfo.name))
+ it.updateIntentVerificationStatusAsUser(params.packageName,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+ UserHandle.myUserId())
+ }, "android.permission.SET_PREFERRED_APPLICATIONS")
+ }
+
+ @Test
+ fun verifyPreviousReceivedSuccess() {
+ file.readTextIfExists()
+ .parseParams()
+ .forEach {
+ context.packageManager.verifyIntentFilter(it.id,
+ PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS, emptyList())
+ }
+ }
+
+ @Test
+ fun verifyPreviousReceivedFailure() {
+ file.readTextIfExists()
+ .parseParams()
+ .forEach {
+ context.packageManager.verifyIntentFilter(it.id,
+ PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, it.hosts)
+ }
+ }
+
+ @Test
+ fun verifyActivityStart() {
+ val params = StartActivityParams
+ .fromArgs(args.keySet().associateWith { args.getString(it)!! })
+ val uri = Uri.parse(params.uri)
+ val intent = Intent(Intent.ACTION_VIEW).apply {
+ data = uri
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+
+ val expectedActivities = params.expected.toMutableList()
+
+ if (params.withBrowsers) {
+ // Since the host doesn't know what browsers the device has, query here and add it to
+ // set if it's expected that browser are returned
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))
+ expectedActivities += context.packageManager.queryIntentActivities(browserIntent, 0)
+ .map { it.activityInfo.name }
+ }
+
+ val infos = context.packageManager.queryIntentActivities(intent, 0)
+ .map { it.activityInfo.name }
+ assertThat(infos).containsExactlyElementsIn(expectedActivities)
+ }
+
+ private fun File.readTextIfExists() = if (exists()) readText() else ""
+
+ // Rudimentary list deserialization by splitting text block into 4 line sections
+ private fun String.parseParams() = trim()
+ .lines()
+ .windowed(4, 4)
+ .map { it.joinToString(separator = "\n") }
+ .map { VerifyRequest.deserialize(it) }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
new file mode 100644
index 000000000000..7161fdd33516
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
@@ -0,0 +1,48 @@
+// 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.
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget1",
+ manifest: "AndroidManifest1.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget2",
+ manifest: "AndroidManifest2.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget3",
+ manifest: "AndroidManifest3.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4Base",
+ manifest: "AndroidManifest4Base.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4NoAutoVerify",
+ manifest: "AndroidManifest4NoAutoVerify.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4Wildcard",
+ manifest: "AndroidManifest4Wildcard.xml",
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify",
+ manifest: "AndroidManifest4WildcardNoAutoVerify.xml",
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml
new file mode 100644
index 000000000000..6cf5c7619a30
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.one" android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="verify.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="false">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="no_verify.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:host="http_only.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="https" />
+ <data android:host="https_only.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="htttps" />
+ <data android:host="non_http.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="https" />
+ <data android:scheme="non_web_scheme" />
+ <data android:host="https_plus_non_web_scheme.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".TargetActivity2" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="other_activity.pm.server.android.com" />
+ </intent-filter>
+
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="multiple.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml
new file mode 100644
index 000000000000..087ef70595f9
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.two"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:scheme="non_web_scheme" />
+ <data android:host="only_https_plus_non_web_scheme.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml
new file mode 100644
index 000000000000..eb75b5e53bc8
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.three"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:host="multiple.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml
new file mode 100644
index 000000000000..7eacb8bc8fb7
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml
new file mode 100644
index 000000000000..ecfee55b9c4a
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="false">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml
new file mode 100644
index 000000000000..0f0f53ba07e9
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="true">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ <data android:host="*.wildcard.tld" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml
new file mode 100644
index 000000000000..d5652e1b924d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.intent.verifier.target.four"
+ android:versionCode="1">
+
+ <application>
+ <activity android:name=".TargetActivity" android:exported="true">
+ <intent-filter android:autoVerify="false">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="failing.pm.server.android.com" />
+ <data android:host="*.wildcard.tld" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 2f5e8839504b..3220dff553d3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -48,6 +48,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
@@ -282,7 +283,7 @@ public class AppStateTrackerTest {
eq(ServiceType.FORCE_ALL_APPS_STANDBY),
powerSaveObserverCaptor.capture());
- verify(mMockContext).registerReceiver(
+ verify(mMockContext, times(2)).registerReceiver(
receiverCaptor.capture(), any(IntentFilter.class));
verify(mMockAppStandbyInternal).addListener(
appIdleStateChangeListenerCaptor.capture());
@@ -1242,6 +1243,67 @@ public class AppStateTrackerTest {
assertTrue(instance.isForceAllAppsStandbyEnabled());
}
+ @Test
+ public void testStateClearedOnPackageRemoved() throws Exception {
+ final AppStateTrackerTestable instance = newInstance();
+ callStart(instance);
+
+ instance.mActiveUids.put(UID_1, true);
+ instance.mForegroundUids.put(UID_2, true);
+ instance.mRunAnyRestrictedPackages.add(Pair.create(UID_1, PACKAGE_1));
+ instance.mExemptedBucketPackages.add(UserHandle.getUserId(UID_2), PACKAGE_2);
+
+ // Replace PACKAGE_1, nothing should change
+ Intent packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_1))
+ .putExtra(Intent.EXTRA_UID, UID_1)
+ .putExtra(Intent.EXTRA_REPLACING, true)
+ .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_1, null));
+ mReceiver.onReceive(mMockContext, packageRemoved);
+
+ assertEquals(1, instance.mActiveUids.size());
+ assertEquals(1, instance.mForegroundUids.size());
+ assertEquals(1, instance.mRunAnyRestrictedPackages.size());
+ assertEquals(1, instance.mExemptedBucketPackages.size());
+
+ // Replace PACKAGE_2, nothing should change
+ packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_2))
+ .putExtra(Intent.EXTRA_UID, UID_2)
+ .putExtra(Intent.EXTRA_REPLACING, true)
+ .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_2, null));
+ mReceiver.onReceive(mMockContext, packageRemoved);
+
+ assertEquals(1, instance.mActiveUids.size());
+ assertEquals(1, instance.mForegroundUids.size());
+ assertEquals(1, instance.mRunAnyRestrictedPackages.size());
+ assertEquals(1, instance.mExemptedBucketPackages.size());
+
+ // Remove PACKAGE_1
+ packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_1))
+ .putExtra(Intent.EXTRA_UID, UID_1)
+ .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_1, null));
+ mReceiver.onReceive(mMockContext, packageRemoved);
+
+ assertEquals(0, instance.mActiveUids.size());
+ assertEquals(1, instance.mForegroundUids.size());
+ assertEquals(0, instance.mRunAnyRestrictedPackages.size());
+ assertEquals(1, instance.mExemptedBucketPackages.size());
+
+ // Remove PACKAGE_2
+ packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_2))
+ .putExtra(Intent.EXTRA_UID, UID_2)
+ .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_2, null));
+ mReceiver.onReceive(mMockContext, packageRemoved);
+
+ assertEquals(0, instance.mActiveUids.size());
+ assertEquals(0, instance.mForegroundUids.size());
+ assertEquals(0, instance.mRunAnyRestrictedPackages.size());
+ assertEquals(0, instance.mExemptedBucketPackages.size());
+ }
+
static int[] array(int... appIds) {
Arrays.sort(appIds);
return appIds;
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 02d10e3b6ce1..b7a36f2eaed2 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -223,9 +223,24 @@ public class VibratorServiceTest {
}
@Test
- public void arePrimitivesSupported_withComposeCapability_returnsAlwaysTrue() {
+ public void arePrimitivesSupported_withNullResultFromNative_returnsAlwaysFalse() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- assertArrayEquals(new boolean[]{true, true},
+ when(mNativeWrapperMock.vibratorGetSupportedPrimitives()).thenReturn(null);
+
+ assertArrayEquals(new boolean[]{false, false},
+ createService().arePrimitivesSupported(new int[]{
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE
+ }));
+ }
+
+ @Test
+ public void arePrimitivesSupported_withSomeSupportedPrimitives_returnsBasedOnNativeResult() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ when(mNativeWrapperMock.vibratorGetSupportedPrimitives())
+ .thenReturn(new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
+
+ assertArrayEquals(new boolean[]{true, false},
createService().arePrimitivesSupported(new int[]{
VibrationEffect.Composition.PRIMITIVE_CLICK,
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index a5df53205a36..94cad1ed18b8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -269,21 +269,6 @@ public class AuthServiceTest {
eq(callback), eq(UserHandle.getCallingUserId()));
}
- @Test
- public void testResetLockout_callsBiometricServiceResetLockout() throws
- Exception {
- mAuthService = new AuthService(mContext, mInjector);
- mAuthService.onStart();
-
- final int userId = 100;
- final byte[] token = new byte[0];
-
- mAuthService.mImpl.resetLockout(userId, token);
-
- waitForIdle();
- verify(mBiometricService).resetLockout(eq(userId), AdditionalMatchers.aryEq(token));
- }
-
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index dad360d40515..36c55cce076b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -17,7 +17,6 @@
package com.android.server.biometrics.sensors;
import android.content.Context;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
@@ -56,8 +55,8 @@ public class BiometricSchedulerTest {
mScheduler.scheduleClientMonitor(client1);
mScheduler.scheduleClientMonitor(client2);
- client1.mFinishCallback.onClientFinished(client1, true /* success */);
- client1.mFinishCallback.onClientFinished(client1, true /* success */);
+ client1.mCallback.onClientFinished(client1, true /* success */);
+ client1.mCallback.onClientFinished(client1, true /* success */);
}
private static class TestClientMonitor extends ClientMonitor<Object> {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8fc228734f37..32afe8244eb6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -149,6 +149,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public static final String NOT_PROFILE_OWNER_MSG = "does not own the profile";
public static final String NOT_ORG_OWNED_PROFILE_OWNER_MSG =
"not the profile owner on organization-owned device";
+ public static final String INVALID_CALLING_IDENTITY_MSG = "Calling identity is not authorized";
public static final String ONGOING_CALL_MSG = "ongoing call on the device";
// TODO replace all instances of this with explicit {@link #mServiceContext}.
@@ -2404,13 +2405,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Set admin1 as DA.
dpm.setActiveAdmin(admin1, false);
assertTrue(dpm.isAdminActive(admin1));
- assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
- () -> dpm.reboot(admin1));
+ assertExpectException(SecurityException.class, /* messageRegex= */
+ INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1));
// Set admin1 as PO.
assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
- assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
- () -> dpm.reboot(admin1));
+ assertExpectException(SecurityException.class, /* messageRegex= */
+ INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1));
// Remove PO and add DO.
dpm.clearProfileOwner(admin1);
@@ -2623,7 +2624,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
UserHandle.myUserId(), UserManager.RESTRICTION_SOURCE_DEVICE_OWNER))
).when(getServices().userManager).getUserRestrictionSources(
eq(UserManager.DISALLOW_ADJUST_VOLUME),
- eq(UserHandle.getUserHandleForUid(UserHandle.myUserId())));
+ eq(UserHandle.of(UserHandle.myUserId())));
intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
assertNotNull(intent);
assertEquals(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS, intent.getAction());
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index f9343236662b..7b07102356f0 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -74,7 +74,7 @@ public class TunerResourceManagerServiceTest {
mReclaimed = true;
}
- public boolean isRelaimed() {
+ public boolean isReclaimed() {
return mReclaimed;
}
}
@@ -387,13 +387,13 @@ public class TunerResourceManagerServiceTest {
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isRelaimed()).isFalse();
+ assertThat(listener.isReclaimed()).isFalse();
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isRelaimed()).isFalse();
+ assertThat(listener.isReclaimed()).isFalse();
}
@Test
@@ -452,7 +452,7 @@ public class TunerResourceManagerServiceTest {
.getOwnerClientId()).isEqualTo(clientId1[0]);
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
.getOwnerClientId()).isEqualTo(clientId1[0]);
- assertThat(listener.isRelaimed()).isTrue();
+ assertThat(listener.isReclaimed()).isTrue();
}
@Test
@@ -486,7 +486,7 @@ public class TunerResourceManagerServiceTest {
// Release frontend
mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
- .getFrontendResource(frontendId));
+ .getFrontendResource(frontendId), clientId[0]);
assertThat(mTunerResourceManagerService
.getFrontendResource(frontendId).isInUse()).isFalse();
assertThat(mTunerResourceManagerService
@@ -548,7 +548,7 @@ public class TunerResourceManagerServiceTest {
assertThat(mTunerResourceManagerService.getCasResource(1)
.getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
- assertThat(listener.isRelaimed()).isTrue();
+ assertThat(listener.isReclaimed()).isTrue();
}
@Test
@@ -633,7 +633,7 @@ public class TunerResourceManagerServiceTest {
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0])
.getOwnerClientId()).isEqualTo(clientId1[0]);
- assertThat(listener.isRelaimed()).isTrue();
+ assertThat(listener.isReclaimed()).isTrue();
assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
.getInUseLnbIds().size()).isEqualTo(0);
}
@@ -761,4 +761,293 @@ public class TunerResourceManagerServiceTest {
backgroundRecordProfile)).isEqualTo(
(backgroundPlaybackPriority > backgroundRecordPriority));
}
+
+ @Test
+ public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() {
+ /**** Register Clients and Set Priority ****/
+
+ // Int array to save the returned client ids
+ int[] ownerClientId0 = new int[1];
+ int[] ownerClientId1 = new int[1];
+ int[] shareClientId0 = new int[1];
+ int[] shareClientId1 = new int[1];
+
+ // Predefined client profiles
+ ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2];
+ ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2];
+ ownerProfiles[0] = new ResourceClientProfile(
+ "0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
+ ownerProfiles[1] = new ResourceClientProfile(
+ "1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
+ shareProfiles[0] = new ResourceClientProfile(
+ "2" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
+ shareProfiles[1] = new ResourceClientProfile(
+ "3" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
+
+ // Predefined client reclaim listeners
+ TestResourcesReclaimListener ownerListener0 = new TestResourcesReclaimListener();
+ TestResourcesReclaimListener shareListener0 = new TestResourcesReclaimListener();
+ TestResourcesReclaimListener ownerListener1 = new TestResourcesReclaimListener();
+ TestResourcesReclaimListener shareListener1 = new TestResourcesReclaimListener();
+ // Register clients and validate the returned client ids
+ mTunerResourceManagerService
+ .registerClientProfileInternal(ownerProfiles[0], ownerListener0, ownerClientId0);
+ mTunerResourceManagerService
+ .registerClientProfileInternal(shareProfiles[0], shareListener0, shareClientId0);
+ mTunerResourceManagerService
+ .registerClientProfileInternal(ownerProfiles[1], ownerListener1, ownerClientId1);
+ mTunerResourceManagerService
+ .registerClientProfileInternal(shareProfiles[1], shareListener1, shareClientId1);
+ assertThat(ownerClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ assertThat(shareClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ assertThat(ownerClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ assertThat(shareClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ ownerClientId0[0],
+ 100/*priority*/,
+ 0/*niceValue*/);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ shareClientId0[0],
+ 200/*priority*/,
+ 0/*niceValue*/);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ ownerClientId1[0],
+ 300/*priority*/,
+ 0/*niceValue*/);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ shareClientId1[0],
+ 400/*priority*/,
+ 0/*niceValue*/);
+
+ /**** Init Frontend Resources ****/
+
+ // Predefined frontend info
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+ infos[0] = new TunerFrontendInfo(
+ 0 /*id*/,
+ FrontendSettings.TYPE_DVBT,
+ 1 /*exclusiveGroupId*/);
+ infos[1] = new TunerFrontendInfo(
+ 1 /*id*/,
+ FrontendSettings.TYPE_DVBS,
+ 1 /*exclusiveGroupId*/);
+
+ /**** Init Lnb Resources ****/
+ int[] lnbIds = {1};
+ mTunerResourceManagerService.setLnbInfoListInternal(lnbIds);
+
+ // Update frontend list in TRM
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ /**** Request Frontend ****/
+
+ // Predefined frontend request and array to save returned frontend handle
+ int[] frontendHandle = new int[1];
+ TunerFrontendRequest request = new TunerFrontendRequest(
+ ownerClientId0[0] /*clientId*/,
+ FrontendSettings.TYPE_DVBT);
+
+ // Request call and validate granted resource and internal mapping
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle))
+ .isTrue();
+ assertThat(mTunerResourceManagerService
+ .getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId0[0])
+ .getInUseFrontendIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ infos[0].getId(),
+ infos[1].getId())));
+
+ /**** Share Frontend ****/
+
+ // Share frontend call and validate the internal mapping
+ mTunerResourceManagerService.shareFrontendInternal(
+ shareClientId0[0]/*selfClientId*/,
+ ownerClientId0[0]/*targetClientId*/);
+ mTunerResourceManagerService.shareFrontendInternal(
+ shareClientId1[0]/*selfClientId*/,
+ ownerClientId0[0]/*targetClientId*/);
+ // Verify fe in use status
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+ .isInUse()).isTrue();
+ // Verify fe owner status
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+ .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+ .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+ // Verify share fe client status in the primary owner client
+ assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
+ .getShareFeClientIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ shareClientId0[0],
+ shareClientId1[0])));
+ // Verify in use frontend list in all the primary owner and share owner clients
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId0[0])
+ .getInUseFrontendIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ infos[0].getId(),
+ infos[1].getId())));
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(shareClientId0[0])
+ .getInUseFrontendIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ infos[0].getId(),
+ infos[1].getId())));
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(shareClientId1[0])
+ .getInUseFrontendIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ infos[0].getId(),
+ infos[1].getId())));
+
+ /**** Remove Frontend Share Owner ****/
+
+ // Unregister the second share fe client
+ mTunerResourceManagerService.unregisterClientProfileInternal(shareClientId1[0]);
+
+ // Validate the internal mapping
+ assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
+ .getShareFeClientIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ shareClientId0[0])));
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId0[0])
+ .getInUseFrontendIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ infos[0].getId(),
+ infos[1].getId())));
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(shareClientId0[0])
+ .getInUseFrontendIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ infos[0].getId(),
+ infos[1].getId())));
+
+ /**** Request Shared Frontend with Higher Priority Client ****/
+
+ // Predefined second frontend request
+ request = new TunerFrontendRequest(
+ ownerClientId1[0] /*clientId*/,
+ FrontendSettings.TYPE_DVBT);
+
+ // Second request call
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle))
+ .isTrue();
+
+ // Validate granted resource and internal mapping
+ assertThat(mTunerResourceManagerService
+ .getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+ .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+ .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId1[0])
+ .getInUseFrontendIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ infos[0].getId(),
+ infos[1].getId())));
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId0[0])
+ .getInUseFrontendIds()
+ .isEmpty())
+ .isTrue();
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(shareClientId0[0])
+ .getInUseFrontendIds()
+ .isEmpty())
+ .isTrue();
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId0[0])
+ .getShareFeClientIds()
+ .isEmpty())
+ .isTrue();
+ assertThat(ownerListener0.isReclaimed()).isTrue();
+ assertThat(shareListener0.isReclaimed()).isTrue();
+
+ /**** Release Frontend Resource From Primary Owner ****/
+
+ // Reshare the frontend
+ mTunerResourceManagerService.shareFrontendInternal(
+ shareClientId0[0]/*selfClientId*/,
+ ownerClientId1[0]/*targetClientId*/);
+
+ // Release the frontend resource from the primary owner
+ mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
+ .getFrontendResource(infos[0].getId()), ownerClientId1[0]);
+
+ // Validate the internal mapping
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+ .isInUse()).isFalse();
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+ .isInUse()).isFalse();
+ // Verify client status
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId1[0])
+ .getInUseFrontendIds()
+ .isEmpty())
+ .isTrue();
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(shareClientId0[0])
+ .getInUseFrontendIds()
+ .isEmpty())
+ .isTrue();
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(ownerClientId1[0])
+ .getShareFeClientIds()
+ .isEmpty())
+ .isTrue();
+
+ /**** Unregister Primary Owner when the Share owner owns an Lnb ****/
+
+ // Predefined Lnb request and handle array
+ TunerLnbRequest requestLnb = new TunerLnbRequest(shareClientId0[0]);
+ int[] lnbHandle = new int[1];
+
+ // Request for an Lnb
+ assertThat(mTunerResourceManagerService
+ .requestLnbInternal(requestLnb, lnbHandle))
+ .isTrue();
+
+ // Request and share the frontend resource again
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle))
+ .isTrue();
+ mTunerResourceManagerService.shareFrontendInternal(
+ shareClientId0[0]/*selfClientId*/,
+ ownerClientId1[0]/*targetClientId*/);
+
+ // Unregister the primary owner of the shared frontend
+ mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]);
+
+ // Validate the internal mapping
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+ .isInUse()).isFalse();
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+ .isInUse()).isFalse();
+ // Verify client status
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(shareClientId0[0])
+ .getInUseFrontendIds()
+ .isEmpty())
+ .isTrue();
+ assertThat(mTunerResourceManagerService
+ .getClientProfile(shareClientId0[0])
+ .getInUseLnbIds())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(
+ lnbIds[0])));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 46c3e22da38c..eb78172cd562 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.content.ComponentName.createRelative;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
@@ -176,7 +177,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
public void testOnActivityLaunchCancelled_hasDrawn() {
onActivityLaunched(mTopActivity);
- mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+ mTopActivity.mVisibleRequested = true;
+ doReturn(true).when(mTopActivity).isReportedDrawn();
// Cannot time already-visible activities.
notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
@@ -187,16 +189,14 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
- mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+ mTopActivity.mVisibleRequested = true;
+ doReturn(true).when(mTopActivity).isReportedDrawn();
- // Suppress resume when creating the record because we want to notify logger manually.
- mSupervisor.beginDeferResume();
// Create an activity with different process that meets process switch.
final ActivityRecord noDrawnActivity = new ActivityBuilder(mAtm)
.setTask(mTopActivity.getTask())
.setProcessName("other")
.build();
- mSupervisor.readyToResume();
notifyActivityLaunching(noDrawnActivity.intent);
notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
@@ -294,7 +294,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
public void testOnActivityLaunchCancelledTrampoline() {
onActivityLaunchedTrampoline();
- mTopActivity.mDrawn = true;
+ doReturn(true).when(mTopActivity).isReportedDrawn();
// Cannot time already-visible activities.
notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 1e95aba0c2d7..27e2d1370fae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -22,6 +22,7 @@ import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -38,13 +39,8 @@ import static org.mockito.ArgumentMatchers.eq;
import android.app.WaitResult;
import android.content.pm.ActivityInfo;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.media.ImageReader;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
-import android.view.DisplayInfo;
import androidx.test.filters.MediumTest;
@@ -180,53 +176,28 @@ public class ActivityStackSupervisorTests extends WindowTestsBase {
eq(true) /* focused */);
}
+ /**
+ * Ensures that a trusted display can launch arbitrary activity and an untrusted display can't.
+ */
@Test
- /** Ensures that a trusted virtual display can launch arbitrary activities. */
- public void testTrustedVirtualDisplayCanLaunchActivities() {
- final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- final Task stack = new StackBuilder(mRootWindowContainer)
- .setDisplay(newDisplay).build();
- final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
- VirtualDisplay virtualDisplay = createVirtualDisplay(true);
- final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
- virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
+ public void testDisplayCanLaunchActivities() {
+ final Display display = mDisplayContent.mDisplay;
+ // An empty info without FLAG_ALLOW_EMBEDDED.
+ final ActivityInfo activityInfo = new ActivityInfo();
+ final int callingPid = 12345;
+ final int callingUid = 12345;
+ spyOn(display);
- assertThat(allowed).isTrue();
+ doReturn(true).when(display).isTrusted();
+ final boolean allowedOnTrusted = mSupervisor.isCallerAllowedToLaunchOnDisplay(callingPid,
+ callingUid, display.getDisplayId(), activityInfo);
- virtualDisplay.release();
- }
+ assertThat(allowedOnTrusted).isTrue();
- @Test
- /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */
- public void testUntrustedVirtualDisplayCannotLaunchActivities() {
- final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- final Task stack = new StackBuilder(mRootWindowContainer)
- .setDisplay(newDisplay).build();
- final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
- VirtualDisplay virtualDisplay = createVirtualDisplay(false);
- final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
- virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
-
- assertThat(allowed).isFalse();
-
- virtualDisplay.release();
- }
+ doReturn(false).when(display).isTrusted();
+ final boolean allowedOnUntrusted = mSupervisor.isCallerAllowedToLaunchOnDisplay(callingPid,
+ callingUid, display.getDisplayId(), activityInfo);
- private VirtualDisplay createVirtualDisplay(boolean trusted) {
- final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
- final DisplayInfo displayInfo = new DisplayInfo();
- final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
- defaultDisplay.getDisplayInfo(displayInfo);
- int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
- if (trusted) {
- flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
- }
-
- final ImageReader imageReader = ImageReader.newInstance(
- displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
-
- return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth,
- displayInfo.logicalHeight,
- displayInfo.logicalDensityDpi, imageReader.getSurface(), flags);
+ assertThat(allowedOnUntrusted).isFalse();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 1ec9bd24ad59..b89d16807a6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -890,8 +890,8 @@ public class RootActivityContainerTests extends WindowTestsBase {
// Make sure the root task is valid and can be reused on default display.
final Task stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea(
- mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, null,
- null);
+ mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task,
+ null /* options */, null /* launchParams */);
assertEquals(task, stack);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 78556ef41edb..1e5d92b270d2 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -61,7 +61,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutServiceInternal;
-import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Environment;
@@ -306,25 +305,25 @@ public class UsageStatsService extends SystemService implements
@Override
public void onUserStopping(@NonNull TargetUser user) {
- final UserInfo userInfo = user.getUserInfo();
+ final int userId = user.getUserIdentifier();
synchronized (mLock) {
// User was started but never unlocked so no need to report a user stopped event
- if (!mUserUnlockedStates.get(userInfo.id)) {
- persistPendingEventsLocked(userInfo.id);
+ if (!mUserUnlockedStates.get(userId)) {
+ persistPendingEventsLocked(userId);
return;
}
// Report a user stopped event before persisting all stats to disk via the user service
final Event event = new Event(USER_STOPPED, SystemClock.elapsedRealtime());
event.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
- reportEvent(event, userInfo.id);
- final UserUsageStatsService userService = mUserState.get(userInfo.id);
+ reportEvent(event, userId);
+ final UserUsageStatsService userService = mUserState.get(userId);
if (userService != null) {
userService.userStopped();
}
- mUserUnlockedStates.put(userInfo.id, false);
- mUserState.put(userInfo.id, null); // release the service (mainly for GC)
+ mUserUnlockedStates.put(userId, false);
+ mUserState.put(userId, null); // release the service (mainly for GC)
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 0ea84da54b60..26d46dbd2ab7 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1276,7 +1276,8 @@ public class SoundTriggerService extends SystemService {
* @return The initialized AudioRecord
*/
private @NonNull AudioRecord createAudioRecordForEvent(
- @NonNull SoundTrigger.GenericRecognitionEvent event) {
+ @NonNull SoundTrigger.GenericRecognitionEvent event)
+ throws IllegalArgumentException, UnsupportedOperationException {
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder();
attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD);
AudioAttributes attributes = attributesBuilder.build();
@@ -1285,21 +1286,15 @@ public class SoundTriggerService extends SystemService {
sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent"));
- try {
- return (new AudioRecord.Builder())
- .setAudioAttributes(attributes)
- .setAudioFormat((new AudioFormat.Builder())
- .setChannelMask(originalFormat.getChannelMask())
- .setEncoding(originalFormat.getEncoding())
- .setSampleRate(originalFormat.getSampleRate())
- .build())
- .setSessionId(event.getCaptureSession())
- .build();
- } catch (IllegalArgumentException | UnsupportedOperationException e) {
- Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event
- + "), failed to create AudioRecord");
- return null;
- }
+ return (new AudioRecord.Builder())
+ .setAudioAttributes(attributes)
+ .setAudioFormat((new AudioFormat.Builder())
+ .setChannelMask(originalFormat.getChannelMask())
+ .setEncoding(originalFormat.getEncoding())
+ .setSampleRate(originalFormat.getSampleRate())
+ .build())
+ .setSessionId(event.getCaptureSession())
+ .build();
}
@Override
@@ -1325,13 +1320,13 @@ public class SoundTriggerService extends SystemService {
// execute if throttled:
() -> {
if (event.isCaptureAvailable()) {
- AudioRecord capturedData = createAudioRecordForEvent(event);
-
- // Currently we need to start and release the audio record to reset
- // the DSP even if we don't want to process the event
- if (capturedData != null) {
+ try {
+ AudioRecord capturedData = createAudioRecordForEvent(event);
capturedData.startRecording();
capturedData.release();
+ } catch (IllegalArgumentException | UnsupportedOperationException e) {
+ Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event
+ + "), failed to create AudioRecord");
}
}
}));
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 9ff44eba8fa0..a2215ceed36a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -164,13 +164,13 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- private boolean isSupported(UserInfo user) {
+ @Override
+ public boolean isUserSupported(@NonNull TargetUser user) {
return user.isFull();
}
- @Override
- public boolean isUserSupported(TargetUser user) {
- return isSupported(user.getUserInfo());
+ private boolean isUserSupported(@NonNull UserInfo user) {
+ return user.isFull();
}
@Override
@@ -461,7 +461,7 @@ public class VoiceInteractionManagerService extends SystemService {
private void setCurrentUserLocked(@UserIdInt int userHandle) {
mCurUser = userHandle;
final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser);
- mCurUserSupported = isSupported(userInfo);
+ mCurUserSupported = isUserSupported(userInfo);
}
public void switchUser(@UserIdInt int userHandle) {