1 2 /* 3 * Copyright (C) 2016 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License 16 */ 17 18 package com.android.server.accounts; 19 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 25 import static org.mockito.Matchers.anyString; 26 import static org.mockito.Mockito.times; 27 import static org.mockito.Mockito.verify; 28 29 import android.accounts.Account; 30 import android.content.Context; 31 import android.database.Cursor; 32 import android.database.sqlite.SQLiteStatement; 33 import android.os.Build; 34 import android.test.suitebuilder.annotation.SmallTest; 35 import android.util.Pair; 36 37 import androidx.test.InstrumentationRegistry; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import org.junit.After; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 45 import org.mockito.Mock; 46 import org.mockito.MockitoAnnotations; 47 48 import java.io.File; 49 import java.io.PrintWriter; 50 import java.util.Arrays; 51 import java.util.List; 52 import java.util.Map; 53 54 /** 55 * Tests for {@link AccountsDb}. 56 * <p>Run with:<pre> 57 * m FrameworksServicesTests && 58 * adb install \ 59 * -r out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && 60 * adb shell am instrument -e class com.android.server.accounts.AccountsDbTest \ 61 * -w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner 62 * </pre> 63 */ 64 @RunWith(AndroidJUnit4.class) 65 @SmallTest 66 public class AccountsDbTest { 67 private static final String PREN_DB = "pren.db"; 68 private static final String DE_DB = "de.db"; 69 private static final String CE_DB = "ce.db"; 70 71 private AccountsDb mAccountsDb; 72 private File preNDb; 73 private File deDb; 74 private File ceDb; 75 76 @Mock private PrintWriter mockWriter; 77 78 @Before setUp()79 public void setUp() { 80 MockitoAnnotations.initMocks(this); 81 Context context = InstrumentationRegistry.getContext(); 82 preNDb = new File(context.getCacheDir(), PREN_DB); 83 ceDb = new File(context.getCacheDir(), CE_DB); 84 deDb = new File(context.getCacheDir(), DE_DB); 85 deleteDbFiles(); 86 mAccountsDb = AccountsDb.create(context, 0, preNDb, deDb); 87 } 88 89 @After tearDown()90 public void tearDown() { 91 deleteDbFiles(); 92 } 93 deleteDbFiles()94 private void deleteDbFiles() { 95 AccountsDb.deleteDbFileWarnIfFailed(preNDb); 96 AccountsDb.deleteDbFileWarnIfFailed(ceDb); 97 AccountsDb.deleteDbFileWarnIfFailed(deDb); 98 } 99 100 @Test testCeNotAvailableInitially()101 public void testCeNotAvailableInitially() { 102 // If the CE database is not attached to the DE database then any calls that modify the CE 103 // database will result in a Log.wtf call that will crash this process on eng builds. To 104 // allow the test to run through to completion skip this test on eng builds. 105 if (Build.IS_ENG) { 106 return; 107 } 108 Account account = new Account("name", "example.com"); 109 long id = mAccountsDb.insertCeAccount(account, ""); 110 assertEquals("Insert into CE should fail until CE database is attached", -1, id); 111 } 112 113 @Test testDeAccountInsertFindDelete()114 public void testDeAccountInsertFindDelete() { 115 Account account = new Account("name", "example.com"); 116 long accId = 1; 117 mAccountsDb.insertDeAccount(account, accId); 118 long actualId = mAccountsDb.findDeAccountId(account); 119 assertEquals(accId, actualId); 120 // Delete and verify that account no longer exists 121 mAccountsDb.deleteDeAccount(accId); 122 actualId = mAccountsDb.findDeAccountId(account); 123 assertEquals(-1, actualId); 124 } 125 126 @Test testCeAccountInsertFindDelete()127 public void testCeAccountInsertFindDelete() { 128 mAccountsDb.attachCeDatabase(ceDb); 129 Account account = new Account("name", "example.com"); 130 long accId = mAccountsDb.insertCeAccount(account, "password"); 131 long actualId = mAccountsDb.findCeAccountId(account); 132 assertEquals(accId, actualId); 133 // Delete and verify that account no longer exists 134 mAccountsDb.deleteCeAccount(accId); 135 actualId = mAccountsDb.findCeAccountId(account); 136 assertEquals(-1, actualId); 137 } 138 139 @Test testAuthTokenInsertFindDelete()140 public void testAuthTokenInsertFindDelete() { 141 mAccountsDb.attachCeDatabase(ceDb); 142 Account account = new Account("name", "example.com"); 143 long accId = mAccountsDb.insertCeAccount(account, "password"); 144 mAccountsDb.insertDeAccount(account, accId); 145 long authTokenId = mAccountsDb.insertAuthToken(accId, "type", "token"); 146 Map<String, String> authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account); 147 assertEquals(1, authTokensByAccount.size()); 148 try (Cursor cursor = mAccountsDb.findAuthtokenForAllAccounts(account.type, "token")) { 149 assertTrue(cursor.moveToNext()); 150 } 151 try (Cursor cursor = mAccountsDb.findAuthtokenForAllAccounts(account.type, "nosuchtoken")) { 152 assertFalse(cursor.moveToNext()); 153 } 154 mAccountsDb.deleteAuthToken(String.valueOf(authTokenId)); 155 // Verify that token no longer exists 156 authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account); 157 assertEquals(0, authTokensByAccount.size()); 158 } 159 160 @Test testAuthTokenDeletes()161 public void testAuthTokenDeletes() { 162 mAccountsDb.attachCeDatabase(ceDb); 163 // 1st account 164 Account account = new Account("name", "example.com"); 165 long accId = mAccountsDb.insertCeAccount(account, "password"); 166 mAccountsDb.insertDeAccount(account, accId); 167 mAccountsDb.insertAuthToken(accId, "type", "token"); 168 mAccountsDb.insertAuthToken(accId, "type2", "token2"); 169 // 2nd account 170 Account account2 = new Account("name", "example2.com"); 171 long accId2 = mAccountsDb.insertCeAccount(account2, "password"); 172 mAccountsDb.insertDeAccount(account2, accId2); 173 mAccountsDb.insertAuthToken(accId2, "type", "token"); 174 175 mAccountsDb.deleteAuthTokensByAccountId(accId2); 176 Map<String, String> authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account2); 177 assertEquals(0, authTokensByAccount.size()); 178 // Authtokens from account 1 are still there 179 authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account); 180 assertEquals(2, authTokensByAccount.size()); 181 182 // Delete authtokens from account 1 and verify 183 mAccountsDb.deleteAuthtokensByAccountIdAndType(accId, "type"); 184 authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account); 185 assertEquals(1, authTokensByAccount.size()); 186 mAccountsDb.deleteAuthtokensByAccountIdAndType(accId, "type2"); 187 authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account); 188 assertEquals(0, authTokensByAccount.size()); 189 } 190 191 @Test testExtrasInsertFindDelete()192 public void testExtrasInsertFindDelete() { 193 mAccountsDb.attachCeDatabase(ceDb); 194 Account account = new Account("name", "example.com"); 195 long accId = mAccountsDb.insertCeAccount(account, "password"); 196 mAccountsDb.insertDeAccount(account, accId); 197 String extraKey = "extra_key"; 198 String extraValue = "extra_value"; 199 long extraId = mAccountsDb.insertExtra(accId, extraKey, extraValue); 200 // Test find methods 201 long actualExtraId = mAccountsDb.findExtrasIdByAccountId(accId, extraKey); 202 assertEquals(extraId, actualExtraId); 203 Map<String, String> extras = mAccountsDb.findUserExtrasForAccount(account); 204 assertEquals(1, extras.size()); 205 assertEquals(extraValue, extras.get(extraKey)); 206 // Test update 207 String newExtraValue = "extra_value2"; 208 mAccountsDb.updateExtra(extraId, newExtraValue); 209 String newValue = mAccountsDb.findUserExtrasForAccount(account).get(extraKey); 210 assertEquals(newExtraValue, newValue); 211 212 // Delete account and verify that extras cascade removed 213 mAccountsDb.deleteCeAccount(accId); 214 actualExtraId = mAccountsDb.findExtrasIdByAccountId(accId, extraKey); 215 assertEquals(-1, actualExtraId); 216 } 217 218 @Test testGrantsInsertFindDelete()219 public void testGrantsInsertFindDelete() { 220 mAccountsDb.attachCeDatabase(ceDb); 221 Account account = new Account("name", "example.com"); 222 long accId = mAccountsDb.insertCeAccount(account, "password"); 223 mAccountsDb.insertDeAccount(account, accId); 224 int testUid = 100500; 225 long grantId = mAccountsDb.insertGrant(accId, "tokenType", testUid); 226 assertTrue(grantId > 0); 227 List<Integer> allUidGrants = mAccountsDb.findAllUidGrants(); 228 List<Integer> expectedUids = Arrays.asList(testUid); 229 assertEquals(expectedUids, allUidGrants); 230 231 long matchingGrantsCount = mAccountsDb.findMatchingGrantsCount( 232 testUid, "tokenType", account); 233 assertEquals(1, matchingGrantsCount); 234 // Test nonexistent type 235 matchingGrantsCount = mAccountsDb.findMatchingGrantsCount( 236 testUid, "noSuchType", account); 237 assertEquals(0, matchingGrantsCount); 238 239 matchingGrantsCount = mAccountsDb.findMatchingGrantsCountAnyToken(testUid, account); 240 assertEquals(1, matchingGrantsCount); 241 242 List<Pair<String, Integer>> allAccountGrants = mAccountsDb.findAllAccountGrants(); 243 assertEquals(1, allAccountGrants.size()); 244 assertEquals(account.name, allAccountGrants.get(0).first); 245 assertEquals(testUid, (int)allAccountGrants.get(0).second); 246 247 mAccountsDb.deleteGrantsByUid(testUid); 248 allUidGrants = mAccountsDb.findAllUidGrants(); 249 assertTrue("Test grants should be removed", allUidGrants.isEmpty()); 250 } 251 252 @Test testSharedAccountsInsertFindDelete()253 public void testSharedAccountsInsertFindDelete() { 254 Account account = new Account("name", "example.com"); 255 long accId = 0; 256 mAccountsDb.insertDeAccount(account, accId); 257 long sharedAccId = mAccountsDb.insertSharedAccount(account); 258 long foundSharedAccountId = mAccountsDb.findSharedAccountId(account); 259 assertEquals(sharedAccId, foundSharedAccountId); 260 List<Account> sharedAccounts = mAccountsDb.getSharedAccounts(); 261 List<Account> expectedList = Arrays.asList(account); 262 assertEquals(expectedList, sharedAccounts); 263 264 // Delete and verify 265 mAccountsDb.deleteSharedAccount(account); 266 foundSharedAccountId = mAccountsDb.findSharedAccountId(account); 267 assertEquals(-1, foundSharedAccountId); 268 } 269 270 @Test testMetaInsertFindDelete()271 public void testMetaInsertFindDelete() { 272 int testUid = 100500; 273 String authenticatorType = "authType"; 274 mAccountsDb.insertOrReplaceMetaAuthTypeAndUid(authenticatorType, testUid); 275 Map<String, Integer> metaAuthUid = mAccountsDb.findMetaAuthUid(); 276 assertEquals(1, metaAuthUid.size()); 277 assertEquals(testUid, (int)metaAuthUid.get(authenticatorType)); 278 279 // Delete and verify 280 boolean deleteResult = mAccountsDb.deleteMetaByAuthTypeAndUid(authenticatorType, testUid); 281 assertTrue(deleteResult); 282 metaAuthUid = mAccountsDb.findMetaAuthUid(); 283 assertEquals(0, metaAuthUid.size()); 284 } 285 286 @Test testUpdateDeAccountLastAuthenticatedTime()287 public void testUpdateDeAccountLastAuthenticatedTime() { 288 Account account = new Account("name", "example.com"); 289 long accId = 1; 290 mAccountsDb.insertDeAccount(account, accId); 291 long now = System.currentTimeMillis(); 292 mAccountsDb.updateAccountLastAuthenticatedTime(account); 293 long time = mAccountsDb.findAccountLastAuthenticatedTime(account); 294 assertTrue("LastAuthenticatedTime should be current", time >= now); 295 } 296 297 @Test testRenameAccount()298 public void testRenameAccount() { 299 mAccountsDb.attachCeDatabase(ceDb); 300 Account account = new Account("name", "example.com"); 301 long accId = mAccountsDb.insertCeAccount(account, "password"); 302 mAccountsDb.insertDeAccount(account, accId); 303 mAccountsDb.renameDeAccount(accId, "newName", "name"); 304 Account newAccount = mAccountsDb.findAllDeAccounts().get(accId); 305 assertEquals("newName", newAccount.name); 306 307 String prevName = mAccountsDb.findDeAccountPreviousName(newAccount); 308 assertEquals("name", prevName); 309 mAccountsDb.renameCeAccount(accId, "newName"); 310 long foundAccId = mAccountsDb.findCeAccountId(account); 311 assertEquals("Account shouldn't be found under the old name", -1, foundAccId); 312 foundAccId = mAccountsDb.findCeAccountId(newAccount); 313 assertEquals(accId, foundAccId); 314 } 315 316 @Test testUpdateCeAccountPassword()317 public void testUpdateCeAccountPassword() { 318 mAccountsDb.attachCeDatabase(ceDb); 319 Account account = new Account("name", "example.com"); 320 long accId = mAccountsDb.insertCeAccount(account, "password"); 321 String newPassword = "newPassword"; 322 mAccountsDb.updateCeAccountPassword(accId, newPassword); 323 String actualPassword = mAccountsDb 324 .findAccountPasswordByNameAndType(account.name, account.type); 325 assertEquals(newPassword, actualPassword); 326 } 327 328 @Test testFindCeAccountsNotInDe()329 public void testFindCeAccountsNotInDe() { 330 mAccountsDb.attachCeDatabase(ceDb); 331 Account account = new Account("name", "example.com"); 332 long accId = mAccountsDb.insertCeAccount(account, "password"); 333 mAccountsDb.insertDeAccount(account, accId); 334 335 Account accountNotInDe = new Account("name2", "example.com"); 336 mAccountsDb.insertCeAccount(accountNotInDe, "password"); 337 338 List<Account> ceAccounts = mAccountsDb.findCeAccountsNotInDe(); 339 List<Account> expectedList = Arrays.asList(accountNotInDe); 340 assertEquals(expectedList, ceAccounts); 341 } 342 343 @Test testCrossDbTransactions()344 public void testCrossDbTransactions() { 345 mAccountsDb.attachCeDatabase(ceDb); 346 mAccountsDb.beginTransaction(); 347 Account account = new Account("name", "example.com"); 348 long accId; 349 accId = mAccountsDb.insertCeAccount(account, "password"); 350 accId = mAccountsDb.insertDeAccount(account, accId); 351 long actualId = mAccountsDb.findCeAccountId(account); 352 assertEquals(accId, actualId); 353 actualId = mAccountsDb.findDeAccountId(account); 354 assertEquals(accId, actualId); 355 mAccountsDb.endTransaction(); 356 // Verify that records were removed 357 actualId = mAccountsDb.findCeAccountId(account); 358 assertEquals(-1, actualId); 359 actualId = mAccountsDb.findDeAccountId(account); 360 assertEquals(-1, actualId); 361 } 362 363 @Test testFindDeAccountByAccountId()364 public void testFindDeAccountByAccountId() { 365 long accId = 10; 366 Account account = new Account("name", "example.com"); 367 assertNull(mAccountsDb.findDeAccountByAccountId(accId)); 368 369 mAccountsDb.insertDeAccount(account, accId); 370 371 Account foundAccount = mAccountsDb.findDeAccountByAccountId(accId); 372 assertEquals(account, foundAccount); 373 } 374 375 @Test testVisibilityFindSetDelete()376 public void testVisibilityFindSetDelete() { 377 long accId = 10; 378 String packageName1 = "com.example.one"; 379 String packageName2 = "com.example.two"; 380 Account account = new Account("name", "example.com"); 381 assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); 382 383 mAccountsDb.insertDeAccount(account, accId); 384 assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); 385 assertNull(mAccountsDb.findAccountVisibility(accId, packageName1)); 386 387 mAccountsDb.setAccountVisibility(accId, packageName1, 1); 388 assertEquals(mAccountsDb.findAccountVisibility(account, packageName1), Integer.valueOf(1)); 389 assertEquals(mAccountsDb.findAccountVisibility(accId, packageName1), Integer.valueOf(1)); 390 391 mAccountsDb.setAccountVisibility(accId, packageName2, 2); 392 assertEquals(mAccountsDb.findAccountVisibility(accId, packageName2), Integer.valueOf(2)); 393 394 mAccountsDb.setAccountVisibility(accId, packageName2, 3); 395 assertEquals(mAccountsDb.findAccountVisibility(accId, packageName2), Integer.valueOf(3)); 396 397 Map<String, Integer> vis = mAccountsDb.findAllVisibilityValuesForAccount(account); 398 assertEquals(vis.size(), 2); 399 assertEquals(vis.get(packageName1), Integer.valueOf(1)); 400 assertEquals(vis.get(packageName2), Integer.valueOf(3)); 401 402 assertTrue(mAccountsDb.deleteAccountVisibilityForPackage(packageName1)); 403 assertNull(mAccountsDb.findAccountVisibility(accId, packageName1)); 404 assertFalse(mAccountsDb.deleteAccountVisibilityForPackage(packageName1)); // 2nd attempt. 405 } 406 407 @Test testFindAllVisibilityValues()408 public void testFindAllVisibilityValues() { 409 long accId = 10; 410 long accId2 = 11; 411 String packageName1 = "com.example.one"; 412 String packageName2 = "com.example.two"; 413 Account account = new Account("name", "example.com"); 414 Account account2 = new Account("name2", "example2.com"); 415 assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); 416 417 mAccountsDb.insertDeAccount(account, accId); 418 assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); 419 assertNull(mAccountsDb.findAccountVisibility(accId, packageName1)); 420 mAccountsDb.insertDeAccount(account2, accId2); 421 422 mAccountsDb.setAccountVisibility(accId, packageName1, 1); 423 mAccountsDb.setAccountVisibility(accId, packageName2, 2); 424 mAccountsDb.setAccountVisibility(accId2, packageName1, 1); 425 426 Map<Account, Map<String, Integer>> vis = mAccountsDb.findAllVisibilityValues(); 427 assertEquals(vis.size(), 2); 428 Map<String, Integer> accnt1Visibility = vis.get(account); 429 assertEquals(accnt1Visibility.size(), 2); 430 assertEquals(accnt1Visibility.get(packageName1), Integer.valueOf(1)); 431 assertEquals(accnt1Visibility.get(packageName2), Integer.valueOf(2)); 432 Map<String, Integer> accnt2Visibility = vis.get(account2); 433 assertEquals(accnt2Visibility.size(), 1); 434 assertEquals(accnt2Visibility.get(packageName1), Integer.valueOf(1)); 435 436 mAccountsDb.setAccountVisibility(accId2, packageName2, 3); 437 vis = mAccountsDb.findAllVisibilityValues(); 438 accnt2Visibility = vis.get(account2); 439 assertEquals(accnt2Visibility.size(), 2); 440 assertEquals(accnt2Visibility.get(packageName2), Integer.valueOf(3)); 441 } 442 443 @Test testVisibilityCleanupTrigger()444 public void testVisibilityCleanupTrigger() { 445 long accId = 10; 446 String packageName1 = "com.example.one"; 447 Account account = new Account("name", "example.com"); 448 449 assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); 450 mAccountsDb.insertDeAccount(account, accId); 451 assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); 452 453 mAccountsDb.setAccountVisibility(accId, packageName1, 1); 454 assertEquals(mAccountsDb.findAccountVisibility(accId, packageName1), Integer.valueOf(1)); 455 456 assertTrue(mAccountsDb.deleteDeAccount(accId)); // Trigger should remove visibility. 457 assertNull(mAccountsDb.findAccountVisibility(account, packageName1)); 458 } 459 460 @Test testDumpDebugTable()461 public void testDumpDebugTable() { 462 long accId = 10; 463 long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint(); 464 465 SQLiteStatement logStatement = mAccountsDb.getStatementForLogging(); 466 467 logStatement.bindLong(1, accId); 468 logStatement.bindString(2, "action"); 469 logStatement.bindString(3, "date"); 470 logStatement.bindLong(4, 10); 471 logStatement.bindString(5, "table"); 472 logStatement.bindLong(6, insertionPoint); 473 logStatement.execute(); 474 475 mAccountsDb.dumpDebugTable(mockWriter); 476 477 verify(mockWriter, times(3)).println(anyString()); 478 } 479 480 @Test testReserveDebugDbInsertionPoint()481 public void testReserveDebugDbInsertionPoint() { 482 long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint(); 483 long insertionPoint2 = mAccountsDb.reserveDebugDbInsertionPoint(); 484 485 assertEquals(0, insertionPoint); 486 assertEquals(1, insertionPoint2); 487 } 488 } 489