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