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