1 /*
2  * Copyright (C) 2014 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 org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertSame;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.when;
29 
30 import android.app.KeyguardManager;
31 import android.app.NotificationManager;
32 import android.app.admin.DevicePolicyManager;
33 import android.app.trust.TrustManager;
34 import android.content.Context;
35 import android.content.pm.PackageManager;
36 import android.content.pm.UserInfo;
37 import android.content.res.Resources;
38 import android.database.sqlite.SQLiteDatabase;
39 import android.hardware.face.FaceManager;
40 import android.hardware.fingerprint.FingerprintManager;
41 import android.os.FileUtils;
42 import android.os.SystemClock;
43 import android.os.UserManager;
44 import android.os.storage.StorageManager;
45 import android.platform.test.annotations.Presubmit;
46 import android.util.Log;
47 import android.util.Log.TerribleFailure;
48 import android.util.Log.TerribleFailureHandler;
49 
50 import androidx.test.InstrumentationRegistry;
51 import androidx.test.filters.SmallTest;
52 import androidx.test.runner.AndroidJUnit4;
53 
54 import com.android.internal.util.test.FakeSettingsProvider;
55 import com.android.internal.util.test.FakeSettingsProviderRule;
56 import com.android.server.PersistentDataBlockManagerInternal;
57 import com.android.server.locksettings.LockSettingsStorage.PersistentData;
58 
59 import org.junit.After;
60 import org.junit.Before;
61 import org.junit.Rule;
62 import org.junit.Test;
63 import org.junit.runner.RunWith;
64 
65 import java.io.File;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.List;
69 import java.util.concurrent.BrokenBarrierException;
70 import java.util.concurrent.CountDownLatch;
71 import java.util.concurrent.CyclicBarrier;
72 import java.util.concurrent.atomic.AtomicReference;
73 
74 /**
75  * atest FrameworksServicesTests:LockSettingsStorageTests
76  */
77 @SmallTest
78 @Presubmit
79 @RunWith(AndroidJUnit4.class)
80 public class LockSettingsStorageTests {
81     private static final int SOME_USER_ID = 1034;
82     private final byte[] PASSWORD = "thepassword".getBytes();
83 
84     public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 33};
85 
86     private LockSettingsStorageTestable mStorage;
87     private File mStorageDir;
88     private File mDb;
89     @Rule
90     public FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
91 
92     @Before
setUp()93     public void setUp() throws Exception {
94         final Context origContext = InstrumentationRegistry.getContext();
95 
96         mStorageDir = new File(origContext.getFilesDir(), "locksettings");
97         mDb = InstrumentationRegistry.getContext().getDatabasePath("locksettings.db");
98 
99         assertTrue(mStorageDir.exists() || mStorageDir.mkdirs());
100         assertTrue(FileUtils.deleteContents(mStorageDir));
101         assertTrue(!mDb.exists() || mDb.delete());
102 
103         final UserManager mockUserManager = mock(UserManager.class);
104         // User 2 is a profile of user 1.
105         when(mockUserManager.getProfileParent(eq(2))).thenReturn(new UserInfo(1, "name", 0));
106         // User 3 is a profile of user 0.
107         when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
108 
109         MockLockSettingsContext context = new MockLockSettingsContext(origContext,
110                 mock(Resources.class), mSettingsRule.mockContentResolver(origContext),
111                 mockUserManager, mock(NotificationManager.class), mock(DevicePolicyManager.class),
112                 mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class),
113                 mock(FingerprintManager.class), mock(FaceManager.class),
114                 mock(PackageManager.class));
115         mStorage = new LockSettingsStorageTestable(context,
116                 new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
117         mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() {
118                     @Override
119                     public void initialize(SQLiteDatabase db) {
120                         mStorage.writeKeyValue(db, "initializedKey", "initialValue", 0);
121                     }
122                 });
123     }
124 
125     @After
tearDown()126     public void tearDown() throws Exception {
127         mStorage.closeDatabase();
128     }
129 
130     @Test
testKeyValue_InitializeWorked()131     public void testKeyValue_InitializeWorked() {
132         assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0));
133         mStorage.clearCache();
134         assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0));
135     }
136 
137     @Test
testKeyValue_WriteThenRead()138     public void testKeyValue_WriteThenRead() {
139         mStorage.writeKeyValue("key", "value", 0);
140         assertEquals("value", mStorage.readKeyValue("key", "default", 0));
141         mStorage.clearCache();
142         assertEquals("value", mStorage.readKeyValue("key", "default", 0));
143     }
144 
145     @Test
testKeyValue_DefaultValue()146     public void testKeyValue_DefaultValue() {
147         assertEquals("default", mStorage.readKeyValue("unititialized key", "default", 0));
148         assertEquals("default2", mStorage.readKeyValue("unititialized key", "default2", 0));
149     }
150 
151     @Test
testKeyValue_ReadWriteConcurrency()152     public void testKeyValue_ReadWriteConcurrency() {
153         final CountDownLatch latch = new CountDownLatch(1);
154         List<Thread> threads = new ArrayList<>();
155         for (int i = 0; i < 100; i++) {
156             final int threadId = i;
157             threads.add(new Thread("testKeyValue_ReadWriteConcurrency_" + i) {
158                 @Override
159                 public void run() {
160                     try {
161                         latch.await();
162                     } catch (InterruptedException e) {
163                         return;
164                     }
165                     mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
166                     mStorage.readKeyValue("key", "default", 0);
167                     mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
168                     mStorage.readKeyValue("key", "default", 0);
169                     mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
170                     mStorage.readKeyValue("key", "default", 0);
171                     mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
172                     mStorage.readKeyValue("key", "default", 0);
173                     mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
174                     mStorage.readKeyValue("key", "default", 0);
175                 }
176             });
177             threads.get(i).start();
178         }
179         mStorage.writeKeyValue("key", "initialValue", 0);
180         latch.countDown();
181         joinAll(threads, 10000);
182         assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
183         mStorage.clearCache();
184         assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
185     }
186 
187     // Test that readKeyValue() doesn't pollute the cache when run concurrently with removeKey().
188     @Test
189     @SuppressWarnings("AssertionFailureIgnored") // intentional try-catch of AssertionError
testKeyValue_ReadRemoveConcurrency()190     public void testKeyValue_ReadRemoveConcurrency() {
191         final int numThreads = 2;
192         final int numIterations = 50;
193         final CyclicBarrier barrier = new CyclicBarrier(numThreads);
194         final List<Thread> threads = new ArrayList<>();
195         final AtomicReference<Throwable> failure = new AtomicReference<>();
196         for (int threadId = 0; threadId < numThreads; threadId++) {
197             final boolean isWriter = (threadId == 0);
198             threads.add(new Thread("testKeyValue_ReadRemoveConcurrency_" + threadId) {
199                 @Override
200                 public void run() {
201                     try {
202                         for (int iter = 0; iter < numIterations; iter++) {
203                             if (isWriter) {
204                                 mStorage.writeKeyValue("key", "value", 0);
205                                 mStorage.clearCache();
206                             }
207                             barrier.await();
208                             if (isWriter) {
209                                 mStorage.removeKey("key", 0);
210                             } else {
211                                 mStorage.readKeyValue("key", "default", 0);
212                             }
213                             barrier.await();
214                             try {
215                                 assertEquals("default", mStorage.readKeyValue("key", "default", 0));
216                             } catch (AssertionError e) {
217                                 failure.compareAndSet(null, e);
218                             }
219                             barrier.await();
220                         }
221                     } catch (InterruptedException | BrokenBarrierException e) {
222                         failure.compareAndSet(null, e);
223                         return;
224                     }
225                 }
226             });
227             threads.get(threadId).start();
228         }
229         joinAll(threads, 60000);
230         assertNull(failure.get());
231     }
232 
233     @Test
testKeyValue_CacheStarvedWriter()234     public void testKeyValue_CacheStarvedWriter() {
235         final CountDownLatch latch = new CountDownLatch(1);
236         List<Thread> threads = new ArrayList<>();
237         for (int i = 0; i < 100; i++) {
238             final int threadId = i;
239             threads.add(new Thread() {
240                 @Override
241                 public void run() {
242                     try {
243                         latch.await();
244                     } catch (InterruptedException e) {
245                         return;
246                     }
247                     if (threadId == 50) {
248                         mStorage.writeKeyValue("starvedWriterKey", "value", 0);
249                     } else {
250                         mStorage.readKeyValue("starvedWriterKey", "default", 0);
251                     }
252                 }
253             });
254             threads.get(i).start();
255         }
256         latch.countDown();
257         for (int i = 0; i < threads.size(); i++) {
258             try {
259                 threads.get(i).join();
260             } catch (InterruptedException e) {
261             }
262         }
263         String cached = mStorage.readKeyValue("key", "default", 0);
264         mStorage.clearCache();
265         String storage = mStorage.readKeyValue("key", "default", 0);
266         assertEquals("Cached value didn't match stored value", storage, cached);
267     }
268 
269     @Test
testNullKey()270     public void testNullKey() {
271         mStorage.setString(null, "value", 0);
272 
273         // Verify that this doesn't throw an exception.
274         assertEquals("value", mStorage.readKeyValue(null, null, 0));
275 
276         // The read that happens as part of prefetchUser shouldn't throw an exception either.
277         mStorage.clearCache();
278         mStorage.prefetchUser(0);
279 
280         assertEquals("value", mStorage.readKeyValue(null, null, 0));
281     }
282 
283     @Test
testRemoveUser()284     public void testRemoveUser() {
285         mStorage.writeKeyValue("key", "value", 0);
286         mStorage.writeKeyValue("key", "value", 1);
287 
288         mStorage.removeUser(0);
289 
290         assertEquals("value", mStorage.readKeyValue("key", "default", 1));
291         assertEquals("default", mStorage.readKeyValue("key", "default", 0));
292     }
293 
294     @Test
testProfileLock_ReadWriteChildProfileLock()295     public void testProfileLock_ReadWriteChildProfileLock() {
296         assertFalse(mStorage.hasChildProfileLock(20));
297         mStorage.writeChildProfileLock(20, PASSWORD);
298         assertArrayEquals(PASSWORD, mStorage.readChildProfileLock(20));
299         assertTrue(mStorage.hasChildProfileLock(20));
300         mStorage.clearCache();
301         assertArrayEquals(PASSWORD, mStorage.readChildProfileLock(20));
302         assertTrue(mStorage.hasChildProfileLock(20));
303     }
304 
305     @Test
testPrefetch()306     public void testPrefetch() {
307         mStorage.writeKeyValue("key1", "value1", 0);
308         mStorage.writeKeyValue("key2", "value2", 0);
309 
310         mStorage.clearCache();
311 
312         assertFalse(mStorage.isUserPrefetched(0));
313         assertFalse(mStorage.isKeyValueCached("key1", 0));
314         assertFalse(mStorage.isKeyValueCached("key2", 0));
315 
316         mStorage.prefetchUser(0);
317 
318         assertTrue(mStorage.isUserPrefetched(0));
319         assertTrue(mStorage.isKeyValueCached("key1", 0));
320         assertTrue(mStorage.isKeyValueCached("key2", 0));
321         assertEquals("value1", mStorage.readKeyValue("key1", "default", 0));
322         assertEquals("value2", mStorage.readKeyValue("key2", "default", 0));
323     }
324 
325     @Test
testFileLocation_Owner()326     public void testFileLocation_Owner() {
327         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
328 
329         assertEquals(new File("/data/system/gatekeeper.profile.key"),
330                 storage.getChildProfileLockFile(0));
331     }
332 
333     @Test
testFileLocation_SecondaryUser()334     public void testFileLocation_SecondaryUser() {
335         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
336 
337         assertEquals(new File("/data/system/users/1/gatekeeper.profile.key"),
338                 storage.getChildProfileLockFile(1));
339     }
340 
341     @Test
testFileLocation_ProfileToSecondary()342     public void testFileLocation_ProfileToSecondary() {
343         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
344 
345         assertEquals(new File("/data/system/users/2/gatekeeper.profile.key"),
346                 storage.getChildProfileLockFile(2));
347     }
348 
349     @Test
testFileLocation_ProfileToOwner()350     public void testFileLocation_ProfileToOwner() {
351         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
352 
353         assertEquals(new File("/data/system/users/3/gatekeeper.profile.key"),
354                 storage.getChildProfileLockFile(3));
355     }
356 
357     @Test
testSyntheticPasswordState()358     public void testSyntheticPasswordState() {
359         final byte[] data = {1,2,3,4};
360         mStorage.writeSyntheticPasswordState(10, 1234L, "state", data);
361         assertArrayEquals(data, mStorage.readSyntheticPasswordState(10, 1234L, "state"));
362         assertEquals(null, mStorage.readSyntheticPasswordState(0, 1234L, "state"));
363 
364         mStorage.deleteSyntheticPasswordState(10, 1234L, "state");
365         assertEquals(null, mStorage.readSyntheticPasswordState(10, 1234L, "state"));
366     }
367 
368     @Test
testPersistentDataBlock_unavailable()369     public void testPersistentDataBlock_unavailable() {
370         mStorage.mPersistentDataBlockManager = null;
371 
372         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
373     }
374 
375     @Test
testPersistentDataBlock_empty()376     public void testPersistentDataBlock_empty() {
377         mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
378 
379         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
380     }
381 
382     @Test
testPersistentDataBlock_withData()383     public void testPersistentDataBlock_withData() {
384         mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
385         when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle())
386                 .thenReturn(PersistentData.toBytes(PersistentData.TYPE_SP_WEAVER, SOME_USER_ID,
387                         DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD));
388 
389         PersistentData data = mStorage.readPersistentDataBlock();
390 
391         assertEquals(PersistentData.TYPE_SP_WEAVER, data.type);
392         assertEquals(SOME_USER_ID, data.userId);
393         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, data.qualityForUi);
394         assertArrayEquals(PAYLOAD, data.payload);
395     }
396 
397     @Test
testPersistentDataBlock_exception()398     public void testPersistentDataBlock_exception() {
399         mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
400         when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle())
401                 .thenThrow(new IllegalStateException("oops"));
402         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
403     }
404 
405     @Test
testPersistentData_serializeUnserialize()406     public void testPersistentData_serializeUnserialize() {
407         byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID,
408                 DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD);
409         PersistentData deserialized = PersistentData.fromBytes(serialized);
410 
411         assertEquals(PersistentData.TYPE_SP_GATEKEEPER, deserialized.type);
412         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, deserialized.qualityForUi);
413         assertArrayEquals(PAYLOAD, deserialized.payload);
414     }
415 
416     @Test
testPersistentData_unserializeNull()417     public void testPersistentData_unserializeNull() {
418         PersistentData deserialized = PersistentData.fromBytes(null);
419         assertSame(PersistentData.NONE, deserialized);
420     }
421 
422     @Test
testPersistentData_unserializeEmptyArray()423     public void testPersistentData_unserializeEmptyArray() {
424         PersistentData deserialized = PersistentData.fromBytes(new byte[0]);
425         assertSame(PersistentData.NONE, deserialized);
426     }
427 
428     @Test
testPersistentData_unserializeInvalid()429     public void testPersistentData_unserializeInvalid() {
430         assertNotNull(suppressAndReturnWtf(() -> {
431             PersistentData deserialized = PersistentData.fromBytes(new byte[]{5});
432             assertSame(PersistentData.NONE, deserialized);
433         }));
434     }
435 
436     @Test
testPersistentData_unserialize_version1()437     public void testPersistentData_unserialize_version1() {
438         // This test ensures that we can read serialized VERSION_1 PersistentData even if we change
439         // the wire format in the future.
440         byte[] serializedVersion1 = new byte[] {
441                 1, /* PersistentData.VERSION_1 */
442                 1, /* PersistentData.TYPE_SP_GATEKEEPER */
443                 0x00, 0x00, 0x04, 0x0A,  /* SOME_USER_ID */
444                 0x00, 0x03, 0x00, 0x00,  /* PASSWORD_NUMERIC_COMPLEX */
445                 1, 2, -1, -2, 33, /* PAYLOAD */
446         };
447         PersistentData deserialized = PersistentData.fromBytes(serializedVersion1);
448         assertEquals(PersistentData.TYPE_SP_GATEKEEPER, deserialized.type);
449         assertEquals(SOME_USER_ID, deserialized.userId);
450         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
451                 deserialized.qualityForUi);
452         assertArrayEquals(PAYLOAD, deserialized.payload);
453 
454         // Make sure the constants we use on the wire do not change.
455         assertEquals(0, PersistentData.TYPE_NONE);
456         assertEquals(1, PersistentData.TYPE_SP_GATEKEEPER);
457         assertEquals(2, PersistentData.TYPE_SP_WEAVER);
458     }
459 
460     @Test
testRepairMode_emptyPersistentData()461     public void testRepairMode_emptyPersistentData() {
462         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
463     }
464 
465     @Test
testRepairMode_writeGatekeeperPersistentData()466     public void testRepairMode_writeGatekeeperPersistentData() {
467         mStorage.writeRepairModePersistentData(
468                 PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID, PAYLOAD);
469 
470         final PersistentData data = mStorage.readRepairModePersistentData();
471         assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type);
472         assertArrayEquals(PAYLOAD, data.payload);
473     }
474 
475     @Test
testRepairMode_writeWeaverPersistentData()476     public void testRepairMode_writeWeaverPersistentData() {
477         mStorage.writeRepairModePersistentData(
478                 PersistentData.TYPE_SP_WEAVER, SOME_USER_ID, PAYLOAD);
479 
480         final PersistentData data = mStorage.readRepairModePersistentData();
481         assertEquals(PersistentData.TYPE_SP_WEAVER, data.type);
482         assertArrayEquals(PAYLOAD, data.payload);
483     }
484 
assertArrayEquals(byte[] expected, byte[] actual)485     private static void assertArrayEquals(byte[] expected, byte[] actual) {
486         if (!Arrays.equals(expected, actual)) {
487             fail("expected:<" + Arrays.toString(expected) +
488                     "> but was:<" + Arrays.toString(actual) + ">");
489         }
490     }
491 
492     /**
493      * Suppresses reporting of the WTF to system_server, so we don't pollute the dropbox with
494      * intentionally caused WTFs.
495      */
suppressAndReturnWtf(Runnable r)496     private TerribleFailure suppressAndReturnWtf(Runnable r) {
497         TerribleFailure[] captured = new TerribleFailure[1];
498         TerribleFailureHandler prevWtfHandler = Log.setWtfHandler((t, w, s) -> captured[0] = w);
499         try {
500             r.run();
501         } finally {
502             Log.setWtfHandler(prevWtfHandler);
503         }
504         return captured[0];
505     }
506 
joinAll(List<Thread> threads, long timeoutMillis)507     private static void joinAll(List<Thread> threads, long timeoutMillis) {
508         long deadline = SystemClock.uptimeMillis() + timeoutMillis;
509         for (Thread t : threads) {
510             try {
511                 t.join(deadline - SystemClock.uptimeMillis());
512                 if (t.isAlive()) {
513                     t.interrupt();
514                     throw new RuntimeException(
515                             "Joining " + t + " timed out. Stack: \n" + getStack(t));
516                 }
517             } catch (InterruptedException e) {
518                 throw new RuntimeException("Interrupted while joining " + t, e);
519             }
520         }
521     }
522 
getStack(Thread t)523     private static String getStack(Thread t) {
524         StringBuilder sb = new StringBuilder();
525         sb.append(t.toString()).append('\n');
526         for (StackTraceElement ste : t.getStackTrace()) {
527             sb.append("\tat ").append(ste.toString()).append('\n');
528         }
529         return sb.toString();
530     }
531 }
532