1 /* 2 * Copyright (C) 2024 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.server.adaptiveauth; 18 19 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; 20 21 import android.app.KeyguardManager; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.hardware.biometrics.AuthenticationStateListener; 25 import android.hardware.biometrics.BiometricManager; 26 import android.hardware.biometrics.events.AuthenticationAcquiredInfo; 27 import android.hardware.biometrics.events.AuthenticationErrorInfo; 28 import android.hardware.biometrics.events.AuthenticationFailedInfo; 29 import android.hardware.biometrics.events.AuthenticationHelpInfo; 30 import android.hardware.biometrics.events.AuthenticationStartedInfo; 31 import android.hardware.biometrics.events.AuthenticationStoppedInfo; 32 import android.hardware.biometrics.events.AuthenticationSucceededInfo; 33 import android.os.Build; 34 import android.os.Handler; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.os.PowerManager; 38 import android.os.SystemClock; 39 import android.util.Log; 40 import android.util.Slog; 41 import android.util.SparseIntArray; 42 import android.util.SparseLongArray; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.util.FrameworkStatsLog; 46 import com.android.internal.widget.LockPatternUtils; 47 import com.android.internal.widget.LockSettingsInternal; 48 import com.android.internal.widget.LockSettingsStateListener; 49 import com.android.server.LocalServices; 50 import com.android.server.SystemService; 51 import com.android.server.pm.UserManagerInternal; 52 import com.android.server.wm.WindowManagerInternal; 53 54 import java.util.Objects; 55 56 /** 57 * @hide 58 */ 59 public class AdaptiveAuthService extends SystemService { 60 private static final String TAG = "AdaptiveAuthService"; 61 private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG); 62 63 @VisibleForTesting 64 static final int MAX_ALLOWED_FAILED_AUTH_ATTEMPTS = 5; 65 private static final int MSG_REPORT_PRIMARY_AUTH_ATTEMPT = 1; 66 private static final int MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT = 2; 67 private static final int AUTH_SUCCESS = 1; 68 private static final int AUTH_FAILURE = 0; 69 private static final int TYPE_PRIMARY_AUTH = 0; 70 private static final int TYPE_BIOMETRIC_AUTH = 1; 71 72 private final LockPatternUtils mLockPatternUtils; 73 private final LockSettingsInternal mLockSettings; 74 private final BiometricManager mBiometricManager; 75 private final KeyguardManager mKeyguardManager; 76 private final PowerManager mPowerManager; 77 private final WindowManagerInternal mWindowManager; 78 private final UserManagerInternal mUserManager; 79 @VisibleForTesting 80 final SparseIntArray mFailedAttemptsForUser = new SparseIntArray(); 81 private final SparseLongArray mLastLockedTimestamp = new SparseLongArray(); 82 AdaptiveAuthService(Context context)83 public AdaptiveAuthService(Context context) { 84 this(context, new LockPatternUtils(context)); 85 } 86 87 @VisibleForTesting AdaptiveAuthService(Context context, LockPatternUtils lockPatternUtils)88 public AdaptiveAuthService(Context context, LockPatternUtils lockPatternUtils) { 89 super(context); 90 mLockPatternUtils = lockPatternUtils; 91 mLockSettings = Objects.requireNonNull( 92 LocalServices.getService(LockSettingsInternal.class)); 93 mBiometricManager = Objects.requireNonNull( 94 context.getSystemService(BiometricManager.class)); 95 mKeyguardManager = Objects.requireNonNull(context.getSystemService(KeyguardManager.class)); 96 mPowerManager = Objects.requireNonNull(context.getSystemService(PowerManager.class)); 97 mWindowManager = Objects.requireNonNull( 98 LocalServices.getService(WindowManagerInternal.class)); 99 mUserManager = Objects.requireNonNull(LocalServices.getService(UserManagerInternal.class)); 100 } 101 102 @Override onStart()103 public void onStart() {} 104 105 @Override onBootPhase(int phase)106 public void onBootPhase(int phase) { 107 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 108 init(); 109 } 110 } 111 112 @VisibleForTesting init()113 void init() { 114 mLockSettings.registerLockSettingsStateListener(mLockSettingsStateListener); 115 mBiometricManager.registerAuthenticationStateListener(mAuthenticationStateListener); 116 } 117 118 private final LockSettingsStateListener mLockSettingsStateListener = 119 new LockSettingsStateListener() { 120 @Override 121 public void onAuthenticationSucceeded(int userId) { 122 if (DEBUG) { 123 Slog.d(TAG, "LockSettingsStateListener#onAuthenticationSucceeded"); 124 } 125 mHandler.obtainMessage(MSG_REPORT_PRIMARY_AUTH_ATTEMPT, AUTH_SUCCESS, userId) 126 .sendToTarget(); 127 } 128 129 @Override 130 public void onAuthenticationFailed(int userId) { 131 Slog.i(TAG, "LockSettingsStateListener#onAuthenticationFailed"); 132 mHandler.obtainMessage(MSG_REPORT_PRIMARY_AUTH_ATTEMPT, AUTH_FAILURE, userId) 133 .sendToTarget(); 134 } 135 }; 136 137 private final AuthenticationStateListener mAuthenticationStateListener = 138 new AuthenticationStateListener.Stub() { 139 @Override 140 public void onAuthenticationAcquired(AuthenticationAcquiredInfo authInfo) {} 141 142 @Override 143 public void onAuthenticationError(AuthenticationErrorInfo authInfo) {} 144 145 @Override 146 public void onAuthenticationFailed(AuthenticationFailedInfo authInfo) { 147 Slog.i(TAG, "AuthenticationStateListener#onAuthenticationFailed"); 148 mHandler.obtainMessage(MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT, AUTH_FAILURE, 149 authInfo.getUserId()).sendToTarget(); 150 } 151 152 @Override 153 public void onAuthenticationHelp(AuthenticationHelpInfo authInfo) {} 154 155 @Override 156 public void onAuthenticationStarted(AuthenticationStartedInfo authInfo) {} 157 158 @Override 159 public void onAuthenticationStopped(AuthenticationStoppedInfo authInfo) {} 160 161 @Override 162 public void onAuthenticationSucceeded(AuthenticationSucceededInfo authInfo) { 163 if (DEBUG) { 164 Slog.d(TAG, "AuthenticationStateListener#onAuthenticationSucceeded"); 165 } 166 mHandler.obtainMessage(MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT, AUTH_SUCCESS, 167 authInfo.getUserId()).sendToTarget(); 168 } 169 }; 170 171 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 172 @Override 173 public void handleMessage(Message msg) { 174 switch (msg.what) { 175 case MSG_REPORT_PRIMARY_AUTH_ATTEMPT: 176 handleReportPrimaryAuthAttempt(msg.arg1 != AUTH_FAILURE, msg.arg2); 177 break; 178 case MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT: 179 handleReportBiometricAuthAttempt(msg.arg1 != AUTH_FAILURE, msg.arg2); 180 break; 181 } 182 } 183 }; 184 handleReportPrimaryAuthAttempt(boolean success, int userId)185 private void handleReportPrimaryAuthAttempt(boolean success, int userId) { 186 if (DEBUG) { 187 Slog.d(TAG, "handleReportPrimaryAuthAttempt: success=" + success 188 + ", userId=" + userId); 189 } 190 reportAuthAttempt(TYPE_PRIMARY_AUTH, success, userId); 191 } 192 handleReportBiometricAuthAttempt(boolean success, int userId)193 private void handleReportBiometricAuthAttempt(boolean success, int userId) { 194 if (DEBUG) { 195 Slog.d(TAG, "handleReportBiometricAuthAttempt: success=" + success 196 + ", userId=" + userId); 197 } 198 reportAuthAttempt(TYPE_BIOMETRIC_AUTH, success, userId); 199 } 200 reportAuthAttempt(int authType, boolean success, int userId)201 private void reportAuthAttempt(int authType, boolean success, int userId) { 202 // Disable adaptive auth for automotive devices by default 203 if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { 204 return; 205 } 206 207 if (success) { 208 // Deleting the entry effectively resets the counter of failed attempts for the user 209 mFailedAttemptsForUser.delete(userId); 210 211 // Collect metrics if the device was locked by adaptive auth before 212 if (mLastLockedTimestamp.indexOfKey(userId) >= 0) { 213 final long lastLockedTime = mLastLockedTimestamp.get(userId); 214 collectTimeElapsedSinceLastLocked( 215 lastLockedTime, SystemClock.elapsedRealtime(), authType); 216 217 // Remove the entry for the last locked time because a successful auth just happened 218 // and metrics have been collected 219 mLastLockedTimestamp.delete(userId); 220 } 221 return; 222 } 223 224 final int numFailedAttempts = mFailedAttemptsForUser.get(userId, 0) + 1; 225 Slog.i(TAG, "reportAuthAttempt: numFailedAttempts=" + numFailedAttempts 226 + ", userId=" + userId); 227 mFailedAttemptsForUser.put(userId, numFailedAttempts); 228 229 // Don't lock again if the device is already locked and if Keyguard is already showing and 230 // isn't trivially dismissible 231 if (mKeyguardManager.isDeviceLocked(userId) && mKeyguardManager.isKeyguardLocked()) { 232 Slog.d(TAG, "Not locking the device because the device is already locked."); 233 return; 234 } 235 236 if (numFailedAttempts < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS) { 237 Slog.d(TAG, "Not locking the device because the number of failed attempts is below" 238 + " the threshold."); 239 return; 240 } 241 242 //TODO: additionally consider the trust signal before locking device 243 lockDevice(userId); 244 } 245 collectTimeElapsedSinceLastLocked(long lastLockedTime, long authTime, int authType)246 private static void collectTimeElapsedSinceLastLocked(long lastLockedTime, long authTime, 247 int authType) { 248 final int unlockType = switch (authType) { 249 case TYPE_PRIMARY_AUTH -> FrameworkStatsLog 250 .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__PRIMARY_AUTH; 251 case TYPE_BIOMETRIC_AUTH -> FrameworkStatsLog 252 .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__BIOMETRIC_AUTH; 253 default -> FrameworkStatsLog 254 .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__UNKNOWN; 255 }; 256 257 if (DEBUG) { 258 Slog.d(TAG, "collectTimeElapsedSinceLastLockedForUser: " 259 + "lastLockedTime=" + lastLockedTime 260 + ", authTime=" + authTime 261 + ", unlockType=" + unlockType); 262 } 263 264 // This usually shouldn't happen, and just check out of an abundance of caution 265 if (lastLockedTime > authTime) { 266 return; 267 } 268 269 // Log to statsd 270 FrameworkStatsLog.write(FrameworkStatsLog.ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED, 271 lastLockedTime, authTime, unlockType); 272 } 273 274 /** 275 * Locks the device and requires primary auth or biometric auth for unlocking 276 */ lockDevice(int userId)277 private void lockDevice(int userId) { 278 // Require either primary auth or biometric auth to unlock the device again. Keyguard and 279 // bouncer will also check the StrongAuthFlag for the user to display correct strings for 280 // explaining why the device is locked 281 mLockPatternUtils.requireStrongAuth(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST, userId); 282 283 // If userId is a profile that has a different parent userId (regardless of its profile 284 // type, or whether it's a profile with unified challenges or not), its parent userId that 285 // owns the Keyguard will also be locked 286 final int parentUserId = mUserManager.getProfileParentId(userId); 287 Slog.i(TAG, "lockDevice: userId=" + userId + ", parentUserId=" + parentUserId); 288 if (parentUserId != userId) { 289 mLockPatternUtils.requireStrongAuth(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST, 290 parentUserId); 291 } 292 293 // Power off the display 294 mPowerManager.goToSleep(SystemClock.uptimeMillis()); 295 296 // Lock the device 297 mWindowManager.lockNow(); 298 299 // Record the time that the device is locked by adaptive auth to collect metrics when the 300 // next successful primary or biometric auth happens 301 mLastLockedTimestamp.put(userId, SystemClock.elapsedRealtime()); 302 } 303 } 304