Home | History | Annotate | Download | only in locksettings
      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