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