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; 18 19 import android.content.Context; 20 import android.content.ContextWrapper; 21 import android.content.pm.UserInfo; 22 import android.database.sqlite.SQLiteDatabase; 23 import android.os.FileUtils; 24 import android.os.UserManager; 25 import android.test.AndroidTestCase; 26 27 import java.io.File; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.List; 31 import java.util.concurrent.CountDownLatch; 32 33 public 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 } 92 93 @Override 94 protected void tearDown() throws Exception { 95 super.tearDown(); 96 mStorage.closeDatabase(); 97 } 98 99 public void testKeyValue_InitializeWorked() { 100 assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0)); 101 mStorage.clearCache(); 102 assertEquals("initialValue", mStorage.readKeyValue("initializedKey", "default", 0)); 103 } 104 105 public void testKeyValue_WriteThenRead() { 106 mStorage.writeKeyValue("key", "value", 0); 107 assertEquals("value", mStorage.readKeyValue("key", "default", 0)); 108 mStorage.clearCache(); 109 assertEquals("value", mStorage.readKeyValue("key", "default", 0)); 110 } 111 112 public void testKeyValue_DefaultValue() { 113 assertEquals("default", mStorage.readKeyValue("unititialized key", "default", 0)); 114 assertEquals("default2", mStorage.readKeyValue("unititialized key", "default2", 0)); 115 } 116 117 public void testKeyValue_Concurrency() { 118 final Object monitor = new Object(); 119 List<Thread> threads = new ArrayList<>(); 120 for (int i = 0; i < 100; i++) { 121 final int threadId = i; 122 threads.add(new Thread() { 123 @Override 124 public void run() { 125 synchronized (monitor) { 126 try { 127 monitor.wait(); 128 } catch (InterruptedException e) { 129 return; 130 } 131 mStorage.writeKeyValue("key", "1 from thread " + threadId, 0); 132 mStorage.readKeyValue("key", "default", 0); 133 mStorage.writeKeyValue("key", "2 from thread " + threadId, 0); 134 mStorage.readKeyValue("key", "default", 0); 135 mStorage.writeKeyValue("key", "3 from thread " + threadId, 0); 136 mStorage.readKeyValue("key", "default", 0); 137 mStorage.writeKeyValue("key", "4 from thread " + threadId, 0); 138 mStorage.readKeyValue("key", "default", 0); 139 mStorage.writeKeyValue("key", "5 from thread " + threadId, 0); 140 mStorage.readKeyValue("key", "default", 0); 141 } 142 } 143 }); 144 threads.get(i).start(); 145 } 146 mStorage.writeKeyValue("key", "initalValue", 0); 147 synchronized (monitor) { 148 monitor.notifyAll(); 149 } 150 for (int i = 0; i < threads.size(); i++) { 151 try { 152 threads.get(i).join(); 153 } catch (InterruptedException e) { 154 } 155 } 156 assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0)); 157 mStorage.clearCache(); 158 assertEquals('5', mStorage.readKeyValue("key", "default", 0).charAt(0)); 159 } 160 161 public void testKeyValue_CacheStarvedWriter() { 162 final CountDownLatch latch = new CountDownLatch(1); 163 List<Thread> threads = new ArrayList<>(); 164 for (int i = 0; i < 100; i++) { 165 final int threadId = i; 166 threads.add(new Thread() { 167 @Override 168 public void run() { 169 try { 170 latch.await(); 171 } catch (InterruptedException e) { 172 return; 173 } 174 if (threadId == 50) { 175 mStorage.writeKeyValue("starvedWriterKey", "value", 0); 176 } else { 177 mStorage.readKeyValue("starvedWriterKey", "default", 0); 178 } 179 } 180 }); 181 threads.get(i).start(); 182 } 183 latch.countDown(); 184 for (int i = 0; i < threads.size(); i++) { 185 try { 186 threads.get(i).join(); 187 } catch (InterruptedException e) { 188 } 189 } 190 String cached = mStorage.readKeyValue("key", "default", 0); 191 mStorage.clearCache(); 192 String storage = mStorage.readKeyValue("key", "default", 0); 193 assertEquals("Cached value didn't match stored value", storage, cached); 194 } 195 196 public void testRemoveUser() { 197 mStorage.writeKeyValue("key", "value", 0); 198 mStorage.writePasswordHash(new byte[]{1}, 0); 199 mStorage.writePatternHash(new byte[]{2}, 0); 200 201 mStorage.writeKeyValue("key", "value", 1); 202 mStorage.writePasswordHash(new byte[]{1}, 1); 203 mStorage.writePatternHash(new byte[]{2}, 1); 204 205 mStorage.removeUser(0); 206 207 assertEquals("value", mStorage.readKeyValue("key", "default", 1)); 208 assertEquals("default", mStorage.readKeyValue("key", "default", 0)); 209 assertNotNull(mStorage.readPasswordHash(1)); 210 assertNull(mStorage.readPasswordHash(0)); 211 assertNotNull(mStorage.readPatternHash(1)); 212 assertNull(mStorage.readPatternHash(0)); 213 } 214 215 public void testPassword_Default() { 216 assertNull(mStorage.readPasswordHash(0)); 217 } 218 219 public void testPassword_Write() { 220 mStorage.writePasswordHash("thepassword".getBytes(), 0); 221 222 assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0)); 223 mStorage.clearCache(); 224 assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0)); 225 } 226 227 public void testPassword_WriteProfileWritesParent() { 228 mStorage.writePasswordHash("parentpasswordd".getBytes(), 1); 229 mStorage.writePasswordHash("profilepassword".getBytes(), 2); 230 231 assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1)); 232 assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2)); 233 mStorage.clearCache(); 234 assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1)); 235 assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2)); 236 } 237 238 public void testPassword_WriteParentWritesProfile() { 239 mStorage.writePasswordHash("profilepassword".getBytes(), 2); 240 mStorage.writePasswordHash("parentpasswordd".getBytes(), 1); 241 242 assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1)); 243 assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2)); 244 mStorage.clearCache(); 245 assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1)); 246 assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2)); 247 } 248 249 public void testPattern_Default() { 250 assertNull(mStorage.readPasswordHash(0)); 251 } 252 253 public void testPattern_Write() { 254 mStorage.writePatternHash("thepattern".getBytes(), 0); 255 256 assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0)); 257 mStorage.clearCache(); 258 assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0)); 259 } 260 261 public void testPattern_WriteProfileWritesParent() { 262 mStorage.writePatternHash("parentpatternn".getBytes(), 1); 263 mStorage.writePatternHash("profilepattern".getBytes(), 2); 264 265 assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1)); 266 assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2)); 267 mStorage.clearCache(); 268 assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1)); 269 assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2)); 270 } 271 272 public void testPattern_WriteParentWritesProfile() { 273 mStorage.writePatternHash("profilepattern".getBytes(), 2); 274 mStorage.writePatternHash("parentpatternn".getBytes(), 1); 275 276 assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1)); 277 assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2)); 278 mStorage.clearCache(); 279 assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1)); 280 assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2)); 281 } 282 283 public void testPrefetch() { 284 mStorage.writeKeyValue("key", "toBeFetched", 0); 285 mStorage.writePatternHash("pattern".getBytes(), 0); 286 mStorage.writePasswordHash("password".getBytes(), 0); 287 288 mStorage.clearCache(); 289 mStorage.prefetchUser(0); 290 291 assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0)); 292 assertArrayEquals("pattern".getBytes(), mStorage.readPatternHash(0)); 293 assertArrayEquals("password".getBytes(), mStorage.readPasswordHash(0)); 294 } 295 296 public void testFileLocation_Owner() { 297 LockSettingsStorage storage = new LockSettingsStorage(getContext(), null); 298 299 assertEquals("/data/system/gesture.key", storage.getLockPatternFilename(0)); 300 assertEquals("/data/system/password.key", storage.getLockPasswordFilename(0)); 301 } 302 303 public void testFileLocation_SecondaryUser() { 304 LockSettingsStorage storage = new LockSettingsStorage(getContext(), null); 305 306 assertEquals("/data/system/users/1/gesture.key", storage.getLockPatternFilename(1)); 307 assertEquals("/data/system/users/1/password.key", storage.getLockPasswordFilename(1)); 308 } 309 310 public void testFileLocation_ProfileToSecondary() { 311 LockSettingsStorage storage = new LockSettingsStorage(getContext(), null); 312 313 assertEquals("/data/system/users/1/gesture.key", storage.getLockPatternFilename(2)); 314 assertEquals("/data/system/users/1/password.key", storage.getLockPasswordFilename(2)); 315 } 316 317 public void testFileLocation_ProfileToOwner() { 318 LockSettingsStorage storage = new LockSettingsStorage(getContext(), null); 319 320 assertEquals("/data/system/gesture.key", storage.getLockPatternFilename(3)); 321 assertEquals("/data/system/password.key", storage.getLockPasswordFilename(3)); 322 } 323 324 private static void assertArrayEquals(byte[] expected, byte[] actual) { 325 if (!Arrays.equals(expected, actual)) { 326 fail("expected:<" + Arrays.toString(expected) + 327 "> but was:<" + Arrays.toString(actual) + ">"); 328 } 329 } 330 } 331