1 /*
2  * Copyright (C) 2023 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.biometrics.activeunlock;
18 
19 import android.content.Context;
20 
21 import androidx.annotation.NonNull;
22 import androidx.annotation.Nullable;
23 import androidx.lifecycle.Lifecycle;
24 import androidx.lifecycle.LifecycleObserver;
25 import androidx.lifecycle.OnLifecycleEvent;
26 import androidx.preference.PreferenceScreen;
27 
28 import com.android.settings.Utils;
29 import com.android.settings.biometrics.BiometricStatusPreferenceController;
30 import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener;
31 import com.android.settings.biometrics.combination.CombinedBiometricStatusUtils;
32 import com.android.settingslib.RestrictedPreference;
33 
34 /**
35  * Preference controller for active unlock settings within the biometrics settings page, that
36  * controls the ability to unlock the phone with watch authentication.
37  */
38 public class ActiveUnlockStatusPreferenceController
39         extends BiometricStatusPreferenceController implements LifecycleObserver {
40     /**
41      * Preference key.
42      *
43      * This must match the key found in security_settings_combined_biometric.xml
44      **/
45     public static final String KEY_ACTIVE_UNLOCK_SETTINGS = "biometric_active_unlock_settings";
46     @Nullable private RestrictedPreference mPreference;
47     @Nullable private PreferenceScreen mPreferenceScreen;
48     @Nullable private String mSummary;
49     private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
50     private final CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
51     private final ActiveUnlockSummaryListener mActiveUnlockSummaryListener;
52     private final ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener;
53     private final boolean mIsAvailable;
54 
ActiveUnlockStatusPreferenceController(@onNull Context context)55     public ActiveUnlockStatusPreferenceController(@NonNull Context context) {
56         this(context, KEY_ACTIVE_UNLOCK_SETTINGS);
57     }
58 
ActiveUnlockStatusPreferenceController( @onNull Context context, @NonNull String key)59     public ActiveUnlockStatusPreferenceController(
60             @NonNull Context context, @NonNull String key) {
61         super(context, key);
62         mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(context);
63         mIsAvailable = mActiveUnlockStatusUtils.isAvailable();
64         mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(context, getUserId());
65         OnContentChangedListener onSummaryChangedListener = new OnContentChangedListener() {
66             @Override
67             public void onContentChanged(String newContent) {
68                 mSummary = newContent;
69                 if (mPreference != null) {
70                     mPreference.setSummary(getSummaryText());
71                 }
72             }
73         };
74         OnContentChangedListener onDeviceNameChangedListener =
75                 new OnContentChangedListener() {
76 
77             @Override
78             public void onContentChanged(String newContent) {
79                 if (mPreference != null) {
80                     mPreference.setSummary(getSummaryText());
81                 }
82             }
83 
84         };
85         mActiveUnlockSummaryListener =
86                 new ActiveUnlockSummaryListener(context, onSummaryChangedListener);
87         mActiveUnlockDeviceNameListener =
88                 new ActiveUnlockDeviceNameListener(context, onDeviceNameChangedListener);
89     }
90 
91 
92     /** Subscribes to update preference summary dynamically. */
93     @OnLifecycleEvent(Lifecycle.Event.ON_START)
onStart()94     public void onStart() {
95         if (mIsAvailable) {
96             mActiveUnlockSummaryListener.subscribe();
97             mActiveUnlockDeviceNameListener.subscribe();
98         }
99     }
100 
101     /** Resets the preference reference on resume. */
102     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
onResume()103     public void onResume() {
104         if (mPreferenceScreen != null) {
105             displayPreference(mPreferenceScreen);
106         }
107     }
108 
109     /** Unsubscribes to prevent leaked listener. */
110     @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
onStop()111     public void onStop() {
112         if (mIsAvailable) {
113             mActiveUnlockSummaryListener.unsubscribe();
114             mActiveUnlockDeviceNameListener.unsubscribe();
115         }
116     }
117 
118     @Override
displayPreference(PreferenceScreen screen)119     public void displayPreference(PreferenceScreen screen) {
120         super.displayPreference(screen);
121         mPreferenceScreen = screen;
122         mPreference = screen.findPreference(mPreferenceKey);
123         updateState(mPreference);
124     }
125 
126     @Override
getAvailabilityStatus()127     public int getAvailabilityStatus() {
128         return mActiveUnlockStatusUtils.getAvailability();
129     }
130 
131     @Override
isDeviceSupported()132     protected boolean isDeviceSupported() {
133         // This should never be called, as getAvailabilityStatus() will return the exact value.
134         // However, this is an abstract method in BiometricStatusPreferenceController, and so
135         // needs to be overridden.
136         return mIsAvailable;
137     }
138 
139     @Override
isHardwareSupported()140     protected boolean isHardwareSupported() {
141         // This should never be called, as getAvailabilityStatus() will return the exact value.
142         // However, this is an abstract method in BiometricStatusPreferenceController, and so
143         // needs to be overridden.
144         return Utils.hasFaceHardware(mContext) || Utils.hasFingerprintHardware(mContext);
145     }
146 
147     @Override
getSummaryText()148     protected String getSummaryText() {
149         if (mActiveUnlockStatusUtils.useBiometricFailureLayout()
150                 && !mActiveUnlockDeviceNameListener.hasEnrolled()
151                 && !mCombinedBiometricStatusUtils.hasEnrolled()) {
152             @Nullable final String setupString =
153                     mActiveUnlockStatusUtils.getSummaryWhenBiometricSetupRequired();
154             if (setupString != null) {
155                 return setupString;
156             }
157         }
158         if (mSummary == null) {
159             // return non-empty string to prevent re-sizing of the tile
160             return " ";
161         }
162         return mSummary;
163     }
164 
165     @Override
getSettingsClassName()166     protected String getSettingsClassName() {
167         return ActiveUnlockRequireBiometricSetup.class.getName();
168     }
169 }
170