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