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