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.mockito.Matchers.eq; 20 import static org.mockito.Mockito.mock; 21 import static org.mockito.Mockito.when; 22 23 import android.app.NotificationManager; 24 import android.app.admin.DevicePolicyManager; 25 import android.app.trust.TrustManager; 26 import android.content.pm.UserInfo; 27 import android.database.sqlite.SQLiteDatabase; 28 import android.os.FileUtils; 29 import android.os.UserManager; 30 import android.os.storage.StorageManager; 31 import android.test.AndroidTestCase; 32 33 import com.android.internal.widget.LockPatternUtils; 34 import com.android.server.locksettings.LockSettingsStorage.CredentialHash; 35 import com.android.server.locksettings.LockSettingsStorage.PersistentData; 36 37 import java.io.File; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.List; 41 import java.util.concurrent.CountDownLatch; 42 43 /** 44 * runtest frameworks-services -c com.android.server.locksettings.LockSettingsStorageTests 45 */ 46 public class LockSettingsStorageTests extends AndroidTestCase { 47 private static final int SOME_USER_ID = 1034; 48 private final byte[] PASSWORD_0 = "thepassword0".getBytes(); 49 private final byte[] PASSWORD_1 = "password1".getBytes(); 50 private final byte[] PATTERN_0 = "123654".getBytes(); 51 private final byte[] PATTERN_1 = "147852369".getBytes(); 52 53 public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 33}; 54 55 LockSettingsStorage mStorage; 56 File mStorageDir; 57 58 private File mDb; 59 60 @Override 61 protected void setUp() throws Exception { 62 super.setUp(); 63 mStorageDir = new File(getContext().getFilesDir(), "locksettings"); 64 mDb = getContext().getDatabasePath("locksettings.db"); 65 66 assertTrue(mStorageDir.exists() || mStorageDir.mkdirs()); 67 assertTrue(FileUtils.deleteContents(mStorageDir)); 68 assertTrue(!mDb.exists() || mDb.delete()); 69 70 final UserManager mockUserManager = mock(UserManager.class); 71 // User 2 is a profile of user 1. 72 when(mockUserManager.getProfileParent(eq(2))).thenReturn(new UserInfo(1, "name", 0)); 73 // User 3 is a profile of user 0. 74 when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0)); 75 76 MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager, 77 mock(NotificationManager.class), mock(DevicePolicyManager.class), 78 mock(StorageManager.class), mock(TrustManager.class)); 79 mStorage = new LockSettingsStorageTestable(context, 80 new File(getContext().getFilesDir(), "locksettings")); 81 mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() { 82 @Override 83 public void initialize(SQLiteDatabase db) { 84 mStorage.writeKeyValue(db, "initializedKey", "initialValue", 0); 85 } 86 }); 87 } 88 89 @Override 90 protected void tearDown() throws Exception { 91 super.tearDown(); 92 mStorage.closeDatabase(); 93 } 94 95 public void testKeyValue_InitializeWorked() { 96 assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0)); 97 mStorage.clearCache(); 98 assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0)); 99 } 100 101 public void testKeyValue_WriteThenRead() { 102 mStorage.writeKeyValue("key", "value", 0); 103 assertEquals("value", mStorage.readKeyValue("key", "default", 0)); 104 mStorage.clearCache(); 105 assertEquals("value", mStorage.readKeyValue("key", "default", 0)); 106 } 107 108 public void testKeyValue_DefaultValue() { 109 assertEquals("default", mStorage.readKeyValue("unititialized key", "default", 0)); 110 assertEquals("default2", mStorage.readKeyValue("unititialized key", "default2", 0)); 111 } 112 113 public void testKeyValue_Concurrency() { 114 final Object monitor = new Object(); 115 List<Thread> threads = new ArrayList<>(); 116 for (int i = 0; i < 100; i++) { 117 final int threadId = i; 118 threads.add(new Thread() { 119 @Override 120 public void run() { 121 synchronized (monitor) { 122 try { 123 monitor.wait(); 124 } catch (InterruptedException e) { 125 return; 126 } 127 mStorage.writeKeyValue("key", "1 from thread " + threadId, 0); 128 mStorage.readKeyValue("key", "default", 0); 129 mStorage.writeKeyValue("key", "2 from thread " + threadId, 0); 130 mStorage.readKeyValue("key", "default", 0); 131 mStorage.writeKeyValue("key", "3 from thread " + threadId, 0); 132 mStorage.readKeyValue("key", "default", 0); 133 mStorage.writeKeyValue("key", "4 from thread " + threadId, 0); 134 mStorage.readKeyValue("key", "default", 0); 135 mStorage.writeKeyValue("key", "5 from thread " + threadId, 0); 136 mStorage.readKeyValue("key", "default", 0); 137 } 138 } 139 }); 140 threads.get(i).start(); 141 } 142 mStorage.writeKeyValue("key", "initalValue", 0); 143 synchronized (monitor) { 144 monitor.notifyAll(); 145 } 146 for (int i = 0; i < threads.size(); i++) { 147 try { 148 threads.get(i).join(); 149 } catch (InterruptedException e) { 150 } 151 } 152 assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0)); 153 mStorage.clearCache(); 154 assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0)); 155 } 156 157 public void testKeyValue_CacheStarvedWriter() { 158 final CountDownLatch latch = new CountDownLatch(1); 159 List<Thread> threads = new ArrayList<>(); 160 for (int i = 0; i < 100; i++) { 161 final int threadId = i; 162 threads.add(new Thread() { 163 @Override 164 public void run() { 165 try { 166 latch.await(); 167 } catch (InterruptedException e) { 168 return; 169 } 170 if (threadId == 50) { 171 mStorage.writeKeyValue("starvedWriterKey", "value", 0); 172 } else { 173 mStorage.readKeyValue("starvedWriterKey", "default", 0); 174 } 175 } 176 }); 177 threads.get(i).start(); 178 } 179 latch.countDown(); 180 for (int i = 0; i < threads.size(); i++) { 181 try { 182 threads.get(i).join(); 183 } catch (InterruptedException e) { 184 } 185 } 186 String cached = mStorage.readKeyValue("key", "default", 0); 187 mStorage.clearCache(); 188 String storage = mStorage.readKeyValue("key", "default", 0); 189 assertEquals("Cached value didn't match stored value", storage, cached); 190 } 191 192 public void testRemoveUser() { 193 mStorage.writeKeyValue("key", "value", 0); 194 writePasswordBytes(PASSWORD_0, 0); 195 writePatternBytes(PATTERN_0, 0); 196 197 mStorage.writeKeyValue("key", "value", 1); 198 writePasswordBytes(PASSWORD_1, 1); 199 writePatternBytes(PATTERN_1, 1); 200 201 mStorage.removeUser(0); 202 203 assertEquals("value", mStorage.readKeyValue("key", "default", 1)); 204 assertEquals("default", mStorage.readKeyValue("key", "default", 0)); 205 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_NONE, mStorage.readCredentialHash(0).type); 206 assertPatternBytes(PATTERN_1, 1); 207 } 208 209 public void testCredential_Default() { 210 assertEquals(mStorage.readCredentialHash(0).type, LockPatternUtils.CREDENTIAL_TYPE_NONE); 211 } 212 213 public void testPassword_Write() { 214 writePasswordBytes(PASSWORD_0, 0); 215 216 assertPasswordBytes(PASSWORD_0, 0); 217 mStorage.clearCache(); 218 assertPasswordBytes(PASSWORD_0, 0); 219 } 220 221 public void testPassword_WriteProfileWritesParent() { 222 writePasswordBytes(PASSWORD_0, 1); 223 writePasswordBytes(PASSWORD_1, 2); 224 225 assertPasswordBytes(PASSWORD_0, 1); 226 assertPasswordBytes(PASSWORD_1, 2); 227 mStorage.clearCache(); 228 assertPasswordBytes(PASSWORD_0, 1); 229 assertPasswordBytes(PASSWORD_1, 2); 230 } 231 232 public void testLockType_WriteProfileWritesParent() { 233 writePasswordBytes(PASSWORD_0, 10); 234 writePatternBytes(PATTERN_0, 20); 235 236 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 237 mStorage.readCredentialHash(10).type); 238 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 239 mStorage.readCredentialHash(20).type); 240 mStorage.clearCache(); 241 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 242 mStorage.readCredentialHash(10).type); 243 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 244 mStorage.readCredentialHash(20).type); 245 } 246 247 public void testPassword_WriteParentWritesProfile() { 248 writePasswordBytes(PASSWORD_0, 2); 249 writePasswordBytes(PASSWORD_1, 1); 250 251 assertPasswordBytes(PASSWORD_1, 1); 252 assertPasswordBytes(PASSWORD_0, 2); 253 mStorage.clearCache(); 254 assertPasswordBytes(PASSWORD_1, 1); 255 assertPasswordBytes(PASSWORD_0, 2); 256 } 257 258 public void testProfileLock_ReadWriteChildProfileLock() { 259 assertFalse(mStorage.hasChildProfileLock(20)); 260 mStorage.writeChildProfileLock(20, PASSWORD_0); 261 assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20)); 262 assertTrue(mStorage.hasChildProfileLock(20)); 263 mStorage.clearCache(); 264 assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20)); 265 assertTrue(mStorage.hasChildProfileLock(20)); 266 } 267 268 public void testPattern_Write() { 269 writePatternBytes(PATTERN_0, 0); 270 271 assertPatternBytes(PATTERN_0, 0); 272 mStorage.clearCache(); 273 assertPatternBytes(PATTERN_0, 0); 274 } 275 276 public void testPattern_WriteProfileWritesParent() { 277 writePatternBytes(PATTERN_0, 1); 278 writePatternBytes(PATTERN_1, 2); 279 280 assertPatternBytes(PATTERN_0, 1); 281 assertPatternBytes(PATTERN_1, 2); 282 mStorage.clearCache(); 283 assertPatternBytes(PATTERN_0, 1); 284 assertPatternBytes(PATTERN_1, 2); 285 } 286 287 public void testPattern_WriteParentWritesProfile() { 288 writePatternBytes(PATTERN_1, 2); 289 writePatternBytes(PATTERN_0, 1); 290 291 assertPatternBytes(PATTERN_0, 1); 292 assertPatternBytes(PATTERN_1, 2); 293 mStorage.clearCache(); 294 assertPatternBytes(PATTERN_0, 1); 295 assertPatternBytes(PATTERN_1, 2); 296 } 297 298 public void testPrefetch() { 299 mStorage.writeKeyValue("key", "toBeFetched", 0); 300 writePatternBytes(PATTERN_0, 0); 301 302 mStorage.clearCache(); 303 mStorage.prefetchUser(0); 304 305 assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0)); 306 assertPatternBytes(PATTERN_0, 0); 307 } 308 309 public void testFileLocation_Owner() { 310 LockSettingsStorage storage = new LockSettingsStorage(getContext()); 311 312 assertEquals("/data/system/gesture.key", storage.getLegacyLockPatternFilename(0)); 313 assertEquals("/data/system/password.key", storage.getLegacyLockPasswordFilename(0)); 314 assertEquals("/data/system/gatekeeper.pattern.key", storage.getLockPatternFilename(0)); 315 assertEquals("/data/system/gatekeeper.password.key", storage.getLockPasswordFilename(0)); 316 } 317 318 public void testFileLocation_SecondaryUser() { 319 LockSettingsStorage storage = new LockSettingsStorage(getContext()); 320 321 assertEquals("/data/system/users/1/gatekeeper.pattern.key", storage.getLockPatternFilename(1)); 322 assertEquals("/data/system/users/1/gatekeeper.password.key", storage.getLockPasswordFilename(1)); 323 } 324 325 public void testFileLocation_ProfileToSecondary() { 326 LockSettingsStorage storage = new LockSettingsStorage(getContext()); 327 328 assertEquals("/data/system/users/2/gatekeeper.pattern.key", storage.getLockPatternFilename(2)); 329 assertEquals("/data/system/users/2/gatekeeper.password.key", storage.getLockPasswordFilename(2)); 330 } 331 332 public void testFileLocation_ProfileToOwner() { 333 LockSettingsStorage storage = new LockSettingsStorage(getContext()); 334 335 assertEquals("/data/system/users/3/gatekeeper.pattern.key", storage.getLockPatternFilename(3)); 336 assertEquals("/data/system/users/3/gatekeeper.password.key", storage.getLockPasswordFilename(3)); 337 } 338 339 public void testSyntheticPasswordState() { 340 final byte[] data = {1,2,3,4}; 341 mStorage.writeSyntheticPasswordState(10, 1234L, "state", data); 342 assertArrayEquals(data, mStorage.readSyntheticPasswordState(10, 1234L, "state")); 343 assertEquals(null, mStorage.readSyntheticPasswordState(0, 1234L, "state")); 344 345 mStorage.deleteSyntheticPasswordState(10, 1234L, "state"); 346 assertEquals(null, mStorage.readSyntheticPasswordState(10, 1234L, "state")); 347 } 348 349 public void testPersistentData_serializeUnserialize() { 350 byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_SP, SOME_USER_ID, 351 DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD); 352 PersistentData deserialized = PersistentData.fromBytes(serialized); 353 354 assertEquals(PersistentData.TYPE_SP, deserialized.type); 355 assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, deserialized.qualityForUi); 356 assertArrayEquals(PAYLOAD, deserialized.payload); 357 } 358 359 public void testPersistentData_unserializeNull() { 360 PersistentData deserialized = PersistentData.fromBytes(null); 361 assertSame(PersistentData.NONE, deserialized); 362 } 363 364 public void testPersistentData_unserializeEmptyArray() { 365 PersistentData deserialized = PersistentData.fromBytes(new byte[0]); 366 assertSame(PersistentData.NONE, deserialized); 367 } 368 369 public void testPersistentData_unserialize_version1() { 370 // This test ensures that we can read serialized VERSION_1 PersistentData even if we change 371 // the wire format in the future. 372 byte[] serializedVersion1 = new byte[] { 373 1, /* PersistentData.VERSION_1 */ 374 1, /* PersistentData.TYPE_SP */ 375 0x00, 0x00, 0x04, 0x0A, /* SOME_USER_ID */ 376 0x00, 0x03, 0x00, 0x00, /* PASSWORD_NUMERIC_COMPLEX */ 377 1, 2, -1, -2, 33, /* PAYLOAD */ 378 }; 379 PersistentData deserialized = PersistentData.fromBytes(serializedVersion1); 380 assertEquals(PersistentData.TYPE_SP, deserialized.type); 381 assertEquals(SOME_USER_ID, deserialized.userId); 382 assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, 383 deserialized.qualityForUi); 384 assertArrayEquals(PAYLOAD, deserialized.payload); 385 386 // Make sure the constants we use on the wire do not change. 387 assertEquals(0, PersistentData.TYPE_NONE); 388 assertEquals(1, PersistentData.TYPE_SP); 389 assertEquals(2, PersistentData.TYPE_SP_WEAVER); 390 } 391 392 public void testCredentialHash_serializeUnserialize() { 393 byte[] serialized = CredentialHash.create( 394 PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes(); 395 CredentialHash deserialized = CredentialHash.fromBytes(serialized); 396 397 assertEquals(CredentialHash.VERSION_GATEKEEPER, deserialized.version); 398 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type); 399 assertArrayEquals(PAYLOAD, deserialized.hash); 400 assertFalse(deserialized.isBaseZeroPattern); 401 } 402 403 public void testCredentialHash_unserialize_versionGatekeeper() { 404 // This test ensures that we can read serialized VERSION_GATEKEEPER CredentialHashes 405 // even if we change the wire format in the future. 406 byte[] serialized = new byte[] { 407 1, /* VERSION_GATEKEEPER */ 408 2, /* CREDENTIAL_TYPE_PASSWORD */ 409 0, 0, 0, 5, /* hash length */ 410 1, 2, -1, -2, 33, /* hash */ 411 }; 412 CredentialHash deserialized = CredentialHash.fromBytes(serialized); 413 414 assertEquals(CredentialHash.VERSION_GATEKEEPER, deserialized.version); 415 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type); 416 assertArrayEquals(PAYLOAD, deserialized.hash); 417 assertFalse(deserialized.isBaseZeroPattern); 418 419 // Make sure the constants we use on the wire do not change. 420 assertEquals(-1, LockPatternUtils.CREDENTIAL_TYPE_NONE); 421 assertEquals(1, LockPatternUtils.CREDENTIAL_TYPE_PATTERN); 422 assertEquals(2, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); 423 } 424 425 private static void assertArrayEquals(byte[] expected, byte[] actual) { 426 if (!Arrays.equals(expected, actual)) { 427 fail("expected:<" + Arrays.toString(expected) + 428 "> but was:<" + Arrays.toString(actual) + ">"); 429 } 430 } 431 432 private void writePasswordBytes(byte[] password, int userId) { 433 mStorage.writeCredentialHash(CredentialHash.create( 434 password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId); 435 } 436 437 private void writePatternBytes(byte[] pattern, int userId) { 438 mStorage.writeCredentialHash(CredentialHash.create( 439 pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId); 440 } 441 442 private void assertPasswordBytes(byte[] password, int userId) { 443 CredentialHash cred = mStorage.readCredentialHash(userId); 444 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, cred.type); 445 assertArrayEquals(password, cred.hash); 446 } 447 448 private void assertPatternBytes(byte[] pattern, int userId) { 449 CredentialHash cred = mStorage.readCredentialHash(userId); 450 assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, cred.type); 451 assertArrayEquals(pattern, cred.hash); 452 } 453 } 454