From 2ba316f58e4429033caa495cbc22a0d66dd92d15 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 13 Aug 2021 13:37:55 -0700 Subject: [PATCH 01/13] StorageManagerService: don't ignore failures to prepare user storage We must never leave directories unencrypted. Bug: 164488924 Bug: 224585613 Change-Id: I9a38ab5cca1ae9c9ebff81fca04615fd83ebe4b2 (cherry picked from commit 50946dd15fd14cbf92b5c7e32ac7a0f088b8b302) Merged-In: I9a38ab5cca1ae9c9ebff81fca04615fd83ebe4b2 (cherry picked from commit 567e7a0476a3251d02705932268cd5f395ef863f) Merged-In: I9a38ab5cca1ae9c9ebff81fca04615fd83ebe4b2 --- .../core/java/com/android/server/StorageManagerService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8727932a87f70..546d008857bbd 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3400,8 +3400,12 @@ class StorageManagerService extends IStorageManager.Stub mInstaller.tryMountDataMirror(volumeUuid); } } - } catch (Exception e) { + } catch (RemoteException | Installer.InstallerException e) { Slog.wtf(TAG, e); + // Make sure to re-throw this exception; we must not ignore failure + // to prepare the user storage as it could indicate that encryption + // wasn't successfully set up. + throw new RuntimeException(e); } } From 6ba336c0e280183a04ca45b217a7e89f8419d62b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 24 Jan 2022 20:33:11 +0000 Subject: [PATCH 02/13] UserDataPreparer: reboot to recovery if preparing user storage fails StorageManager.prepareUserStorage() can throw an exception if a directory cannot be encrypted, for example due to already being nonempty. In this case, usage of the directory must not be allowed to proceed. UserDataPreparer currently handles this by deleting the user's directories, but the error is still ultimately suppressed and starting the user is still allowed to proceed. The correct behavior in this case is to reboot into recovery to ask the user to factory reset the device. This is already what happens when 'init' fails to encrypt a directory with the system DE policy. However, this was overlooked for the user directories. Start doing this. Bug: 164488924 Bug: 224585613 Change-Id: Ib5e91d2510b25780d7a161b91b5cee2f6f7a2e54 (cherry picked from commit 5256365e65882b81509ec2f6b9dfe2dcf0025254) Merged-In: Ib5e91d2510b25780d7a161b91b5cee2f6f7a2e54 (cherry picked from commit e1f17026ca80e43952fcc5d3a246615b711eba0a) Merged-In: Ib5e91d2510b25780d7a161b91b5cee2f6f7a2e54 --- .../core/java/com/android/server/pm/UserDataPreparer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index 045a295da965f..504769064808c 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.pm.UserInfo; import android.os.Environment; import android.os.FileUtils; +import android.os.RecoverySystem; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.os.SystemProperties; @@ -115,6 +116,13 @@ class UserDataPreparer { // Try one last time; if we fail again we're really in trouble prepareUserDataLI(volumeUuid, userId, userSerial, flags | StorageManager.FLAG_STORAGE_DE, false); + } else { + try { + Log.e(TAG, "prepareUserData failed", e); + RecoverySystem.rebootPromptAndWipeUserData(mContext, "prepareUserData failed"); + } catch (IOException e2) { + throw new RuntimeException("error rebooting into recovery", e2); + } } } } From 0067ba2426506ec7516dcb18bec5f8a68c116fe9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 4 Mar 2022 00:07:29 +0000 Subject: [PATCH 03/13] UserDataPreparer: reboot to recovery for system user only With the next CL, old devices might contain a combination of old users with prepareUserStorage error checking disabled and new users with prepareUserStorage error checking enabled. Factory resetting the whole device when any user fails to prepare may be too aggressive. Also, UserDataPreparer already destroys the affected user's storage when it fails to prepare, which seems to be fairly effective at breaking things for that user (absent proper error handling by upper layers). Therefore, let's only factory reset the device if the failing user is the system user. Bug: 164488924 Bug: 224585613 Change-Id: Ia1db01ab4ec6b3b17d725f391c3500d92aa00f97 (cherry picked from commit 4c76da76c9831266e4e63c0618150bed10a929a7) Merged-In: Ia1db01ab4ec6b3b17d725f391c3500d92aa00f97 (cherry picked from commit fd31f740ce5e7beb342e0e21b983de1100782bdb) Merged-In: Ia1db01ab4ec6b3b17d725f391c3500d92aa00f97 --- .../core/java/com/android/server/pm/UserDataPreparer.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index 504769064808c..95482d7c7f1aa 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -118,8 +118,11 @@ class UserDataPreparer { flags | StorageManager.FLAG_STORAGE_DE, false); } else { try { - Log.e(TAG, "prepareUserData failed", e); - RecoverySystem.rebootPromptAndWipeUserData(mContext, "prepareUserData failed"); + Log.wtf(TAG, "prepareUserData failed for user " + userId, e); + if (userId == UserHandle.USER_SYSTEM) { + RecoverySystem.rebootPromptAndWipeUserData(mContext, + "prepareUserData failed for system user"); + } } catch (IOException e2) { throw new RuntimeException("error rebooting into recovery", e2); } From 16cf824d3a3dab638698ffaa995621ae18cfcf4e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 4 Mar 2022 00:07:43 +0000 Subject: [PATCH 04/13] Ignore errors preparing user storage for existing users Unfortunately we can't rule out the existence of devices where the user storage wasn't properly prepared, due to StorageManagerService previously ignoring errors from mVold.prepareUserStorage, combined with OEMs potentially creating files in per-user directories too early. And forcing these broken devices to be factory reset upon taking an OTA is not currently considered to be acceptable. One option is to only check for prepareUserStorage errors on devices that launched with T or later. However, this is a serious issue and it would be strongly preferable to do more than that. Therefore, this CL makes it so that errors are checked for all new users, rather than all new devices. A field ignorePrepareStorageErrors is added to the user record; it is only ever set to true implicitly, when reading a user record from disk that lacks this field. This field is used by StorageManagerService to decide whether to check for errors. Bug: 164488924 Bug: 224585613 Test: Intentionally made a device affected by this issue by reverting the CLs that introduced the error checks, and changing vold to inject an error into prepareUserStorage. Then, flashed a build with this CL without wiping userdata. The device still boots, as expected, and the log shows that the error was intentionally ignored. Tested that if a second user is added, the error is *not* ignored and the second user's storage is destroyed before it can be used. Finally, wiped the device and verified that it won't boot up anymore, as expected since error checking is enabled for the system user in that case. Change-Id: I9bdd1a4bf5b14542adb901f264a91d489115c89b (cherry picked from commit 60d8318c47b7b659716d71243d087b34ab327f64) Merged-In: I9bdd1a4bf5b14542adb901f264a91d489115c89b (cherry picked from commit e667b604021e543436aabece21dc78d19fe9948e) Merged-In: I9bdd1a4bf5b14542adb901f264a91d489115c89b --- .../android/server/StorageManagerService.java | 11 ++++- .../server/pm/UserManagerInternal.java | 8 ++++ .../android/server/pm/UserManagerService.java | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 546d008857bbd..e5841f9e7a59f 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3400,11 +3400,20 @@ class StorageManagerService extends IStorageManager.Stub mInstaller.tryMountDataMirror(volumeUuid); } } - } catch (RemoteException | Installer.InstallerException e) { + } catch (Exception e) { Slog.wtf(TAG, e); // Make sure to re-throw this exception; we must not ignore failure // to prepare the user storage as it could indicate that encryption // wasn't successfully set up. + // + // Very unfortunately, these errors need to be ignored for broken + // users that already existed on-disk from older Android versions. + UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class); + if (umInternal.shouldIgnorePrepareStorageErrors(userId)) { + Slog.wtf(TAG, "ignoring error preparing storage for existing user " + userId + + "; device may be insecure!"); + return; + } throw new RuntimeException(e); } } diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index eb2de60127451..0e6d5e5ed4639 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -312,4 +312,12 @@ public abstract class UserManagerInternal { */ public abstract void setDefaultCrossProfileIntentFilters( @UserIdInt int parentUserId, @UserIdInt int profileUserId); + + /** + * Returns {@code true} if the system should ignore errors when preparing + * the storage directories for the user with ID {@code userId}. This will + * return {@code false} for all new users; it will only return {@code true} + * for users that already existed on-disk from an older version of Android. + */ + public abstract boolean shouldIgnorePrepareStorageErrors(int userId); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 6d8137e740613..09c6b50571d42 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -204,6 +204,8 @@ public class UserManagerService extends IUserManager.Stub { private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions"; private static final String TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL = "lastRequestQuietModeEnabledCall"; + private static final String TAG_IGNORE_PREPARE_STORAGE_ERRORS = + "ignorePrepareStorageErrors"; private static final String ATTR_KEY = "key"; private static final String ATTR_VALUE_TYPE = "type"; private static final String ATTR_MULTIPLE = "m"; @@ -313,6 +315,14 @@ public class UserManagerService extends IUserManager.Stub { private long mLastRequestQuietModeEnabledMillis; + /** + * {@code true} if the system should ignore errors when preparing the + * storage directories for this user. This is {@code false} for all new + * users; it will only be {@code true} for users that already existed + * on-disk from an older version of Android. + */ + private boolean mIgnorePrepareStorageErrors; + void setLastRequestQuietModeEnabledMillis(long millis) { mLastRequestQuietModeEnabledMillis = millis; } @@ -321,6 +331,14 @@ public class UserManagerService extends IUserManager.Stub { return mLastRequestQuietModeEnabledMillis; } + boolean getIgnorePrepareStorageErrors() { + return mIgnorePrepareStorageErrors; + } + + void setIgnorePrepareStorageErrors() { + mIgnorePrepareStorageErrors = true; + } + void clearSeedAccountData() { seedAccountName = null; seedAccountType = null; @@ -3177,6 +3195,10 @@ public class UserManagerService extends IUserManager.Stub { serializer.endTag(/* namespace */ null, TAG_LAST_REQUEST_QUIET_MODE_ENABLED_CALL); } + serializer.startTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS); + serializer.text(String.valueOf(userData.getIgnorePrepareStorageErrors())); + serializer.endTag(/* namespace */ null, TAG_IGNORE_PREPARE_STORAGE_ERRORS); + serializer.endTag(null, TAG_USER); serializer.endDocument(); @@ -3286,6 +3308,7 @@ public class UserManagerService extends IUserManager.Stub { Bundle legacyLocalRestrictions = null; RestrictionsSet localRestrictions = null; Bundle globalRestrictions = null; + boolean ignorePrepareStorageErrors = true; // default is true for old users final TypedXmlPullParser parser = Xml.resolvePullParser(is); int type; @@ -3364,6 +3387,11 @@ public class UserManagerService extends IUserManager.Stub { if (type == XmlPullParser.TEXT) { lastRequestQuietModeEnabledTimestamp = Long.parseLong(parser.getText()); } + } else if (TAG_IGNORE_PREPARE_STORAGE_ERRORS.equals(tag)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + ignorePrepareStorageErrors = Boolean.parseBoolean(parser.getText()); + } } } } @@ -3391,6 +3419,9 @@ public class UserManagerService extends IUserManager.Stub { userData.persistSeedData = persistSeedData; userData.seedAccountOptions = seedAccountOptions; userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp); + if (ignorePrepareStorageErrors) { + userData.setIgnorePrepareStorageErrors(); + } synchronized (mRestrictionsLock) { if (baseRestrictions != null) { @@ -5232,6 +5263,9 @@ public class UserManagerService extends IUserManager.Stub { pw.println(); } } + + pw.println(" Ignore errors preparing storage: " + + userData.getIgnorePrepareStorageErrors()); } } @@ -5721,6 +5755,14 @@ public class UserManagerService extends IUserManager.Stub { UserManagerService.this.setDefaultCrossProfileIntentFilters( profileUserId, userTypeDetails, restrictions, parentUserId); } + + @Override + public boolean shouldIgnorePrepareStorageErrors(int userId) { + synchronized (mUsersLock) { + UserData userData = mUsers.get(userId); + return userData != null && userData.getIgnorePrepareStorageErrors(); + } + } } /** From 187187a31441e44c29d13c1a04c932abc420b709 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 26 Mar 2022 01:08:07 +0000 Subject: [PATCH 05/13] Log to EventLog on prepareUserStorage failure Bug: 224585613 Change-Id: Id6dfb4f4c48d5cf4e71f54bdb6d0d6eea527caf5 Merged-In: Id6dfb4f4c48d5cf4e71f54bdb6d0d6eea527caf5 (cherry picked from commit 7d4ab9b698a83f131585f25662ac9211302a3400) Merged-In: Id6dfb4f4c48d5cf4e71f54bdb6d0d6eea527caf5 --- .../core/java/com/android/server/StorageManagerService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index e5841f9e7a59f..6fc01bd64688b 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -128,6 +128,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.DataUnit; +import android.util.EventLog; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -3401,6 +3402,7 @@ class StorageManagerService extends IStorageManager.Stub } } } catch (Exception e) { + EventLog.writeEvent(0x534e4554, "224585613", -1, ""); Slog.wtf(TAG, e); // Make sure to re-throw this exception; we must not ignore failure // to prepare the user storage as it could indicate that encryption From 8a5bbe3094120c9fec151cf952b51a205a8dbed9 Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Fri, 18 Mar 2022 23:56:35 +0000 Subject: [PATCH 06/13] [DO NOT MERGE] Do not clear calling identify when using BiometricPrompt from FingerprintService. Bug: 214261879 Test: atest AuthControllerTest Test: Manually verify with test apps in bug Change-Id: I8ae9f2b8a970bf7e5d32121dc358f7d0f0d060b8 (cherry picked from commit 2d60fb3647a838c4c7f10c6f98bf98be4508b119) Merged-In: I8ae9f2b8a970bf7e5d32121dc358f7d0f0d060b8 --- .../hardware/biometrics/BiometricPrompt.java | 38 ++++++-- .../biometrics/ITestSessionCallback.aidl | 2 +- .../hardware/biometrics/PromptInfo.java | 22 ++++- .../systemui/biometrics/AuthController.java | 10 ++- .../biometrics/AuthControllerTest.java | 32 +++++-- .../sensors/AuthenticationClient.java | 87 ++++++++++--------- .../fingerprint/FingerprintService.java | 22 ++--- 7 files changed, 143 insertions(+), 70 deletions(-) diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 6b5bec99e6741..520234bcf0506 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -420,6 +420,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan return this; } + /** + * Set if BiometricPrompt is being used by the legacy fingerprint manager API. + * @param sensorId sensor id + * @return This builder. + * @hide + */ + @NonNull + public Builder setIsForLegacyFingerprintManager(int sensorId) { + mPromptInfo.setIsForLegacyFingerprintManager(sensorId); + return this; + } + /** * Creates a {@link BiometricPrompt}. * @@ -861,28 +873,36 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { - authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */); + if (cancel == null) { + throw new IllegalArgumentException("Must supply a cancellation signal"); + } + if (executor == null) { + throw new IllegalArgumentException("Must supply an executor"); + } + if (callback == null) { + throw new IllegalArgumentException("Must supply a callback"); + } + + authenticateInternal(0 /* operationId */, cancel, executor, callback, userId); } /** - * Authenticates for the given user and keystore operation. + * Authenticates for the given keystore operation. * * @param cancel An object that can be used to cancel authentication * @param executor An executor to handle callback events * @param callback An object to receive authentication events - * @param userId The user to authenticate * @param operationId The keystore operation associated with authentication * * @return A requestId that can be used to cancel this operation. * * @hide */ - @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public long authenticateUserForOperation( + @RequiresPermission(USE_BIOMETRIC) + public long authenticateForOperation( @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, - int userId, long operationId) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); @@ -894,7 +914,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan throw new IllegalArgumentException("Must supply a callback"); } - return authenticateInternal(operationId, cancel, executor, callback, userId); + return authenticateInternal(operationId, cancel, executor, callback, mContext.getUserId()); } /** @@ -1028,7 +1048,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private void cancelAuthentication(long requestId) { if (mService != null) { try { - mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId); + mService.cancelAuthentication(mToken, mContext.getPackageName(), requestId); } catch (RemoteException e) { Log.e(TAG, "Unable to cancel authentication", e); } @@ -1087,7 +1107,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } final long authId = mService.authenticate(mToken, operationId, userId, - mBiometricServiceReceiver, mContext.getOpPackageName(), promptInfo); + mBiometricServiceReceiver, mContext.getPackageName(), promptInfo); cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); return authId; } catch (RemoteException e) { diff --git a/core/java/android/hardware/biometrics/ITestSessionCallback.aidl b/core/java/android/hardware/biometrics/ITestSessionCallback.aidl index 3d9517f29548f..b336a9f21b60a 100644 --- a/core/java/android/hardware/biometrics/ITestSessionCallback.aidl +++ b/core/java/android/hardware/biometrics/ITestSessionCallback.aidl @@ -19,7 +19,7 @@ package android.hardware.biometrics; * ITestSession callback for FingerprintManager and BiometricManager. * @hide */ -interface ITestSessionCallback { +oneway interface ITestSessionCallback { void onCleanupStarted(int userId); void onCleanupFinished(int userId); } diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index e6b762a64384d..2742f0effde6d 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -46,6 +46,7 @@ public class PromptInfo implements Parcelable { @NonNull private List mAllowedSensorIds = new ArrayList<>(); private boolean mAllowBackgroundAuthentication; private boolean mIgnoreEnrollmentState; + private boolean mIsForLegacyFingerprintManager = false; public PromptInfo() { @@ -68,6 +69,7 @@ public class PromptInfo implements Parcelable { mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader()); mAllowBackgroundAuthentication = in.readBoolean(); mIgnoreEnrollmentState = in.readBoolean(); + mIsForLegacyFingerprintManager = in.readBoolean(); } public static final Creator CREATOR = new Creator() { @@ -105,10 +107,15 @@ public class PromptInfo implements Parcelable { dest.writeList(mAllowedSensorIds); dest.writeBoolean(mAllowBackgroundAuthentication); dest.writeBoolean(mIgnoreEnrollmentState); + dest.writeBoolean(mIsForLegacyFingerprintManager); } public boolean containsTestConfigurations() { - if (!mAllowedSensorIds.isEmpty()) { + if (mIsForLegacyFingerprintManager + && mAllowedSensorIds.size() == 1 + && !mAllowBackgroundAuthentication) { + return false; + } else if (!mAllowedSensorIds.isEmpty()) { return true; } else if (mAllowBackgroundAuthentication) { return true; @@ -188,7 +195,8 @@ public class PromptInfo implements Parcelable { } public void setAllowedSensorIds(@NonNull List sensorIds) { - mAllowedSensorIds = sensorIds; + mAllowedSensorIds.clear(); + mAllowedSensorIds.addAll(sensorIds); } public void setAllowBackgroundAuthentication(boolean allow) { @@ -199,6 +207,12 @@ public class PromptInfo implements Parcelable { mIgnoreEnrollmentState = ignoreEnrollmentState; } + public void setIsForLegacyFingerprintManager(int sensorId) { + mIsForLegacyFingerprintManager = true; + mAllowedSensorIds.clear(); + mAllowedSensorIds.add(sensorId); + } + // Getters public CharSequence getTitle() { @@ -272,4 +286,8 @@ public class PromptInfo implements Parcelable { public boolean isIgnoreEnrollmentState() { return mIgnoreEnrollmentState; } + + public boolean isForLegacyFingerprintManager() { + return mIsForLegacyFingerprintManager; + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index df20b83a36ca0..7ab214e94230d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -134,7 +134,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private class BiometricTaskStackListener extends TaskStackListener { @Override public void onTaskStackChanged() { - mHandler.post(AuthController.this::handleTaskStackChanged); + mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground); } } @@ -181,7 +181,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } }; - private void handleTaskStackChanged() { + private void cancelIfOwnerIsNotInForeground() { mExecution.assertIsMainThread(); if (mCurrentDialog != null) { try { @@ -193,7 +193,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, final String topPackage = runningTasks.get(0).topActivity.getPackageName(); if (!topPackage.contentEquals(clientPackage) && !Utils.isSystem(mContext, clientPackage)) { - Log.w(TAG, "Evicting client due to: " + topPackage); + Log.e(TAG, "Evicting client due to: " + topPackage); mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog = null; mOrientationListener.disable(); @@ -814,6 +814,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCurrentDialog = newDialog; mCurrentDialog.show(mWindowManager, savedState); mOrientationListener.enable(); + + if (!promptInfo.isAllowBackgroundAuthentication()) { + mHandler.post(this::cancelIfOwnerIsNotInForeground); + } } private void onDialogDismissed(@DismissedReason int reason) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 08c77146d34c6..2b7c984f04b12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -554,16 +554,26 @@ public class AuthControllerTest extends SysuiTestCase { mAuthController.mLastBiometricPromptInfo.getAuthenticators()); } + @Test + public void testClientNotified_whenTaskStackChangesDuringShow() throws Exception { + switchTask("other_package"); + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + + mTestableLooper.processAllMessages(); + + assertNull(mAuthController.mCurrentDialog); + assertNull(mAuthController.mReceiver); + verify(mDialog1).dismissWithoutCallback(true /* animate */); + verify(mReceiver).onDialogDismissed( + eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), + eq(null) /* credentialAttestation */); + } + @Test public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - List tasks = new ArrayList<>(); - ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); - taskInfo.topActivity = mock(ComponentName.class); - when(taskInfo.topActivity.getPackageName()).thenReturn("other_package"); - tasks.add(taskInfo); - when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); + switchTask("other_package"); mAuthController.mTaskStackListener.onTaskStackChanged(); mTestableLooper.processAllMessages(); @@ -640,6 +650,16 @@ public class AuthControllerTest extends SysuiTestCase { BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT); } + private void switchTask(String packageName) { + final List tasks = new ArrayList<>(); + final ActivityManager.RunningTaskInfo taskInfo = + mock(ActivityManager.RunningTaskInfo.class); + taskInfo.topActivity = mock(ComponentName.class); + when(taskInfo.topActivity.getPackageName()).thenReturn(packageName); + tasks.add(taskInfo); + when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); + } + private PromptInfo createTestPromptInfo() { PromptInfo promptInfo = new PromptInfo(); 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 358263df916b3..92c8c9bb57ec3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -118,7 +118,7 @@ public abstract class AuthenticationClient extends AcquisitionClient mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; mRequireConfirmation = requireConfirmation; - mActivityTaskManager = ActivityTaskManager.getInstance(); + mActivityTaskManager = getActivityTaskManager(); mBiometricManager = context.getSystemService(BiometricManager.class); mTaskStackListener = taskStackListener; mLockoutTracker = lockoutTracker; @@ -146,6 +146,10 @@ public abstract class AuthenticationClient extends AcquisitionClient return mStartTimeMs; } + protected ActivityTaskManager getActivityTaskManager() { + return ActivityTaskManager.getInstance(); + } + @Override public void binderDied() { final boolean clearListener = !isBiometricPrompt(); @@ -322,45 +326,50 @@ public abstract class AuthenticationClient extends AcquisitionClient sendCancelOnly(listener); } }); - } else { - // Allow system-defined limit of number of attempts before giving up - final @LockoutTracker.LockoutMode int lockoutMode = - handleFailedAttempt(getTargetUserId()); - if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { - markAlreadyDone(); + } else { // not authenticated + if (isBackgroundAuth) { + Slog.e(TAG, "cancelling due to background auth"); + cancel(); + } else { + // Allow system-defined limit of number of attempts before giving up + final @LockoutTracker.LockoutMode int lockoutMode = + handleFailedAttempt(getTargetUserId()); + if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { + markAlreadyDone(); + } + + final CoexCoordinator coordinator = CoexCoordinator.getInstance(); + coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode, + new CoexCoordinator.Callback() { + @Override + public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { + if (listener != null) { + try { + listener.onAuthenticationFailed(getSensorId()); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify listener", e); + } + } + } + + @Override + public void sendHapticFeedback() { + if (listener != null && mShouldVibrate) { + vibrateError(); + } + } + + @Override + public void handleLifecycleAfterAuth() { + AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */); + } + + @Override + public void sendAuthenticationCanceled() { + sendCancelOnly(listener); + } + }); } - - final CoexCoordinator coordinator = CoexCoordinator.getInstance(); - coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode, - new CoexCoordinator.Callback() { - @Override - public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { - if (listener != null) { - try { - listener.onAuthenticationFailed(getSensorId()); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to notify listener", e); - } - } - } - - @Override - public void sendHapticFeedback() { - if (listener != null && mShouldVibrate) { - vibrateError(); - } - } - - @Override - public void handleLifecycleAfterAuth() { - AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */); - } - - @Override - public void sendAuthenticationCanceled() { - sendCancelOnly(listener); - } - }); } } 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 b44f4dc682745..3a93d82a68eec 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 @@ -331,12 +331,12 @@ public class FingerprintService extends SystemService { provider.second.getSensorProperties(sensorId); if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName) && sensorProps != null && sensorProps.isAnyUdfpsType()) { - identity = Binder.clearCallingIdentity(); try { return authenticateWithPrompt(operationId, sensorProps, userId, receiver, - ignoreEnrollmentState); - } finally { - Binder.restoreCallingIdentity(identity); + opPackageName, ignoreEnrollmentState); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Invalid package", e); + return -1; } } return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, @@ -349,12 +349,15 @@ public class FingerprintService extends SystemService { @NonNull final FingerprintSensorPropertiesInternal props, final int userId, final IFingerprintServiceReceiver receiver, - boolean ignoreEnrollmentState) { + final String opPackageName, + boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException { final Context context = getUiContext(); + final Context promptContext = context.createPackageContextAsUser( + opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(userId)); final Executor executor = context.getMainExecutor(); - final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context) + final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(promptContext) .setTitle(context.getString(R.string.biometric_dialog_default_title)) .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle)) .setNegativeButton( @@ -368,8 +371,7 @@ public class FingerprintService extends SystemService { Slog.e(TAG, "Remote exception in negative button onClick()", e); } }) - .setAllowedSensorIds(new ArrayList<>( - Collections.singletonList(props.sensorId))) + .setIsForLegacyFingerprintManager(props.sensorId) .setIgnoreEnrollmentState(ignoreEnrollmentState) .build(); @@ -423,8 +425,8 @@ public class FingerprintService extends SystemService { } }; - return biometricPrompt.authenticateUserForOperation( - new CancellationSignal(), executor, promptCallback, userId, operationId); + return biometricPrompt.authenticateForOperation( + new CancellationSignal(), executor, promptCallback, operationId); } @Override From 823df81dfb7ce0aced020b48a77849d38872b751 Mon Sep 17 00:00:00 2001 From: Wenhao Wang Date: Wed, 2 Feb 2022 10:56:44 -0800 Subject: [PATCH 07/13] DO NOT MERGE Suppress notifications when device enter lockdown This CL makes the following modifcations: 1. Add LockPatternUtils.StrongAuthTracker to monitor the lockdown mode status of the phone. 2. Call mListeners.notifyRemovedLocked with all the notifications in the mNotificationList when entering the lockdown mode. 3. Call mListeners.notifyPostedLocked with all the notifications in the mNotificationList when exiting the lockdown mode. 4. Dismiss the function calls of notifyPostedLocked, notifyRemovedLocked, and notifyRankingUpdateLocked during the lockdown mode. The CL also adds corresponding tests. Bug: 173721373 Test: atest NotificationManagerServiceTest Test: atest NotificationListenersTest Test: manually verify the paired device cannot receive notifications when the host phone is in lockdown mode. Ignore-AOSP-First: pending fix for a security issue. Change-Id: I7e83544863eeadf8272b6ff8a9bb8136d6466203 Merged-In: I7e83544863eeadf8272b6ff8a9bb8136d6466203 (cherry picked from commit 3cb6842a053e236cc98d7616ba4433c31ffda3ac) (cherry picked from commit 62845d9af7d9695d1019bd6d8b0ccb223ace52d3) Merged-In: I7e83544863eeadf8272b6ff8a9bb8136d6466203 --- .../NotificationManagerService.java | 99 ++++++++++++++++++- .../tests/uiservicestests/AndroidManifest.xml | 1 + .../NotificationListenersTest.java | 74 +++++++++++++- .../NotificationManagerServiceTest.java | 65 +++++++++++- 4 files changed, 232 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0fda3a36b8e90..3c2631904f42a 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -246,6 +246,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.StatsEvent; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -277,6 +278,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; +import com.android.internal.widget.LockPatternUtils; import com.android.server.DeviceIdleInternal; import com.android.server.EventLogTags; import com.android.server.IoThread; @@ -1888,6 +1890,54 @@ public class NotificationManagerService extends SystemService { private SettingsObserver mSettingsObserver; protected ZenModeHelper mZenModeHelper; + protected class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { + + SparseBooleanArray mUserInLockDownMode = new SparseBooleanArray(); + boolean mIsInLockDownMode = false; + + StrongAuthTracker(Context context) { + super(context); + } + + private boolean containsFlag(int haystack, int needle) { + return (haystack & needle) != 0; + } + + public boolean isInLockDownMode() { + return mIsInLockDownMode; + } + + @Override + public synchronized void onStrongAuthRequiredChanged(int userId) { + boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mUserInLockDownMode.put(userId, userInLockDownModeNext); + boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; + + if (mIsInLockDownMode == isInLockDownModeNext) { + return; + } + + if (isInLockDownModeNext) { + cancelNotificationsWhenEnterLockDownMode(); + } + + // When the mIsInLockDownMode is true, both notifyPostedLocked and + // notifyRemovedLocked will be dismissed. So we shall call + // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode + // as true and call postNotificationsWhenExitLockDownMode after we set + // mIsInLockDownMode as false. + mIsInLockDownMode = isInLockDownModeNext; + + if (!isInLockDownModeNext) { + postNotificationsWhenExitLockDownMode(); + } + } + } + + private LockPatternUtils mLockPatternUtils; + private StrongAuthTracker mStrongAuthTracker; + public NotificationManagerService(Context context) { this(context, new NotificationRecordLoggerImpl(), @@ -1910,6 +1960,11 @@ public class NotificationManagerService extends SystemService { mAudioManager = audioMananger; } + @VisibleForTesting + void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) { + mStrongAuthTracker = strongAuthTracker; + } + @VisibleForTesting void setKeyguardManager(KeyguardManager keyguardManager) { mKeyguardManager = keyguardManager; @@ -2097,6 +2152,8 @@ public class NotificationManagerService extends SystemService { ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); + mLockPatternUtils = new LockPatternUtils(getContext()); + mStrongAuthTracker = new StrongAuthTracker(getContext()); String[] extractorNames; try { extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); @@ -2572,6 +2629,7 @@ public class NotificationManagerService extends SystemService { bubbsExtractor.setShortcutHelper(mShortcutHelper); } registerNotificationPreferencesPullers(); + mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to // bind to listener services. @@ -9117,6 +9175,29 @@ public class NotificationManagerService extends SystemService { } } + private void cancelNotificationsWhenEnterLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, + rec.getStats()); + } + + } + } + + private void postNotificationsWhenExitLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyPostedLocked(rec, rec); + } + + } + } + private void updateNotificationPulse() { synchronized (mNotificationLock) { updateLightsLocked(); @@ -9352,6 +9433,10 @@ public class NotificationManagerService extends SystemService { rankings.toArray(new NotificationListenerService.Ranking[0])); } + boolean isInLockDownMode() { + return mStrongAuthTracker.isInLockDownMode(); + } + boolean hasCompanionDevice(ManagedServiceInfo info) { if (mCompanionManager == null) { mCompanionManager = getCompanionManager(); @@ -10403,8 +10488,12 @@ public class NotificationManagerService extends SystemService { * targetting <= O_MR1 */ @GuardedBy("mNotificationLock") - private void notifyPostedLocked(NotificationRecord r, NotificationRecord old, + void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { + if (isInLockDownMode()) { + return; + } + try { // Lazily initialized snapshots of the notification. StatusBarNotification sbn = r.getSbn(); @@ -10502,6 +10591,10 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { + if (isInLockDownMode()) { + return; + } + final StatusBarNotification sbn = r.getSbn(); // make a copy in case changes are made to the underlying Notification object @@ -10547,6 +10640,10 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List changedHiddenNotifications) { + if (isInLockDownMode()) { + return; + } + boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; // TODO (b/73052211): if the ranking update changed the notification type, diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index 767857bf2de8b..e8e3a8f84f218 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -33,6 +33,7 @@ + diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index 7c0f29dce1abb..eb9847f7eb7e6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -24,15 +24,14 @@ import static com.android.server.notification.NotificationManagerService.Notific import static com.google.common.truth.Truth.assertThat; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -47,10 +46,11 @@ import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationStats; +import android.service.notification.StatusBarNotification; import android.testing.TestableContext; import android.util.ArraySet; import android.util.Pair; -import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -61,11 +61,13 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.FieldSetter; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.List; public class NotificationListenersTest extends UiServiceTestCase { @@ -374,4 +376,66 @@ public class NotificationListenersTest extends UiServiceTestCase { verify(mContext).sendBroadcastAsUser( any(), eq(UserHandle.of(userId)), nullable(String.class)); } + + @Test + public void testNotifyPostedLockedInLockdownMode() { + NotificationRecord r = mock(NotificationRecord.class); + NotificationRecord old = mock(NotificationRecord.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(old); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(r, never()).getSbn(); + } + + @Test + public void testnotifyRankingUpdateLockedInLockdownMode() { + List chn = mock(List.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, atLeast(1)).size(); + + // in the lockdown mode + reset(chn); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, never()).size(); + } + + @Test + public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { + NotificationRecord r = mock(NotificationRecord.class); + NotificationStats rs = mock(NotificationStats.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); + FieldSetter.setField(mNm, + NotificationManagerService.class.getDeclaredField("mHandler"), + mock(NotificationManagerService.WorkerHandler.class)); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(rs); + when(mNm.isInLockDownMode()).thenReturn(true); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, never()).getSbn(); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index b98401e76cc20..9a221a8f2bf9b 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -58,10 +58,13 @@ import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; +import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; @@ -223,7 +226,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -409,8 +411,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { interface NotificationAssistantAccessGrantedCallback { void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet); } + + class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { + private int mGetStrongAuthForUserReturnValue = 0; + StrongAuthTrackerFake(Context context) { + super(context); + } + + public void setGetStrongAuthForUserReturnValue(int val) { + mGetStrongAuthForUserReturnValue = val; + } + + @Override + public int getStrongAuthForUser(int userId) { + return mGetStrongAuthForUserReturnValue; + } + } } + TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; + private class TestableToastCallback extends ITransientNotification.Stub { @Override public void show(IBinder windowToken) { @@ -530,6 +550,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setAudioManager(mAudioManager); + mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext); + mService.setStrongAuthTracker(mStrongAuthTracker); + mShortcutHelper = mService.getShortcutHelper(); mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); @@ -8354,4 +8377,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } } } + + @Test + public void testStrongAuthTracker_isInLockDownMode() { + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertFalse(mStrongAuthTracker.isInLockDownMode()); + } + + @Test + public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { + // post 2 notifications from 2 packages + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("b", 1001, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgB); + + // when entering the lockdown mode, cancel the 2 notifications. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + + // the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN. + ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); + verify(mListeners, times(2)).notifyRemovedLocked(any(), captor.capture(), any()); + assertEquals(REASON_CANCEL_ALL, captor.getValue().intValue()); + + // exit lockdown mode. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + + // the notifyPostedLocked function is called twice. + verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + } } From 4bc03ff22aff9fac1fb2c27a22d973868c171507 Mon Sep 17 00:00:00 2001 From: James Wei Date: Tue, 5 Jan 2021 14:09:40 +0530 Subject: [PATCH 08/13] USB: Increase debounce time for DISCONNECT processing (revised) Original patch: aosp/1539944 Bug: 207057578 Test: See details in b/209342433 Change-Id: I7ff58a1a9755939ccb26dad61969902ec91f2225 Signed-off-by: James Wei (cherry picked from commit 06626cef7a9bc8a437aeb912a09484b2b0c922dc) Merged-In: I7ff58a1a9755939ccb26dad61969902ec91f2225 (cherry picked from commit 615d5d653e036cbb6e09b0be35bdd6520aed9017) Merged-In: I7ff58a1a9755939ccb26dad61969902ec91f2225 --- .../com/android/server/usb/UsbDeviceManager.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 661dcbb7f489c..9f31647e038e1 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -175,7 +175,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser // Delay for debouncing USB disconnects. // We often get rapid connect/disconnect events when enabling USB functions, // which need debouncing. - private static final int UPDATE_DELAY = 1000; + private static final int DEVICE_STATE_UPDATE_DELAY_EXT = 3000; + private static final int DEVICE_STATE_UPDATE_DELAY = 1000; + + // Delay for debouncing USB disconnects on Type-C ports in host mode + private static final int HOST_STATE_UPDATE_DELAY = 1000; // Timeout for entering USB request mode. // Request is cancelled if host does not configure device within 10 seconds. @@ -636,7 +640,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser msg.arg1 = connected; msg.arg2 = configured; // debounce disconnects to avoid problems bringing up USB tethering - sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0); + sendMessageDelayed(msg, + (connected == 0) ? (mScreenLocked ? DEVICE_STATE_UPDATE_DELAY + : DEVICE_STATE_UPDATE_DELAY_EXT) : 0); } public void updateHostState(UsbPort port, UsbPortStatus status) { @@ -651,7 +657,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser removeMessages(MSG_UPDATE_PORT_STATE); Message msg = obtainMessage(MSG_UPDATE_PORT_STATE, args); // debounce rapid transitions of connect/disconnect on type-c ports - sendMessageDelayed(msg, UPDATE_DELAY); + sendMessageDelayed(msg, HOST_STATE_UPDATE_DELAY); } private void setAdbEnabled(boolean enable) { From a3bf26d6bc71d61566eabb842b6f420c3d5fcfb6 Mon Sep 17 00:00:00 2001 From: Evan Severson Date: Thu, 5 May 2022 23:19:43 -0700 Subject: [PATCH 09/13] Add excludedPackages parameter to broadcast This parameter is useful for when a broadcast's intent is not targeting specific package and it is not wanted to be delievered to a specific one. Test: Install two apps and create shell command argument to exlude packages, verify the correct packages can receive or get skipped. Verified with manifest and registered receivers. Bug: 231635380 Bug: 210118427 Merged-In: I9a331f71c7052b294d4eada0f0f658dba989d881 Change-Id: I9a331f71c7052b294d4eada0f0f658dba989d881 (cherry picked from commit c0ce69bc8d5225e38214dfdc2636b2cb5a375546) Merged-In: I9a331f71c7052b294d4eada0f0f658dba989d881 --- core/java/android/app/ActivityManager.java | 4 +- core/java/android/app/ContextImpl.java | 49 ++++++------- core/java/android/app/IActivityManager.aidl | 2 +- core/java/android/content/Context.java | 13 ++++ core/java/android/content/ContextWrapper.java | 6 +- .../server/am/ActivityManagerService.java | 72 ++++++++++--------- .../am/ActivityManagerShellCommand.java | 4 +- .../com/android/server/am/BroadcastQueue.java | 30 ++++++++ .../android/server/am/BroadcastRecord.java | 17 +++-- .../android/server/am/PreBootBroadcaster.java | 2 +- .../com/android/server/am/UserController.java | 4 +- .../apphibernation/AppHibernationService.java | 2 + .../server/pm/PackageManagerService.java | 10 +-- .../server/am/BroadcastRecordTest.java | 1 + .../AppHibernationServiceTest.java | 2 +- 15 files changed, 141 insertions(+), 77 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index db45466d98d25..9d59225f43443 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4366,8 +4366,8 @@ public class ActivityManager { try { getService().broadcastIntentWithFeature( null, null, intent, null, null, Activity.RESULT_OK, null, null, - null /*requiredPermissions*/, null /*excludedPermissions*/, appOp, null, false, - true, userId); + null /*requiredPermissions*/, null /*excludedPermissions*/, + null /*excludedPackages*/, appOp, null, false, true, userId); } catch (RemoteException ex) { } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 06af6b180d075..05f91299876a3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1185,7 +1185,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, false, getUserId()); + null, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1202,7 +1202,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1218,7 +1218,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1235,8 +1235,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, - getUserId()); + null /*excludedPermissions=*/, null /*excludedPackages*/, + AppOpsManager.OP_NONE, options, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1251,7 +1251,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1260,7 +1260,7 @@ class ContextImpl extends Context { @Override public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, - String[] excludedPermissions) { + String[] excludedPermissions, String[] excludedPackages) { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { @@ -1268,7 +1268,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, excludedPermissions, - AppOpsManager.OP_NONE, null, false, false, getUserId()); + excludedPackages, AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1285,8 +1285,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, - getUserId()); + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, options, false, + false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1303,7 +1303,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, appOp, null, false, false, getUserId()); + null /*excludedPermissions=*/, null, appOp, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1320,7 +1320,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, false, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, true, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1384,7 +1384,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermissions, - null /*excludedPermissions=*/, appOp, options, true, false, getUserId()); + null /*excludedPermissions=*/, null, appOp, options, true, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1398,7 +1398,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); + null, AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1421,8 +1421,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, - user.getIdentifier()); + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, options, false, + false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1439,7 +1439,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - null /*excludedPermissions=*/, appOp, null, false, false, user.getIdentifier()); + null /*excludedPermissions=*/, null, appOp, null, false, false, + user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1490,7 +1491,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermissions, - null /*excludedPermissions=*/, appOp, options, true, false, + null /*excludedPermissions=*/, null, appOp, options, true, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1532,7 +1533,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, true, getUserId()); + null, AppOpsManager.OP_NONE, null, false, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1571,7 +1572,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, options, false, true, getUserId()); + null, AppOpsManager.OP_NONE, options, false, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1607,7 +1608,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, true, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1640,7 +1641,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, null, false, true, user.getIdentifier()); + null, AppOpsManager.OP_NONE, null, false, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1655,7 +1656,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, - AppOpsManager.OP_NONE, options, false, true, user.getIdentifier()); + null, AppOpsManager.OP_NONE, options, false, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1690,7 +1691,7 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true, + null /*excludedPermissions=*/, null, AppOpsManager.OP_NONE, null, true, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 9e23b5fa692be..2ca3e27343493 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -138,7 +138,7 @@ interface IActivityManager { int broadcastIntentWithFeature(in IApplicationThread caller, in String callingFeatureId, in Intent intent, in String resolvedType, in IIntentReceiver resultTo, int resultCode, in String resultData, in Bundle map, in String[] requiredPermissions, in String[] excludePermissions, - int appOp, in Bundle options, boolean serialized, boolean sticky, int userId); + in String[] excludePackages, int appOp, in Bundle options, boolean serialized, boolean sticky, int userId); void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId); @UnsupportedAppUsage oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7f2a740d3228b..913c3b8a33cd2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2212,6 +2212,19 @@ public abstract class Context { */ public void sendBroadcastMultiplePermissions(@NonNull Intent intent, @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) { + sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions, null); + } + + + /** + * Like {@link #sendBroadcastMultiplePermissions(Intent, String[], String[])}, but also allows + * specification of a list of excluded packages. + * + * @hide + */ + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions, + @Nullable String[] excludedPackages) { throw new RuntimeException("Not implemented. Must override in a subclass."); } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 6324d0ecb0e07..985293ca21120 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -494,8 +494,10 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendBroadcastMultiplePermissions(@NonNull Intent intent, - @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) { - mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions); + @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions, + @Nullable String[] excludedPackages) { + mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions, + excludedPackages); } /** @hide */ diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f5103df80d6ab..a32aa6d895ef5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2565,7 +2565,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void batterySendBroadcast(Intent intent) { synchronized (this) { broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null, - OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), + null, OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } @@ -4036,7 +4036,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_UID, uid); intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid)); broadcastIntentLocked(null, null, null, intent, - null, null, 0, null, null, null, null, OP_NONE, + null, null, 0, null, null, null, null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getUserId(uid)); } @@ -7727,7 +7727,7 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, null, intent, - null, null, 0, null, null, null, null, OP_NONE, + null, null, 0, null, null, null, null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, currentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); @@ -7739,8 +7739,8 @@ public class ActivityManagerService extends IActivityManager.Stub public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {} - }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, null, + OP_NONE, null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, UserHandle.USER_ALL); } catch (Throwable e) { Slog.wtf(TAG, "Failed sending first user broadcasts", e); @@ -12556,8 +12556,8 @@ public class ActivityManagerService extends IActivityManager.Stub Intent intent = allSticky.get(i); BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, - null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers, - null, 0, null, null, false, true, true, -1, false, null, + null, null, -1, -1, false, null, null, null, null, OP_NONE, null, + receivers, null, 0, null, null, false, true, true, -1, false, null, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); @@ -12799,12 +12799,14 @@ public class ActivityManagerService extends IActivityManager.Stub String callerPackage, String callerFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, String[] excludedPermissions, - int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, + String[] excludedPackages, int appOp, Bundle bOptions, boolean ordered, + boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId) { return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, - excludedPermissions, appOp, bOptions, ordered, sticky, callingPid, callingUid, - realCallingUid, realCallingPid, userId, false /* allowBackgroundActivityStarts */, + excludedPermissions, excludedPackages, appOp, bOptions, ordered, sticky, callingPid, + callingUid, realCallingUid, realCallingPid, userId, + false /* allowBackgroundActivityStarts */, null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastAllowList */); } @@ -12813,7 +12815,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Nullable String callerFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, - String[] excludedPermissions, int appOp, Bundle bOptions, + String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId, boolean allowBackgroundActivityStarts, @@ -13390,10 +13392,10 @@ public class ActivityManagerService extends IActivityManager.Stub final BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers, - resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - allowBackgroundActivityStarts, backgroundActivityStartsToken, - timeoutExempt); + requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, + registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, + sticky, false, userId, allowBackgroundActivityStarts, + backgroundActivityStartsToken, timeoutExempt); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && (queue.replaceParallelBroadcastLocked(r) != null); @@ -13488,7 +13490,7 @@ public class ActivityManagerService extends IActivityManager.Stub BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, excludedPermissions, appOp, brOptions, + requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, allowBackgroundActivityStarts, backgroundActivityStartsToken, timeoutExempt); @@ -13617,14 +13619,16 @@ public class ActivityManagerService extends IActivityManager.Stub String[] requiredPermissions, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { return broadcastIntentWithFeature(caller, null, intent, resolvedType, resultTo, resultCode, - resultData, resultExtras, requiredPermissions, null, appOp, bOptions, serialized, - sticky, userId); + resultData, resultExtras, requiredPermissions, null, null, appOp, bOptions, + serialized, sticky, userId); } + @Override public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, - String[] requiredPermissions, String[] excludedPermissions, int appOp, Bundle bOptions, + String[] requiredPermissions, String[] excludedPermissions, + String[] excludedPackages, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { enforceNotIsolatedCaller("broadcastIntent"); synchronized(this) { @@ -13639,8 +13643,8 @@ public class ActivityManagerService extends IActivityManager.Stub return broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, callingFeatureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, - requiredPermissions, excludedPermissions, appOp, bOptions, serialized, - sticky, callingPid, callingUid, callingUid, callingPid, userId); + requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions, + serialized, sticky, callingPid, callingUid, callingUid, callingPid, userId); } finally { Binder.restoreCallingIdentity(origId); } @@ -13662,7 +13666,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, null, - OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid, + null, OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid, realCallingPid, userId, allowBackgroundActivityStarts, backgroundActivityStartsToken, null /* broadcastAllowList */); @@ -15903,10 +15907,11 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/, null /*callerPackage*/, null /*callingFeatureId*/, intent, null /*resolvedType*/, resultTo, 0 /*resultCode*/, null /*resultData*/, - null /*resultExtras*/, requiredPermissions, null, AppOpsManager.OP_NONE, - bOptions /*options*/, serialized, false /*sticky*/, callingPid, - callingUid, callingUid, callingPid, userId, - false /*allowBackgroundStarts*/, + null /*resultExtras*/, requiredPermissions, + null /*excludedPermissions*/, null /*excludedPackages*/, + AppOpsManager.OP_NONE, bOptions /*options*/, serialized, + false /*sticky*/, callingPid, callingUid, callingUid, callingPid, + userId, false /*allowBackgroundStarts*/, null /*tokenNeededForBackgroundActivityStarts*/, appIdAllowList); } finally { Binder.restoreCallingIdentity(origId); @@ -16028,7 +16033,7 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, + null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); @@ -16043,8 +16048,8 @@ public class ActivityManagerService extends IActivityManager.Stub TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, PowerExemptionManager.REASON_LOCALE_CHANGED, ""); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, SYSTEM_UID, - Binder.getCallingUid(), Binder.getCallingPid(), + null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, + SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } @@ -16059,8 +16064,9 @@ public class ActivityManagerService extends IActivityManager.Stub String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES }; broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, - permissions, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); + permissions, null, null, OP_NONE, null, false, false, MY_PID, + SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), + UserHandle.USER_ALL); } } } @@ -16084,8 +16090,8 @@ public class ActivityManagerService extends IActivityManager.Stub } broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - null, OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), - Binder.getCallingPid(), UserHandle.USER_ALL); + null, null, OP_NONE, null, false, false, -1, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index ea28117a6a3df..89c51b7e73eae 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -773,8 +773,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.flush(); Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle(); mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, bundle, true, false, - mUserId); + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, bundle, true, + false, mUserId); if (!mAsync) { receiver.waitForFinish(); } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 2da41070a6f4e..6daf7099fd446 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -67,6 +67,7 @@ import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import java.io.FileDescriptor; @@ -766,6 +767,22 @@ public final class BroadcastQueue { skip = true; } } + + // Check that the receiver does *not* belong to any of the excluded packages + if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) { + if (ArrayUtils.contains(r.excludedPackages, filter.packageName)) { + Slog.w(TAG, "Skipping delivery of excluded package " + + r.intent.toString() + + " to " + filter.receiverList.app + + " (pid=" + filter.receiverList.pid + + ", uid=" + filter.receiverList.uid + ")" + + " excludes package " + filter.packageName + + " due to sender " + r.callerPackage + + " (uid " + r.callingUid + ")"); + skip = true; + } + } + // If the broadcast also requires an app op check that as well. if (!skip && r.appOp != AppOpsManager.OP_NONE && mService.getAppOpsManager().noteOpNoThrow(r.appOp, @@ -1600,6 +1617,19 @@ public final class BroadcastQueue { } } + // Check that the receiver does *not* belong to any of the excluded packages + if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) { + if (ArrayUtils.contains(r.excludedPackages, component.getPackageName())) { + Slog.w(TAG, "Skipping delivery of excluded package " + + r.intent + " to " + + component.flattenToShortString() + + " excludes package " + component.getPackageName() + + " due to sender " + r.callerPackage + + " (uid " + r.callingUid + ")"); + skip = true; + } + } + if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID && r.requiredPermissions != null && r.requiredPermissions.length > 0) { for (int i = 0; i < r.requiredPermissions.length; i++) { diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 8015596204575..84a948217c9a5 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -63,6 +63,7 @@ final class BroadcastRecord extends Binder { final String resolvedType; // the resolved data type final String[] requiredPermissions; // permissions the caller has required final String[] excludedPermissions; // permissions to exclude + final String[] excludedPackages; // packages to exclude final int appOp; // an app op that is associated with this broadcast final BroadcastOptions options; // BroadcastOptions supplied by caller final List receivers; // contains BroadcastFilter and ResolveInfo @@ -147,6 +148,10 @@ final class BroadcastRecord extends Binder { pw.print(prefix); pw.print("excludedPermissions="); pw.print(Arrays.toString(excludedPermissions)); } + if (excludedPackages != null && excludedPackages.length > 0) { + pw.print(prefix); pw.print("excludedPackages="); + pw.print(Arrays.toString(excludedPackages)); + } if (options != null) { pw.print(prefix); pw.print("options="); pw.println(options.toBundle()); } @@ -245,7 +250,8 @@ final class BroadcastRecord extends Binder { Intent _intent, ProcessRecord _callerApp, String _callerPackage, @Nullable String _callerFeatureId, int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType, - String[] _requiredPermissions, String[] _excludedPermissions, int _appOp, + String[] _requiredPermissions, String[] _excludedPermissions, + String[] _excludedPackages, int _appOp, BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId, boolean allowBackgroundActivityStarts, @@ -265,6 +271,7 @@ final class BroadcastRecord extends Binder { resolvedType = _resolvedType; requiredPermissions = _requiredPermissions; excludedPermissions = _excludedPermissions; + excludedPackages = _excludedPackages; appOp = _appOp; options = _options; receivers = _receivers; @@ -306,6 +313,7 @@ final class BroadcastRecord extends Binder { resolvedType = from.resolvedType; requiredPermissions = from.requiredPermissions; excludedPermissions = from.excludedPermissions; + excludedPackages = from.excludedPackages; appOp = from.appOp; options = from.options; receivers = from.receivers; @@ -363,9 +371,10 @@ final class BroadcastRecord extends Binder { // build a new BroadcastRecord around that single-target list BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, excludedPermissions, appOp, options, splitReceivers, resultTo, - resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, - allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt); + requiredPermissions, excludedPermissions, excludedPackages, appOp, options, + splitReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, + initialSticky, userId, allowBackgroundActivityStarts, + mBackgroundActivityStartsToken, timeoutExempt); split.splitToken = this.splitToken; return split; diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 7562098246140..35f91ba1169b5 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -124,7 +124,7 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { REASON_PRE_BOOT_COMPLETED, ""); synchronized (mService) { mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null, - null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true, + null, null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index bf741790fcc27..212316f4266ad 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -3003,8 +3003,8 @@ class UserController implements Handler.Callback { synchronized (mService) { return mService.broadcastIntentLocked(null, null, null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, null, - appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid, - realCallingPid, userId); + null, appOp, bOptions, ordered, sticky, callingPid, callingUid, + realCallingUid, realCallingPid, userId); } } diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index 4d025c981ce94..28a919115f56c 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -423,6 +423,7 @@ public final class AppHibernationService extends SystemService { null /* resultExtras */, requiredPermissions, null /* excludedPermissions */, + null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* serialized */, @@ -441,6 +442,7 @@ public final class AppHibernationService extends SystemService { null /* resultExtras */, requiredPermissions, null /* excludedPermissions */, + null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* serialized */, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6f1c88722e21a..6db8bc6b825fb 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -15968,7 +15968,7 @@ public class PackageManagerService extends IPackageManager.Stub final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions( REASON_LOCKED_BOOT_COMPLETED); am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(), false, false, userId); // Deliver BOOT_COMPLETED only if user is unlocked @@ -15979,7 +15979,7 @@ public class PackageManagerService extends IPackageManager.Stub bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); } am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(), false, false, userId); } } catch (RemoteException e) { @@ -22915,7 +22915,7 @@ public class PackageManagerService extends IPackageManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); try { am.broadcastIntentWithFeature(null, null, intent, null, null, - 0, null, null, null, null, android.app.AppOpsManager.OP_NONE, + 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId); } catch (RemoteException e) { } @@ -28789,8 +28789,8 @@ public class PackageManagerService extends IPackageManager.Stub }; try { am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null, - requiredPermissions, null, android.app.AppOpsManager.OP_NONE, null, false, - false, UserHandle.USER_ALL); + requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, null, + false, false, UserHandle.USER_ALL); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java index e9b5b62430893..f44104e10967f 100644 --- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -185,6 +185,7 @@ public class BroadcastRecordTest { null /* resolvedType */, null /* requiredPermissions */, null /* excludedPermissions */, + null /* excludedPackages */, 0 /* appOp */, null /* options */, new ArrayList<>(receivers), // Make a copy to not affect the original list. diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 1c49e6e64dd88..70853b6881d91 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -298,7 +298,7 @@ public final class AppHibernationServiceTest { ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mIActivityManager, times(2)).broadcastIntentWithFeature(any(), any(), intentArgumentCaptor.capture(), any(), any(), anyInt(), any(), any(), any(), any(), - anyInt(), any(), anyBoolean(), anyBoolean(), eq(USER_ID_1)); + any(), anyInt(), any(), anyBoolean(), anyBoolean(), eq(USER_ID_1)); List capturedIntents = intentArgumentCaptor.getAllValues(); assertEquals(capturedIntents.get(0).getAction(), Intent.ACTION_LOCKED_BOOT_COMPLETED); assertEquals(capturedIntents.get(1).getAction(), Intent.ACTION_BOOT_COMPLETED); From 0a7b58f5ac08f01535201f9341b84bdf44eb92ef Mon Sep 17 00:00:00 2001 From: Sarah Chin Date: Sat, 7 May 2022 23:30:31 -0700 Subject: [PATCH 10/13] Update ServiceState broadcast for location permissions Add extra checks for master location switch and location bypass packages and send broadcasts accordingly. Test: Verify location is sanitized using testapp Test: Verify privacy dashboard with MLS on/off Test: Verify location is sanitized/unsanitized with bypass list Bug: 230919427 Bug: 210118427 Change-Id: I9784527d6d79235830f562c1944562f5a6ac1fb3 Merged-In: I9784527d6d79235830f562c1944562f5a6ac1fb3 (cherry picked from commit ac5f03c07fafe1a9d5dba89737297da7c74214cf) Merged-In: I9784527d6d79235830f562c1944562f5a6ac1fb3 --- core/res/res/values/config.xml | 8 ++ core/res/res/values/symbols.xml | 2 +- .../com/android/server/TelephonyRegistry.java | 102 +++++++++++++----- .../telephony/LocationAccessPolicy.java | 13 ++- 4 files changed, 95 insertions(+), 30 deletions(-) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 2ad2a5cfd2858..21c6f087a2d40 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5308,4 +5308,12 @@ 4 + + + + "com.android.phone" + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index af8472f9a90a4..a1ed949b94726 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4492,6 +4492,6 @@ - + diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index a8a24f19f6ba6..9d5d167da7223 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -2891,42 +2891,88 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Binder.restoreCallingIdentity(ident); } + // Send the broadcast exactly once to all possible disjoint sets of apps. + // If the location master switch is on, broadcast the ServiceState 4 times: + // - Full ServiceState sent to apps with ACCESS_FINE_LOCATION and READ_PHONE_STATE + // - Full ServiceState sent to apps with ACCESS_FINE_LOCATION and + // READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE + // - Sanitized ServiceState sent to apps with READ_PHONE_STATE but not ACCESS_FINE_LOCATION + // - Sanitized ServiceState sent to apps with READ_PRIVILEGED_PHONE_STATE but neither + // READ_PHONE_STATE nor ACCESS_FINE_LOCATION + // If the location master switch is off, broadcast the ServiceState multiple times: + // - Full ServiceState sent to all apps permitted to bypass the location master switch if + // they have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE + // - Sanitized ServiceState sent to all other apps with READ_PHONE_STATE + // - Sanitized ServiceState sent to all other apps with READ_PRIVILEGED_PHONE_STATE but not + // READ_PHONE_STATE + if (Binder.withCleanCallingIdentity(() -> + LocationAccessPolicy.isLocationModeEnabled(mContext, mContext.getUserId()))) { + Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION}); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION}, + new String[]{Manifest.permission.READ_PHONE_STATE}); + + Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PHONE_STATE}, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, + new String[]{Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION}); + } else { + String[] locationBypassPackages = Binder.withCleanCallingIdentity(() -> + LocationAccessPolicy.getLocationBypassPackages(mContext)); + for (String locationBypassPackage : locationBypassPackages) { + Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false); + fullIntent.setPackage(locationBypassPackage); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PHONE_STATE}); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + fullIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, + new String[]{Manifest.permission.READ_PHONE_STATE}); + } + + Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PHONE_STATE}, + new String[]{/* no excluded permissions */}, + locationBypassPackages); + mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions( + sanitizedIntent, + new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, + new String[]{Manifest.permission.READ_PHONE_STATE}, + locationBypassPackages); + } + } + + private Intent createServiceStateIntent(ServiceState state, int subId, int phoneId, + boolean sanitizeLocation) { Intent intent = new Intent(Intent.ACTION_SERVICE_STATE); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); Bundle data = new Bundle(); - state.fillInNotifierBundle(data); + if (sanitizeLocation) { + state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data); + } else { + state.fillInNotifierBundle(data); + } intent.putExtras(data); - // Pass the subscription along with the intent. intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId); intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId); intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId); - - // Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again - // for all apps with READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE. - // Do this again twice, the first time for apps with ACCESS_FINE_LOCATION, then again with - // the location-sanitized service state for all apps without ACCESS_FINE_LOCATION. - // This ensures that any app holding either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE - // get this broadcast exactly once, and we are not exposing location without permission. - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION}); - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION}, - new String[] {Manifest.permission.READ_PHONE_STATE}); - - // Replace bundle with location-sanitized ServiceState - data = new Bundle(); - state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data); - intent.putExtras(data); - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PHONE_STATE}, - new String[] {Manifest.permission.ACCESS_FINE_LOCATION}); - mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent, - new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE}, - new String[] {Manifest.permission.READ_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION}); + return intent; } private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId, diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java index 85d59a216f25a..9dfb0cc289eed 100644 --- a/telephony/common/android/telephony/LocationAccessPolicy.java +++ b/telephony/common/android/telephony/LocationAccessPolicy.java @@ -361,7 +361,10 @@ public final class LocationAccessPolicy { return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid); } - private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { + /** + * @return Whether location is enabled for the given user. + */ + public static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { LocationManager locationManager = context.getSystemService(LocationManager.class); if (locationManager == null) { Log.w(TAG, "Couldn't get location manager, denying location access"); @@ -370,6 +373,14 @@ public final class LocationAccessPolicy { return locationManager.isLocationEnabledForUser(UserHandle.of(userId)); } + /** + * @return An array of packages that are always allowed to access location. + */ + public static @NonNull String[] getLocationBypassPackages(@NonNull Context context) { + return context.getResources().getStringArray( + com.android.internal.R.array.config_serviceStateLocationAllowedPackages); + } + private static boolean checkInteractAcrossUsersFull( @NonNull Context context, int pid, int uid) { return checkManifestPermission(context, pid, uid, From 0d3a05af3303a23b157dcb6fefa4ead3d7de7a25 Mon Sep 17 00:00:00 2001 From: Steve Berbary Date: Tue, 17 May 2022 18:20:19 +0000 Subject: [PATCH 11/13] Revert "DO NOT MERGE Suppress notifications when device enter lockdown" This reverts commit 62845d9af7d9695d1019bd6d8b0ccb223ace52d3. Reason for revert: b/232714129 | [C10][BootStress][reboot]reset message: KP: sysrq triggered crash by init Change-Id: I54296e0973c8fb206acdfbf54cfa793b7cd3a902 (cherry picked from commit 04836fa17f1499d2de63f1a0dd0ad430bd5da920) Merged-In: I54296e0973c8fb206acdfbf54cfa793b7cd3a902 --- .../NotificationManagerService.java | 99 +------------------ .../tests/uiservicestests/AndroidManifest.xml | 1 - .../NotificationListenersTest.java | 74 +------------- .../NotificationManagerServiceTest.java | 65 +----------- 4 files changed, 7 insertions(+), 232 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3c2631904f42a..0fda3a36b8e90 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -246,7 +246,6 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.StatsEvent; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -278,7 +277,6 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; -import com.android.internal.widget.LockPatternUtils; import com.android.server.DeviceIdleInternal; import com.android.server.EventLogTags; import com.android.server.IoThread; @@ -1890,54 +1888,6 @@ public class NotificationManagerService extends SystemService { private SettingsObserver mSettingsObserver; protected ZenModeHelper mZenModeHelper; - protected class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { - - SparseBooleanArray mUserInLockDownMode = new SparseBooleanArray(); - boolean mIsInLockDownMode = false; - - StrongAuthTracker(Context context) { - super(context); - } - - private boolean containsFlag(int haystack, int needle) { - return (haystack & needle) != 0; - } - - public boolean isInLockDownMode() { - return mIsInLockDownMode; - } - - @Override - public synchronized void onStrongAuthRequiredChanged(int userId) { - boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mUserInLockDownMode.put(userId, userInLockDownModeNext); - boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; - - if (mIsInLockDownMode == isInLockDownModeNext) { - return; - } - - if (isInLockDownModeNext) { - cancelNotificationsWhenEnterLockDownMode(); - } - - // When the mIsInLockDownMode is true, both notifyPostedLocked and - // notifyRemovedLocked will be dismissed. So we shall call - // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode - // as true and call postNotificationsWhenExitLockDownMode after we set - // mIsInLockDownMode as false. - mIsInLockDownMode = isInLockDownModeNext; - - if (!isInLockDownModeNext) { - postNotificationsWhenExitLockDownMode(); - } - } - } - - private LockPatternUtils mLockPatternUtils; - private StrongAuthTracker mStrongAuthTracker; - public NotificationManagerService(Context context) { this(context, new NotificationRecordLoggerImpl(), @@ -1960,11 +1910,6 @@ public class NotificationManagerService extends SystemService { mAudioManager = audioMananger; } - @VisibleForTesting - void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) { - mStrongAuthTracker = strongAuthTracker; - } - @VisibleForTesting void setKeyguardManager(KeyguardManager keyguardManager) { mKeyguardManager = keyguardManager; @@ -2152,8 +2097,6 @@ public class NotificationManagerService extends SystemService { ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); - mLockPatternUtils = new LockPatternUtils(getContext()); - mStrongAuthTracker = new StrongAuthTracker(getContext()); String[] extractorNames; try { extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); @@ -2629,7 +2572,6 @@ public class NotificationManagerService extends SystemService { bubbsExtractor.setShortcutHelper(mShortcutHelper); } registerNotificationPreferencesPullers(); - mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to // bind to listener services. @@ -9175,29 +9117,6 @@ public class NotificationManagerService extends SystemService { } } - private void cancelNotificationsWhenEnterLockDownMode() { - synchronized (mNotificationLock) { - int numNotifications = mNotificationList.size(); - for (int i = 0; i < numNotifications; i++) { - NotificationRecord rec = mNotificationList.get(i); - mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, - rec.getStats()); - } - - } - } - - private void postNotificationsWhenExitLockDownMode() { - synchronized (mNotificationLock) { - int numNotifications = mNotificationList.size(); - for (int i = 0; i < numNotifications; i++) { - NotificationRecord rec = mNotificationList.get(i); - mListeners.notifyPostedLocked(rec, rec); - } - - } - } - private void updateNotificationPulse() { synchronized (mNotificationLock) { updateLightsLocked(); @@ -9433,10 +9352,6 @@ public class NotificationManagerService extends SystemService { rankings.toArray(new NotificationListenerService.Ranking[0])); } - boolean isInLockDownMode() { - return mStrongAuthTracker.isInLockDownMode(); - } - boolean hasCompanionDevice(ManagedServiceInfo info) { if (mCompanionManager == null) { mCompanionManager = getCompanionManager(); @@ -10488,12 +10403,8 @@ public class NotificationManagerService extends SystemService { * targetting <= O_MR1 */ @GuardedBy("mNotificationLock") - void notifyPostedLocked(NotificationRecord r, NotificationRecord old, + private void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { - if (isInLockDownMode()) { - return; - } - try { // Lazily initialized snapshots of the notification. StatusBarNotification sbn = r.getSbn(); @@ -10591,10 +10502,6 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { - if (isInLockDownMode()) { - return; - } - final StatusBarNotification sbn = r.getSbn(); // make a copy in case changes are made to the underlying Notification object @@ -10640,10 +10547,6 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List changedHiddenNotifications) { - if (isInLockDownMode()) { - return; - } - boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; // TODO (b/73052211): if the ranking update changed the notification type, diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index e8e3a8f84f218..767857bf2de8b 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -33,7 +33,6 @@ - diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index eb9847f7eb7e6..7c0f29dce1abb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -24,14 +24,15 @@ import static com.android.server.notification.NotificationManagerService.Notific import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,11 +47,10 @@ import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationStats; -import android.service.notification.StatusBarNotification; import android.testing.TestableContext; import android.util.ArraySet; import android.util.Pair; +import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -61,13 +61,11 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.internal.util.reflection.FieldSetter; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.List; public class NotificationListenersTest extends UiServiceTestCase { @@ -376,66 +374,4 @@ public class NotificationListenersTest extends UiServiceTestCase { verify(mContext).sendBroadcastAsUser( any(), eq(UserHandle.of(userId)), nullable(String.class)); } - - @Test - public void testNotifyPostedLockedInLockdownMode() { - NotificationRecord r = mock(NotificationRecord.class); - NotificationRecord old = mock(NotificationRecord.class); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, atLeast(2)).getSbn(); - - // in the lockdown mode - reset(r); - reset(old); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, never()).getSbn(); - } - - @Test - public void testnotifyRankingUpdateLockedInLockdownMode() { - List chn = mock(List.class); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, atLeast(1)).size(); - - // in the lockdown mode - reset(chn); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, never()).size(); - } - - @Test - public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { - NotificationRecord r = mock(NotificationRecord.class); - NotificationStats rs = mock(NotificationStats.class); - StatusBarNotification sbn = mock(StatusBarNotification.class); - FieldSetter.setField(mNm, - NotificationManagerService.class.getDeclaredField("mHandler"), - mock(NotificationManagerService.WorkerHandler.class)); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, atLeast(2)).getSbn(); - - // in the lockdown mode - reset(r); - reset(rs); - when(mNm.isInLockDownMode()).thenReturn(true); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, never()).getSbn(); - } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9a221a8f2bf9b..b98401e76cc20 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -58,13 +58,10 @@ import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; -import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; - import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; @@ -226,6 +223,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -411,26 +409,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { interface NotificationAssistantAccessGrantedCallback { void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet); } - - class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { - private int mGetStrongAuthForUserReturnValue = 0; - StrongAuthTrackerFake(Context context) { - super(context); - } - - public void setGetStrongAuthForUserReturnValue(int val) { - mGetStrongAuthForUserReturnValue = val; - } - - @Override - public int getStrongAuthForUser(int userId) { - return mGetStrongAuthForUserReturnValue; - } - } } - TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; - private class TestableToastCallback extends ITransientNotification.Stub { @Override public void show(IBinder windowToken) { @@ -550,9 +530,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setAudioManager(mAudioManager); - mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext); - mService.setStrongAuthTracker(mStrongAuthTracker); - mShortcutHelper = mService.getShortcutHelper(); mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); @@ -8377,44 +8354,4 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } } } - - @Test - public void testStrongAuthTracker_isInLockDownMode() { - mStrongAuthTracker.setGetStrongAuthForUserReturnValue( - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); - mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertFalse(mStrongAuthTracker.isInLockDownMode()); - } - - @Test - public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { - // post 2 notifications from 2 packages - NotificationRecord pkgA = new NotificationRecord(mContext, - generateSbn("a", 1000, 9, 0), mTestNotificationChannel); - mService.addNotification(pkgA); - NotificationRecord pkgB = new NotificationRecord(mContext, - generateSbn("b", 1001, 9, 0), mTestNotificationChannel); - mService.addNotification(pkgB); - - // when entering the lockdown mode, cancel the 2 notifications. - mStrongAuthTracker.setGetStrongAuthForUserReturnValue( - STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); - - // the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN. - ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); - verify(mListeners, times(2)).notifyRemovedLocked(any(), captor.capture(), any()); - assertEquals(REASON_CANCEL_ALL, captor.getValue().intValue()); - - // exit lockdown mode. - mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - - // the notifyPostedLocked function is called twice. - verify(mListeners, times(2)).notifyPostedLocked(any(), any()); - } } From e1e5f4a46740a505772ac75a522fbd7629d447e8 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Tue, 10 May 2022 06:15:54 +0000 Subject: [PATCH 12/13] DO NOT MERGE: Revert "RESTRICT AUTOMERGE: Polish IME transition from fullscree..." Revert submission 17397265-presubmit-am-5310c6a98b9041fa8e3d0cd9070573d2 Reason for revert: b/231594180 and b/231548400 for SC QPR3 Reverted Changes: If2c6d7bd6:[automerge] RESTRICT AUTOMERGE: Polish IME transit... I332c0e4ff:RESTRICT AUTOMERGE: Polish IME transition from ful... Change-Id: I24750ada98c954244fa9ee8f844ec92aa0494c86 (cherry picked from commit 20e8b225a069c52b8d7ef0f4402b2f379fb46ffb) Merged-In: I24750ada98c954244fa9ee8f844ec92aa0494c86 --- .../com/android/server/wm/DisplayContent.java | 10 ++--- .../com/android/server/wm/InsetsPolicy.java | 10 +++-- .../server/wm/ActivityRecordTests.java | 4 +- .../server/wm/DisplayContentTests.java | 15 ------- .../android/server/wm/WindowStateTests.java | 41 ------------------- 5 files changed, 13 insertions(+), 67 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index b184d5c62008f..42c6dd43ebcee 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4193,17 +4193,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting SurfaceControl computeImeParent() { - if (mImeLayeringTarget != null && mImeInputTarget != null - && mImeLayeringTarget.mActivityRecord != mImeInputTarget.mActivityRecord) { - // Do not change parent if the window hasn't requested IME. - return null; - } // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. if (shouldImeAttachedToApp()) { + if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.mActivityRecord) { + // Do not change parent if the window hasn't requested IME. + return null; + } return mImeLayeringTarget.mActivityRecord.getSurfaceControl(); } + // Otherwise, we just attach it to where the display area policy put it. return mImeWindowsContainer.getParent() != null ? mImeWindowsContainer.getParent().getSurfaceControl() : null; diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 5124841bcbbbd..10ae152c33653 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -265,12 +265,14 @@ class InsetsPolicy { return state; } } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { - // During switching tasks with gestural navigation, before the next IME input target - // starts the input, we should adjust and freeze the last IME visibility of the window - // in case delivering obsoleted IME insets state during transitioning. + // During switching tasks with gestural navigation, if the IME is attached to + // one app window on that time, even the next app window is behind the IME window, + // conceptually the window should not receive the IME insets if the next window is + // not eligible IME requester and ready to show IME on top of it. + final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); - if (originalImeSource != null) { + if (shouldImeAttachedToApp && originalImeSource != null) { final boolean imeVisibility = w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); final InsetsState state = copyState ? new InsetsState(originalState) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 32cee44ce6ff3..b770b3e3a55b7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -3065,11 +3065,11 @@ public class ActivityRecordTests extends WindowTestsBase { // Simulate app re-start input or turning screen off/on then unlocked by un-secure // keyguard to back to the app, expect IME insets is not frozen - mDisplayContent.updateImeInputAndControlTarget(app); - assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); imeSource.setFrame(new Rect(100, 400, 500, 500)); app.getInsetsState().addSource(imeSource); app.getInsetsState().setSourceVisible(ITYPE_IME, true); + mDisplayContent.updateImeInputAndControlTarget(app); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Verify when IME is visible and the app can receive the right IME insets from policy. makeWindowVisibleAndDrawn(app, mImeWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 9c0c213c3efe9..f3c1ec5b200e9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1075,21 +1075,6 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); } - @UseTestDisplay(addWindows = W_ACTIVITY) - @Test - public void testComputeImeParent_inputTargetNotUpdate() throws Exception { - WindowState app1 = createWindow(null, TYPE_BASE_APPLICATION, "app1"); - WindowState app2 = createWindow(null, TYPE_BASE_APPLICATION, "app2"); - doReturn(true).when(mDisplayContent).shouldImeAttachedToApp(); - mDisplayContent.setImeLayeringTarget(app1); - mDisplayContent.setImeInputTarget(app1); - assertEquals(app1.mActivityRecord.getSurfaceControl(), mDisplayContent.computeImeParent()); - mDisplayContent.setImeLayeringTarget(app2); - // Expect null means no change IME parent when the IME layering target not yet - // request IME to be the input target. - assertNull(mDisplayContent.computeImeParent()); - } - @Test public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception { final DisplayContent dc = createNewDisplay(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 03b818843aa2d..7d501356d4696 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -931,47 +931,6 @@ public class WindowStateTests extends WindowTestsBase { @UseTestDisplay(addWindows = { W_ACTIVITY }) @Test - public void testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - final WindowState app2 = createWindow(null, WINDOWING_MODE_MULTI_WINDOW, - ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app2"); - final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); - spyOn(imeWindow); - doReturn(true).when(imeWindow).isVisible(); - mDisplayContent.mInputMethodWindow = imeWindow; - - final InsetsStateController controller = mDisplayContent.getInsetsStateController(); - controller.getImeSourceProvider().setWindow(imeWindow, null, null); - - // Simulate app2 in multi-window mode is going to background to switch to the fullscreen - // app which requests IME with updating all windows Insets State when IME is above app. - app2.mActivityRecord.mImeInsetsFrozenUntilStartInput = true; - mDisplayContent.setImeLayeringTarget(app); - mDisplayContent.setImeInputTarget(app); - assertTrue(mDisplayContent.shouldImeAttachedToApp()); - controller.getImeSourceProvider().scheduleShowImePostLayout(app); - controller.getImeSourceProvider().getSource().setVisible(true); - controller.updateAboveInsetsState(imeWindow, false); - - // Expect app windows behind IME can receive IME insets visible, - // but not for app2 in background. - assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); - assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); - - // Simulate app plays closing transition to app2. - // And app2 is now IME layering target but not yet to be the IME input target. - mDisplayContent.setImeLayeringTarget(app2); - app.mActivityRecord.commitVisibility(false, false); - assertTrue(app.mActivityRecord.mLastImeShown); - assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); - - // Verify the IME insets is still visible on app, but not for app2 during task switching. - assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); - assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); - } - - @UseTestDisplay(addWindows = {W_ACTIVITY}) - @Test public void testUpdateImeControlTargetWhenLeavingMultiWindow() { WindowState app = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken, "app"); From 228f5ee6525a52cef43fe5ec98364cb9e8720132 Mon Sep 17 00:00:00 2001 From: Steve Berbary Date: Wed, 1 Jun 2022 18:35:54 +0000 Subject: [PATCH 13/13] Revert "[DO NOT MERGE] Do not clear calling identify when using BiometricPrompt from FingerprintService." This reverts commit 2d60fb3647a838c4c7f10c6f98bf98be4508b119. Reason for revert: CTS failure functional regression. More info can be found in this bug: b/214261879 Change-Id: I596ba36f6bd433cb9457c21b0bc2294baafc21b2 (cherry picked from commit 12d48b137afc9a678e597ccb3d9978db775a4f1b) Merged-In: I596ba36f6bd433cb9457c21b0bc2294baafc21b2 --- .../hardware/biometrics/BiometricPrompt.java | 38 +++------ .../biometrics/ITestSessionCallback.aidl | 2 +- .../hardware/biometrics/PromptInfo.java | 22 +---- .../systemui/biometrics/AuthController.java | 10 +-- .../biometrics/AuthControllerTest.java | 32 ++------ .../sensors/AuthenticationClient.java | 81 +++++++++---------- .../fingerprint/FingerprintService.java | 22 +++-- 7 files changed, 67 insertions(+), 140 deletions(-) diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 520234bcf0506..6b5bec99e6741 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -420,18 +420,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan return this; } - /** - * Set if BiometricPrompt is being used by the legacy fingerprint manager API. - * @param sensorId sensor id - * @return This builder. - * @hide - */ - @NonNull - public Builder setIsForLegacyFingerprintManager(int sensorId) { - mPromptInfo.setIsForLegacyFingerprintManager(sensorId); - return this; - } - /** * Creates a {@link BiometricPrompt}. * @@ -873,36 +861,28 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { - if (cancel == null) { - throw new IllegalArgumentException("Must supply a cancellation signal"); - } - if (executor == null) { - throw new IllegalArgumentException("Must supply an executor"); - } - if (callback == null) { - throw new IllegalArgumentException("Must supply a callback"); - } - - authenticateInternal(0 /* operationId */, cancel, executor, callback, userId); + authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */); } /** - * Authenticates for the given keystore operation. + * Authenticates for the given user and keystore operation. * * @param cancel An object that can be used to cancel authentication * @param executor An executor to handle callback events * @param callback An object to receive authentication events + * @param userId The user to authenticate * @param operationId The keystore operation associated with authentication * * @return A requestId that can be used to cancel this operation. * * @hide */ - @RequiresPermission(USE_BIOMETRIC) - public long authenticateForOperation( + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public long authenticateUserForOperation( @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, + int userId, long operationId) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); @@ -914,7 +894,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan throw new IllegalArgumentException("Must supply a callback"); } - return authenticateInternal(operationId, cancel, executor, callback, mContext.getUserId()); + return authenticateInternal(operationId, cancel, executor, callback, userId); } /** @@ -1048,7 +1028,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private void cancelAuthentication(long requestId) { if (mService != null) { try { - mService.cancelAuthentication(mToken, mContext.getPackageName(), requestId); + mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId); } catch (RemoteException e) { Log.e(TAG, "Unable to cancel authentication", e); } @@ -1107,7 +1087,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } final long authId = mService.authenticate(mToken, operationId, userId, - mBiometricServiceReceiver, mContext.getPackageName(), promptInfo); + mBiometricServiceReceiver, mContext.getOpPackageName(), promptInfo); cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); return authId; } catch (RemoteException e) { diff --git a/core/java/android/hardware/biometrics/ITestSessionCallback.aidl b/core/java/android/hardware/biometrics/ITestSessionCallback.aidl index b336a9f21b60a..3d9517f29548f 100644 --- a/core/java/android/hardware/biometrics/ITestSessionCallback.aidl +++ b/core/java/android/hardware/biometrics/ITestSessionCallback.aidl @@ -19,7 +19,7 @@ package android.hardware.biometrics; * ITestSession callback for FingerprintManager and BiometricManager. * @hide */ -oneway interface ITestSessionCallback { +interface ITestSessionCallback { void onCleanupStarted(int userId); void onCleanupFinished(int userId); } diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index 2742f0effde6d..e6b762a64384d 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -46,7 +46,6 @@ public class PromptInfo implements Parcelable { @NonNull private List mAllowedSensorIds = new ArrayList<>(); private boolean mAllowBackgroundAuthentication; private boolean mIgnoreEnrollmentState; - private boolean mIsForLegacyFingerprintManager = false; public PromptInfo() { @@ -69,7 +68,6 @@ public class PromptInfo implements Parcelable { mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader()); mAllowBackgroundAuthentication = in.readBoolean(); mIgnoreEnrollmentState = in.readBoolean(); - mIsForLegacyFingerprintManager = in.readBoolean(); } public static final Creator CREATOR = new Creator() { @@ -107,15 +105,10 @@ public class PromptInfo implements Parcelable { dest.writeList(mAllowedSensorIds); dest.writeBoolean(mAllowBackgroundAuthentication); dest.writeBoolean(mIgnoreEnrollmentState); - dest.writeBoolean(mIsForLegacyFingerprintManager); } public boolean containsTestConfigurations() { - if (mIsForLegacyFingerprintManager - && mAllowedSensorIds.size() == 1 - && !mAllowBackgroundAuthentication) { - return false; - } else if (!mAllowedSensorIds.isEmpty()) { + if (!mAllowedSensorIds.isEmpty()) { return true; } else if (mAllowBackgroundAuthentication) { return true; @@ -195,8 +188,7 @@ public class PromptInfo implements Parcelable { } public void setAllowedSensorIds(@NonNull List sensorIds) { - mAllowedSensorIds.clear(); - mAllowedSensorIds.addAll(sensorIds); + mAllowedSensorIds = sensorIds; } public void setAllowBackgroundAuthentication(boolean allow) { @@ -207,12 +199,6 @@ public class PromptInfo implements Parcelable { mIgnoreEnrollmentState = ignoreEnrollmentState; } - public void setIsForLegacyFingerprintManager(int sensorId) { - mIsForLegacyFingerprintManager = true; - mAllowedSensorIds.clear(); - mAllowedSensorIds.add(sensorId); - } - // Getters public CharSequence getTitle() { @@ -286,8 +272,4 @@ public class PromptInfo implements Parcelable { public boolean isIgnoreEnrollmentState() { return mIgnoreEnrollmentState; } - - public boolean isForLegacyFingerprintManager() { - return mIsForLegacyFingerprintManager; - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 7ab214e94230d..df20b83a36ca0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -134,7 +134,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private class BiometricTaskStackListener extends TaskStackListener { @Override public void onTaskStackChanged() { - mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground); + mHandler.post(AuthController.this::handleTaskStackChanged); } } @@ -181,7 +181,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } }; - private void cancelIfOwnerIsNotInForeground() { + private void handleTaskStackChanged() { mExecution.assertIsMainThread(); if (mCurrentDialog != null) { try { @@ -193,7 +193,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, final String topPackage = runningTasks.get(0).topActivity.getPackageName(); if (!topPackage.contentEquals(clientPackage) && !Utils.isSystem(mContext, clientPackage)) { - Log.e(TAG, "Evicting client due to: " + topPackage); + Log.w(TAG, "Evicting client due to: " + topPackage); mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog = null; mOrientationListener.disable(); @@ -814,10 +814,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCurrentDialog = newDialog; mCurrentDialog.show(mWindowManager, savedState); mOrientationListener.enable(); - - if (!promptInfo.isAllowBackgroundAuthentication()) { - mHandler.post(this::cancelIfOwnerIsNotInForeground); - } } private void onDialogDismissed(@DismissedReason int reason) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 2b7c984f04b12..08c77146d34c6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -554,26 +554,16 @@ public class AuthControllerTest extends SysuiTestCase { mAuthController.mLastBiometricPromptInfo.getAuthenticators()); } - @Test - public void testClientNotified_whenTaskStackChangesDuringShow() throws Exception { - switchTask("other_package"); - showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - - mTestableLooper.processAllMessages(); - - assertNull(mAuthController.mCurrentDialog); - assertNull(mAuthController.mReceiver); - verify(mDialog1).dismissWithoutCallback(true /* animate */); - verify(mReceiver).onDialogDismissed( - eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), - eq(null) /* credentialAttestation */); - } - @Test public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - switchTask("other_package"); + List tasks = new ArrayList<>(); + ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); + taskInfo.topActivity = mock(ComponentName.class); + when(taskInfo.topActivity.getPackageName()).thenReturn("other_package"); + tasks.add(taskInfo); + when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); mAuthController.mTaskStackListener.onTaskStackChanged(); mTestableLooper.processAllMessages(); @@ -650,16 +640,6 @@ public class AuthControllerTest extends SysuiTestCase { BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT); } - private void switchTask(String packageName) { - final List tasks = new ArrayList<>(); - final ActivityManager.RunningTaskInfo taskInfo = - mock(ActivityManager.RunningTaskInfo.class); - taskInfo.topActivity = mock(ComponentName.class); - when(taskInfo.topActivity.getPackageName()).thenReturn(packageName); - tasks.add(taskInfo); - when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); - } - private PromptInfo createTestPromptInfo() { PromptInfo promptInfo = new PromptInfo(); 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 92c8c9bb57ec3..358263df916b3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -118,7 +118,7 @@ public abstract class AuthenticationClient extends AcquisitionClient mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; mRequireConfirmation = requireConfirmation; - mActivityTaskManager = getActivityTaskManager(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mBiometricManager = context.getSystemService(BiometricManager.class); mTaskStackListener = taskStackListener; mLockoutTracker = lockoutTracker; @@ -146,10 +146,6 @@ public abstract class AuthenticationClient extends AcquisitionClient return mStartTimeMs; } - protected ActivityTaskManager getActivityTaskManager() { - return ActivityTaskManager.getInstance(); - } - @Override public void binderDied() { final boolean clearListener = !isBiometricPrompt(); @@ -326,50 +322,45 @@ public abstract class AuthenticationClient extends AcquisitionClient sendCancelOnly(listener); } }); - } else { // not authenticated - if (isBackgroundAuth) { - Slog.e(TAG, "cancelling due to background auth"); - cancel(); - } else { - // Allow system-defined limit of number of attempts before giving up - final @LockoutTracker.LockoutMode int lockoutMode = - handleFailedAttempt(getTargetUserId()); - if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { - markAlreadyDone(); + } else { + // Allow system-defined limit of number of attempts before giving up + final @LockoutTracker.LockoutMode int lockoutMode = + handleFailedAttempt(getTargetUserId()); + if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { + markAlreadyDone(); + } + + final CoexCoordinator coordinator = CoexCoordinator.getInstance(); + coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode, + new CoexCoordinator.Callback() { + @Override + public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { + if (listener != null) { + try { + listener.onAuthenticationFailed(getSensorId()); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify listener", e); + } + } } - final CoexCoordinator coordinator = CoexCoordinator.getInstance(); - coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode, - new CoexCoordinator.Callback() { - @Override - public void sendAuthenticationResult(boolean addAuthTokenIfStrong) { - if (listener != null) { - try { - listener.onAuthenticationFailed(getSensorId()); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to notify listener", e); - } - } - } + @Override + public void sendHapticFeedback() { + if (listener != null && mShouldVibrate) { + vibrateError(); + } + } - @Override - public void sendHapticFeedback() { - if (listener != null && mShouldVibrate) { - vibrateError(); - } - } + @Override + public void handleLifecycleAfterAuth() { + AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */); + } - @Override - public void handleLifecycleAfterAuth() { - AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */); - } - - @Override - public void sendAuthenticationCanceled() { - sendCancelOnly(listener); - } - }); - } + @Override + public void sendAuthenticationCanceled() { + sendCancelOnly(listener); + } + }); } } 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 3a93d82a68eec..b44f4dc682745 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 @@ -331,12 +331,12 @@ public class FingerprintService extends SystemService { provider.second.getSensorProperties(sensorId); if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName) && sensorProps != null && sensorProps.isAnyUdfpsType()) { + identity = Binder.clearCallingIdentity(); try { return authenticateWithPrompt(operationId, sensorProps, userId, receiver, - opPackageName, ignoreEnrollmentState); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(TAG, "Invalid package", e); - return -1; + ignoreEnrollmentState); + } finally { + Binder.restoreCallingIdentity(identity); } } return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, @@ -349,15 +349,12 @@ public class FingerprintService extends SystemService { @NonNull final FingerprintSensorPropertiesInternal props, final int userId, final IFingerprintServiceReceiver receiver, - final String opPackageName, - boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException { + boolean ignoreEnrollmentState) { final Context context = getUiContext(); - final Context promptContext = context.createPackageContextAsUser( - opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(userId)); final Executor executor = context.getMainExecutor(); - final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(promptContext) + final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context) .setTitle(context.getString(R.string.biometric_dialog_default_title)) .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle)) .setNegativeButton( @@ -371,7 +368,8 @@ public class FingerprintService extends SystemService { Slog.e(TAG, "Remote exception in negative button onClick()", e); } }) - .setIsForLegacyFingerprintManager(props.sensorId) + .setAllowedSensorIds(new ArrayList<>( + Collections.singletonList(props.sensorId))) .setIgnoreEnrollmentState(ignoreEnrollmentState) .build(); @@ -425,8 +423,8 @@ public class FingerprintService extends SystemService { } }; - return biometricPrompt.authenticateForOperation( - new CancellationSignal(), executor, promptCallback, operationId); + return biometricPrompt.authenticateUserForOperation( + new CancellationSignal(), executor, promptCallback, userId, operationId); } @Override