diff --git a/res/values/strings.xml b/res/values/strings.xml index 0ddab5bba51..9ab843be571 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -809,6 +809,12 @@ Setup needed + Face + + Face for work + + Face for private space + Face Unlock Face Unlock for work @@ -938,6 +944,8 @@ When using Fingerprint Unlock Fingerprint for work + + Fingerprint for work Check enrolled fingerprints @@ -1381,6 +1389,8 @@ Tap to set up Fingerprint Unlock for private space + + Fingerprint for private space Face Unlock for private space diff --git a/src/com/android/settings/biometrics/face/FaceStatusUtils.java b/src/com/android/settings/biometrics/face/FaceStatusUtils.java index 5af0a8a220b..302d6773213 100644 --- a/src/com/android/settings/biometrics/face/FaceStatusUtils.java +++ b/src/com/android/settings/biometrics/face/FaceStatusUtils.java @@ -26,12 +26,11 @@ import com.android.settings.R; import com.android.settings.Settings; import com.android.settings.Utils; import com.android.settings.biometrics.ParentalControlsUtils; +import com.android.settings.flags.Flags; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; -/** - * Utilities for face details shared between Security Settings and Safety Center. - */ +/** Utilities for face details shared between Security Settings and Safety Center. */ public class FaceStatusUtils { private final int mUserId; @@ -44,9 +43,7 @@ public class FaceStatusUtils { mUserId = userId; } - /** - * Returns whether the face settings entity should be shown. - */ + /** Returns whether the face settings entity should be shown. */ public boolean isAvailable() { return !Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFaceHardware(mContext); } @@ -61,55 +58,70 @@ public class FaceStatusUtils { mContext, BiometricAuthenticator.TYPE_FACE); } - /** - * Returns the title of face settings entity. - */ + /** Returns the title of face settings entity. */ public String getTitle() { UserManager userManager = mContext.getSystemService(UserManager.class); if (userManager != null && userManager.isProfile()) { return mContext.getString( Utils.isPrivateProfile(mUserId, mContext) - ? R.string.private_space_face_unlock_title - : R.string.security_settings_face_profile_preference_title); + ? getPrivateSpaceTitle() + : getWorkProfileTitle()); } else { - return mContext.getString(R.string.security_settings_face_preference_title); + return mContext.getString(getRegularTitle()); } } - /** - * Returns the summary of face settings entity. - */ + private int getPrivateSpaceTitle() { + if (Flags.biometricsOnboardingEducation()) { + return R.string.private_space_face_unlock_title_new; + } + return R.string.private_space_face_unlock_title; + } + + private int getWorkProfileTitle() { + if (Flags.biometricsOnboardingEducation()) { + return R.string.security_settings_face_profile_preference_title_new; + } + return R.string.security_settings_face_profile_preference_title; + } + + private int getRegularTitle() { + if (Flags.biometricsOnboardingEducation()) { + return R.string.security_settings_face_preference_title_new; + } + return R.string.security_settings_face_preference_title; + } + + /** Returns the summary of face settings entity. */ public String getSummary() { if (shouldShowDisabledByAdminStr()) { return mContext.getString( com.android.settingslib.widget.restricted.R.string.disabled_by_admin); } else { - return mContext.getResources().getString(hasEnrolled() - ? R.string.security_settings_face_preference_summary - : R.string.security_settings_face_preference_summary_none); + return mContext.getResources() + .getString( + hasEnrolled() + ? R.string.security_settings_face_preference_summary + : R.string.security_settings_face_preference_summary_none); } } - /** - * Returns the class name of the Settings page corresponding to face settings. - */ + /** Returns the class name of the Settings page corresponding to face settings. */ public String getSettingsClassName() { - return hasEnrolled() ? Settings.FaceSettingsInternalActivity.class.getName() + return hasEnrolled() + ? Settings.FaceSettingsInternalActivity.class.getName() : FaceEnrollIntroductionInternal.class.getName(); } - /** - * Returns whether at least one face template has been enrolled. - */ + /** Returns whether at least one face template has been enrolled. */ public boolean hasEnrolled() { return mFaceManager.hasEnrolledTemplates(mUserId); } - /** - * Indicates if the face feature is enabled or disabled by the Device Admin. - */ + /** Indicates if the face feature is enabled or disabled by the Device Admin. */ private boolean shouldShowDisabledByAdminStr() { return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( - mContext, DevicePolicyManager.KEYGUARD_DISABLE_FACE, mUserId) != null; + mContext, DevicePolicyManager.KEYGUARD_DISABLE_FACE, mUserId) + != null; } } diff --git a/src/com/android/settings/biometrics/face/FaceUpdater.java b/src/com/android/settings/biometrics/face/FaceUpdater.java index ddb68129df1..99d6f09fc12 100644 --- a/src/com/android/settings/biometrics/face/FaceUpdater.java +++ b/src/com/android/settings/biometrics/face/FaceUpdater.java @@ -29,7 +29,9 @@ import androidx.annotation.Nullable; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricUtils; +import com.android.settings.flags.Flags; import com.android.settings.safetycenter.BiometricsSafetySource; +import com.android.settings.safetycenter.FaceSafetySource; /** * Responsible for making {@link FaceManager#enroll} and {@link FaceManager#remove} calls and thus @@ -51,20 +53,43 @@ public class FaceUpdater { } /** Wrapper around the {@link FaceManager#enroll} method. */ - public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, - FaceManager.EnrollmentCallback callback, int[] disabledFeatures, Intent intent) { - this.enroll(userId, hardwareAuthToken, cancel, - new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures, - null, false, intent); + public void enroll( + int userId, + byte[] hardwareAuthToken, + CancellationSignal cancel, + FaceManager.EnrollmentCallback callback, + int[] disabledFeatures, + Intent intent) { + this.enroll( + userId, + hardwareAuthToken, + cancel, + new NotifyingEnrollmentCallback(mContext, callback), + disabledFeatures, + null, + false, + intent); } /** Wrapper around the {@link FaceManager#enroll} method. */ - public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, - FaceManager.EnrollmentCallback callback, int[] disabledFeatures, - @Nullable Surface previewSurface, boolean debugConsent, Intent intent) { - mFaceManager.enroll(userId, hardwareAuthToken, cancel, - new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures, - previewSurface, debugConsent, toFaceEnrollOptions(intent)); + public void enroll( + int userId, + byte[] hardwareAuthToken, + CancellationSignal cancel, + FaceManager.EnrollmentCallback callback, + int[] disabledFeatures, + @Nullable Surface previewSurface, + boolean debugConsent, + Intent intent) { + mFaceManager.enroll( + userId, + hardwareAuthToken, + cancel, + new NotifyingEnrollmentCallback(mContext, callback), + disabledFeatures, + previewSurface, + debugConsent, + toFaceEnrollOptions(intent)); } /** Wrapper around the {@link FaceManager#remove} method. */ @@ -73,17 +98,15 @@ public class FaceUpdater { } /** - * Decorator of the {@link FaceManager.EnrollmentCallback} class that notifies other - * interested parties that a face setting has changed. + * Decorator of the {@link FaceManager.EnrollmentCallback} class that notifies other interested + * parties that a face setting has changed. */ - private static class NotifyingEnrollmentCallback - extends FaceManager.EnrollmentCallback { + private static class NotifyingEnrollmentCallback extends FaceManager.EnrollmentCallback { private final Context mContext; private final FaceManager.EnrollmentCallback mCallback; - NotifyingEnrollmentCallback(Context context, - FaceManager.EnrollmentCallback callback) { + NotifyingEnrollmentCallback(Context context, FaceManager.EnrollmentCallback callback) { mContext = context; mCallback = callback; } @@ -99,8 +122,14 @@ public class FaceUpdater { } @Override - public void onEnrollmentFrame(int helpCode, @Nullable CharSequence helpMessage, - @Nullable FaceEnrollCell cell, int stage, float pan, float tilt, float distance) { + public void onEnrollmentFrame( + int helpCode, + @Nullable CharSequence helpMessage, + @Nullable FaceEnrollCell cell, + int stage, + float pan, + float tilt, + float distance) { mCallback.onEnrollmentFrame(helpCode, helpMessage, cell, stage, pan, tilt, distance); } @@ -108,14 +137,18 @@ public class FaceUpdater { public void onEnrollmentProgress(int remaining) { mCallback.onEnrollmentProgress(remaining); if (remaining == 0) { - BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + if (Flags.biometricsOnboardingEducation()) { + FaceSafetySource.onBiometricsChanged(mContext); + } else { + BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + } } } } /** - * Decorator of the {@link FaceManager.RemovalCallback} class that notifies other - * interested parties that a face setting has changed. + * Decorator of the {@link FaceManager.RemovalCallback} class that notifies other interested + * parties that a face setting has changed. */ private static class NotifyingRemovalCallback extends FaceManager.RemovalCallback { @@ -135,7 +168,11 @@ public class FaceUpdater { @Override public void onRemovalSucceeded(@Nullable Face fp, int remaining) { mCallback.onRemovalSucceeded(fp, remaining); - BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + if (Flags.biometricsOnboardingEducation()) { + FaceSafetySource.onBiometricsChanged(mContext); + } else { + BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + } } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java index a7c9e9d7be8..1ca564c8e35 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java @@ -25,29 +25,26 @@ import android.os.UserManager; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.ParentalControlsUtils; +import com.android.settings.flags.Flags; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.utils.StringUtil; -/** - * Utilities for fingerprint details shared between Security Settings and Safety Center. - */ +/** Utilities for fingerprint details shared between Security Settings and Safety Center. */ public class FingerprintStatusUtils { private final int mUserId; private final Context mContext; private final FingerprintManager mFingerprintManager; - public FingerprintStatusUtils(Context context, FingerprintManager fingerprintManager, - int userId) { + public FingerprintStatusUtils( + Context context, FingerprintManager fingerprintManager, int userId) { mContext = context; mFingerprintManager = fingerprintManager; mUserId = userId; } - /** - * Returns whether the fingerprint settings entity should be shown. - */ + /** Returns whether the fingerprint settings entity should be shown. */ public boolean isAvailable() { return !Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFingerprintHardware(mContext); @@ -62,24 +59,42 @@ public class FingerprintStatusUtils { return ParentalControlsUtils.parentConsentRequired( mContext, BiometricAuthenticator.TYPE_FINGERPRINT); } - /** - * Returns the title of fingerprint settings entity. - */ + + /** Returns the title of fingerprint settings entity. */ public String getTitle() { UserManager userManager = mContext.getSystemService(UserManager.class); if (userManager != null && userManager.isProfile()) { return mContext.getString( Utils.isPrivateProfile(mUserId, mContext) - ? R.string.private_space_fingerprint_unlock_title - : R.string.security_settings_work_fingerprint_preference_title); + ? getPrivateSpaceTitle() + : getWorkProfileTitle()); } else { - return mContext.getString(R.string.security_settings_fingerprint_preference_title); + return mContext.getString(getRegularTitle()); } } - /** - * Returns the summary of fingerprint settings entity. - */ + private int getPrivateSpaceTitle() { + if (Flags.biometricsOnboardingEducation()) { + return R.string.private_space_fingerprint_unlock_title_new; + } + return R.string.private_space_fingerprint_unlock_title; + } + + private int getWorkProfileTitle() { + if (Flags.biometricsOnboardingEducation()) { + return R.string.security_settings_work_fingerprint_preference_title_new; + } + return R.string.security_settings_work_fingerprint_preference_title; + } + + private int getRegularTitle() { + if (Flags.biometricsOnboardingEducation()) { + return R.string.security_settings_fingerprint; // doesn't have an overlay + } + return R.string.security_settings_fingerprint_preference_title; + } + + /** Returns the summary of fingerprint settings entity. */ public String getSummary() { if (shouldShowDisabledByAdminStr()) { return mContext.getString( @@ -87,7 +102,9 @@ public class FingerprintStatusUtils { } if (hasEnrolled()) { final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size(); - return StringUtil.getIcuPluralsString(mContext, numEnrolled, + return StringUtil.getIcuPluralsString( + mContext, + numEnrolled, R.string.security_settings_fingerprint_preference_summary); } else { return mContext.getString( @@ -95,25 +112,20 @@ public class FingerprintStatusUtils { } } - /** - * Returns the class name of the Settings page corresponding to fingerprint settings. - */ + /** Returns the class name of the Settings page corresponding to fingerprint settings. */ public String getSettingsClassName() { return FingerprintSettings.class.getName(); } - /** - * Returns whether at least one fingerprint has been enrolled. - */ + /** Returns whether at least one fingerprint has been enrolled. */ public boolean hasEnrolled() { return mFingerprintManager.hasEnrolledFingerprints(mUserId); } - /** - * Indicates if the fingerprint feature should show the "Disabled by Admin" string. - */ + /** Indicates if the fingerprint feature should show the "Disabled by Admin" string. */ private boolean shouldShowDisabledByAdminStr() { return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( - mContext, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId) != null; + mContext, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId) + != null; } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java index ea9abf1bb5c..84ee27e3586 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java @@ -27,7 +27,9 @@ import androidx.annotation.Nullable; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricUtils; +import com.android.settings.flags.Flags; import com.android.settings.safetycenter.BiometricsSafetySource; +import com.android.settings.safetycenter.FingerprintSafetySource; /** * Responsible for making {@link FingerprintManager#enroll} and {@link FingerprintManager#remove} @@ -49,11 +51,19 @@ public class FingerprintUpdater { } /** Wrapper around the {@link FingerprintManager#enroll} method. */ - public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, + public void enroll( + byte[] hardwareAuthToken, + CancellationSignal cancel, + int userId, FingerprintManager.EnrollmentCallback callback, - @FingerprintManager.EnrollReason int enrollReason, Intent intent) { - mFingerprintManager.enroll(hardwareAuthToken, cancel, userId, - new NotifyingEnrollmentCallback(mContext, callback), enrollReason, + @FingerprintManager.EnrollReason int enrollReason, + Intent intent) { + mFingerprintManager.enroll( + hardwareAuthToken, + cancel, + userId, + new NotifyingEnrollmentCallback(mContext, callback), + enrollReason, toFingerprintEnrollOptions(intent)); } @@ -66,14 +76,13 @@ public class FingerprintUpdater { * Decorator of the {@link FingerprintManager.EnrollmentCallback} class that notifies other * interested parties that a fingerprint setting has changed. */ - private static class NotifyingEnrollmentCallback - extends FingerprintManager.EnrollmentCallback { + private static class NotifyingEnrollmentCallback extends FingerprintManager.EnrollmentCallback { private final Context mContext; private final FingerprintManager.EnrollmentCallback mCallback; - NotifyingEnrollmentCallback(Context context, - FingerprintManager.EnrollmentCallback callback) { + NotifyingEnrollmentCallback( + Context context, FingerprintManager.EnrollmentCallback callback) { mContext = context; mCallback = callback; } @@ -92,7 +101,11 @@ public class FingerprintUpdater { public void onEnrollmentProgress(int remaining) { mCallback.onEnrollmentProgress(remaining); if (remaining == 0) { - BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + if (Flags.biometricsOnboardingEducation()) { + FingerprintSafetySource.onBiometricsChanged(mContext); + } else { + BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + } } } @@ -139,7 +152,11 @@ public class FingerprintUpdater { @Override public void onRemovalSucceeded(@Nullable Fingerprint fp, int remaining) { mCallback.onRemovalSucceeded(fp, remaining); - BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + if (Flags.biometricsOnboardingEducation()) { + FingerprintSafetySource.onBiometricsChanged(mContext); + } else { + BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed + } } } diff --git a/src/com/android/settings/safetycenter/BiometricSourcesUtils.java b/src/com/android/settings/safetycenter/BiometricSourcesUtils.java new file mode 100644 index 00000000000..fcda654b761 --- /dev/null +++ b/src/com/android/settings/safetycenter/BiometricSourcesUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.safetycenter; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.safetycenter.SafetyEvent; +import android.safetycenter.SafetySourceData; +import android.safetycenter.SafetySourceStatus; + +/** Static helpers for setting SafetyCenter data for biometric safety sources. */ +public final class BiometricSourcesUtils { + + public static final int REQUEST_CODE_COMBINED_BIOMETRIC_SETTING = 10; + public static final int REQUEST_CODE_FACE_SETTING = 20; + public static final int REQUEST_CODE_FINGERPRINT_SETTING = 30; + + private BiometricSourcesUtils() {} + + /** Sets data for one of the biometrics sources */ + public static void setBiometricSafetySourceData( + String safetySourceId, + Context context, + String title, + String summary, + PendingIntent pendingIntent, + boolean enabled, + boolean hasEnrolled, + SafetyEvent safetyEvent) { + int severityLevel = + enabled && hasEnrolled + ? SafetySourceData.SEVERITY_LEVEL_INFORMATION + : SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED; + + SafetySourceStatus status = + new SafetySourceStatus.Builder(title, summary, severityLevel) + .setPendingIntent(pendingIntent) + .setEnabled(enabled) + .build(); + SafetySourceData safetySourceData = + new SafetySourceData.Builder().setStatus(status).build(); + + SafetyCenterManagerWrapper.get() + .setSafetySourceData(context, safetySourceId, safetySourceData, safetyEvent); + } + + /** Helper method for creating a pending intent. */ + public static PendingIntent createPendingIntent( + Context context, Intent intent, int requestCode) { + return PendingIntent.getActivity( + context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE); + } +} diff --git a/src/com/android/settings/safetycenter/BiometricsSafetySource.java b/src/com/android/settings/safetycenter/BiometricsSafetySource.java index c93ced1a50d..97a922e1831 100644 --- a/src/com/android/settings/safetycenter/BiometricsSafetySource.java +++ b/src/com/android/settings/safetycenter/BiometricsSafetySource.java @@ -16,9 +16,11 @@ package com.android.settings.safetycenter; -import android.app.PendingIntent; +import static com.android.settings.safetycenter.BiometricSourcesUtils.REQUEST_CODE_COMBINED_BIOMETRIC_SETTING; +import static com.android.settings.safetycenter.BiometricSourcesUtils.REQUEST_CODE_FACE_SETTING; +import static com.android.settings.safetycenter.BiometricSourcesUtils.REQUEST_CODE_FINGERPRINT_SETTING; + import android.content.Context; -import android.content.Intent; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; @@ -26,8 +28,6 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.safetycenter.SafetyEvent; -import android.safetycenter.SafetySourceData; -import android.safetycenter.SafetySourceStatus; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricNavigationUtils; @@ -41,9 +41,6 @@ import com.android.settingslib.RestrictedLockUtils; public final class BiometricsSafetySource { public static final String SAFETY_SOURCE_ID = "AndroidBiometrics"; - private static final int REQUEST_CODE_COMBINED_BIOMETRIC_SETTING = 10; - private static final int REQUEST_CODE_FACE_SETTING = 20; - private static final int REQUEST_CODE_FINGERPRINT_SETTING = 30; private BiometricsSafetySource() {} @@ -53,42 +50,38 @@ public final class BiometricsSafetySource { return; } - final UserHandle userHandle = Process.myUserHandle(); - final int userId = userHandle.getIdentifier(); - final UserManager userManager = UserManager.get(context); + UserHandle userHandle = Process.myUserHandle(); + int userId = userHandle.getIdentifier(); + UserManager userManager = UserManager.get(context); UserHandle profileParentUserHandle = userManager.getProfileParent(userHandle); if (profileParentUserHandle == null) { profileParentUserHandle = userHandle; } - final Context profileParentContext = - context.createContextAsUser(profileParentUserHandle, 0); + Context profileParentContext = context.createContextAsUser(profileParentUserHandle, 0); if (android.os.Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures() && userManager.isPrivateProfile()) { // SC always expects a response from the source if the broadcast has been sent for this // source, therefore, we need to send a null SafetySourceData. - SafetyCenterManagerWrapper.get().setSafetySourceData( - context, - SAFETY_SOURCE_ID, - /* safetySourceData= */ null, - safetyEvent); + SafetyCenterManagerWrapper.get() + .setSafetySourceData( + context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent); return; } - final BiometricNavigationUtils biometricNavigationUtils = - new BiometricNavigationUtils(userId); - final CombinedBiometricStatusUtils combinedBiometricStatusUtils = + BiometricNavigationUtils biometricNavigationUtils = new BiometricNavigationUtils(userId); + CombinedBiometricStatusUtils combinedBiometricStatusUtils = new CombinedBiometricStatusUtils(context, userId); - final ActiveUnlockStatusUtils activeUnlockStatusUtils = - new ActiveUnlockStatusUtils(context); + ActiveUnlockStatusUtils activeUnlockStatusUtils = new ActiveUnlockStatusUtils(context); if (!userManager.isProfile() && activeUnlockStatusUtils.isAvailable()) { - final RestrictedLockUtils.EnforcedAdmin disablingAdmin = + RestrictedLockUtils.EnforcedAdmin disablingAdmin = combinedBiometricStatusUtils.getDisablingAdmin(); - setBiometricSafetySourceData( + BiometricSourcesUtils.setBiometricSafetySourceData( + SAFETY_SOURCE_ID, context, activeUnlockStatusUtils.getTitleForActiveUnlock(), combinedBiometricStatusUtils.getSummary(), - createPendingIntent( + BiometricSourcesUtils.createPendingIntent( context, biometricNavigationUtils.getBiometricSettingsIntent( context, @@ -102,13 +95,14 @@ public final class BiometricsSafetySource { return; } if (combinedBiometricStatusUtils.isAvailable()) { - final RestrictedLockUtils.EnforcedAdmin disablingAdmin = + RestrictedLockUtils.EnforcedAdmin disablingAdmin = combinedBiometricStatusUtils.getDisablingAdmin(); - setBiometricSafetySourceData( + BiometricSourcesUtils.setBiometricSafetySourceData( + SAFETY_SOURCE_ID, context, combinedBiometricStatusUtils.getTitle(), combinedBiometricStatusUtils.getSummary(), - createPendingIntent( + BiometricSourcesUtils.createPendingIntent( profileParentContext, biometricNavigationUtils .getBiometricSettingsIntent( @@ -125,17 +119,17 @@ public final class BiometricsSafetySource { return; } - final FaceManager faceManager = Utils.getFaceManagerOrNull(context); - final FaceStatusUtils faceStatusUtils = new FaceStatusUtils(context, faceManager, userId); + FaceManager faceManager = Utils.getFaceManagerOrNull(context); + FaceStatusUtils faceStatusUtils = new FaceStatusUtils(context, faceManager, userId); if (faceStatusUtils.isAvailable()) { - final RestrictedLockUtils.EnforcedAdmin disablingAdmin = - faceStatusUtils.getDisablingAdmin(); - setBiometricSafetySourceData( + RestrictedLockUtils.EnforcedAdmin disablingAdmin = faceStatusUtils.getDisablingAdmin(); + BiometricSourcesUtils.setBiometricSafetySourceData( + SAFETY_SOURCE_ID, context, faceStatusUtils.getTitle(), faceStatusUtils.getSummary(), - createPendingIntent( + BiometricSourcesUtils.createPendingIntent( profileParentContext, biometricNavigationUtils .getBiometricSettingsIntent( @@ -152,18 +146,19 @@ public final class BiometricsSafetySource { return; } - final FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(context); - final FingerprintStatusUtils fingerprintStatusUtils = + FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(context); + FingerprintStatusUtils fingerprintStatusUtils = new FingerprintStatusUtils(context, fingerprintManager, userId); if (fingerprintStatusUtils.isAvailable()) { - final RestrictedLockUtils.EnforcedAdmin disablingAdmin = + RestrictedLockUtils.EnforcedAdmin disablingAdmin = fingerprintStatusUtils.getDisablingAdmin(); - setBiometricSafetySourceData( + BiometricSourcesUtils.setBiometricSafetySourceData( + SAFETY_SOURCE_ID, context, fingerprintStatusUtils.getTitle(), fingerprintStatusUtils.getSummary(), - createPendingIntent( + BiometricSourcesUtils.createPendingIntent( profileParentContext, biometricNavigationUtils .getBiometricSettingsIntent( @@ -191,35 +186,4 @@ public final class BiometricsSafetySource { new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED) .build()); } - - private static void setBiometricSafetySourceData( - Context context, - String title, - String summary, - PendingIntent pendingIntent, - boolean enabled, - boolean hasEnrolled, - SafetyEvent safetyEvent) { - final int severityLevel = - enabled && hasEnrolled - ? SafetySourceData.SEVERITY_LEVEL_INFORMATION - : SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED; - - final SafetySourceStatus status = - new SafetySourceStatus.Builder(title, summary, severityLevel) - .setPendingIntent(pendingIntent) - .setEnabled(enabled) - .build(); - final SafetySourceData safetySourceData = - new SafetySourceData.Builder().setStatus(status).build(); - - SafetyCenterManagerWrapper.get() - .setSafetySourceData(context, SAFETY_SOURCE_ID, safetySourceData, safetyEvent); - } - - private static PendingIntent createPendingIntent( - Context context, Intent intent, int requestCode) { - return PendingIntent.getActivity( - context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE); - } } diff --git a/src/com/android/settings/safetycenter/FaceSafetySource.java b/src/com/android/settings/safetycenter/FaceSafetySource.java new file mode 100644 index 00000000000..a945bc02c70 --- /dev/null +++ b/src/com/android/settings/safetycenter/FaceSafetySource.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.safetycenter; + +import static com.android.settings.safetycenter.BiometricSourcesUtils.REQUEST_CODE_FACE_SETTING; + +import android.content.Context; +import android.hardware.face.FaceManager; +import android.os.Bundle; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.safetycenter.SafetyEvent; + +import com.android.settings.Utils; +import com.android.settings.biometrics.BiometricNavigationUtils; +import com.android.settings.biometrics.face.FaceStatusUtils; +import com.android.settingslib.RestrictedLockUtils; + +/** Face biometrics Safety Source for Safety Center. */ +public final class FaceSafetySource { + + public static final String SAFETY_SOURCE_ID = "AndroidFaceUnlock"; + + private FaceSafetySource() {} + + /** Sets biometric safety data for Safety Center. */ + public static void setSafetySourceData(Context context, SafetyEvent safetyEvent) { + if (!SafetyCenterManagerWrapper.get().isEnabled(context)) { + return; + } + + // Handle private profile case + UserManager userManager = UserManager.get(context); + if (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enablePrivateSpaceFeatures() + && userManager.isPrivateProfile()) { + // SC always expects a response from the source if the broadcast has been sent for this + // source, therefore, we need to send a null SafetySourceData. + SafetyCenterManagerWrapper.get() + .setSafetySourceData( + context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent); + return; + } + + UserHandle userHandle = Process.myUserHandle(); + int userId = userHandle.getIdentifier(); + FaceManager faceManager = Utils.getFaceManagerOrNull(context); + FaceStatusUtils faceStatusUtils = new FaceStatusUtils(context, faceManager, userId); + BiometricNavigationUtils biometricNavigationUtils = new BiometricNavigationUtils(userId); + UserHandle profileParentUserHandle = userManager.getProfileParent(userHandle); + if (profileParentUserHandle == null) { + profileParentUserHandle = userHandle; + } + Context profileParentContext = context.createContextAsUser(profileParentUserHandle, 0); + + if (Utils.hasFaceHardware(context)) { + RestrictedLockUtils.EnforcedAdmin disablingAdmin = faceStatusUtils.getDisablingAdmin(); + BiometricSourcesUtils.setBiometricSafetySourceData( + SAFETY_SOURCE_ID, + context, + faceStatusUtils.getTitle(), + faceStatusUtils.getSummary(), + BiometricSourcesUtils.createPendingIntent( + profileParentContext, + biometricNavigationUtils + .getBiometricSettingsIntent( + context, + faceStatusUtils.getSettingsClassName(), + disablingAdmin, + Bundle.EMPTY) + .setIdentifier(Integer.toString(userId)), + REQUEST_CODE_FACE_SETTING), + disablingAdmin == null /* enabled */, + faceStatusUtils.hasEnrolled(), + safetyEvent); + + return; + } + + SafetyCenterManagerWrapper.get() + .setSafetySourceData( + context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent); + } + + /** Notifies Safety Center of a change in face biometrics settings. */ + public static void onBiometricsChanged(Context context) { + setSafetySourceData( + context, + new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED) + .build()); + } +} diff --git a/src/com/android/settings/safetycenter/FingerprintSafetySource.java b/src/com/android/settings/safetycenter/FingerprintSafetySource.java new file mode 100644 index 00000000000..9d5d3b7e36e --- /dev/null +++ b/src/com/android/settings/safetycenter/FingerprintSafetySource.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.safetycenter; + +import static com.android.settings.safetycenter.BiometricSourcesUtils.REQUEST_CODE_FINGERPRINT_SETTING; + +import android.content.Context; +import android.hardware.fingerprint.FingerprintManager; +import android.os.Bundle; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.safetycenter.SafetyEvent; + +import com.android.settings.Utils; +import com.android.settings.biometrics.BiometricNavigationUtils; +import com.android.settings.biometrics.fingerprint.FingerprintStatusUtils; +import com.android.settingslib.RestrictedLockUtils; + +/** Fingerprint biometrics Safety Source for Safety Center. */ +public final class FingerprintSafetySource { + + public static final String SAFETY_SOURCE_ID = "AndroidFingerprintUnlock"; + + private FingerprintSafetySource() {} + + /** Sets biometric safety data for Safety Center. */ + public static void setSafetySourceData(Context context, SafetyEvent safetyEvent) { + if (!SafetyCenterManagerWrapper.get().isEnabled(context)) { + return; + } + + // Handle private profile case + UserManager userManager = UserManager.get(context); + if (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enablePrivateSpaceFeatures() + && userManager.isPrivateProfile()) { + // SC always expects a response from the source if the broadcast has been sent for this + // source, therefore, we need to send a null SafetySourceData. + SafetyCenterManagerWrapper.get() + .setSafetySourceData( + context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent); + return; + } + + UserHandle userHandle = Process.myUserHandle(); + int userId = userHandle.getIdentifier(); + FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(context); + FingerprintStatusUtils fingerprintStatusUtils = + new FingerprintStatusUtils(context, fingerprintManager, userId); + BiometricNavigationUtils biometricNavigationUtils = new BiometricNavigationUtils(userId); + UserHandle profileParentUserHandle = userManager.getProfileParent(userHandle); + if (profileParentUserHandle == null) { + profileParentUserHandle = userHandle; + } + Context profileParentContext = context.createContextAsUser(profileParentUserHandle, 0); + + if (Utils.hasFingerprintHardware(context)) { + RestrictedLockUtils.EnforcedAdmin disablingAdmin = + fingerprintStatusUtils.getDisablingAdmin(); + BiometricSourcesUtils.setBiometricSafetySourceData( + SAFETY_SOURCE_ID, + context, + fingerprintStatusUtils.getTitle(), + fingerprintStatusUtils.getSummary(), + BiometricSourcesUtils.createPendingIntent( + profileParentContext, + biometricNavigationUtils + .getBiometricSettingsIntent( + context, + fingerprintStatusUtils.getSettingsClassName(), + disablingAdmin, + Bundle.EMPTY) + .setIdentifier(Integer.toString(userId)), + REQUEST_CODE_FINGERPRINT_SETTING), + disablingAdmin == null /* enabled */, + fingerprintStatusUtils.hasEnrolled(), + safetyEvent); + return; + } + + SafetyCenterManagerWrapper.get() + .setSafetySourceData( + context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent); + } + + /** Notifies Safety Center of a change in fingerprint biometrics settings. */ + public static void onBiometricsChanged(Context context) { + setSafetySourceData( + context, + new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED) + .build()); + } +} diff --git a/src/com/android/settings/safetycenter/LockScreenSafetySource.java b/src/com/android/settings/safetycenter/LockScreenSafetySource.java index 6960fc6c2d2..14ad268c3cf 100644 --- a/src/com/android/settings/safetycenter/LockScreenSafetySource.java +++ b/src/com/android/settings/safetycenter/LockScreenSafetySource.java @@ -31,6 +31,7 @@ import android.safetycenter.SafetySourceStatus; import android.safetycenter.SafetySourceStatus.IconAction; import com.android.settings.R; +import com.android.settings.flags.Flags; import com.android.settings.security.ScreenLockPreferenceDetailsUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; @@ -122,7 +123,12 @@ public final class LockScreenSafetySource { // Also send refreshed safety center data for biometrics, since changing lockscreen settings // can unset biometrics. - BiometricsSafetySource.onBiometricsChanged(context); + if (Flags.biometricsOnboardingEducation()) { + FaceSafetySource.onBiometricsChanged(context); + FingerprintSafetySource.onBiometricsChanged(context); + } else { + BiometricsSafetySource.onBiometricsChanged(context); + } } private static IconAction createGearMenuIconAction( diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java index cc0f892e873..992c0eca589 100644 --- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java +++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.safetycenter.SafetyCenterManager; import android.safetycenter.SafetyEvent; +import com.android.settings.flags.Flags; import com.android.settings.privatespace.PrivateSpaceSafetySource; import com.android.settings.security.ScreenLockPreferenceDetailsUtils; @@ -48,48 +49,60 @@ public class SafetySourceBroadcastReceiver extends BroadcastReceiver { } if (ACTION_REFRESH_SAFETY_SOURCES.equals(intent.getAction())) { - String[] sourceIdsExtra = - intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS); - final String refreshBroadcastId = intent.getStringExtra( - SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID); + String[] sourceIdsExtra = intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS); + final String refreshBroadcastId = + intent.getStringExtra( + SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID); if (sourceIdsExtra != null && sourceIdsExtra.length > 0 && refreshBroadcastId != null) { - final SafetyEvent safetyEvent = new SafetyEvent.Builder( - SAFETY_EVENT_TYPE_REFRESH_REQUESTED) - .setRefreshBroadcastId(refreshBroadcastId).build(); - refreshSafetySources( - context, - ImmutableList.copyOf(sourceIdsExtra), - safetyEvent); + final SafetyEvent safetyEvent = + new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED) + .setRefreshBroadcastId(refreshBroadcastId) + .build(); + refreshSafetySources(context, ImmutableList.copyOf(sourceIdsExtra), safetyEvent); } return; } - if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) { refreshAllSafetySources(context, EVENT_DEVICE_REBOOTED); } } - private static void refreshSafetySources(Context context, List sourceIds, - SafetyEvent safetyEvent) { + private static void refreshSafetySources( + Context context, List sourceIds, SafetyEvent safetyEvent) { if (sourceIds.contains(LockScreenSafetySource.SAFETY_SOURCE_ID)) { - LockScreenSafetySource.setSafetySourceData(context, - new ScreenLockPreferenceDetailsUtils(context), safetyEvent); + LockScreenSafetySource.setSafetySourceData( + context, new ScreenLockPreferenceDetailsUtils(context), safetyEvent); } - if (sourceIds.contains(BiometricsSafetySource.SAFETY_SOURCE_ID)) { + if (sourceIds.contains(BiometricsSafetySource.SAFETY_SOURCE_ID) + && !Flags.biometricsOnboardingEducation()) { BiometricsSafetySource.setSafetySourceData(context, safetyEvent); } if (sourceIds.contains(PrivateSpaceSafetySource.SAFETY_SOURCE_ID)) { PrivateSpaceSafetySource.setSafetySourceData(context, safetyEvent); } + if (sourceIds.contains(FaceSafetySource.SAFETY_SOURCE_ID) + && Flags.biometricsOnboardingEducation()) { + FaceSafetySource.setSafetySourceData(context, safetyEvent); + } + if (sourceIds.contains(FingerprintSafetySource.SAFETY_SOURCE_ID) + && Flags.biometricsOnboardingEducation()) { + FingerprintSafetySource.setSafetySourceData(context, safetyEvent); + } } private static void refreshAllSafetySources(Context context, SafetyEvent safetyEvent) { - LockScreenSafetySource.setSafetySourceData(context, - new ScreenLockPreferenceDetailsUtils(context), safetyEvent); - BiometricsSafetySource.setSafetySourceData(context, safetyEvent); + LockScreenSafetySource.setSafetySourceData( + context, new ScreenLockPreferenceDetailsUtils(context), safetyEvent); + if (!Flags.biometricsOnboardingEducation()) { + BiometricsSafetySource.setSafetySourceData(context, safetyEvent); + } PrivateSpaceSafetySource.setSafetySourceData(context, safetyEvent); + if (Flags.biometricsOnboardingEducation()) { + FaceSafetySource.setSafetySourceData(context, safetyEvent); + FingerprintSafetySource.setSafetySourceData(context, safetyEvent); + } } } diff --git a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java index 71899fbb730..574b4a7a77f 100644 --- a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java +++ b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java @@ -39,6 +39,9 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.os.UserHandle; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; import android.safetycenter.SafetySourceStatus; @@ -50,6 +53,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.settings.Settings; import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal; import com.android.settings.biometrics.fingerprint.FingerprintSettings; +import com.android.settings.flags.Flags; import com.android.settings.testutils.ActiveUnlockTestUtils; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.ResourcesUtils; @@ -57,6 +61,7 @@ import com.android.settingslib.utils.StringUtil; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -67,6 +72,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +@RequiresFlagsDisabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) @RunWith(AndroidJUnit4.class) public class BiometricsSafetySourceTest { @@ -75,6 +81,9 @@ public class BiometricsSafetySourceTest { private static final SafetyEvent EVENT_SOURCE_STATE_CHANGED = new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private Context mApplicationContext; @Mock private PackageManager mPackageManager; @@ -196,7 +205,7 @@ public class BiometricsSafetySourceTest { @Test public void setSafetySourceData_withFingerprintsEnrolled_whenDisabledByAdmin_setsData() { - final int enrolledFingerprintsCount = 2; + int enrolledFingerprintsCount = 2; when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(false); @@ -216,7 +225,7 @@ public class BiometricsSafetySourceTest { @Test public void setSafetySourceData_withFingerprintsEnrolled_whenNotDisabledByAdmin_setsData() { - final int enrolledFingerprintsCount = 2; + int enrolledFingerprintsCount = 2; when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(false); @@ -364,7 +373,7 @@ public class BiometricsSafetySourceTest { @Test public void setSafetySourceData_faceAndFingerprint_whenFaceEnrolled_withMpFingers_setsData() { - final int enrolledFingerprintsCount = 2; + int enrolledFingerprintsCount = 2; when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(true); @@ -382,7 +391,7 @@ public class BiometricsSafetySourceTest { @Test public void setSafetySourceData_faceAndFingerprint_whenFaceEnrolled_withOneFinger_setsData() { - final int enrolledFingerprintsCount = 1; + int enrolledFingerprintsCount = 1; when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(true); @@ -417,7 +426,7 @@ public class BiometricsSafetySourceTest { @Test public void setSafetySourceData_activeUnlockEnabled_withFingerprintOnly_setsData() { - final int enrolledFingerprintsCount = 1; + int enrolledFingerprintsCount = 1; when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(false); @@ -453,7 +462,7 @@ public class BiometricsSafetySourceTest { @Test public void setSafetySourceData_activeUnlockEnabled_withFaceAndFingerprint_setsData() { - final int enrolledFingerprintsCount = 1; + int enrolledFingerprintsCount = 1; when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(true); @@ -472,7 +481,7 @@ public class BiometricsSafetySourceTest { @Test public void setSafetySourceData_faceAndFingerprint_whenNoFaceEnrolled_withFingers_setsData() { - final int enrolledFingerprintsCount = 1; + int enrolledFingerprintsCount = 1; when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(true); @@ -660,7 +669,7 @@ public class BiometricsSafetySourceTest { String expectedTitleResName, String expectedSummaryResName, int expectedSummaryQuantity) { - final int stringResId = + int stringResId = ResourcesUtils.getResourcesId( ApplicationProvider.getApplicationContext(), "string", @@ -676,7 +685,7 @@ public class BiometricsSafetySourceTest { String expectedSummaryResName, int expectedSummaryQuantity, String expectedSettingsClassName) { - final int stringResId = + int stringResId = ResourcesUtils.getResourcesId( ApplicationProvider.getApplicationContext(), "string", @@ -705,7 +714,7 @@ public class BiometricsSafetySourceTest { assertThat(safetySourceStatus.getSeverityLevel()) .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED); - final Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); + Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); assertThat(clickIntent).isNotNull(); assertThat(clickIntent.getAction()).isEqualTo(ACTION_SHOW_ADMIN_SUPPORT_DETAILS); } @@ -725,14 +734,14 @@ public class BiometricsSafetySourceTest { assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle); assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary); assertThat(safetySourceStatus.isEnabled()).isTrue(); - final Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); + Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); assertThat(clickIntent).isNotNull(); assertThat(clickIntent.getComponent().getPackageName()).isEqualTo("com.android.settings"); assertThat(clickIntent.getComponent().getClassName()).isEqualTo(expectedSettingsClassName); } private List createFingerprintList(int size) { - final List fingerprintList = new ArrayList<>(size); + List fingerprintList = new ArrayList<>(size); for (int i = 0; i < size; i++) { fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0)); } diff --git a/tests/unit/src/com/android/settings/safetycenter/FaceSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/FaceSafetySourceTest.java new file mode 100644 index 00000000000..e42a4d1fd9d --- /dev/null +++ b/tests/unit/src/com/android/settings/safetycenter/FaceSafetySourceTest.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.safetycenter; + +import static android.provider.Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS; +import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.hardware.face.FaceManager; +import android.os.UserHandle; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.safetycenter.SafetyEvent; +import android.safetycenter.SafetySourceData; +import android.safetycenter.SafetySourceStatus; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.Settings; +import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal; +import com.android.settings.flags.Flags; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.ResourcesUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) +@RunWith(AndroidJUnit4.class) +public class FaceSafetySourceTest { + + private static final ComponentName COMPONENT_NAME = new ComponentName("package", "class"); + private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId()); + private static final SafetyEvent EVENT_SOURCE_STATE_CHANGED = + new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build(); + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private Context mApplicationContext; + + @Mock private PackageManager mPackageManager; + @Mock private DevicePolicyManager mDevicePolicyManager; + @Mock private FaceManager mFaceManager; + @Mock private LockPatternUtils mLockPatternUtils; + @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mApplicationContext = spy(ApplicationProvider.getApplicationContext()); + when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); + when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE)) + .thenReturn(COMPONENT_NAME); + when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) + .thenReturn(mDevicePolicyManager); + when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager); + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + when(featureFactory.securityFeatureProvider.getLockPatternUtils(mApplicationContext)) + .thenReturn(mLockPatternUtils); + doReturn(true).when(mLockPatternUtils).isSecure(anyInt()); + SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper; + } + + @After + public void tearDown() { + SafetyCenterManagerWrapper.sInstance = null; + } + + @Test + public void setSafetyData_whenSafetyCenterIsDisabled_doesNotSetData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper, never()) + .setSafetySourceData(any(), any(), any(), any()); + } + + @Test + public void setSafetySourceData_whenSafetyCenterIsEnabled_withoutFaceHardware_setsNullData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(false); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData(any(), eq(FaceSafetySource.SAFETY_SOURCE_ID), eq(null), any()); + } + + @Test + public void setSafetySourceData_setsDataForFaceSource() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData(any(), eq(FaceSafetySource.SAFETY_SOURCE_ID), any(), any()); + } + + @Test + public void setSafetySourceData_setsDataWithCorrectSafetyEvent() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData(any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED)); + } + + @Test + public void setSafetySourceData_withFaceNotEnrolled_whenDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceDisabledDataSetWithSingularSummary( + "security_settings_face_preference_title_new", + "security_settings_face_preference_summary_none"); + } + + @Test + public void setSafetySourceData_withFaceNotEnrolled_whenNotDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceEnabledDataSetWithSingularSummary( + "security_settings_face_preference_title_new", + "security_settings_face_preference_summary_none", + FaceEnrollIntroductionInternal.class.getName()); + } + + @Test + public void setSafetySourceData_withFaceEnrolled_whenDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceDisabledDataSetWithSingularSummary( + "security_settings_face_preference_title_new", + "security_settings_face_preference_summary"); + } + + @Test + public void setSafetySourceData_withFaceEnrolled_whenNotDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceEnabledDataSetWithSingularSummary( + "security_settings_face_preference_title_new", + "security_settings_face_preference_summary", + Settings.FaceSettingsInternalActivity.class.getName()); + } + + @Test + public void setSafetySourceData_face_whenEnrolled_setsInfoSeverity() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(FaceSafetySource.SAFETY_SOURCE_ID), captor.capture(), any()); + SafetySourceStatus safetySourceStatus = captor.getValue().getStatus(); + assertThat(safetySourceStatus.getSeverityLevel()) + .isEqualTo(SafetySourceData.SEVERITY_LEVEL_INFORMATION); + } + + @Test + public void setSafetySourceData_face_whenNotEnrolled_setsUnspSeverity() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + + FaceSafetySource.setSafetySourceData(mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(FaceSafetySource.SAFETY_SOURCE_ID), captor.capture(), any()); + SafetySourceStatus safetySourceStatus = captor.getValue().getStatus(); + assertThat(safetySourceStatus.getSeverityLevel()) + .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED); + } + + private void assertSafetySourceDisabledDataSetWithSingularSummary( + String expectedTitleResName, String expectedSummaryResName) { + assertSafetySourceDisabledDataSet( + ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName), + ResourcesUtils.getResourcesString(mApplicationContext, expectedSummaryResName)); + } + + private void assertSafetySourceEnabledDataSetWithSingularSummary( + String expectedTitleResName, + String expectedSummaryResName, + String expectedSettingsClassName) { + assertSafetySourceEnabledDataSet( + ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName), + ResourcesUtils.getResourcesString(mApplicationContext, expectedSummaryResName), + expectedSettingsClassName); + } + + private void assertSafetySourceDisabledDataSet(String expectedTitle, String expectedSummary) { + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(FaceSafetySource.SAFETY_SOURCE_ID), captor.capture(), any()); + SafetySourceData safetySourceData = captor.getValue(); + SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); + + assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle); + assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary); + assertThat(safetySourceStatus.isEnabled()).isFalse(); + assertThat(safetySourceStatus.getSeverityLevel()) + .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED); + + Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); + assertThat(clickIntent).isNotNull(); + assertThat(clickIntent.getAction()).isEqualTo(ACTION_SHOW_ADMIN_SUPPORT_DETAILS); + } + + private void assertSafetySourceEnabledDataSet( + String expectedTitle, String expectedSummary, String expectedSettingsClassName) { + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(FaceSafetySource.SAFETY_SOURCE_ID), captor.capture(), any()); + SafetySourceData safetySourceData = captor.getValue(); + SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); + + assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle); + assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary); + assertThat(safetySourceStatus.isEnabled()).isTrue(); + Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); + assertThat(clickIntent).isNotNull(); + assertThat(clickIntent.getComponent().getPackageName()).isEqualTo("com.android.settings"); + assertThat(clickIntent.getComponent().getClassName()).isEqualTo(expectedSettingsClassName); + } +} diff --git a/tests/unit/src/com/android/settings/safetycenter/FingerprintSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/FingerprintSafetySourceTest.java new file mode 100644 index 00000000000..8a34aaa48d3 --- /dev/null +++ b/tests/unit/src/com/android/settings/safetycenter/FingerprintSafetySourceTest.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.safetycenter; + +import static android.provider.Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS; +import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; +import android.os.UserHandle; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.safetycenter.SafetyEvent; +import android.safetycenter.SafetySourceData; +import android.safetycenter.SafetySourceStatus; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.biometrics.fingerprint.FingerprintSettings; +import com.android.settings.flags.Flags; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.ResourcesUtils; +import com.android.settingslib.utils.StringUtil; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) +@RunWith(AndroidJUnit4.class) +public class FingerprintSafetySourceTest { + + private static final ComponentName COMPONENT_NAME = new ComponentName("package", "class"); + private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId()); + private static final SafetyEvent EVENT_SOURCE_STATE_CHANGED = + new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build(); + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private Context mApplicationContext; + + @Mock private PackageManager mPackageManager; + @Mock private DevicePolicyManager mDevicePolicyManager; + @Mock private FingerprintManager mFingerprintManager; + @Mock private LockPatternUtils mLockPatternUtils; + @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mApplicationContext = spy(ApplicationProvider.getApplicationContext()); + when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); + when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE)) + .thenReturn(COMPONENT_NAME); + when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE)) + .thenReturn(mFingerprintManager); + when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE)) + .thenReturn(mDevicePolicyManager); + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + when(featureFactory.securityFeatureProvider.getLockPatternUtils(mApplicationContext)) + .thenReturn(mLockPatternUtils); + doReturn(true).when(mLockPatternUtils).isSecure(anyInt()); + SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper; + } + + @After + public void tearDown() { + SafetyCenterManagerWrapper.sInstance = null; + } + + @Test + public void setSafetyData_whenSafetyCenterIsDisabled_doesNotSetData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper, never()) + .setSafetySourceData(any(), any(), any(), any()); + } + + @Test + public void setSafetySourceData_whenSafetyCenterIsEnabled_withoutFingerprint_setsNullData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(false); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(FingerprintSafetySource.SAFETY_SOURCE_ID), eq(null), any()); + } + + @Test + public void setSafetySourceData_setsDataForFingerprintSource() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(FingerprintSafetySource.SAFETY_SOURCE_ID), any(), any()); + } + + @Test + public void setSafetySourceData_setsDataWithCorrectSafetyEvent() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData(any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED)); + } + + @Test + public void setSafetySourceData_withFingerprintNotEnrolled_whenDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceDisabledDataSetWithSingularSummary( + "security_settings_fingerprint", + "security_settings_fingerprint_preference_summary_none"); + } + + @Test + public void setSafetySourceData_withFingerprintNotEnrolled_whenNotDisabledByAdmin_setsData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceEnabledDataSetWithSingularSummary( + "security_settings_fingerprint", + "security_settings_fingerprint_preference_summary_none", + FingerprintSettings.class.getName()); + } + + @Test + public void setSafetySourceData_withFingerprintsEnrolled_whenDisabledByAdmin_setsData() { + int enrolledFingerprintsCount = 2; + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true); + when(mFingerprintManager.getEnrolledFingerprints(anyInt())) + .thenReturn(createFingerprintList(enrolledFingerprintsCount)); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceDisabledDataSetWithPluralSummary( + "security_settings_fingerprint", + "security_settings_fingerprint_preference_summary", + enrolledFingerprintsCount); + } + + @Test + public void setSafetySourceData_withFingerprintsEnrolled_whenNotDisabledByAdmin_setsData() { + int enrolledFingerprintsCount = 2; + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true); + when(mFingerprintManager.getEnrolledFingerprints(anyInt())) + .thenReturn(createFingerprintList(enrolledFingerprintsCount)); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + assertSafetySourceEnabledDataSetWithPluralSummary( + "security_settings_fingerprint", + "security_settings_fingerprint_preference_summary", + enrolledFingerprintsCount, + FingerprintSettings.class.getName()); + } + + @Test + public void setSafetySourceData_fingerprint_whenEnrolled_setsInfoSeverity() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), + eq(FingerprintSafetySource.SAFETY_SOURCE_ID), + captor.capture(), + any()); + SafetySourceStatus safetySourceStatus = captor.getValue().getStatus(); + assertThat(safetySourceStatus.getSeverityLevel()) + .isEqualTo(SafetySourceData.SEVERITY_LEVEL_INFORMATION); + } + + @Test + public void setSafetySourceData_fingerprint_whenNotEnrolled_setsUnspSeverity() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false); + + FingerprintSafetySource.setSafetySourceData( + mApplicationContext, EVENT_SOURCE_STATE_CHANGED); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), + eq(FingerprintSafetySource.SAFETY_SOURCE_ID), + captor.capture(), + any()); + SafetySourceStatus safetySourceStatus = captor.getValue().getStatus(); + assertThat(safetySourceStatus.getSeverityLevel()) + .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED); + } + + private void assertSafetySourceDisabledDataSetWithSingularSummary( + String expectedTitleResName, String expectedSummaryResName) { + assertSafetySourceDisabledDataSet( + ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName), + ResourcesUtils.getResourcesString(mApplicationContext, expectedSummaryResName)); + } + + private void assertSafetySourceEnabledDataSetWithSingularSummary( + String expectedTitleResName, + String expectedSummaryResName, + String expectedSettingsClassName) { + assertSafetySourceEnabledDataSet( + ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName), + ResourcesUtils.getResourcesString(mApplicationContext, expectedSummaryResName), + expectedSettingsClassName); + } + + private void assertSafetySourceDisabledDataSetWithPluralSummary( + String expectedTitleResName, + String expectedSummaryResName, + int expectedSummaryQuantity) { + int stringResId = + ResourcesUtils.getResourcesId( + ApplicationProvider.getApplicationContext(), + "string", + expectedSummaryResName); + assertSafetySourceDisabledDataSet( + ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName), + StringUtil.getIcuPluralsString( + mApplicationContext, expectedSummaryQuantity, stringResId)); + } + + private void assertSafetySourceEnabledDataSetWithPluralSummary( + String expectedTitleResName, + String expectedSummaryResName, + int expectedSummaryQuantity, + String expectedSettingsClassName) { + int stringResId = + ResourcesUtils.getResourcesId( + ApplicationProvider.getApplicationContext(), + "string", + expectedSummaryResName); + assertSafetySourceEnabledDataSet( + ResourcesUtils.getResourcesString(mApplicationContext, expectedTitleResName), + StringUtil.getIcuPluralsString( + mApplicationContext, expectedSummaryQuantity, stringResId), + expectedSettingsClassName); + } + + private void assertSafetySourceDisabledDataSet(String expectedTitle, String expectedSummary) { + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), + eq(FingerprintSafetySource.SAFETY_SOURCE_ID), + captor.capture(), + any()); + SafetySourceData safetySourceData = captor.getValue(); + SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); + + assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle); + assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary); + assertThat(safetySourceStatus.isEnabled()).isFalse(); + assertThat(safetySourceStatus.getSeverityLevel()) + .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED); + + Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); + assertThat(clickIntent).isNotNull(); + assertThat(clickIntent.getAction()).isEqualTo(ACTION_SHOW_ADMIN_SUPPORT_DETAILS); + } + + private void assertSafetySourceEnabledDataSet( + String expectedTitle, String expectedSummary, String expectedSettingsClassName) { + ArgumentCaptor captor = ArgumentCaptor.forClass(SafetySourceData.class); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), + eq(FingerprintSafetySource.SAFETY_SOURCE_ID), + captor.capture(), + any()); + SafetySourceData safetySourceData = captor.getValue(); + SafetySourceStatus safetySourceStatus = safetySourceData.getStatus(); + + assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle); + assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary); + assertThat(safetySourceStatus.isEnabled()).isTrue(); + Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent(); + assertThat(clickIntent).isNotNull(); + assertThat(clickIntent.getComponent().getPackageName()).isEqualTo("com.android.settings"); + assertThat(clickIntent.getComponent().getClassName()).isEqualTo(expectedSettingsClassName); + } + + private List createFingerprintList(int size) { + List fingerprintList = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0)); + } + return fingerprintList; + } +} diff --git a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java index e528c4e790e..f16113ab13d 100644 --- a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java +++ b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java @@ -29,6 +29,10 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; import android.safetycenter.SafetySourceIssue; @@ -39,12 +43,14 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.flags.Flags; import com.android.settings.security.ScreenLockPreferenceDetailsUtils; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.ResourcesUtils; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -62,6 +68,9 @@ public class LockScreenSafetySourceTest { private static final SafetyEvent EVENT_SOURCE_STATE_CHANGED = new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private Context mApplicationContext; @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; @@ -486,7 +495,9 @@ public class LockScreenSafetySourceTest { } @Test - public void onLockScreenChange_whenSafetyCenterEnabled_setsLockscreenAndBiometricData() { + @RequiresFlagsDisabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void + onLockScreenChange_whenSafetyCenterEnabled_flagOff_setsLockscreenAndBiometricData() { whenScreenLockIsEnabled(); when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); @@ -500,6 +511,24 @@ public class LockScreenSafetySourceTest { any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), any(), any()); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void onLockScreenChange_whenSafetyCenterEnabled_flagOn_setsLockscreenAndBiometricData() { + whenScreenLockIsEnabled(); + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + + LockScreenSafetySource.onLockScreenChange(mApplicationContext); + + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), any(), any()); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData(any(), eq(FaceSafetySource.SAFETY_SOURCE_ID), any(), any()); + verify(mSafetyCenterManagerWrapper) + .setSafetySourceData( + any(), eq(FingerprintSafetySource.SAFETY_SOURCE_ID), any(), any()); + } + @Test public void onLockScreenChange_whenSafetyCenterDisabled_doesNotSetData() { whenScreenLockIsEnabled(); diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java index cd4c3c64fe7..a0a532714df 100644 --- a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java +++ b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java @@ -21,8 +21,11 @@ import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOUR import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS; import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED; import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED; + import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -30,7 +33,10 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; -import android.os.Flags; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.safetycenter.SafetyEvent; import android.safetycenter.SafetySourceData; @@ -39,6 +45,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.flags.Flags; import com.android.settings.privatespace.PrivateSpaceSafetySource; import com.android.settings.testutils.FakeFeatureFactory; @@ -51,7 +58,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.Arrays; import java.util.List; @RunWith(AndroidJUnit4.class) @@ -61,6 +67,9 @@ public class SafetySourceBroadcastReceiverTest { private Context mApplicationContext; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper; @Mock private LockPatternUtils mLockPatternUtils; @@ -202,6 +211,7 @@ public class SafetySourceBroadcastReceiverTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) public void onReceive_onRefresh_withBiometricsSourceId_setsBiometricData() { when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); Intent intent = @@ -220,9 +230,49 @@ public class SafetySourceBroadcastReceiverTest { assertThat(captor.getValue()).isEqualTo(BiometricsSafetySource.SAFETY_SOURCE_ID); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void onReceive_onRefresh_withFaceUnlockSourceId_setsFaceUnlockData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + Intent intent = + new Intent() + .setAction(ACTION_REFRESH_SAFETY_SOURCES) + .putExtra( + EXTRA_REFRESH_SAFETY_SOURCE_IDS, + new String[] {FaceSafetySource.SAFETY_SOURCE_ID}) + .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_BROADCAST_ID); + + new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(mSafetyCenterManagerWrapper, times(1)) + .setSafetySourceData(any(), captor.capture(), any(), any()); + + assertThat(captor.getValue()).isEqualTo(FaceSafetySource.SAFETY_SOURCE_ID); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void onReceive_onRefresh_withFingerprintUnlockSourceId_setsFingerprintUnlockData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + Intent intent = + new Intent() + .setAction(ACTION_REFRESH_SAFETY_SOURCES) + .putExtra( + EXTRA_REFRESH_SAFETY_SOURCE_IDS, + new String[] {FingerprintSafetySource.SAFETY_SOURCE_ID}) + .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_BROADCAST_ID); + + new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(mSafetyCenterManagerWrapper, times(1)) + .setSafetySourceData(any(), captor.capture(), any(), any()); + + assertThat(captor.getValue()).isEqualTo(FingerprintSafetySource.SAFETY_SOURCE_ID); + } + /** - * Tests that on receiving the refresh broadcast request with the PS source id, the PS data - * is set. + * Tests that on receiving the refresh broadcast request with the PS source id, the PS data is + * set. */ @Test public void onReceive_onRefresh_withPrivateSpaceSourceId_setsPrivateSpaceData() { @@ -247,7 +297,8 @@ public class SafetySourceBroadcastReceiverTest { @Test public void onReceive_onRefresh_withPrivateSpaceFeatureDisabled_setsNullData() { when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); - mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE, + mSetFlagsRule.disableFlags( + android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); Intent intent = @@ -273,16 +324,48 @@ public class SafetySourceBroadcastReceiverTest { new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent); ArgumentCaptor captor = ArgumentCaptor.forClass(SafetyEvent.class); - verify(mSafetyCenterManagerWrapper, times(3)) + verify(mSafetyCenterManagerWrapper, atLeastOnce()) .setSafetySourceData(any(), any(), any(), captor.capture()); SafetyEvent bootEvent = new SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build(); - assertThat(captor.getAllValues()) - .containsExactlyElementsIn(Arrays.asList(bootEvent, bootEvent, bootEvent)); + assertThat(captor.getAllValues()).contains(bootEvent); } @Test - public void onReceive_onBootCompleted_sendsAllSafetySourcesData() { + @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void onReceive_onBootCompleted_flagOn_sendsAllSafetySourcesData() { + when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); + Intent intent = new Intent().setAction(Intent.ACTION_BOOT_COMPLETED); + + new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(mSafetyCenterManagerWrapper, times(4)) + .setSafetySourceData(any(), captor.capture(), any(), any()); + List safetySourceIdList = captor.getAllValues(); + + assertThat( + safetySourceIdList.stream() + .anyMatch(id -> id.equals(LockScreenSafetySource.SAFETY_SOURCE_ID))) + .isTrue(); + assertThat( + safetySourceIdList.stream() + .anyMatch(id -> id.equals(FaceSafetySource.SAFETY_SOURCE_ID))) + .isTrue(); + assertThat( + safetySourceIdList.stream() + .anyMatch( + id -> id.equals(FingerprintSafetySource.SAFETY_SOURCE_ID))) + .isTrue(); + assertThat( + safetySourceIdList.stream() + .anyMatch( + id -> id.equals(PrivateSpaceSafetySource.SAFETY_SOURCE_ID))) + .isTrue(); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void onReceive_onBootCompleted_flagOff_sendsAllSafetySourcesData() { when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true); Intent intent = new Intent().setAction(Intent.ACTION_BOOT_COMPLETED); @@ -292,11 +375,18 @@ public class SafetySourceBroadcastReceiverTest { .setSafetySourceData(any(), captor.capture(), any(), any()); List safetySourceIdList = captor.getAllValues(); - assertThat(safetySourceIdList.stream().anyMatch( - id -> id.equals(LockScreenSafetySource.SAFETY_SOURCE_ID))).isTrue(); - assertThat(safetySourceIdList.stream().anyMatch( - id -> id.equals(BiometricsSafetySource.SAFETY_SOURCE_ID))).isTrue(); - assertThat(safetySourceIdList.stream().anyMatch( - id -> id.equals(PrivateSpaceSafetySource.SAFETY_SOURCE_ID))).isTrue(); + assertThat( + safetySourceIdList.stream() + .anyMatch(id -> id.equals(LockScreenSafetySource.SAFETY_SOURCE_ID))) + .isTrue(); + assertThat( + safetySourceIdList.stream() + .anyMatch(id -> id.equals(BiometricsSafetySource.SAFETY_SOURCE_ID))) + .isTrue(); + assertThat( + safetySourceIdList.stream() + .anyMatch( + id -> id.equals(PrivateSpaceSafetySource.SAFETY_SOURCE_ID))) + .isTrue(); } }