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