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