1 /* 2 * Copyright (C) 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.safetycenter; 18 19 import static com.android.settings.biometrics.BiometricEnrollActivity.EXTRA_LAUNCH_FACE_ENROLL_FIRST; 20 import static com.android.settings.safetycenter.BiometricSourcesUtils.REQUEST_CODE_FACE_SETTING; 21 22 import android.content.Context; 23 import android.hardware.face.FaceManager; 24 import android.os.Bundle; 25 import android.os.Process; 26 import android.os.UserHandle; 27 import android.os.UserManager; 28 import android.safetycenter.SafetyEvent; 29 30 import com.android.settings.Utils; 31 import com.android.settings.biometrics.BiometricEnrollActivity; 32 import com.android.settings.biometrics.BiometricNavigationUtils; 33 import com.android.settings.biometrics.face.FaceStatusUtils; 34 import com.android.settings.flags.Flags; 35 import com.android.settings.overlay.FeatureFactory; 36 import com.android.settingslib.RestrictedLockUtils; 37 38 /** Face biometrics Safety Source for Safety Center. */ 39 public final class FaceSafetySource { 40 41 public static final String SAFETY_SOURCE_ID = "AndroidFaceUnlock"; 42 FaceSafetySource()43 private FaceSafetySource() {} 44 45 /** Sets biometric safety data for Safety Center. */ setSafetySourceData(Context context, SafetyEvent safetyEvent)46 public static void setSafetySourceData(Context context, SafetyEvent safetyEvent) { 47 if (!SafetyCenterManagerWrapper.get().isEnabled(context)) { 48 return; 49 } 50 if (!Flags.biometricsOnboardingEducation()) { // this source is effectively turned off 51 sendNullData(context, safetyEvent); 52 return; 53 } 54 55 // Handle private profile case 56 UserManager userManager = UserManager.get(context); 57 if (android.os.Flags.allowPrivateProfile() 58 && android.multiuser.Flags.enablePrivateSpaceFeatures() 59 && userManager.isPrivateProfile()) { 60 // SC always expects a response from the source if the broadcast has been sent for this 61 // source, therefore, we need to send a null SafetySourceData. 62 sendNullData(context, safetyEvent); 63 return; 64 } 65 66 UserHandle userHandle = Process.myUserHandle(); 67 int userId = userHandle.getIdentifier(); 68 FaceManager faceManager = Utils.getFaceManagerOrNull(context); 69 FaceStatusUtils faceStatusUtils = new FaceStatusUtils(context, faceManager, userId); 70 BiometricNavigationUtils biometricNavigationUtils = new BiometricNavigationUtils(userId); 71 UserHandle profileParentUserHandle = userManager.getProfileParent(userHandle); 72 if (profileParentUserHandle == null) { 73 profileParentUserHandle = userHandle; 74 } 75 Context profileParentContext = context.createContextAsUser(profileParentUserHandle, 0); 76 77 if (Utils.hasFaceHardware(context)) { 78 boolean isMultipleBiometricsEnrollmentNeeded = 79 BiometricSourcesUtils.isMultipleBiometricsEnrollmentNeeded(context, userId); 80 String settingClassName = isMultipleBiometricsEnrollmentNeeded 81 ? BiometricEnrollActivity.InternalActivity.class.getName() 82 : faceStatusUtils.getSettingsClassName(); 83 Bundle bundle = new Bundle(); 84 if (isMultipleBiometricsEnrollmentNeeded) { 85 // Launch face enrollment first then fingerprint enrollment. 86 bundle.putBoolean(EXTRA_LAUNCH_FACE_ENROLL_FIRST, true); 87 } 88 RestrictedLockUtils.EnforcedAdmin disablingAdmin = faceStatusUtils.getDisablingAdmin(); 89 BiometricSourcesUtils.setBiometricSafetySourceData( 90 SAFETY_SOURCE_ID, 91 context, 92 faceStatusUtils.getTitle(), 93 faceStatusUtils.getSummary(), 94 BiometricSourcesUtils.createPendingIntent( 95 profileParentContext, 96 biometricNavigationUtils 97 .getBiometricSettingsIntent( 98 context, 99 settingClassName, 100 disablingAdmin, 101 bundle) 102 .setIdentifier(Integer.toString(userId)), 103 REQUEST_CODE_FACE_SETTING), 104 disablingAdmin == null /* enabled */, 105 faceStatusUtils.hasEnrolled(), 106 safetyEvent, 107 FeatureFactory.getFeatureFactory().getBiometricsFeatureProvider() 108 .getSafetySourceIssue(SAFETY_SOURCE_ID)); 109 110 return; 111 } 112 113 sendNullData(context, safetyEvent); 114 } 115 sendNullData(Context context, SafetyEvent safetyEvent)116 private static void sendNullData(Context context, SafetyEvent safetyEvent) { 117 SafetyCenterManagerWrapper.get() 118 .setSafetySourceData( 119 context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent); 120 } 121 122 /** Notifies Safety Center of a change in face biometrics settings. */ onBiometricsChanged(Context context)123 public static void onBiometricsChanged(Context context) { 124 setSafetySourceData( 125 context, 126 new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED) 127 .build()); 128 } 129 } 130