• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ContentResolver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.ComponentInfo;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ProviderInfo;
28 import android.provider.DeviceConfig;
29 import android.provider.Settings;
30 import android.util.Log;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 import androidx.annotation.StringRes;
35 
36 import com.android.settings.R;
37 import com.android.settings.Utils;
38 import com.android.settings.core.BasePreferenceController;
39 import com.android.settings.core.BasePreferenceController.AvailabilityStatus;
40 
41 import java.util.List;
42 
43 /** Utilities for active unlock details shared between Security Settings and Safety Center. */
44 public class ActiveUnlockStatusUtils {
45 
46     /** The flag to determining whether active unlock in settings is enabled. */
47     public static final String CONFIG_FLAG_NAME = "active_unlock_in_settings";
48 
49     /** Flag value that represents the layout for unlock intent should be used. */
50     public static final String UNLOCK_INTENT_LAYOUT = "unlock_intent_layout";
51 
52     /** Flag value that represents the layout for biometric failure should be used. */
53     public static final String BIOMETRIC_FAILURE_LAYOUT = "biometric_failure_layout";
54 
55     private static final String ACTIVE_UNLOCK_PROVIDER = "active_unlock_provider";
56     private static final String ACTIVE_UNLOCK_TARGET = "active_unlock_target";
57 
58     private static final String TAG = "ActiveUnlockStatusUtils";
59 
60     private final Context mContext;
61     private final ContentResolver mContentResolver;
62 
ActiveUnlockStatusUtils(@onNull Context context)63     public ActiveUnlockStatusUtils(@NonNull Context context) {
64         mContext = context;
65         mContentResolver = mContext.getContentResolver();
66     }
67 
68     /** Returns whether the active unlock settings entity should be shown. */
isAvailable()69     public boolean isAvailable() {
70         return getAvailability() == BasePreferenceController.AVAILABLE;
71     }
72 
73     /**
74      * Returns whether the active unlock layout with the unlock on intent configuration should be
75      * used.
76      */
useUnlockIntentLayout()77     public boolean useUnlockIntentLayout() {
78         return isAvailable();
79     }
80 
81     /**
82      *
83      * Returns whether the active unlock layout with the unlock on biometric failure configuration
84      * should be used.
85      */
useBiometricFailureLayout()86     public boolean useBiometricFailureLayout() {
87         return false;
88     }
89 
90     /**
91      * Returns the authority used to fetch dynamic active unlock content.
92      */
93     @Nullable
getAuthority()94     public String getAuthority() {
95         final String authority = Settings.Secure.getString(
96                 mContext.getContentResolver(), ACTIVE_UNLOCK_PROVIDER);
97         if (authority == null) {
98             Log.i(TAG, "authority not set");
99             return null;
100         }
101         final List<PackageInfo> packageInfos =
102                 mContext.getPackageManager().getInstalledPackages(
103                         PackageManager.PackageInfoFlags.of(PackageManager.GET_PROVIDERS));
104         for (PackageInfo packageInfo : packageInfos) {
105             final ProviderInfo[] providers = packageInfo.providers;
106             if (providers != null) {
107                 for (ProviderInfo provider : providers) {
108                     if (authority.equals(provider.authority) && isSystemApp(provider)) {
109                         return authority;
110                     }
111                 }
112             }
113         }
114         Log.e(TAG, "authority not valid");
115         return null;
116     }
117 
isSystemApp(ComponentInfo componentInfo)118     private static boolean isSystemApp(ComponentInfo componentInfo) {
119         final ApplicationInfo applicationInfo = componentInfo.applicationInfo;
120         if (applicationInfo == null) {
121             Log.e(TAG, "application info is null");
122             return false;
123         }
124         return applicationInfo.isSystemApp();
125     }
126 
127     /**
128      * Returns the intent used to launch the active unlock activity.
129      */
130     @Nullable
getIntent()131     public Intent getIntent() {
132         final String targetAction = Settings.Secure.getString(
133                 mContentResolver, ACTIVE_UNLOCK_TARGET);
134         if (targetAction == null) {
135             Log.i(TAG, "Target action not set");
136             return null;
137         }
138         final Intent intent = new Intent(targetAction);
139         final ActivityInfo activityInfo = intent.resolveActivityInfo(
140                 mContext.getPackageManager(), PackageManager.MATCH_ALL);
141         if (activityInfo == null) {
142             Log.e(TAG, "Target activity not found");
143             return null;
144         }
145         if (!isSystemApp(activityInfo)) {
146             Log.e(TAG, "Target application is not system");
147             return null;
148         }
149         Log.i(TAG, "Target application is valid");
150         return intent;
151     }
152 
153     /** Returns the availability status of the active unlock feature. */
154     @AvailabilityStatus
getAvailability()155     int getAvailability() {
156         if (!Utils.hasFingerprintHardware(mContext) && !Utils.hasFaceHardware(mContext)) {
157             return BasePreferenceController.UNSUPPORTED_ON_DEVICE;
158         }
159         if (getAuthority() != null && getIntent() != null) {
160             return BasePreferenceController.AVAILABLE;
161         }
162         return BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
163     }
164 
165     /**
166      * Returns the title of the combined biometric settings entity when active unlock is enabled.
167      */
getTitleForActiveUnlock()168     public String getTitleForActiveUnlock() {
169         final boolean faceAllowed = Utils.hasFaceHardware(mContext);
170         final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
171         return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed));
172     }
173 
174     @StringRes
getTitleRes(boolean isFaceAllowed, boolean isFingerprintAllowed)175     private static int getTitleRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
176         if (isFaceAllowed && isFingerprintAllowed) {
177             return R.string.security_settings_biometric_preference_title;
178         } else if (isFaceAllowed) {
179             return R.string.security_settings_face_preference_title;
180         } else if (isFingerprintAllowed) {
181             return R.string.security_settings_fingerprint_preference_title;
182         } else {
183             // Default to original summary, but this case should never happen.
184             return R.string.security_settings_biometric_preference_title;
185         }
186     }
187 
188     /**
189      * Returns the intro of the combined biometric settings entity when active unlock is enabled.
190      */
getIntroForActiveUnlock()191     public String getIntroForActiveUnlock() {
192         final boolean faceAllowed = Utils.hasFaceHardware(mContext);
193         final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
194         if (useBiometricFailureLayout()) {
195             int introRes = getIntroRes(faceAllowed, fingerprintAllowed);
196             return introRes == 0 ? "" : mContext.getString(introRes);
197         }
198         if (useUnlockIntentLayout() && (!faceAllowed || !fingerprintAllowed)) {
199             return "";
200         }
201         return mContext.getString(R.string.biometric_settings_intro);
202     }
203 
204     @StringRes
getIntroRes(boolean isFaceAllowed, boolean isFingerprintAllowed)205     private static int getIntroRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
206         if (isFaceAllowed && isFingerprintAllowed) {
207             return R.string.biometric_settings_intro_with_activeunlock;
208         } else if (isFaceAllowed) {
209             return R.string.biometric_settings_intro_with_face;
210         } else if (isFingerprintAllowed) {
211             return R.string.biometric_settings_intro_with_fingerprint;
212         } else {
213             return 0;
214         }
215     }
216 
217     /**
218      * Returns the summary of the unlock device entity when active unlock is enabled.
219      */
getUnlockDeviceSummaryForActiveUnlock()220     public String getUnlockDeviceSummaryForActiveUnlock() {
221         final boolean faceAllowed = Utils.hasFaceHardware(mContext);
222         final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
223 
224         return mContext.getString(getUnlockDeviceSummaryRes(faceAllowed, fingerprintAllowed));
225     }
226 
227     @StringRes
getUnlockDeviceSummaryRes( boolean isFaceAllowed, boolean isFingerprintAllowed)228     private static int getUnlockDeviceSummaryRes(
229             boolean isFaceAllowed, boolean isFingerprintAllowed) {
230         if (isFaceAllowed && isFingerprintAllowed) {
231             return R.string.biometric_settings_use_face_fingerprint_or_watch_preference_summary;
232         } else if (isFaceAllowed) {
233             return R.string.biometric_settings_use_face_or_watch_preference_summary;
234         } else if (isFingerprintAllowed) {
235             return R.string.biometric_settings_use_fingerprint_or_watch_preference_summary;
236         } else {
237             return R.string.biometric_settings_use_watch_preference_summary;
238         }
239     }
240 
241     /**
242      * Returns the summary of the active unlock preference when biometrics are needed to set up the
243      * feature.
244      */
245     @Nullable
getSummaryWhenBiometricSetupRequired()246     public String getSummaryWhenBiometricSetupRequired() {
247         final boolean faceAllowed = Utils.hasFaceHardware(mContext);
248         final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
249 
250         int summaryRes = getSetupBiometricRes(faceAllowed, fingerprintAllowed);
251         return summaryRes == 0 ? null : mContext.getString(summaryRes);
252     }
253 
254     @StringRes
getSetupBiometricRes(boolean faceAllowed, boolean fingerprintAllowed)255     private static int getSetupBiometricRes(boolean faceAllowed, boolean fingerprintAllowed) {
256         if (faceAllowed && fingerprintAllowed) {
257             return R.string.security_settings_activeunlock_require_face_fingerprint_setup_title;
258         } else if (faceAllowed) {
259             return R.string.security_settings_activeunlock_require_face_setup_title;
260         } else if (fingerprintAllowed) {
261             return R.string.security_settings_activeunlock_require_fingerprint_setup_title;
262         } else {
263             return 0;
264         }
265     }
266 
267     /**
268      * Returns the preference title of how to use biometrics when active unlock is enabled.
269      */
getUseBiometricTitleForActiveUnlock()270     public String getUseBiometricTitleForActiveUnlock() {
271         final boolean faceAllowed = Utils.hasFaceHardware(mContext);
272         final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
273 
274         return mContext.getString(getUseBiometricTitleRes(faceAllowed, fingerprintAllowed));
275     }
276 
277     @StringRes
getUseBiometricTitleRes( boolean isFaceAllowed, boolean isFingerprintAllowed)278     private static int getUseBiometricTitleRes(
279             boolean isFaceAllowed, boolean isFingerprintAllowed) {
280         if (isFaceAllowed && isFingerprintAllowed) {
281             return R.string.biometric_settings_use_face_fingerprint_or_watch_for;
282         } else if (isFaceAllowed) {
283             return R.string.biometric_settings_use_face_or_watch_for;
284         } else if (isFingerprintAllowed) {
285             return R.string.biometric_settings_use_fingerprint_or_watch_for;
286         } else {
287             return R.string.biometric_settings_use_watch_for;
288         }
289     }
290 
getFlagState()291     private static String getFlagState() {
292         return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME);
293     }
294 }
295