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