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 the combined biometric settings entity when active unlock is enabled. 160 */ getTitleForActiveUnlock()161 public String getTitleForActiveUnlock() { 162 final boolean faceAllowed = Utils.hasFaceHardware(mContext); 163 final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext); 164 return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed)); 165 } 166 167 @StringRes getTitleRes(boolean isFaceAllowed, boolean isFingerprintAllowed)168 private static int getTitleRes(boolean isFaceAllowed, boolean isFingerprintAllowed) { 169 if (isFaceAllowed && isFingerprintAllowed) { 170 return R.string.security_settings_biometric_preference_title; 171 } else if (isFaceAllowed) { 172 return R.string.security_settings_face_preference_title; 173 } else if (isFingerprintAllowed) { 174 return R.string.security_settings_fingerprint_preference_title; 175 } else { 176 // Default to original summary, but this case should never happen. 177 return R.string.security_settings_biometric_preference_title; 178 } 179 } 180 181 /** 182 * Returns the intro of the combined biometric settings entity when active unlock is enabled. 183 */ getIntroForActiveUnlock()184 public String getIntroForActiveUnlock() { 185 final boolean faceAllowed = Utils.hasFaceHardware(mContext); 186 final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext); 187 if (isAvailable()) { 188 int introRes = getIntroRes(faceAllowed, fingerprintAllowed); 189 return introRes == 0 ? "" : mContext.getString(introRes); 190 } 191 return mContext.getString(R.string.biometric_settings_intro); 192 } 193 194 @StringRes getIntroRes(boolean isFaceAllowed, boolean isFingerprintAllowed)195 private static int getIntroRes(boolean isFaceAllowed, boolean isFingerprintAllowed) { 196 if (isFaceAllowed && isFingerprintAllowed) { 197 return R.string.biometric_settings_intro_with_activeunlock; 198 } else if (isFaceAllowed) { 199 return R.string.biometric_settings_intro_with_face; 200 } else if (isFingerprintAllowed) { 201 return R.string.biometric_settings_intro_with_fingerprint; 202 } else { 203 return 0; 204 } 205 } 206 207 /** 208 * Returns the summary of the unlock device entity when active unlock is enabled. 209 */ getUnlockDeviceSummaryForActiveUnlock()210 public String getUnlockDeviceSummaryForActiveUnlock() { 211 final boolean faceAllowed = Utils.hasFaceHardware(mContext); 212 final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext); 213 214 return mContext.getString(getUnlockDeviceSummaryRes(faceAllowed, fingerprintAllowed)); 215 } 216 217 @StringRes getUnlockDeviceSummaryRes( boolean isFaceAllowed, boolean isFingerprintAllowed)218 private static int getUnlockDeviceSummaryRes( 219 boolean isFaceAllowed, boolean isFingerprintAllowed) { 220 if (isFaceAllowed && isFingerprintAllowed) { 221 return R.string.biometric_settings_use_face_fingerprint_or_watch_preference_summary; 222 } else if (isFaceAllowed) { 223 return R.string.biometric_settings_use_face_or_watch_preference_summary; 224 } else if (isFingerprintAllowed) { 225 return R.string.biometric_settings_use_fingerprint_or_watch_preference_summary; 226 } else { 227 return R.string.biometric_settings_use_watch_preference_summary; 228 } 229 } 230 231 /** 232 * Returns the summary of the active unlock preference when biometrics are needed to set up the 233 * feature. 234 */ 235 @Nullable getSummaryWhenBiometricSetupRequired()236 public String getSummaryWhenBiometricSetupRequired() { 237 final boolean faceAllowed = Utils.hasFaceHardware(mContext); 238 final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext); 239 240 int summaryRes = getSetupBiometricRes(faceAllowed, fingerprintAllowed); 241 return summaryRes == 0 ? null : mContext.getString(summaryRes); 242 } 243 244 @StringRes getSetupBiometricRes(boolean faceAllowed, boolean fingerprintAllowed)245 private static int getSetupBiometricRes(boolean faceAllowed, boolean fingerprintAllowed) { 246 if (faceAllowed && fingerprintAllowed) { 247 return R.string.security_settings_activeunlock_require_face_fingerprint_setup_title; 248 } else if (faceAllowed) { 249 return R.string.security_settings_activeunlock_require_face_setup_title; 250 } else if (fingerprintAllowed) { 251 return R.string.security_settings_activeunlock_require_fingerprint_setup_title; 252 } else { 253 return 0; 254 } 255 } 256 257 /** 258 * Returns the preference title of how to use biometrics when active unlock is enabled. 259 */ getUseBiometricTitleForActiveUnlock()260 public String getUseBiometricTitleForActiveUnlock() { 261 final boolean faceAllowed = Utils.hasFaceHardware(mContext); 262 final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext); 263 264 return mContext.getString(getUseBiometricTitleRes(faceAllowed, fingerprintAllowed)); 265 } 266 267 @StringRes getUseBiometricTitleRes( boolean isFaceAllowed, boolean isFingerprintAllowed)268 private static int getUseBiometricTitleRes( 269 boolean isFaceAllowed, boolean isFingerprintAllowed) { 270 if (isFaceAllowed && isFingerprintAllowed) { 271 return R.string.biometric_settings_use_face_fingerprint_or_watch_for; 272 } else if (isFaceAllowed) { 273 return R.string.biometric_settings_use_face_or_watch_for; 274 } else if (isFingerprintAllowed) { 275 return R.string.biometric_settings_use_fingerprint_or_watch_for; 276 } else { 277 return R.string.biometric_settings_use_watch_for; 278 } 279 } 280 getFlagState()281 private static String getFlagState() { 282 return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME); 283 } 284 } 285