1 /*
2  * Copyright (C) 2010 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 android.content.cts;
18 
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.accounts.AccountManagerCallback;
22 import android.accounts.AccountManagerFuture;
23 import android.accounts.AuthenticatorException;
24 import android.accounts.OperationCanceledException;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.SyncAdapterType;
28 import android.os.Bundle;
29 import android.os.SystemClock;
30 import android.platform.test.annotations.AppModeFull;
31 import android.test.AndroidTestCase;
32 import android.util.Log;
33 
34 import java.io.IOException;
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.TimeUnit;
37 
38 @AppModeFull(reason = "Sync manager not supported")
39 public class ContentResolverSyncTestCase extends AndroidTestCase {
40     private static final String TAG = "SyncTest";
41 
42     private static final String AUTHORITY = "android.content.cts.authority";
43 
44     private static final Account ACCOUNT = new Account(MockAccountAuthenticator.ACCOUNT_NAME,
45             MockAccountAuthenticator.ACCOUNT_TYPE);
46 
47     private static final int INITIAL_SYNC_TIMEOUT_MS = 60 * 1000;
48     private static final int CANCEL_TIMEOUT_MS = 60 * 1000;
49     private static final int LATCH_TIMEOUT_MS = 5000;
50 
51     private static AccountManager sAccountManager;
52 
53     @Override
setUp()54     public void setUp() throws Exception {
55         super.setUp();
56         getMockSyncAdapter();
57         sAccountManager = AccountManager.get(getContext());
58     }
59 
60     @Override
tearDown()61     public void tearDown() throws Exception {
62         getMockSyncAdapter().clearData();
63 
64         // Need to clean up created account
65         removeAccount(sAccountManager, ACCOUNT, null /* callback */);
66 
67         // Need to cancel any sync that was started.
68         cancelSync(null, AUTHORITY, LATCH_TIMEOUT_MS);
69 
70         super.tearDown();
71     }
72 
getMockSyncAdapter()73     public static synchronized MockSyncAdapter getMockSyncAdapter() {
74         return MockSyncAdapter.getMockSyncAdapter();
75 
76     }
77 
getMockAuthenticator(Context context)78     public static synchronized MockAccountAuthenticator getMockAuthenticator(Context context) {
79         return MockAccountAuthenticator.getMockAuthenticator(context);
80     }
81 
addAccountExplicitly(Account account, String password, Bundle userdata)82     private void addAccountExplicitly(Account account, String password, Bundle userdata) {
83         assertTrue(sAccountManager.addAccountExplicitly(account, password, userdata));
84     }
85 
removeAccount(AccountManager am, Account account, AccountManagerCallback<Boolean> callback)86     private boolean removeAccount(AccountManager am, Account account,
87             AccountManagerCallback<Boolean> callback) throws IOException, AuthenticatorException,
88                 OperationCanceledException {
89 
90         AccountManagerFuture<Boolean> futureBoolean = am.removeAccount(account,
91                 callback,
92                 null /* handler */);
93         Boolean resultBoolean = futureBoolean.getResult();
94         assertTrue(futureBoolean.isDone());
95 
96         return resultBoolean;
97     }
98 
setNewLatch(CountDownLatch latch)99     private CountDownLatch setNewLatch(CountDownLatch latch) {
100         getMockSyncAdapter().clearData();
101         getMockSyncAdapter().setLatch(latch);
102         return latch;
103     }
104 
addAccountAndVerifyInitSync(Account account, String password, String authority, int accountIndex)105     private void addAccountAndVerifyInitSync(Account account, String password,
106             String authority, int accountIndex) {
107 
108         CountDownLatch latch = setNewLatch(new CountDownLatch(1));
109 
110         addAccountExplicitly(account, password, null /* userData */);
111 
112         // Wait with timeout for the callback to do its work
113         try {
114             if (!latch.await(INITIAL_SYNC_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
115                 fail("should not time out waiting on latch");
116             }
117         } catch (InterruptedException e) {
118             fail("should not throw an InterruptedException");
119         }
120 
121         assertFalse(getMockSyncAdapter().isStartSync());
122         assertFalse(getMockSyncAdapter().isCancelSync());
123         assertTrue(getMockSyncAdapter().isInitialized());
124         assertEquals(account, getMockSyncAdapter().getAccounts().get(accountIndex));
125         assertEquals(authority, getMockSyncAdapter().getAuthority());
126     }
127 
cancelSync(Account account, String authority, int latchTimeoutMillis)128     private void cancelSync(Account account, String authority, int latchTimeoutMillis) {
129         CountDownLatch latch = setNewLatch(new CountDownLatch(1));
130 
131         Bundle extras = new Bundle();
132         extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
133 
134         ContentResolver.cancelSync(account, authority);
135 
136         // Wait with timeout for the callback to do its work
137         try {
138             latch.await(latchTimeoutMillis, TimeUnit.MILLISECONDS);
139         } catch (InterruptedException e) {
140             fail("should not throw an InterruptedException");
141         }
142         // Make sure the sync manager thinks the sync finished.
143 
144         final long timeout = SystemClock.uptimeMillis() + CANCEL_TIMEOUT_MS;
145         while (SystemClock.uptimeMillis() < timeout) {
146             if (!ContentResolver.isSyncActive(ACCOUNT, AUTHORITY)
147                 && !ContentResolver.isSyncPending(ACCOUNT, AUTHORITY)) {
148                 break;
149             }
150             Log.i(TAG, "Waiting for sync to finish...");
151             try {
152                 Thread.sleep(500);
153             } catch (InterruptedException e) {
154             }
155         }
156     }
157 
requestSync(Account account, String authority, int latchTimeoutMillis)158     private void requestSync(Account account, String authority, int latchTimeoutMillis) {
159         CountDownLatch latch = setNewLatch(new CountDownLatch(1));
160 
161         Bundle extras = new Bundle();
162         extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
163 
164         ContentResolver.requestSync(account, authority, extras);
165 
166         // Wait with timeout for the callback to do its work
167         try {
168             latch.await(latchTimeoutMillis, TimeUnit.MILLISECONDS);
169         } catch (InterruptedException e) {
170             fail("should not throw an InterruptedException");
171         }
172     }
173 
setIsSyncable(Account account, String authority, boolean b)174     private void setIsSyncable(Account account, String authority, boolean b) {
175         ContentResolver.setIsSyncable(account, authority, (b) ? 1 : 0);
176     }
177 
178     /**
179      * Test a sync request
180      */
testRequestSync()181     public void testRequestSync() throws IOException, AuthenticatorException,
182             OperationCanceledException {
183 
184         // Prevent auto sync
185         ContentResolver.setMasterSyncAutomatically(false);
186         assertEquals(false, ContentResolver.getMasterSyncAutomatically());
187 
188         addAccountAndVerifyInitSync(ACCOUNT,
189                 MockAccountAuthenticator.ACCOUNT_PASSWORD,
190                 AUTHORITY,
191                 0);
192 
193         getMockSyncAdapter().clearData();
194 
195         setIsSyncable(ACCOUNT, AUTHORITY, true);
196         cancelSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
197 
198         getMockSyncAdapter().clearData();
199 
200         requestSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
201 
202         assertTrue(getMockSyncAdapter().isStartSync());
203         assertFalse(getMockSyncAdapter().isCancelSync());
204         assertFalse(getMockSyncAdapter().isInitialized());
205         assertEquals(ACCOUNT, getMockSyncAdapter().getAccounts().get(0));
206         assertEquals(AUTHORITY, getMockSyncAdapter().getAuthority());
207     }
208 
209     /**
210      * Test a sync cancel
211      */
testCancelSync()212     public void testCancelSync() throws IOException, AuthenticatorException,
213             OperationCanceledException {
214 
215         // Prevent auto sync
216         ContentResolver.setMasterSyncAutomatically(false);
217         assertEquals(false, ContentResolver.getMasterSyncAutomatically());
218 
219         addAccountAndVerifyInitSync(ACCOUNT,
220                 MockAccountAuthenticator.ACCOUNT_PASSWORD,
221                 AUTHORITY,
222                 0);
223 
224         getMockSyncAdapter().clearData();
225 
226         setIsSyncable(ACCOUNT, AUTHORITY, true);
227         requestSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
228 
229         getMockSyncAdapter().clearData();
230 
231         cancelSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
232 
233         assertFalse(getMockSyncAdapter().isStartSync());
234         assertTrue(getMockSyncAdapter().isCancelSync());
235         assertFalse(getMockSyncAdapter().isInitialized());
236 
237         assertFalse(ContentResolver.isSyncActive(ACCOUNT, AUTHORITY));
238         assertFalse(ContentResolver.isSyncPending(ACCOUNT, AUTHORITY));
239     }
240 
241     /**
242      * Test if we can set and get the MasterSyncAutomatically switch
243      */
testGetAndSetMasterSyncAutomatically()244     public void testGetAndSetMasterSyncAutomatically() throws Exception {
245         ContentResolver.setMasterSyncAutomatically(true);
246         assertEquals(true, ContentResolver.getMasterSyncAutomatically());
247 
248         ContentResolver.setMasterSyncAutomatically(false);
249         assertEquals(false, ContentResolver.getMasterSyncAutomatically());
250         Thread.sleep(3000);
251     }
252 
253     /**
254      * Test if we can set and get the SyncAutomatically switch for an account
255      */
testGetAndSetSyncAutomatically()256     public void testGetAndSetSyncAutomatically() {
257         try {
258             Thread.sleep(5000);
259         } catch (InterruptedException e) {
260         }
261         // Prevent auto sync
262         ContentResolver.setMasterSyncAutomatically(false);
263         assertEquals(false, ContentResolver.getMasterSyncAutomatically());
264 
265         ContentResolver.setSyncAutomatically(ACCOUNT, AUTHORITY, false);
266         assertEquals(false, ContentResolver.getSyncAutomatically(ACCOUNT, AUTHORITY));
267 
268         ContentResolver.setSyncAutomatically(ACCOUNT, AUTHORITY, true);
269         assertEquals(true, ContentResolver.getSyncAutomatically(ACCOUNT, AUTHORITY));
270     }
271 
272     /**
273      * Test if we can set and get the IsSyncable switch for an account
274      */
testGetAndSetIsSyncable()275     public void testGetAndSetIsSyncable() {
276         // Prevent auto sync
277         ContentResolver.setMasterSyncAutomatically(false);
278         assertEquals(false, ContentResolver.getMasterSyncAutomatically());
279 
280         addAccountExplicitly(ACCOUNT, MockAccountAuthenticator.ACCOUNT_PASSWORD, null /* userData */);
281 
282         ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, 2);
283         assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) > 0);
284 
285         ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, 1);
286         assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) > 0);
287 
288         ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, 0);
289         assertEquals(0, ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY));
290 
291         ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, -1);
292         assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) < 0);
293 
294         ContentResolver.setIsSyncable(ACCOUNT, AUTHORITY, -2);
295         assertTrue(ContentResolver.getIsSyncable(ACCOUNT, AUTHORITY) < 0);
296     }
297 
298     /**
299      * Test if we can get the sync adapter types
300      */
301     public void testGetSyncAdapterTypes() {
302         SyncAdapterType[] types = ContentResolver.getSyncAdapterTypes();
303         assertNotNull(types);
304         int length = types.length;
305         assertTrue(length > 0);
306         boolean found = false;
307         for (int n=0; n < length; n++) {
308             SyncAdapterType type = types[n];
309             if (MockAccountAuthenticator.ACCOUNT_TYPE.equals(type.accountType) &&
310                     AUTHORITY.equals(type.authority)) {
311                 found = true;
312                 break;
313             }
314         }
315         assertTrue(found);
316     }
317 
318     /**
319      * Test if a badly formed sync request is throwing exceptions
320      */
321     public void testStartSyncFailure() {
322         try {
323             ContentResolver.requestSync(null, null, null);
324             fail("did not throw IllegalArgumentException when extras is null.");
325         } catch (IllegalArgumentException e) {
326             //expected.
327         }
328     }
329 
330     /**
331      * Test validate sync extra bundle
332      */
333     public void testValidateSyncExtrasBundle() {
334         Bundle extras = new Bundle();
335         extras.putInt("Integer", 20);
336         extras.putLong("Long", 10l);
337         extras.putBoolean("Boolean", true);
338         extras.putFloat("Float", 5.5f);
339         extras.putDouble("Double", 2.5);
340         extras.putString("String", MockAccountAuthenticator.ACCOUNT_NAME);
341         extras.putCharSequence("CharSequence", null);
342 
343         ContentResolver.validateSyncExtrasBundle(extras);
344 
345         extras.putChar("Char", 'a'); // type Char is invalid
346         try {
347             ContentResolver.validateSyncExtrasBundle(extras);
348             fail("did not throw IllegalArgumentException when extras is invalide.");
349         } catch (IllegalArgumentException e) {
350             //expected.
351         }
352     }
353 
354     /**
355      * Test to verify that a SyncAdapter is called on all the accounts accounts
356      */
357     public void testCallMultipleAccounts() {
358         // Prevent auto sync
359         ContentResolver.setMasterSyncAutomatically(false);
360         assertEquals(false, ContentResolver.getMasterSyncAutomatically());
361 
362         addAccountAndVerifyInitSync(ACCOUNT,
363                 MockAccountAuthenticator.ACCOUNT_PASSWORD,
364                 AUTHORITY,
365                 0);
366 
367         getMockSyncAdapter().clearData();
368 
369         setIsSyncable(ACCOUNT, AUTHORITY, true);
370         cancelSync(ACCOUNT, AUTHORITY, LATCH_TIMEOUT_MS);
371 
372         getMockSyncAdapter().clearData();
373 
374         requestSync(null /* all accounts */, AUTHORITY, LATCH_TIMEOUT_MS);
375 
376         assertTrue(getMockSyncAdapter().isStartSync());
377         assertFalse(getMockSyncAdapter().isCancelSync());
378         assertFalse(getMockSyncAdapter().isInitialized());
379         assertEquals(ACCOUNT, getMockSyncAdapter().getAccounts().get(0));
380         assertEquals(AUTHORITY, getMockSyncAdapter().getAuthority());
381 
382     }
383 }
384