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