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