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