LockSettingsStorageTests.java revision 1de89b3bec2f296763f3ecde9a36ecbca2110f3d
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
17package com.android.server;
18
19import static org.mockito.Matchers.eq;
20import static org.mockito.Mockito.mock;
21import static org.mockito.Mockito.when;
22
23import android.content.Context;
24import android.content.ContextWrapper;
25import android.content.pm.UserInfo;
26import android.database.sqlite.SQLiteDatabase;
27import android.os.FileUtils;
28import android.os.UserManager;
29import android.test.AndroidTestCase;
30
31import com.android.internal.widget.LockPatternUtils;
32import com.android.server.LockSettingsStorage.CredentialHash;
33import java.io.File;
34import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.List;
37import java.util.concurrent.CountDownLatch;
38
39/**
40 * runtest frameworks-services -c com.android.server.LockSettingsStorageTests
41 */
42public class LockSettingsStorageTests extends AndroidTestCase {
43    private final byte[] PASSWORD_0 = "thepassword0".getBytes();
44    private final byte[] PASSWORD_1 = "password1".getBytes();
45    private final byte[] PATTERN_0 = "123654".getBytes();
46    private final byte[] PATTERN_1 = "147852369".getBytes();
47
48    LockSettingsStorage mStorage;
49    File mStorageDir;
50
51    private File mDb;
52
53    @Override
54    protected void setUp() throws Exception {
55        super.setUp();
56        mStorageDir = new File(getContext().getFilesDir(), "locksettings");
57        mDb = getContext().getDatabasePath("locksettings.db");
58
59        assertTrue(mStorageDir.exists() || mStorageDir.mkdirs());
60        assertTrue(FileUtils.deleteContents(mStorageDir));
61        assertTrue(!mDb.exists() || mDb.delete());
62
63        final Context ctx = getContext();
64        final UserManager mockUserManager = mock(UserManager.class);
65        // User 2 is a profile of user 1.
66        when(mockUserManager.getProfileParent(eq(2))).thenReturn(new UserInfo(1, "name", 0));
67        // User 3 is a profile of user 0.
68        when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
69        setContext(new ContextWrapper(ctx) {
70            @Override
71            public Object getSystemService(String name) {
72                if (USER_SERVICE.equals(name)) {
73                    return mockUserManager;
74                }
75                return super.getSystemService(name);
76            }
77        });
78
79        mStorage = new LockSettingsStorage(getContext(), new LockSettingsStorage.Callback() {
80            @Override
81            public void initialize(SQLiteDatabase db) {
82                mStorage.writeKeyValue(db, "initializedKey", "initialValue", 0);
83            }
84        }) {
85            @Override
86            String getLockPatternFilename(int userId) {
87                return new File(mStorageDir,
88                        super.getLockPatternFilename(userId).replace('/', '-')).getAbsolutePath();
89            }
90
91            @Override
92            String getLockPasswordFilename(int userId) {
93                return new File(mStorageDir,
94                        super.getLockPasswordFilename(userId).replace('/', '-')).getAbsolutePath();
95            }
96
97            @Override
98            String getChildProfileLockFile(int userId) {
99                return new File(mStorageDir,
100                        super.getChildProfileLockFile(userId).replace('/', '-')).getAbsolutePath();
101            }
102        };
103    }
104
105    @Override
106    protected void tearDown() throws Exception {
107        super.tearDown();
108        mStorage.closeDatabase();
109    }
110
111    public void testKeyValue_InitializeWorked() {
112        assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0));
113        mStorage.clearCache();
114        assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0));
115    }
116
117    public void testKeyValue_WriteThenRead() {
118        mStorage.writeKeyValue("key", "value", 0);
119        assertEquals("value", mStorage.readKeyValue("key", "default", 0));
120        mStorage.clearCache();
121        assertEquals("value", mStorage.readKeyValue("key", "default", 0));
122    }
123
124    public void testKeyValue_DefaultValue() {
125        assertEquals("default", mStorage.readKeyValue("unititialized key", "default", 0));
126        assertEquals("default2", mStorage.readKeyValue("unititialized key", "default2", 0));
127    }
128
129    public void testKeyValue_Concurrency() {
130        final Object monitor = new Object();
131        List<Thread> threads = new ArrayList<>();
132        for (int i = 0; i < 100; i++) {
133            final int threadId = i;
134            threads.add(new Thread() {
135                @Override
136                public void run() {
137                    synchronized (monitor) {
138                        try {
139                            monitor.wait();
140                        } catch (InterruptedException e) {
141                            return;
142                        }
143                        mStorage.writeKeyValue("key", "1 from thread " + threadId, 0);
144                        mStorage.readKeyValue("key", "default", 0);
145                        mStorage.writeKeyValue("key", "2 from thread " + threadId, 0);
146                        mStorage.readKeyValue("key", "default", 0);
147                        mStorage.writeKeyValue("key", "3 from thread " + threadId, 0);
148                        mStorage.readKeyValue("key", "default", 0);
149                        mStorage.writeKeyValue("key", "4 from thread " + threadId, 0);
150                        mStorage.readKeyValue("key", "default", 0);
151                        mStorage.writeKeyValue("key", "5 from thread " + threadId, 0);
152                        mStorage.readKeyValue("key", "default", 0);
153                    }
154                }
155            });
156            threads.get(i).start();
157        }
158        mStorage.writeKeyValue("key", "initalValue", 0);
159        synchronized (monitor) {
160            monitor.notifyAll();
161        }
162        for (int i = 0; i < threads.size(); i++) {
163            try {
164                threads.get(i).join();
165            } catch (InterruptedException e) {
166            }
167        }
168        assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
169        mStorage.clearCache();
170        assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0));
171    }
172
173    public void testKeyValue_CacheStarvedWriter() {
174        final CountDownLatch latch = new CountDownLatch(1);
175        List<Thread> threads = new ArrayList<>();
176        for (int i = 0; i < 100; i++) {
177            final int threadId = i;
178            threads.add(new Thread() {
179                @Override
180                public void run() {
181                    try {
182                        latch.await();
183                    } catch (InterruptedException e) {
184                        return;
185                    }
186                    if (threadId == 50) {
187                        mStorage.writeKeyValue("starvedWriterKey", "value", 0);
188                    } else {
189                        mStorage.readKeyValue("starvedWriterKey", "default", 0);
190                    }
191                }
192            });
193            threads.get(i).start();
194        }
195        latch.countDown();
196        for (int i = 0; i < threads.size(); i++) {
197            try {
198                threads.get(i).join();
199            } catch (InterruptedException e) {
200            }
201        }
202        String cached = mStorage.readKeyValue("key", "default", 0);
203        mStorage.clearCache();
204        String storage = mStorage.readKeyValue("key", "default", 0);
205        assertEquals("Cached value didn't match stored value", storage, cached);
206    }
207
208    public void testRemoveUser() {
209        mStorage.writeKeyValue("key", "value", 0);
210        writePasswordBytes(PASSWORD_0, 0);
211        writePatternBytes(PATTERN_0, 0);
212
213        mStorage.writeKeyValue("key", "value", 1);
214        writePasswordBytes(PASSWORD_1, 1);
215        writePatternBytes(PATTERN_1, 1);
216
217        mStorage.removeUser(0);
218
219        assertEquals("value", mStorage.readKeyValue("key", "default", 1));
220        assertEquals("default", mStorage.readKeyValue("key", "default", 0));
221        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_NONE, mStorage.readCredentialHash(0).type);
222        assertPatternBytes(PATTERN_1, 1);
223    }
224
225    public void testCredential_Default() {
226        assertEquals(mStorage.readCredentialHash(0).type, LockPatternUtils.CREDENTIAL_TYPE_NONE);
227    }
228
229    public void testPassword_Write() {
230        writePasswordBytes(PASSWORD_0, 0);
231
232        assertPasswordBytes(PASSWORD_0, 0);
233        mStorage.clearCache();
234        assertPasswordBytes(PASSWORD_0, 0);
235    }
236
237    public void testPassword_WriteProfileWritesParent() {
238        writePasswordBytes(PASSWORD_0, 1);
239        writePasswordBytes(PASSWORD_1, 2);
240
241        assertPasswordBytes(PASSWORD_0, 1);
242        assertPasswordBytes(PASSWORD_1, 2);
243        mStorage.clearCache();
244        assertPasswordBytes(PASSWORD_0, 1);
245        assertPasswordBytes(PASSWORD_1, 2);
246    }
247
248    public void testLockType_WriteProfileWritesParent() {
249        writePasswordBytes(PASSWORD_0, 10);
250        writePatternBytes(PATTERN_0, 20);
251
252        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
253                mStorage.readCredentialHash(10).type);
254        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
255                mStorage.readCredentialHash(20).type);
256        mStorage.clearCache();
257        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
258                mStorage.readCredentialHash(10).type);
259        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
260                mStorage.readCredentialHash(20).type);
261    }
262
263    public void testPassword_WriteParentWritesProfile() {
264        writePasswordBytes(PASSWORD_0, 2);
265        writePasswordBytes(PASSWORD_1, 1);
266
267        assertPasswordBytes(PASSWORD_1, 1);
268        assertPasswordBytes(PASSWORD_0, 2);
269        mStorage.clearCache();
270        assertPasswordBytes(PASSWORD_1, 1);
271        assertPasswordBytes(PASSWORD_0, 2);
272    }
273
274    public void testProfileLock_ReadWriteChildProfileLock() {
275        assertFalse(mStorage.hasChildProfileLock(20));
276        mStorage.writeChildProfileLock(20, PASSWORD_0);
277        assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
278        assertTrue(mStorage.hasChildProfileLock(20));
279        mStorage.clearCache();
280        assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
281        assertTrue(mStorage.hasChildProfileLock(20));
282    }
283
284    public void testPattern_Write() {
285        writePatternBytes(PATTERN_0, 0);
286
287        assertPatternBytes(PATTERN_0, 0);
288        mStorage.clearCache();
289        assertPatternBytes(PATTERN_0, 0);
290    }
291
292    public void testPattern_WriteProfileWritesParent() {
293        writePatternBytes(PATTERN_0, 1);
294        writePatternBytes(PATTERN_1, 2);
295
296        assertPatternBytes(PATTERN_0, 1);
297        assertPatternBytes(PATTERN_1, 2);
298        mStorage.clearCache();
299        assertPatternBytes(PATTERN_0, 1);
300        assertPatternBytes(PATTERN_1, 2);
301    }
302
303    public void testPattern_WriteParentWritesProfile() {
304        writePatternBytes(PATTERN_1, 2);
305        writePatternBytes(PATTERN_0, 1);
306
307        assertPatternBytes(PATTERN_0, 1);
308        assertPatternBytes(PATTERN_1, 2);
309        mStorage.clearCache();
310        assertPatternBytes(PATTERN_0, 1);
311        assertPatternBytes(PATTERN_1, 2);
312    }
313
314    public void testPrefetch() {
315        mStorage.writeKeyValue("key", "toBeFetched", 0);
316        writePatternBytes(PATTERN_0, 0);
317
318        mStorage.clearCache();
319        mStorage.prefetchUser(0);
320
321        assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0));
322        assertPatternBytes(PATTERN_0, 0);
323    }
324
325    public void testFileLocation_Owner() {
326        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
327
328        assertEquals("/data/system/gesture.key", storage.getLegacyLockPatternFilename(0));
329        assertEquals("/data/system/password.key", storage.getLegacyLockPasswordFilename(0));
330        assertEquals("/data/system/gatekeeper.pattern.key", storage.getLockPatternFilename(0));
331        assertEquals("/data/system/gatekeeper.password.key", storage.getLockPasswordFilename(0));
332    }
333
334    public void testFileLocation_SecondaryUser() {
335        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
336
337        assertEquals("/data/system/users/1/gatekeeper.pattern.key", storage.getLockPatternFilename(1));
338        assertEquals("/data/system/users/1/gatekeeper.password.key", storage.getLockPasswordFilename(1));
339    }
340
341    public void testFileLocation_ProfileToSecondary() {
342        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
343
344        assertEquals("/data/system/users/2/gatekeeper.pattern.key", storage.getLockPatternFilename(2));
345        assertEquals("/data/system/users/2/gatekeeper.password.key", storage.getLockPasswordFilename(2));
346    }
347
348    public void testFileLocation_ProfileToOwner() {
349        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
350
351        assertEquals("/data/system/users/3/gatekeeper.pattern.key", storage.getLockPatternFilename(3));
352        assertEquals("/data/system/users/3/gatekeeper.password.key", storage.getLockPasswordFilename(3));
353    }
354
355    private static void assertArrayEquals(byte[] expected, byte[] actual) {
356        if (!Arrays.equals(expected, actual)) {
357            fail("expected:<" + Arrays.toString(expected) +
358                    "> but was:<" + Arrays.toString(actual) + ">");
359        }
360    }
361
362    private void writePasswordBytes(byte[] password, int userId) {
363        mStorage.writeCredentialHash(CredentialHash.create(
364                password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
365    }
366
367    private void writePatternBytes(byte[] pattern, int userId) {
368        mStorage.writeCredentialHash(CredentialHash.create(
369                pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
370    }
371
372    private void assertPasswordBytes(byte[] password, int userId) {
373        CredentialHash cred = mStorage.readCredentialHash(userId);
374        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, cred.type);
375        assertArrayEquals(password, cred.hash);
376    }
377
378    private void assertPatternBytes(byte[] pattern, int userId) {
379        CredentialHash cred = mStorage.readCredentialHash(userId);
380        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, cred.type);
381        assertArrayEquals(pattern, cred.hash);
382    }
383}
384