1 /* 2 * Copyright (C) 2020 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.locksettings; 18 19 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; 20 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT; 21 import static com.android.server.locksettings.LockSettingsStrongAuth.DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS; 22 import static com.android.server.locksettings.LockSettingsStrongAuth.DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS; 23 import static com.android.server.locksettings.LockSettingsStrongAuth.NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG; 24 import static com.android.server.locksettings.LockSettingsStrongAuth.NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG; 25 import static com.android.server.locksettings.LockSettingsStrongAuth.STRONG_AUTH_TIMEOUT_ALARM_TAG; 26 27 import static junit.framework.Assert.assertEquals; 28 import static junit.framework.Assert.assertFalse; 29 import static junit.framework.Assert.assertNotNull; 30 import static junit.framework.Assert.assertNull; 31 import static junit.framework.Assert.assertTrue; 32 33 import static org.mockito.ArgumentMatchers.any; 34 import static org.mockito.ArgumentMatchers.eq; 35 import static org.mockito.Mockito.never; 36 import static org.mockito.Mockito.verify; 37 import static org.mockito.Mockito.when; 38 39 import android.app.AlarmManager; 40 import android.app.admin.DevicePolicyManager; 41 import android.content.Context; 42 import android.platform.test.annotations.Presubmit; 43 import android.util.Log; 44 45 import androidx.test.InstrumentationRegistry; 46 import androidx.test.filters.SmallTest; 47 import androidx.test.runner.AndroidJUnit4; 48 49 import com.android.server.locksettings.LockSettingsStrongAuth.NonStrongBiometricIdleTimeoutAlarmListener; 50 import com.android.server.locksettings.LockSettingsStrongAuth.NonStrongBiometricTimeoutAlarmListener; 51 import com.android.server.locksettings.LockSettingsStrongAuth.StrongAuthTimeoutAlarmListener; 52 53 import org.junit.Before; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.Mock; 57 import org.mockito.MockitoAnnotations; 58 59 @SmallTest 60 @Presubmit 61 @RunWith(AndroidJUnit4.class) 62 public class LockSettingsStrongAuthTest { 63 64 private static final String TAG = LockSettingsStrongAuthTest.class.getSimpleName(); 65 66 private static final int PRIMARY_USER_ID = 0; 67 68 private LockSettingsStrongAuth mStrongAuth; 69 private final int mDefaultStrongAuthFlags = STRONG_AUTH_NOT_REQUIRED; 70 private final boolean mDefaultIsNonStrongBiometricAllowed = true; 71 72 @Mock 73 private Context mContext; 74 @Mock 75 private LockSettingsStrongAuth.Injector mInjector; 76 @Mock 77 private AlarmManager mAlarmManager; 78 @Mock 79 private DevicePolicyManager mDPM; 80 81 @Before setUp()82 public void setUp() { 83 MockitoAnnotations.initMocks(this); 84 85 when(mInjector.getAlarmManager(mContext)).thenReturn(mAlarmManager); 86 when(mInjector.getDefaultStrongAuthFlags(mContext)).thenReturn(mDefaultStrongAuthFlags); 87 when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(mDPM); 88 89 mStrongAuth = new LockSettingsStrongAuth(mContext, mInjector); 90 } 91 92 @Test testScheduleNonStrongBiometricIdleTimeout()93 public void testScheduleNonStrongBiometricIdleTimeout() { 94 final long nextAlarmTime = 1000; 95 when(mInjector.getNextAlarmTimeMs(DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS)) 96 .thenReturn(nextAlarmTime); 97 mStrongAuth.scheduleNonStrongBiometricIdleTimeout(PRIMARY_USER_ID); 98 99 waitForIdle(); 100 NonStrongBiometricIdleTimeoutAlarmListener alarm = mStrongAuth 101 .mNonStrongBiometricIdleTimeoutAlarmListener.get(PRIMARY_USER_ID); 102 // verify that a new alarm for idle timeout is added for the user 103 assertNotNull(alarm); 104 // verify that the alarm is scheduled 105 verifyAlarm(nextAlarmTime, NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG, alarm); 106 } 107 108 @Test testSetIsNonStrongBiometricAllowed_disallowed()109 public void testSetIsNonStrongBiometricAllowed_disallowed() { 110 mStrongAuth.setIsNonStrongBiometricAllowed(false /* allowed */, PRIMARY_USER_ID); 111 112 waitForIdle(); 113 // verify that unlocking with non-strong biometrics is not allowed 114 assertFalse(mStrongAuth.mIsNonStrongBiometricAllowedForUser 115 .get(PRIMARY_USER_ID, mDefaultIsNonStrongBiometricAllowed)); 116 } 117 118 @Test testReportSuccessfulBiometricUnlock_nonStrongBiometric_fallbackTimeout()119 public void testReportSuccessfulBiometricUnlock_nonStrongBiometric_fallbackTimeout() { 120 final long nextAlarmTime = 1000; 121 when(mInjector.getNextAlarmTimeMs(DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS)) 122 .thenReturn(nextAlarmTime); 123 mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, PRIMARY_USER_ID); 124 125 waitForIdle(); 126 NonStrongBiometricTimeoutAlarmListener alarm = 127 mStrongAuth.mNonStrongBiometricTimeoutAlarmListener.get(PRIMARY_USER_ID); 128 // verify that a new alarm for fallback timeout is added for the user 129 assertNotNull(alarm); 130 // verify that the alarm is scheduled 131 verifyAlarm(nextAlarmTime, NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG, alarm); 132 } 133 134 @Test testRequireStrongAuth_nonStrongBiometric_fallbackTimeout()135 public void testRequireStrongAuth_nonStrongBiometric_fallbackTimeout() { 136 mStrongAuth.requireStrongAuth( 137 STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT /* strongAuthReason */, 138 PRIMARY_USER_ID); 139 140 waitForIdle(); 141 // verify that the StrongAuthFlags for the user contains the expected flag 142 final int expectedFlag = STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT; 143 verifyStrongAuthFlags(expectedFlag, PRIMARY_USER_ID); 144 } 145 146 @Test testReportSuccessfulBiometricUnlock_nonStrongBiometric_cancelIdleTimeout()147 public void testReportSuccessfulBiometricUnlock_nonStrongBiometric_cancelIdleTimeout() { 148 // lock device and schedule an alarm for non-strong biometric idle timeout 149 mStrongAuth.scheduleNonStrongBiometricIdleTimeout(PRIMARY_USER_ID); 150 // unlock with non-strong biometric 151 mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, PRIMARY_USER_ID); 152 153 waitForIdle(); 154 155 // verify that the current alarm for idle timeout is cancelled after a successful unlock 156 verify(mAlarmManager).cancel(any(NonStrongBiometricIdleTimeoutAlarmListener.class)); 157 } 158 159 @Test testReportSuccessfulBiometricUnlock_strongBio_cancelAlarmsAndAllowNonStrongBio()160 public void testReportSuccessfulBiometricUnlock_strongBio_cancelAlarmsAndAllowNonStrongBio() { 161 setupAlarms(PRIMARY_USER_ID); 162 mStrongAuth.reportSuccessfulBiometricUnlock(true /* isStrongBiometric */, PRIMARY_USER_ID); 163 164 waitForIdle(); 165 // verify that unlocking with strong biometric cancels alarms for fallback and idle timeout 166 // and re-allow unlocking with non-strong biometric 167 verifyAlarmsCancelledAndNonStrongBiometricAllowed(PRIMARY_USER_ID); 168 } 169 170 @Test testReportSuccessfulStrongAuthUnlock_schedulePrimaryAuthTimeout()171 public void testReportSuccessfulStrongAuthUnlock_schedulePrimaryAuthTimeout() { 172 final long currentTime = 1000; 173 final long timeout = 1000; 174 final long nextAlarmTime = currentTime + timeout; 175 when(mInjector.getElapsedRealtimeMs()).thenReturn(currentTime); 176 when(mDPM.getRequiredStrongAuthTimeout(null, PRIMARY_USER_ID)).thenReturn(timeout); 177 mStrongAuth.reportSuccessfulStrongAuthUnlock(PRIMARY_USER_ID); 178 179 waitForIdle(); 180 StrongAuthTimeoutAlarmListener alarm = 181 mStrongAuth.mStrongAuthTimeoutAlarmListenerForUser.get(PRIMARY_USER_ID); 182 // verify that a new alarm for primary auth timeout is added for the user 183 assertNotNull(alarm); 184 // verify that the alarm is scheduled 185 verifyAlarm(nextAlarmTime, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm); 186 } 187 188 @Test testReportSuccessfulStrongAuthUnlock_testRefreshStrongAuthTimeout()189 public void testReportSuccessfulStrongAuthUnlock_testRefreshStrongAuthTimeout() { 190 final long currentTime = 1000; 191 final long oldTimeout = 5000; 192 final long nextAlarmTime = currentTime + oldTimeout; 193 when(mInjector.getElapsedRealtimeMs()).thenReturn(currentTime); 194 when(mDPM.getRequiredStrongAuthTimeout(null, PRIMARY_USER_ID)).thenReturn(oldTimeout); 195 mStrongAuth.reportSuccessfulStrongAuthUnlock(PRIMARY_USER_ID); 196 waitForIdle(); 197 198 StrongAuthTimeoutAlarmListener alarm = 199 mStrongAuth.mStrongAuthTimeoutAlarmListenerForUser.get(PRIMARY_USER_ID); 200 assertEquals(currentTime, alarm.getLatestStrongAuthTime()); 201 verifyAlarm(nextAlarmTime, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm); 202 203 final long newTimeout = 3000; 204 when(mDPM.getRequiredStrongAuthTimeout(null, PRIMARY_USER_ID)).thenReturn(newTimeout); 205 mStrongAuth.refreshStrongAuthTimeout(PRIMARY_USER_ID); 206 waitForIdle(); 207 verify(mAlarmManager).cancel(alarm); 208 verifyAlarm(currentTime + newTimeout, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm); 209 } 210 211 @Test testReportSuccessfulStrongAuthUnlock_cancelAlarmsAndAllowNonStrongBio()212 public void testReportSuccessfulStrongAuthUnlock_cancelAlarmsAndAllowNonStrongBio() { 213 setupAlarms(PRIMARY_USER_ID); 214 mStrongAuth.reportSuccessfulStrongAuthUnlock(PRIMARY_USER_ID); 215 216 waitForIdle(); 217 // verify that unlocking with primary auth (PIN/pattern/password) cancels alarms 218 // for fallback and idle timeout and re-allow unlocking with non-strong biometric 219 verifyAlarmsCancelledAndNonStrongBiometricAllowed(PRIMARY_USER_ID); 220 } 221 222 @Test testFallbackTimeout_convenienceBiometric_weakBiometric()223 public void testFallbackTimeout_convenienceBiometric_weakBiometric() { 224 // assume that unlock with convenience biometric 225 mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, PRIMARY_USER_ID); 226 // assume that unlock again with weak biometric 227 mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, PRIMARY_USER_ID); 228 229 waitForIdle(); 230 // verify that the fallback alarm scheduled when unlocking with convenience biometric is 231 // not affected when unlocking again with weak biometric 232 verify(mAlarmManager, never()).cancel(any(NonStrongBiometricTimeoutAlarmListener.class)); 233 assertNotNull(mStrongAuth.mNonStrongBiometricTimeoutAlarmListener.get(PRIMARY_USER_ID)); 234 } 235 verifyAlarm(long when, String tag, AlarmManager.OnAlarmListener alarm)236 private void verifyAlarm(long when, String tag, AlarmManager.OnAlarmListener alarm) { 237 verify(mAlarmManager).setExact( 238 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), 239 eq(when), 240 eq(tag), 241 eq(alarm), 242 eq(mStrongAuth.mHandler)); 243 } 244 verifyStrongAuthFlags(int reason, int userId)245 private void verifyStrongAuthFlags(int reason, int userId) { 246 final int flags = mStrongAuth.mStrongAuthForUser.get(userId, mDefaultStrongAuthFlags); 247 Log.d(TAG, "verifyStrongAuthFlags:" 248 + " reason=" + Integer.toHexString(reason) 249 + " userId=" + userId 250 + " flags=" + Integer.toHexString(flags)); 251 assertTrue(containsFlag(flags, reason)); 252 } 253 setupAlarms(int userId)254 private void setupAlarms(int userId) { 255 // schedule (a) an alarm for non-strong biometric fallback timeout and (b) an alarm for 256 // non-strong biometric idle timeout, so later we can verify that unlocking with 257 // strong biometric or primary auth will cancel those alarms 258 mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, userId); 259 mStrongAuth.scheduleNonStrongBiometricIdleTimeout(userId); 260 } 261 verifyAlarmsCancelledAndNonStrongBiometricAllowed(int userId)262 private void verifyAlarmsCancelledAndNonStrongBiometricAllowed(int userId) { 263 // verify that the current alarm for non-strong biometric fallback timeout is cancelled and 264 // removed 265 verify(mAlarmManager).cancel(any(NonStrongBiometricTimeoutAlarmListener.class)); 266 assertNull(mStrongAuth.mNonStrongBiometricTimeoutAlarmListener.get(userId)); 267 268 // verify that the current alarm for non-strong biometric idle timeout is cancelled 269 verify(mAlarmManager).cancel(any(NonStrongBiometricIdleTimeoutAlarmListener.class)); 270 271 // verify that unlocking with non-strong biometrics is allowed 272 assertTrue(mStrongAuth.mIsNonStrongBiometricAllowedForUser 273 .get(userId, mDefaultIsNonStrongBiometricAllowed)); 274 } 275 containsFlag(int haystack, int needle)276 private static boolean containsFlag(int haystack, int needle) { 277 return (haystack & needle) != 0; 278 } 279 waitForIdle()280 private static void waitForIdle() { 281 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 282 } 283 } 284