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