1 /* 2 * Copyright (C) 2021 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 package com.android.settings.biometrics.combination; 17 18 import android.content.Context; 19 import android.hardware.biometrics.BiometricAuthenticator; 20 import android.hardware.face.FaceManager; 21 import android.hardware.fingerprint.FingerprintManager; 22 23 import androidx.annotation.Nullable; 24 import androidx.lifecycle.Lifecycle; 25 import androidx.lifecycle.LifecycleObserver; 26 import androidx.lifecycle.OnLifecycleEvent; 27 import androidx.preference.Preference; 28 import androidx.preference.PreferenceScreen; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.settings.R; 32 import com.android.settings.Settings; 33 import com.android.settings.Utils; 34 import com.android.settings.biometrics.BiometricStatusPreferenceController; 35 import com.android.settings.biometrics.ParentalControlsUtils; 36 import com.android.settingslib.RestrictedLockUtils; 37 import com.android.settingslib.RestrictedPreference; 38 39 /** 40 * Preference controller for biometrics settings page controlling the ability to unlock the phone 41 * with face and fingerprint. 42 */ 43 public class CombinedBiometricStatusPreferenceController extends 44 BiometricStatusPreferenceController implements LifecycleObserver { 45 private static final String KEY_BIOMETRIC_SETTINGS = "biometric_settings"; 46 47 @Nullable 48 FingerprintManager mFingerprintManager; 49 @Nullable 50 FaceManager mFaceManager; 51 @VisibleForTesting 52 RestrictedPreference mPreference; 53 CombinedBiometricStatusPreferenceController(Context context)54 public CombinedBiometricStatusPreferenceController(Context context) { 55 this(context, KEY_BIOMETRIC_SETTINGS, null /* lifecycle */); 56 } 57 CombinedBiometricStatusPreferenceController(Context context, String key)58 public CombinedBiometricStatusPreferenceController(Context context, String key) { 59 this(context, key, null /* lifecycle */); 60 } 61 CombinedBiometricStatusPreferenceController(Context context, Lifecycle lifecycle)62 public CombinedBiometricStatusPreferenceController(Context context, Lifecycle lifecycle) { 63 this(context, KEY_BIOMETRIC_SETTINGS, lifecycle); 64 } 65 CombinedBiometricStatusPreferenceController( Context context, String key, Lifecycle lifecycle)66 public CombinedBiometricStatusPreferenceController( 67 Context context, String key, Lifecycle lifecycle) { 68 super(context, key); 69 mFingerprintManager = Utils.getFingerprintManagerOrNull(context); 70 mFaceManager = Utils.getFaceManagerOrNull(context); 71 72 if (lifecycle != null) { 73 lifecycle.addObserver(this); 74 } 75 } 76 77 @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) onResume()78 public void onResume() { 79 updateStateInternal(); 80 } 81 82 @Override displayPreference(PreferenceScreen screen)83 public void displayPreference(PreferenceScreen screen) { 84 super.displayPreference(screen); 85 mPreference = screen.findPreference(mPreferenceKey); 86 } 87 88 @Override isDeviceSupported()89 protected boolean isDeviceSupported() { 90 return Utils.hasFingerprintHardware(mContext) && Utils.hasFaceHardware(mContext); 91 } 92 93 @Override hasEnrolledBiometrics()94 protected boolean hasEnrolledBiometrics() { 95 return false; 96 } 97 98 @Override updateState(Preference preference)99 public void updateState(Preference preference) { 100 super.updateState(preference); 101 updateStateInternal(); 102 } 103 updateStateInternal()104 private void updateStateInternal() { 105 // This controller currently is shown if fingerprint&face exist on the device. If this 106 // changes in the future, the modalities passed into the below will need to be updated. 107 108 final RestrictedLockUtils.EnforcedAdmin faceAdmin = ParentalControlsUtils 109 .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FACE); 110 final RestrictedLockUtils.EnforcedAdmin fpAdmin = ParentalControlsUtils 111 .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FINGERPRINT); 112 113 // If the admins are non-null, they are actually always the same. Just the helper class 114 // we create above always return the admin, instead of a boolean. 115 final boolean faceConsentRequired = faceAdmin != null; 116 final boolean fpConsentRequired = fpAdmin != null; 117 final RestrictedLockUtils.EnforcedAdmin admin = faceAdmin != null ? faceAdmin : fpAdmin; 118 119 updateStateInternal(admin, faceConsentRequired, fpConsentRequired); 120 } 121 122 @VisibleForTesting updateStateInternal(@ullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin, boolean faceConsentRequired, boolean fpConsentRequired)123 void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin, 124 boolean faceConsentRequired, boolean fpConsentRequired) { 125 // Disable the preference (and show the consent flow) only if consent is required for all 126 // modalities. Otherwise, users will not be able to enter and modify settings for modalities 127 // which have already been consented. In any case, the controllers for the modalities which 128 // have not yet been consented will be disabled in the combined page anyway - users can 129 // go through the consent+enrollment flow from there. 130 final boolean disablePreference = faceConsentRequired && fpConsentRequired; 131 if (!disablePreference) { 132 enforcedAdmin = null; 133 } 134 135 if (mPreference != null) { 136 mPreference.setDisabledByAdmin(enforcedAdmin); 137 } 138 } 139 140 @Override getSummaryTextEnrolled()141 protected String getSummaryTextEnrolled() { 142 // Note that this is currently never called (see the super class) 143 return mContext.getString( 144 R.string.security_settings_biometric_preference_summary_none_enrolled); 145 } 146 147 @Override getSummaryTextNoneEnrolled()148 protected String getSummaryTextNoneEnrolled() { 149 final int numFingerprintsEnrolled = mFingerprintManager != null ? 150 mFingerprintManager.getEnrolledFingerprints(getUserId()).size() : 0; 151 final boolean faceEnrolled = mFaceManager != null 152 && mFaceManager.hasEnrolledTemplates(getUserId()); 153 154 if (faceEnrolled && numFingerprintsEnrolled > 1) { 155 return mContext.getString( 156 R.string.security_settings_biometric_preference_summary_both_fp_multiple); 157 } else if (faceEnrolled && numFingerprintsEnrolled == 1) { 158 return mContext.getString( 159 R.string.security_settings_biometric_preference_summary_both_fp_single); 160 } else if (faceEnrolled) { 161 return mContext.getString(R.string.security_settings_face_preference_summary); 162 } else if (numFingerprintsEnrolled > 0) { 163 return mContext.getResources().getQuantityString( 164 R.plurals.security_settings_fingerprint_preference_summary, 165 numFingerprintsEnrolled, numFingerprintsEnrolled); 166 } else { 167 return mContext.getString( 168 R.string.security_settings_biometric_preference_summary_none_enrolled); 169 } 170 } 171 172 @Override getSettingsClassName()173 protected String getSettingsClassName() { 174 return Settings.CombinedBiometricSettingsActivity.class.getName(); 175 } 176 177 @Override getEnrollClassName()178 protected String getEnrollClassName() { 179 return Settings.CombinedBiometricSettingsActivity.class.getName(); 180 } 181 } 182