From 95ae8d1fe54b028688b8307026e88657cae4e95c Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Wed, 3 Sep 2025 16:01:01 +0300 Subject: [PATCH] Merge tag 'android-security-15.0.0_r10' into staging/lineage-22.2_merge-android-security-15.0.0_r10 Android Security 15.0.0 Release 10 (13793697) # -----BEGIN PGP SIGNATURE----- # # iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCaLcitgAKCRDorT+BmrEO # eKcQAJ9egQZPntvkA1gUOGO6gzQN2Y53EQCdF5qsX/O/3HFEIRXZfQKTy6e9VQ8= # =ISOZ # -----END PGP SIGNATURE----- # gpg: Signature made Tue Sep 2 20:00:38 2025 EEST # gpg: using DSA key 4340D13570EF945E83810964E8AD3F819AB10E78 # gpg: Good signature from "The Android Open Source Project " [ultimate] * tag 'android-security-15.0.0_r10': (23 commits) Ignore face settings extras when called by an external package. startActivityForResult with earlier new Intent Hide notification content in history Use correct API to get calling package name in CredentialStorage Drop PendingIntent extras from external packages during enrollment. Do not enable the Content Protection toggle for non-admin users. AppRestrictions - use vetted component Add ComponentName explicitly to make sure arbitary intents aren't launched from Settings. Disable the ContentProtection setting switch bar when current user is a guest user. [CDM] Checks NLS component name instead of package name Check package name format before using it for SPA nav [SPA] Add biometric authentication for package modification Remove incorrect call to Window.addFlags with a system flag Check the permission of the callingUid instead of the calling package Set component name only for ConfirmDeviceCredentialActivity. [Safer intents] App Time Spent Preference Don't let profiles open the UserSettings overflow [DO NOT MERGE] Block the content scheme intent in AccountTypePreferenceLoader [CDM][NLS] Check if the NLS service has an intent-filter Checks cross user permission before handling intent ... Conflicts: src/com/android/settings/accessibility/AccessibilitySettings.java src/com/android/settings/applications/AppInfoBase.java src/com/android/settings/password/ConfirmDeviceCredentialActivity.java src/com/android/settings/security/ContentProtectionTogglePreferenceController.java src/com/android/settings/users/AppRestrictionsFragment.java src/com/android/settings/users/UserSettings.java tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java tests/robotests/src/com/android/settings/security/ContentProtectionTogglePreferenceControllerTest.java Change-Id: I8cb282480b2a75cca58e381c17e72ebba2aceb0d --- .../accounts/AccountTypePreferenceLoader.java | 9 +++++- .../BiometricEnrollIntroduction.java | 20 ++++++++++-- .../biometrics/face/FaceSettings.java | 21 ++++++++---- .../history/NotificationHistoryActivity.java | 32 ++++++++++++++++--- .../history/NotificationHistoryAdapter.java | 22 ++++++++++--- .../history/NotificationSbnAdapter.java | 15 +++++++-- ...tProtectionTogglePreferenceController.java | 2 +- .../settings/security/CredentialStorage.java | 15 +++++++-- .../users/AppRestrictionsFragment.java | 22 ++++++++++--- .../face/FaceEnrollIntroductionTest.java | 21 ++++++++++++ .../FingerprintEnrollIntroductionTest.java | 25 +++++++++++++++ ...tectionTogglePreferenceControllerTest.java | 10 +++--- 12 files changed, 182 insertions(+), 32 deletions(-) diff --git a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java index ff2d5916710..ded0f872c11 100644 --- a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java +++ b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java @@ -270,7 +270,14 @@ public class AccountTypePreferenceLoader { try { // Allows to launch only authenticator owned activities. ApplicationInfo authenticatorAppInf = pm.getApplicationInfo(authDesc.packageName, 0); - return resolvedAppInfo.uid == authenticatorAppInf.uid; + if (resolvedAppInfo.uid == authenticatorAppInf.uid) { + // Explicitly set the component to be same as authenticator to + // prevent launching arbitrary activities. + intent.setComponent(resolvedActivityInfo.getComponentName()); + return true; + } else { + return false; + } } catch (NameNotFoundException e) { Log.e(TAG, "Intent considered unsafe due to exception.", diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java index 1f7b3e512b2..79c7e3a563a 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java @@ -50,6 +50,8 @@ import com.google.android.setupdesign.span.LinkSpan; import com.google.android.setupdesign.template.RequireScrollMixin; import com.google.android.setupdesign.util.DynamicColorPalette; +import java.util.List; + /** * Abstract base class for the intro onboarding activity for biometric enrollment. */ @@ -242,6 +244,19 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase !isScrollNeeded && !enrollmentCompleted ? View.VISIBLE : View.INVISIBLE); } + @Override + protected void onStart() { + super.onStart(); + + if (!getPackageName().equals(getCallingPackage())) { + for (String key : List.of(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, + MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, + MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT)) { + getIntent().removeExtra(key); + } + } + } + @Override protected void onResume() { super.onResume(); @@ -490,14 +505,15 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase getIntent().removeExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT); } - protected void removeEnrollNextBiometricIfSkipEnroll(@Nullable Intent data) { + private void removeEnrollNextBiometricIfSkipEnroll(@Nullable Intent data) { if (data != null && data.getBooleanExtra( MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, false)) { removeEnrollNextBiometric(); } } - protected void handleBiometricResultSkipOrFinished(int resultCode, @Nullable Intent data) { + + private void handleBiometricResultSkipOrFinished(int resultCode, @Nullable Intent data) { removeEnrollNextBiometricIfSkipEnroll(data); if (resultCode == RESULT_SKIP) { onEnrollmentSkipped(data); diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java index 5a3949b8923..f69d8252627 100644 --- a/src/com/android/settings/biometrics/face/FaceSettings.java +++ b/src/com/android/settings/biometrics/face/FaceSettings.java @@ -83,8 +83,8 @@ public class FaceSettings extends DashboardFragment { private FaceManager mFaceManager; private DevicePolicyManager mDevicePolicyManager; private int mUserId; - private int mSensorId; - private long mChallenge; + private int mSensorId = -1; + private long mChallenge = 0; private byte[] mToken; private FaceSettingsAttentionPreferenceController mAttentionController; private FaceSettingsRemoveButtonPreferenceController mRemoveController; @@ -166,12 +166,19 @@ public class FaceSettings extends DashboardFragment { mUserManager = context.getSystemService(UserManager.class); mFaceManager = context.getSystemService(FaceManager.class); mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); - mToken = getIntent().getByteArrayExtra(KEY_TOKEN); - mSensorId = getIntent().getIntExtra(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, -1); - mChallenge = getIntent().getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, 0L); - mUserId = getActivity().getIntent().getIntExtra( - Intent.EXTRA_USER_ID, UserHandle.myUserId()); + final SettingsActivity activity = (SettingsActivity) requireActivity(); + final String callingPackage = activity.getInitialCallingPackage(); + if (callingPackage == null || !callingPackage.equals(activity.getPackageName())) { + mUserId = UserHandle.myUserId(); + } else { + // only allow these extras when called internally by Settings + mToken = getIntent().getByteArrayExtra(KEY_TOKEN); + mSensorId = getIntent().getIntExtra(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, -1); + mChallenge = getIntent().getLongExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, 0L); + mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); + } + mFaceFeatureProvider = FeatureFactory.getFeatureFactory().getFaceFeatureProvider(); if (mUserManager.getUserInfo(mUserId).isManagedProfile()) { diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java index 156df96e04e..701b94c749f 100644 --- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java +++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java @@ -16,6 +16,7 @@ package com.android.settings.notification.history; +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED; import static androidx.core.view.accessibility.AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED; @@ -25,9 +26,11 @@ import android.annotation.ColorInt; import android.app.ActionBar; import android.app.ActivityManager; import android.app.INotificationManager; +import android.app.KeyguardManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Outline; @@ -58,6 +61,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.NotificationExpandButton; import com.android.settings.R; import com.android.settings.notification.NotificationBackend; @@ -68,6 +72,7 @@ import com.android.settingslib.widget.MainSwitchBar; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -113,6 +118,9 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { }; private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + // List of users that have the setting "hide sensitive content" enabled on the lockscreen + private ArrayList mContentRestrictedUsers = new ArrayList<>(); + enum NotificationHistoryEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "User turned on notification history") NOTIFICATION_HISTORY_ON(504), @@ -212,14 +220,14 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { final NotificationHistoryRecyclerView rv = viewForPackage.findViewById(R.id.notification_list); - rv.setAdapter(new NotificationHistoryAdapter(mNm, rv, + rv.setAdapter(new NotificationHistoryAdapter(NotificationHistoryActivity.this, mNm, rv, newCount -> { count.setText(StringUtil.getIcuPluralsString(this, newCount, R.string.notification_history_count)); if (newCount == 0) { viewForPackage.setVisibility(View.GONE); } - }, mUiEventLogger)); + }, mUiEventLogger, mContentRestrictedUsers)); ((NotificationHistoryAdapter) rv.getAdapter()).onRebuildComplete( new ArrayList<>(nhp.notifications)); @@ -263,6 +271,21 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { mPm = getPackageManager(); mUm = getSystemService(UserManager.class); + + mContentRestrictedUsers.clear(); + List users = mUm.getProfiles(getUserId()); + mContentRestrictedUsers.clear(); + for (UserInfo user : users) { + if (Settings.Secure.getIntForUser(getContentResolver(), + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, user.id) == 0) { + LockPatternUtils lpu = new LockPatternUtils(this); + KeyguardManager km = getSystemService(KeyguardManager.class); + if (lpu.isSecure(user.id) && km.isDeviceLocked(user.id)) { + mContentRestrictedUsers.add(user.id); + } + } + } + // wait for history loading and recent/snooze loading mCountdownLatch = new CountDownLatch(2); @@ -317,6 +340,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { if (mCountdownFuture != null) { mCountdownFuture.cancel(true); } + mContentRestrictedUsers.clear(); super.onDestroy(); } @@ -406,7 +430,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { mSnoozedRv.setLayoutManager(lm); mSnoozedRv.setAdapter( new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm, mUm, - true, mUiEventLogger)); + true, mUiEventLogger, mContentRestrictedUsers)); mSnoozedRv.setNestedScrollingEnabled(false); if (snoozed == null || snoozed.length == 0) { @@ -422,7 +446,7 @@ public class NotificationHistoryActivity extends CollapsingToolbarBaseActivity { mDismissedRv.setLayoutManager(dismissLm); mDismissedRv.setAdapter( new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm, mUm, - false, mUiEventLogger)); + false, mUiEventLogger, mContentRestrictedUsers)); mDismissedRv.setNestedScrollingEnabled(false); if (dismissed == null || dismissed.length == 0) { diff --git a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java index 5368f25e76e..44e15442a1a 100644 --- a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java +++ b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java @@ -22,6 +22,7 @@ import static android.provider.Settings.EXTRA_CONVERSATION_ID; import android.app.INotificationManager; import android.app.NotificationHistory.HistoricalNotification; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.RemoteException; @@ -53,16 +54,23 @@ public class NotificationHistoryAdapter extends private List mValues; private OnItemDeletedListener mListener; private UiEventLogger mUiEventLogger; - public NotificationHistoryAdapter(INotificationManager nm, + private ArrayList mContentRestrictedUsers = new ArrayList<>(); + Context mContext; + + public NotificationHistoryAdapter(Context context, + INotificationManager nm, NotificationHistoryRecyclerView listView, OnItemDeletedListener listener, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + ArrayList contentRestrictedUsers) { + mContext = context; mValues = new ArrayList<>(); setHasStableIds(true); listView.setOnItemSwipeDeleteListener(this); mNm = nm; mListener = listener; mUiEventLogger = uiEventLogger; + mContentRestrictedUsers = contentRestrictedUsers; } @Override @@ -81,8 +89,14 @@ public class NotificationHistoryAdapter extends public void onBindViewHolder(final @NonNull NotificationHistoryViewHolder holder, int position) { final HistoricalNotification hn = mValues.get(position); - holder.setTitle(hn.getTitle()); - holder.setSummary(hn.getText()); + // Redact sensitive notification content if needed + if (mContentRestrictedUsers.contains(hn.getUserId())) { + holder.setSummary(mContext.getString( + com.android.internal.R.string.notification_hidden_text)); + } else { + holder.setTitle(hn.getTitle()); + holder.setSummary(hn.getText()); + } holder.setPostedTime(hn.getPostedTimeMs()); final View.OnClickListener onClick = v -> { mUiEventLogger.logWithPosition(NotificationHistoryActivity.NotificationHistoryEvent diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java index 0301d7b7fd5..844e9bc980f 100644 --- a/src/com/android/settings/notification/history/NotificationSbnAdapter.java +++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java @@ -74,9 +74,11 @@ public class NotificationSbnAdapter extends private List mEnabledProfiles = new ArrayList<>(); private boolean mIsSnoozed; private UiEventLogger mUiEventLogger; + private ArrayList mContentRestrictedUsers = new ArrayList<>(); public NotificationSbnAdapter(Context context, PackageManager pm, UserManager um, - boolean isSnoozed, UiEventLogger uiEventLogger) { + boolean isSnoozed, UiEventLogger uiEventLogger, + ArrayList contentRestrictedUsers) { mContext = context; mPm = pm; mUserBadgeCache = new HashMap<>(); @@ -97,6 +99,7 @@ public class NotificationSbnAdapter extends // If true, this is the panel for snoozed notifs, otherwise the one for dismissed notifs. mIsSnoozed = isSnoozed; mUiEventLogger = uiEventLogger; + mContentRestrictedUsers = contentRestrictedUsers; } @Override @@ -114,8 +117,14 @@ public class NotificationSbnAdapter extends holder.setIconBackground(loadBackground(sbn)); holder.setIcon(loadIcon(sbn)); holder.setPackageLabel(loadPackageLabel(sbn.getPackageName()).toString()); - holder.setTitle(getTitleString(sbn.getNotification())); - holder.setSummary(getTextString(mContext, sbn.getNotification())); + // If the notification is from a content restricted user, show generic text. + if (mContentRestrictedUsers.contains(sbn.getNormalizedUserId())) { + holder.setSummary(mContext.getString( + com.android.internal.R.string.notification_hidden_text)); + } else { + holder.setTitle(getTitleString(sbn.getNotification())); + holder.setSummary(getTextString(mContext, sbn.getNotification())); + } holder.setPostedTime(sbn.getPostTime()); holder.setDividerVisible(position < (mValues.size() -1)); int userId = normalizeUserId(sbn); diff --git a/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java b/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java index 69ac6b100be..61987cdd8ad 100644 --- a/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java +++ b/src/com/android/settings/security/ContentProtectionTogglePreferenceController.java @@ -132,7 +132,7 @@ public class ContentProtectionTogglePreferenceController extends TogglePreferenc UserManager userManager = mContext.getSystemService(UserManager.class); if (userManager != null - && userManager.isGuestUser() + && !userManager.isAdminUser() && mSwitchBar != null) { mSwitchBar.setEnabled(false); } diff --git a/src/com/android/settings/security/CredentialStorage.java b/src/com/android/settings/security/CredentialStorage.java index b1c65a7c3c0..5ea9b7ac21f 100644 --- a/src/com/android/settings/security/CredentialStorage.java +++ b/src/com/android/settings/security/CredentialStorage.java @@ -17,6 +17,7 @@ package com.android.settings.security; import android.app.Activity; +import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.DialogInterface; @@ -322,15 +323,25 @@ public final class CredentialStorage extends FragmentActivity { } } + private String getCallingPackageName() { + try { + return ActivityManager.getService().getLaunchedFromPackage(getActivityToken()); + } catch (RemoteException re) { + // Error talking to ActivityManager, just give up + return null; + } + } + /** * Check that the caller is either certinstaller or Settings running in a profile of this user. */ private boolean checkCallerIsCertInstallerOrSelfInProfile() { - if (TextUtils.equals("com.android.certinstaller", getCallingPackage())) { + String callingPackage = getCallingPackageName(); + if (TextUtils.equals("com.android.certinstaller", callingPackage)) { // CertInstaller is allowed to install credentials if it has the same signature as // Settings package. return getPackageManager().checkSignatures( - getCallingPackage(), getPackageName()) == PackageManager.SIGNATURE_MATCH; + callingPackage, getPackageName()) == PackageManager.SIGNATURE_MATCH; } final int launchedFromUserId; diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index c42e2f57b1d..5b2a86fff42 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -639,8 +639,11 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen } else if (restrictionsIntent != null) { preference.setRestrictions(restrictions); if (invokeIfCustom && AppRestrictionsFragment.this.isResumed()) { + // We don't necessarily trust the given intent to launch its component. + // We will first check it, and only use parts of it that were indeed checked. + final Intent vettedIntent; try { - assertSafeToStartCustomActivity(restrictionsIntent); + vettedIntent = assertSafeToStartCustomActivity(restrictionsIntent); } catch (ActivityNotFoundException | SecurityException e) { // return without startActivity Log.e(TAG, "Cannot start restrictionsIntent " + e); @@ -651,15 +654,20 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen int requestCode = generateCustomActivityRequestCode( RestrictionsResultReceiver.this.preference); AppRestrictionsFragment.this.startActivityForResult( - new Intent(restrictionsIntent), requestCode); + vettedIntent, requestCode); } } } - private void assertSafeToStartCustomActivity(Intent intent) { + /** + * Checks that it is safe to start the custom activity, and, if so, returns a copy of the + * Intent using its vetted components. + */ + private Intent assertSafeToStartCustomActivity(Intent intent) { EventLog.writeEvent(0x534e4554, "223578534", -1 /* UID */, ""); + final Intent vettedIntent = new Intent(intent); ResolveInfo resolveInfo = mPackageManager.resolveActivity( - intent, PackageManager.MATCH_DEFAULT_ONLY); + vettedIntent, PackageManager.MATCH_DEFAULT_ONLY); if (resolveInfo == null) { throw new ActivityNotFoundException("No result for resolving " + intent); @@ -670,6 +678,12 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen throw new SecurityException("Application " + packageName + " is not allowed to start activity " + intent); } + + // We were able to vet the given intent this time. Make a copy using the components + // that were used to do the vetting, since that's as much as we've verified is safe. + vettedIntent.setComponent(activityInfo.getComponentName()); + vettedIntent.setPackage(activityInfo.packageName); + return vettedIntent; } } diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java index 81a72694592..984073f19b5 100644 --- a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java @@ -32,11 +32,13 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; +import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -64,6 +66,7 @@ import com.android.settings.R; import com.android.settings.Settings; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricUtils; +import com.android.settings.biometrics.MultiBiometricEnrollHelper; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.SettingsShadowResources; @@ -206,6 +209,12 @@ public class FaceEnrollIntroductionTest { testIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, FaceEnrollOptions.ENROLL_REASON_SETTINGS); + testIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, + mock(PendingIntent.class)); + testIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT, + mock(PendingIntent.class)); + testIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, false); + when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn( null /* Simulate no posture intent */); mContext = spy(ApplicationProvider.getApplicationContext()); @@ -690,4 +699,16 @@ public class FaceEnrollIntroductionTest { .isEqualTo(FaceEnrollOptions.ENROLL_REASON_SETTINGS); } + @Test + public void drops_pendingIntents() { + setupActivity(); + + mController.start(); + Shadows.shadowOf(Looper.getMainLooper()).idle(); + + final Intent intent = mActivity.getIntent(); + assertThat(intent.hasExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL)).isFalse(); + assertThat(intent.hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE)).isFalse(); + assertThat(intent.hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT)).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java index edd50a6e6f6..24fb7165fd0 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -44,6 +45,7 @@ import android.hardware.fingerprint.FingerprintEnrollOptions; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.os.Looper; import android.os.UserManager; import android.view.View; @@ -55,6 +57,7 @@ import com.android.internal.widget.VerifyCredentialResponse; import com.android.settings.R; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.GatekeeperPasswordProvider; +import com.android.settings.biometrics.MultiBiometricEnrollHelper; import com.google.android.setupcompat.util.WizardManagerHelper; import com.google.android.setupdesign.GlifLayout; @@ -70,6 +73,7 @@ import org.mockito.stubbing.Answer; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; import org.robolectric.android.controller.ActivityController; import java.util.ArrayList; @@ -353,7 +357,19 @@ public class FingerprintEnrollIntroductionTest { false); Assert.assertEquals(View.INVISIBLE, mFingerprintEnrollIntroduction.getSecondaryFooterButton().getVisibility()); + } + + @Test + public void drops_pendingIntents() { + setupFingerprintEnrollIntroWith(newExternalPendingIntent()); + + mController.start(); + Shadows.shadowOf(Looper.getMainLooper()).idle(); + final Intent intent = mFingerprintEnrollIntroduction.getIntent(); + assertThat(intent.hasExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL)).isFalse(); + assertThat(intent.hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE)).isFalse(); + assertThat(intent.hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT)).isFalse(); } private Intent newTokenOnlyIntent() { @@ -383,6 +399,15 @@ public class FingerprintEnrollIntroductionTest { .putExtra(EXTRA_KEY_GK_PW_HANDLE, 1L); } + private Intent newExternalPendingIntent() { + return newTokenOnlyIntent() + .putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, + mock(PendingIntent.class)) + .putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT, + mock(PendingIntent.class)) + .putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, false); + } + private VerifyCredentialResponse newGoodCredential(long gkPwHandle, @NonNull byte[] hat) { return new VerifyCredentialResponse.Builder() .setGatekeeperPasswordHandle(gkPwHandle) diff --git a/tests/robotests/src/com/android/settings/security/ContentProtectionTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/ContentProtectionTogglePreferenceControllerTest.java index 6514a4e4043..5f63da19fe6 100644 --- a/tests/robotests/src/com/android/settings/security/ContentProtectionTogglePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/security/ContentProtectionTogglePreferenceControllerTest.java @@ -85,7 +85,7 @@ public class ContentProtectionTogglePreferenceControllerTest { @Before public void setUp() { mShadowUserManager = ShadowUserManager.getShadow(); - mShadowUserManager.setGuestUser(false); + mShadowUserManager.setIsAdminUser(true); mController = new TestContentProtectionTogglePreferenceController(); SettingsMainSwitchPreference switchPreference = new SettingsMainSwitchPreference(mContext); when(mMockPreferenceScreen.findPreference(mController.getPreferenceKey())) @@ -277,8 +277,8 @@ public class ContentProtectionTogglePreferenceControllerTest { } @Test - public void updateState_flagEnabled_noEnforcedAdmin_guestUser_switchBarDisabled() { - mShadowUserManager.setGuestUser(true); + public void updateState_flagEnabled_noEnforcedAdmin_nonAdminUser_switchBarDisabled() { + mShadowUserManager.setIsAdminUser(false); mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED); mContentProtectionPolicy = DevicePolicyManager.CONTENT_PROTECTION_ENABLED; setupForUpdateState(); @@ -289,13 +289,15 @@ public class ContentProtectionTogglePreferenceControllerTest { } @Test - public void updateState_flagEnabled_noEnforcedAdmin_nonGuestUser_switchBarEnabled() { + public void updateState_flagEnabled_noEnforcedAdmin_adminUser_switchBarEnabled() { + mShadowUserManager.setIsAdminUser(true); mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED); mContentProtectionPolicy = DevicePolicyManager.CONTENT_PROTECTION_ENABLED; setupForUpdateState(); mController.updateState(mMockSwitchPreference); + // Verify that the switch bar is *not* set to disabled. verify(mMockSwitchPreference, never()).setEnabled(false); } -- GitLab