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