• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.locksettings.LockSettingsStorage.PersistentData;
57 import com.android.server.pdb.PersistentDataBlockManagerInternal;
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         if (mStorage != null) {
128             mStorage.closeDatabase();
129         }
130     }
131 
132     @Test
testKeyValue_InitializeWorked()133     public void testKeyValue_InitializeWorked() {
134         assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0));
135         mStorage.clearCache();
136         assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0));
137     }
138 
139     @Test
testKeyValue_WriteThenRead()140     public void testKeyValue_WriteThenRead() {
141         mStorage.writeKeyValue("key", "value", 0);
142         assertEquals("value", mStorage.readKeyValue("key", "default", 0));
143         mStorage.clearCache();
144         assertEquals("value", mStorage.readKeyValue("key", "default", 0));
145     }
146 
147     @Test
testKeyValue_DefaultValue()148     public void testKeyValue_DefaultValue() {
149         assertEquals("default", mStorage.readKeyValue("unititialized key", "default", 0));
150         assertEquals("default2", mStorage.readKeyValue("unititialized key", "default2", 0));
151     }
152 
153     @Test
testKeyValue_ReadWriteConcurrency()154     public void testKeyValue_ReadWriteConcurrency() {
155         final CountDownLatch latch = new CountDownLatch(1);
156         List<Thread> threads = new ArrayList<>();
157         for (int i = 0; i < 100; i++) {
158             final int threadId = i;
159             threads.add(new Thread("testKeyValue_ReadWriteConcurrency_" + i) {
160                 @Override
161                 public void run() {
162                     try {
163                         latch.await();
164                     } catch (InterruptedException e) {
165                         return;
166                     }
167                     mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
168                     mStorage.readKeyValue("key", "default", 0);
169                     mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
170                     mStorage.readKeyValue("key", "default", 0);
171                     mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
172                     mStorage.readKeyValue("key", "default", 0);
173                     mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
174                     mStorage.readKeyValue("key", "default", 0);
175                     mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
176                     mStorage.readKeyValue("key", "default", 0);
177                 }
178             });
179             threads.get(i).start();
180         }
181         mStorage.writeKeyValue("key", "initialValue", 0);
182         latch.countDown();
183         joinAll(threads, 10000);
184         assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
185         mStorage.clearCache();
186         assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
187     }
188 
189     // Test that readKeyValue() doesn't pollute the cache when run concurrently with removeKey().
190     @Test
191     @SuppressWarnings("AssertionFailureIgnored") // intentional try-catch of AssertionError
testKeyValue_ReadRemoveConcurrency()192     public void testKeyValue_ReadRemoveConcurrency() {
193         final int numThreads = 2;
194         final int numIterations = 50;
195         final CyclicBarrier barrier = new CyclicBarrier(numThreads);
196         final List<Thread> threads = new ArrayList<>();
197         final AtomicReference<Throwable> failure = new AtomicReference<>();
198         for (int threadId = 0; threadId < numThreads; threadId++) {
199             final boolean isWriter = (threadId == 0);
200             threads.add(new Thread("testKeyValue_ReadRemoveConcurrency_" + threadId) {
201                 @Override
202                 public void run() {
203                     try {
204                         for (int iter = 0; iter < numIterations; iter++) {
205                             if (isWriter) {
206                                 mStorage.writeKeyValue("key", "value", 0);
207                                 mStorage.clearCache();
208                             }
209                             barrier.await();
210                             if (isWriter) {
211                                 mStorage.removeKey("key", 0);
212                             } else {
213                                 mStorage.readKeyValue("key", "default", 0);
214                             }
215                             barrier.await();
216                             try {
217                                 assertEquals("default", mStorage.readKeyValue("key", "default", 0));
218                             } catch (AssertionError e) {
219                                 failure.compareAndSet(null, e);
220                             }
221                             barrier.await();
222                         }
223                     } catch (InterruptedException | BrokenBarrierException e) {
224                         failure.compareAndSet(null, e);
225                         return;
226                     }
227                 }
228             });
229             threads.get(threadId).start();
230         }
231         joinAll(threads, 60000);
232         assertNull(failure.get());
233     }
234 
235     @Test
testKeyValue_CacheStarvedWriter()236     public void testKeyValue_CacheStarvedWriter() {
237         final CountDownLatch latch = new CountDownLatch(1);
238         List<Thread> threads = new ArrayList<>();
239         for (int i = 0; i < 100; i++) {
240             final int threadId = i;
241             threads.add(new Thread() {
242                 @Override
243                 public void run() {
244                     try {
245                         latch.await();
246                     } catch (InterruptedException e) {
247                         return;
248                     }
249                     if (threadId == 50) {
250                         mStorage.writeKeyValue("starvedWriterKey", "value", 0);
251                     } else {
252                         mStorage.readKeyValue("starvedWriterKey", "default", 0);
253                     }
254                 }
255             });
256             threads.get(i).start();
257         }
258         latch.countDown();
259         for (int i = 0; i < threads.size(); i++) {
260             try {
261                 threads.get(i).join();
262             } catch (InterruptedException e) {
263             }
264         }
265         String cached = mStorage.readKeyValue("key", "default", 0);
266         mStorage.clearCache();
267         String storage = mStorage.readKeyValue("key", "default", 0);
268         assertEquals("Cached value didn't match stored value", storage, cached);
269     }
270 
271     @Test
testNullKey()272     public void testNullKey() {
273         mStorage.setString(null, "value", 0);
274 
275         // Verify that this doesn't throw an exception.
276         assertEquals("value", mStorage.readKeyValue(null, null, 0));
277 
278         // The read that happens as part of prefetchUser shouldn't throw an exception either.
279         mStorage.clearCache();
280         mStorage.prefetchUser(0);
281 
282         assertEquals("value", mStorage.readKeyValue(null, null, 0));
283     }
284 
285     @Test
testRemoveUser()286     public void testRemoveUser() {
287         mStorage.writeKeyValue("key", "value", 0);
288         mStorage.writeKeyValue("key", "value", 1);
289 
290         mStorage.removeUser(0);
291 
292         assertEquals("value", mStorage.readKeyValue("key", "default", 1));
293         assertEquals("default", mStorage.readKeyValue("key", "default", 0));
294     }
295 
296     @Test
testProfileLock_ReadWriteChildProfileLock()297     public void testProfileLock_ReadWriteChildProfileLock() {
298         assertFalse(mStorage.hasChildProfileLock(20));
299         mStorage.writeChildProfileLock(20, PASSWORD);
300         assertArrayEquals(PASSWORD, mStorage.readChildProfileLock(20));
301         assertTrue(mStorage.hasChildProfileLock(20));
302         mStorage.clearCache();
303         assertArrayEquals(PASSWORD, mStorage.readChildProfileLock(20));
304         assertTrue(mStorage.hasChildProfileLock(20));
305     }
306 
307     @Test
testPrefetch()308     public void testPrefetch() {
309         mStorage.writeKeyValue("key1", "value1", 0);
310         mStorage.writeKeyValue("key2", "value2", 0);
311 
312         mStorage.clearCache();
313 
314         assertFalse(mStorage.isUserPrefetched(0));
315         assertFalse(mStorage.isKeyValueCached("key1", 0));
316         assertFalse(mStorage.isKeyValueCached("key2", 0));
317 
318         mStorage.prefetchUser(0);
319 
320         assertTrue(mStorage.isUserPrefetched(0));
321         assertTrue(mStorage.isKeyValueCached("key1", 0));
322         assertTrue(mStorage.isKeyValueCached("key2", 0));
323         assertEquals("value1", mStorage.readKeyValue("key1", "default", 0));
324         assertEquals("value2", mStorage.readKeyValue("key2", "default", 0));
325     }
326 
327     @Test
testFileLocation_Owner()328     public void testFileLocation_Owner() {
329         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
330 
331         assertEquals(new File("/data/system/gatekeeper.profile.key"),
332                 storage.getChildProfileLockFile(0));
333     }
334 
335     @Test
testFileLocation_SecondaryUser()336     public void testFileLocation_SecondaryUser() {
337         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
338 
339         assertEquals(new File("/data/system/users/1/gatekeeper.profile.key"),
340                 storage.getChildProfileLockFile(1));
341     }
342 
343     @Test
testFileLocation_ProfileToSecondary()344     public void testFileLocation_ProfileToSecondary() {
345         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
346 
347         assertEquals(new File("/data/system/users/2/gatekeeper.profile.key"),
348                 storage.getChildProfileLockFile(2));
349     }
350 
351     @Test
testFileLocation_ProfileToOwner()352     public void testFileLocation_ProfileToOwner() {
353         LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
354 
355         assertEquals(new File("/data/system/users/3/gatekeeper.profile.key"),
356                 storage.getChildProfileLockFile(3));
357     }
358 
359     @Test
testSyntheticPasswordState()360     public void testSyntheticPasswordState() {
361         final byte[] data = {1,2,3,4};
362         mStorage.writeSyntheticPasswordState(10, 1234L, "state", data);
363         assertArrayEquals(data, mStorage.readSyntheticPasswordState(10, 1234L, "state"));
364         assertEquals(null, mStorage.readSyntheticPasswordState(0, 1234L, "state"));
365 
366         mStorage.deleteSyntheticPasswordState(10, 1234L, "state");
367         assertEquals(null, mStorage.readSyntheticPasswordState(10, 1234L, "state"));
368     }
369 
370     @Test
testPersistentDataBlock_unavailable()371     public void testPersistentDataBlock_unavailable() {
372         mStorage.mPersistentDataBlockManager = null;
373 
374         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
375     }
376 
377     @Test
testPersistentDataBlock_empty()378     public void testPersistentDataBlock_empty() {
379         mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
380 
381         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
382     }
383 
384     @Test
testPersistentDataBlock_withData()385     public void testPersistentDataBlock_withData() {
386         mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
387         when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle())
388                 .thenReturn(PersistentData.toBytes(PersistentData.TYPE_SP_WEAVER, SOME_USER_ID,
389                         DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD));
390 
391         PersistentData data = mStorage.readPersistentDataBlock();
392 
393         assertEquals(PersistentData.TYPE_SP_WEAVER, data.type);
394         assertEquals(SOME_USER_ID, data.userId);
395         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, data.qualityForUi);
396         assertArrayEquals(PAYLOAD, data.payload);
397     }
398 
399     @Test
testPersistentDataBlock_exception()400     public void testPersistentDataBlock_exception() {
401         mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
402         when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle())
403                 .thenThrow(new IllegalStateException("oops"));
404         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
405     }
406 
407     @Test
testPersistentData_serializeUnserialize()408     public void testPersistentData_serializeUnserialize() {
409         byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID,
410                 DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD);
411         PersistentData deserialized = PersistentData.fromBytes(serialized);
412 
413         assertEquals(PersistentData.TYPE_SP_GATEKEEPER, deserialized.type);
414         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, deserialized.qualityForUi);
415         assertArrayEquals(PAYLOAD, deserialized.payload);
416     }
417 
418     @Test
testPersistentData_unserializeNull()419     public void testPersistentData_unserializeNull() {
420         PersistentData deserialized = PersistentData.fromBytes(null);
421         assertSame(PersistentData.NONE, deserialized);
422     }
423 
424     @Test
testPersistentData_unserializeEmptyArray()425     public void testPersistentData_unserializeEmptyArray() {
426         PersistentData deserialized = PersistentData.fromBytes(new byte[0]);
427         assertSame(PersistentData.NONE, deserialized);
428     }
429 
430     @Test
testPersistentData_unserializeInvalid()431     public void testPersistentData_unserializeInvalid() {
432         assertNotNull(suppressAndReturnWtf(() -> {
433             PersistentData deserialized = PersistentData.fromBytes(new byte[]{5});
434             assertSame(PersistentData.NONE, deserialized);
435         }));
436     }
437 
438     @Test
testPersistentData_unserialize_version1()439     public void testPersistentData_unserialize_version1() {
440         // This test ensures that we can read serialized VERSION_1 PersistentData even if we change
441         // the wire format in the future.
442         byte[] serializedVersion1 = new byte[] {
443                 1, /* PersistentData.VERSION_1 */
444                 1, /* PersistentData.TYPE_SP_GATEKEEPER */
445                 0x00, 0x00, 0x04, 0x0A,  /* SOME_USER_ID */
446                 0x00, 0x03, 0x00, 0x00,  /* PASSWORD_NUMERIC_COMPLEX */
447                 1, 2, -1, -2, 33, /* PAYLOAD */
448         };
449         PersistentData deserialized = PersistentData.fromBytes(serializedVersion1);
450         assertEquals(PersistentData.TYPE_SP_GATEKEEPER, deserialized.type);
451         assertEquals(SOME_USER_ID, deserialized.userId);
452         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
453                 deserialized.qualityForUi);
454         assertArrayEquals(PAYLOAD, deserialized.payload);
455 
456         // Make sure the constants we use on the wire do not change.
457         assertEquals(0, PersistentData.TYPE_NONE);
458         assertEquals(1, PersistentData.TYPE_SP_GATEKEEPER);
459         assertEquals(2, PersistentData.TYPE_SP_WEAVER);
460     }
461 
462     @Test
testRepairMode_emptyPersistentData()463     public void testRepairMode_emptyPersistentData() {
464         assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
465     }
466 
467     @Test
testRepairMode_writeGatekeeperPersistentData()468     public void testRepairMode_writeGatekeeperPersistentData() {
469         mStorage.writeRepairModePersistentData(
470                 PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID, PAYLOAD);
471 
472         final PersistentData data = mStorage.readRepairModePersistentData();
473         assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type);
474         assertArrayEquals(PAYLOAD, data.payload);
475     }
476 
477     @Test
testRepairMode_writeWeaverPersistentData()478     public void testRepairMode_writeWeaverPersistentData() {
479         mStorage.writeRepairModePersistentData(
480                 PersistentData.TYPE_SP_WEAVER, SOME_USER_ID, PAYLOAD);
481 
482         final PersistentData data = mStorage.readRepairModePersistentData();
483         assertEquals(PersistentData.TYPE_SP_WEAVER, data.type);
484         assertArrayEquals(PAYLOAD, data.payload);
485     }
486 
assertArrayEquals(byte[] expected, byte[] actual)487     private static void assertArrayEquals(byte[] expected, byte[] actual) {
488         if (!Arrays.equals(expected, actual)) {
489             fail("expected:<" + Arrays.toString(expected) +
490                     "> but was:<" + Arrays.toString(actual) + ">");
491         }
492     }
493 
494     /**
495      * Suppresses reporting of the WTF to system_server, so we don't pollute the dropbox with
496      * intentionally caused WTFs.
497      */
suppressAndReturnWtf(Runnable r)498     private TerribleFailure suppressAndReturnWtf(Runnable r) {
499         TerribleFailure[] captured = new TerribleFailure[1];
500         TerribleFailureHandler prevWtfHandler = Log.setWtfHandler((t, w, s) -> captured[0] = w);
501         try {
502             r.run();
503         } finally {
504             Log.setWtfHandler(prevWtfHandler);
505         }
506         return captured[0];
507     }
508 
joinAll(List<Thread> threads, long timeoutMillis)509     private static void joinAll(List<Thread> threads, long timeoutMillis) {
510         long deadline = SystemClock.uptimeMillis() + timeoutMillis;
511         for (Thread t : threads) {
512             try {
513                 t.join(deadline - SystemClock.uptimeMillis());
514                 if (t.isAlive()) {
515                     t.interrupt();
516                     throw new RuntimeException(
517                             "Joining " + t + " timed out. Stack: \n" + getStack(t));
518                 }
519             } catch (InterruptedException e) {
520                 throw new RuntimeException("Interrupted while joining " + t, e);
521             }
522         }
523     }
524 
getStack(Thread t)525     private static String getStack(Thread t) {
526         StringBuilder sb = new StringBuilder();
527         sb.append(t.toString()).append('\n');
528         for (StackTraceElement ste : t.getStackTrace()) {
529             sb.append("\tat ").append(ste.toString()).append('\n');
530         }
531         return sb.toString();
532     }
533 }
534