1 /*
2  * Copyright (C) 2007 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 com.android.server.content;
18 
19 import android.accounts.Account;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.ContextWrapper;
24 import android.content.Intent;
25 import android.content.PeriodicSync;
26 import android.content.res.Resources;
27 import android.os.Bundle;
28 import android.test.AndroidTestCase;
29 import android.test.RenamingDelegatingContext;
30 import android.test.mock.MockContentResolver;
31 import android.test.mock.MockContext;
32 import android.test.suitebuilder.annotation.LargeTest;
33 import android.test.suitebuilder.annotation.MediumTest;
34 import android.test.suitebuilder.annotation.SmallTest;
35 
36 import com.android.server.content.SyncStorageEngine.EndPoint;
37 
38 import com.android.internal.os.AtomicFile;
39 
40 import java.io.File;
41 import java.io.FileOutputStream;
42 import java.util.List;
43 
44 import com.android.server.content.SyncStorageEngine.EndPoint;
45 
46 public class SyncStorageEngineTest extends AndroidTestCase {
47 
48     protected Account account1;
49     protected Account account2;
50     protected ComponentName syncService1;
51     protected String authority1 = "testprovider";
52     protected Bundle defaultBundle;
53     protected final int DEFAULT_USER = 0;
54 
55     /* Some default poll frequencies. */
56     final long dayPoll = (60 * 60 * 24);
57     final long dayFuzz = 60;
58     final long thousandSecs = 1000;
59     final long thousandSecsFuzz = 100;
60 
61     MockContentResolver mockResolver;
62     SyncStorageEngine engine;
63 
getSyncDir()64     private File getSyncDir() {
65         return new File(new File(getContext().getFilesDir(), "system"), "sync");
66     }
67 
68     @Override
setUp()69     public void setUp() {
70         account1 = new Account("a@example.com", "example.type");
71         account2 = new Account("b@example.com", "example.type");
72         syncService1 = new ComponentName("com.example", "SyncService");
73         // Default bundle.
74         defaultBundle = new Bundle();
75         defaultBundle.putInt("int_key", 0);
76         defaultBundle.putString("string_key", "hello");
77         // Set up storage engine.
78         mockResolver = new MockContentResolver();
79         engine = SyncStorageEngine.newTestInstance(
80                 new TestContext(mockResolver, getContext()));
81     }
82 
83     /**
84      * Test that we handle the case of a history row being old enough to purge before the
85      * corresponding sync is finished. This can happen if the clock changes while we are syncing.
86      *
87      */
88     // TODO: this test causes AidlTest to fail. Omit for now
89     // @SmallTest
testPurgeActiveSync()90     public void testPurgeActiveSync() throws Exception {
91         final Account account = new Account("a@example.com", "example.type");
92         final String authority = "testprovider";
93 
94         MockContentResolver mockResolver = new MockContentResolver();
95 
96         SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
97                 new TestContext(mockResolver, getContext()));
98         long time0 = 1000;
99         SyncOperation op = new SyncOperation(account, 0,
100                 SyncOperation.REASON_PERIODIC,
101                 SyncStorageEngine.SOURCE_LOCAL,
102                 authority,
103                 Bundle.EMPTY, time0, 0 /* flex*/, 0, 0, true);
104         long historyId = engine.insertStartSyncEvent(op, time0);
105         long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
106         engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
107     }
108 
109     /**
110      * Test persistence of pending operations.
111      */
112     @MediumTest
testAppendPending()113     public void testAppendPending() throws Exception {
114         SyncOperation sop = new SyncOperation(account1,
115                 DEFAULT_USER,
116                 SyncOperation.REASON_PERIODIC,
117                 SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
118                 0 /* runtime */, 0 /* flex */, 0 /* backoff */, 0 /* delayuntil */,
119                 true /* expedited */);
120         engine.insertIntoPending(sop);
121 
122         // Force engine to read from disk.
123         engine.clearAndReadState();
124 
125         assertTrue(engine.getPendingOperationCount() == 1);
126         List<SyncStorageEngine.PendingOperation> pops = engine.getPendingOperations();
127         SyncStorageEngine.PendingOperation popRetrieved = pops.get(0);
128         assertEquals(sop.target.account, popRetrieved.target.account);
129         assertEquals(sop.target.provider, popRetrieved.target.provider);
130         assertEquals(sop.target.service, popRetrieved.target.service);
131         assertEquals(sop.target.userId, popRetrieved.target.userId);
132         assertEquals(sop.reason, popRetrieved.reason);
133         assertEquals(sop.syncSource, popRetrieved.syncSource);
134         assertEquals(sop.isExpedited(), popRetrieved.expedited);
135         assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
136     }
137 
138     /**
139      * Verify {@link com.android.server.content.SyncStorageEngine#writePendingOperationsLocked()}
140      */
testWritePendingOperationsLocked()141     public void testWritePendingOperationsLocked() throws Exception {
142         SyncOperation sop = new SyncOperation(account1,
143                 DEFAULT_USER,
144                 SyncOperation.REASON_IS_SYNCABLE,
145                 SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
146                 1000L /* runtime */, 57L /* flex */, 0 /* backoff */, 0 /* delayuntil */,
147                 true /* expedited */);
148         SyncOperation sop1 = new SyncOperation(account2,
149                 DEFAULT_USER,
150                 SyncOperation.REASON_PERIODIC,
151                 SyncStorageEngine.SOURCE_LOCAL, authority1, defaultBundle,
152                 0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
153                 false /* expedited */);
154         SyncOperation deleted = new SyncOperation(account2,
155                 DEFAULT_USER,
156                 SyncOperation.REASON_SYNC_AUTO,
157                 SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
158                 0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
159                 false /* expedited */);
160         engine.insertIntoPending(sop);
161         engine.insertIntoPending(sop1);
162         engine.insertIntoPending(deleted);
163 
164         SyncStorageEngine.PendingOperation popDeleted = engine.getPendingOperations().get(2);
165         // Free verifying, going to delete it anyway.
166         assertEquals(deleted.target.account, popDeleted.target.account);
167         assertEquals(deleted.target.provider, popDeleted.target.provider);
168         assertEquals(deleted.target.service, popDeleted.target.service);
169         assertEquals(deleted.target.userId, popDeleted.target.userId);
170         assertEquals(deleted.reason, popDeleted.reason);
171         assertEquals(deleted.syncSource, popDeleted.syncSource);
172         assertEquals(deleted.isExpedited(), popDeleted.expedited);
173         assert(android.content.PeriodicSync.syncExtrasEquals(deleted.extras, popDeleted.extras));
174         // Delete one to force write-all
175         engine.deleteFromPending(popDeleted);
176         assertEquals("Delete of pending op failed.", 2, engine.getPendingOperationCount());
177         // If there's dirty pending data (which there is because we deleted a pending op) this
178         // re-writes the entire file.
179         engine.writeAllState();
180 
181         engine.clearAndReadState();
182 
183         // Validate state read back out.
184         assertEquals("Delete of pending op failed.", 2, engine.getPendingOperationCount());
185 
186         List<SyncStorageEngine.PendingOperation> pops = engine.getPendingOperations();
187 
188         SyncStorageEngine.PendingOperation popRetrieved = pops.get(0);
189         assertEquals(sop.target.account, popRetrieved.target.account);
190         assertEquals(sop.target.provider, popRetrieved.target.provider);
191         assertEquals(sop.target.service, popRetrieved.target.service);
192         assertEquals(sop.target.userId, popRetrieved.target.userId);
193         assertEquals(sop.reason, popRetrieved.reason);
194         assertEquals(sop.syncSource, popRetrieved.syncSource);
195         assertEquals(sop.isExpedited(), popRetrieved.expedited);
196         assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
197 
198         popRetrieved = pops.get(1);
199         assertEquals(sop1.target.account, popRetrieved.target.account);
200         assertEquals(sop1.target.provider, popRetrieved.target.provider);
201         assertEquals(sop1.target.service, popRetrieved.target.service);
202         assertEquals(sop1.target.userId, popRetrieved.target.userId);
203         assertEquals(sop1.reason, popRetrieved.reason);
204         assertEquals(sop1.syncSource, popRetrieved.syncSource);
205         assertEquals(sop1.isExpedited(), popRetrieved.expedited);
206         assert(android.content.PeriodicSync.syncExtrasEquals(sop1.extras, popRetrieved.extras));
207     }
208 
209     /**
210      * Test that we can create, remove and retrieve periodic syncs. Backwards compatibility -
211      * periodic syncs with no flex time are no longer used.
212      */
213     @MediumTest
testPeriodics()214     public void testPeriodics() throws Exception {
215         final Account account1 = new Account("a@example.com", "example.type");
216         final Account account2 = new Account("b@example.com", "example.type.2");
217         final String authority = "testprovider";
218         final Bundle extras1 = new Bundle();
219         extras1.putString("a", "1");
220         final Bundle extras2 = new Bundle();
221         extras2.putString("a", "2");
222         final int period1 = 200;
223         final int period2 = 1000;
224 
225         PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1);
226         EndPoint end1 = new EndPoint(account1, authority, 0);
227 
228         PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1);
229         PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2);
230         PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2);
231 
232 
233 
234         removePeriodicSyncs(engine, account1, 0, authority);
235         removePeriodicSyncs(engine, account2, 0, authority);
236         removePeriodicSyncs(engine, account1, 1, authority);
237 
238         // this should add two distinct periodic syncs for account1 and one for account2
239         engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period1, 0, extras1);
240         engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period1, 0, extras2);
241         engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period2, 0, extras2);
242         engine.updateOrAddPeriodicSync(new EndPoint(account2, authority, 0), period2, 0, extras2);
243         // add a second user
244         engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 1), period1, 0, extras2);
245 
246         List<PeriodicSync> syncs = engine.getPeriodicSyncs(new EndPoint(account1, authority, 0));
247 
248         assertEquals(2, syncs.size());
249 
250         assertEquals(sync1, syncs.get(0));
251         assertEquals(sync3, syncs.get(1));
252 
253         engine.removePeriodicSync(new EndPoint(account1, authority, 0), extras1);
254 
255         syncs = engine.getPeriodicSyncs(new EndPoint(account1, authority, 0));
256         assertEquals(1, syncs.size());
257         assertEquals(sync3, syncs.get(0));
258 
259         syncs = engine.getPeriodicSyncs(new EndPoint(account2, authority, 0));
260         assertEquals(1, syncs.size());
261         assertEquals(sync4, syncs.get(0));
262 
263         syncs = engine.getPeriodicSyncs(new EndPoint(sync2.account, sync2.authority, 1));
264         assertEquals(1, syncs.size());
265         assertEquals(sync2, syncs.get(0));
266     }
267 
268     /**
269      * Test that we can create, remove and retrieve periodic syncs with a provided flex time.
270      */
271     @MediumTest
testPeriodicsV2()272     public void testPeriodicsV2() throws Exception {
273         final Account account1 = new Account("a@example.com", "example.type");
274         final Account account2 = new Account("b@example.com", "example.type.2");
275         final String authority = "testprovider";
276         final Bundle extras1 = new Bundle();
277         extras1.putString("a", "1");
278         final Bundle extras2 = new Bundle();
279         extras2.putString("a", "2");
280         final int period1 = 200;
281         final int period2 = 1000;
282         final int flex1 = 10;
283         final int flex2 = 100;
284         EndPoint point1 = new EndPoint(account1, authority, 0);
285         EndPoint point2 = new EndPoint(account2, authority, 0);
286         EndPoint point1User2 = new EndPoint(account1, authority, 1);
287 
288         PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1, flex1);
289         PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1, flex1);
290         PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2, flex2);
291         PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2, flex2);
292 
293         EndPoint target1 = new EndPoint(account1, authority, 0);
294         EndPoint target2 = new EndPoint(account2, authority, 0);
295         EndPoint target1UserB = new EndPoint(account1, authority, 1);
296 
297         MockContentResolver mockResolver = new MockContentResolver();
298 
299         SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
300                 new TestContext(mockResolver, getContext()));
301 
302         removePeriodicSyncs(engine, account1, 0, authority);
303         removePeriodicSyncs(engine, account2, 0, authority);
304         removePeriodicSyncs(engine, account1, 1, authority);
305 
306         // This should add two distinct periodic syncs for account1 and one for account2
307         engine.updateOrAddPeriodicSync(target1, period1, flex1, extras1);
308         engine.updateOrAddPeriodicSync(target1, period1, flex1, extras2);
309         // Edit existing sync and update the period and flex.
310         engine.updateOrAddPeriodicSync(target1, period2, flex2, extras2);
311         engine.updateOrAddPeriodicSync(target2, period2, flex2, extras2);
312         // add a target for a second user.
313         engine.updateOrAddPeriodicSync(target1UserB, period1, flex1, extras2);
314 
315         List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
316 
317         assertEquals(2, syncs.size());
318 
319         assertEquals(sync1, syncs.get(0));
320         assertEquals(sync3, syncs.get(1));
321 
322         engine.removePeriodicSync(target1, extras1);
323 
324         syncs = engine.getPeriodicSyncs(target1);
325         assertEquals(1, syncs.size());
326         assertEquals(sync3, syncs.get(0));
327 
328         syncs = engine.getPeriodicSyncs(target2);
329         assertEquals(1, syncs.size());
330         assertEquals(sync4, syncs.get(0));
331 
332         syncs = engine.getPeriodicSyncs(target1UserB);
333         assertEquals(1, syncs.size());
334         assertEquals(sync2, syncs.get(0));
335     }
336 
removePeriodicSyncs(SyncStorageEngine engine, Account account, int userId, String authority)337     private void removePeriodicSyncs(SyncStorageEngine engine, Account account, int userId, String authority) {
338         EndPoint target = new EndPoint(account, authority, userId);
339         engine.setIsSyncable(account, userId, authority, engine.getIsSyncable(account, userId, authority));
340         List<PeriodicSync> syncs = engine.getPeriodicSyncs(target);
341         for (PeriodicSync sync : syncs) {
342             engine.removePeriodicSync(target, sync.extras);
343         }
344     }
345 
346     @LargeTest
testAuthorityPersistence()347     public void testAuthorityPersistence() throws Exception {
348         final Account account1 = new Account("a@example.com", "example.type");
349         final Account account2 = new Account("b@example.com", "example.type.2");
350         final String authority1 = "testprovider1";
351         final String authority2 = "testprovider2";
352         final Bundle extras1 = new Bundle();
353         extras1.putString("a", "1");
354         final Bundle extras2 = new Bundle();
355         extras2.putString("a", "2");
356         extras2.putLong("b", 2);
357         extras2.putInt("c", 1);
358         extras2.putBoolean("d", true);
359         extras2.putDouble("e", 1.2);
360         extras2.putFloat("f", 4.5f);
361         extras2.putParcelable("g", account1);
362         final int period1 = 200;
363         final int period2 = 1000;
364         final int flex1 = 10;
365         final int flex2 = 100;
366 
367         EndPoint point1 = new EndPoint(account1, authority1, 0);
368         EndPoint point2 = new EndPoint(account1, authority2, 0);
369         EndPoint point3 = new EndPoint(account2, authority1, 0);
370 
371         PeriodicSync sync1 = new PeriodicSync(account1, authority1, extras1, period1, flex1);
372         PeriodicSync sync2 = new PeriodicSync(account1, authority1, extras2, period1, flex1);
373         PeriodicSync sync3 = new PeriodicSync(account1, authority2, extras1, period1, flex1);
374         PeriodicSync sync4 = new PeriodicSync(account1, authority2, extras2, period2, flex2);
375         PeriodicSync sync5 = new PeriodicSync(account2, authority1, extras1, period1, flex1);
376 
377         EndPoint target1 = new EndPoint(account1, authority1, 0);
378         EndPoint target2 = new EndPoint(account1, authority2, 0);
379         EndPoint target3 = new EndPoint(account2, authority1, 0);
380 
381         removePeriodicSyncs(engine, account1, 0, authority1);
382         removePeriodicSyncs(engine, account2, 0, authority1);
383         removePeriodicSyncs(engine, account1, 0, authority2);
384         removePeriodicSyncs(engine, account2, 0, authority2);
385 
386         engine.setMasterSyncAutomatically(false, 0);
387 
388         engine.setIsSyncable(account1, 0, authority1, 1);
389         engine.setSyncAutomatically(account1, 0, authority1, true);
390 
391         engine.setIsSyncable(account2, 0, authority1, 1);
392         engine.setSyncAutomatically(account2, 0, authority1, true);
393 
394         engine.setIsSyncable(account1, 0, authority2, 1);
395         engine.setSyncAutomatically(account1, 0, authority2, false);
396 
397         engine.setIsSyncable(account2, 0, authority2, 0);
398         engine.setSyncAutomatically(account2, 0, authority2, true);
399 
400         engine.updateOrAddPeriodicSync(target1, period1, flex1, extras1);
401         engine.updateOrAddPeriodicSync(target1, period1, flex1, extras2);
402         engine.updateOrAddPeriodicSync(target2, period1, flex1, extras1);
403         engine.updateOrAddPeriodicSync(target2, period2, flex2, extras2);
404         engine.updateOrAddPeriodicSync(target3, period1, flex1, extras1);
405 
406         engine.writeAllState();
407         engine.clearAndReadState();
408 
409         List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
410         assertEquals(2, syncs.size());
411         assertEquals(sync1, syncs.get(0));
412         assertEquals(sync2, syncs.get(1));
413 
414         syncs = engine.getPeriodicSyncs(target2);
415         assertEquals(2, syncs.size());
416         assertEquals(sync3, syncs.get(0));
417         assertEquals(sync4, syncs.get(1));
418 
419         syncs = engine.getPeriodicSyncs(target3);
420         assertEquals(1, syncs.size());
421         assertEquals(sync5, syncs.get(0));
422 
423         assertEquals(true, engine.getSyncAutomatically(account1, 0, authority1));
424         assertEquals(true, engine.getSyncAutomatically(account2, 0, authority1));
425         assertEquals(false, engine.getSyncAutomatically(account1, 0, authority2));
426         assertEquals(true, engine.getSyncAutomatically(account2, 0, authority2));
427 
428         assertEquals(1, engine.getIsSyncable(account1, 0, authority1));
429         assertEquals(1, engine.getIsSyncable(account2, 0, authority1));
430         assertEquals(1, engine.getIsSyncable(account1, 0, authority2));
431         assertEquals(0, engine.getIsSyncable(account2, 0, authority2));
432     }
433 
434     @SmallTest
testComponentParsing()435     public void testComponentParsing() throws Exception {
436 
437         byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
438                 + "<accounts version=\"2\" >\n"
439                 + "<authority id=\"0\" user=\"0\" package=\"" + syncService1.getPackageName() + "\""
440                 + " class=\"" + syncService1.getClassName() + "\" syncable=\"true\">"
441                 + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
442                 + "\n</authority>"
443                 + "</accounts>").getBytes();
444 
445         File syncDir = getSyncDir();
446         syncDir.mkdirs();
447         AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
448         FileOutputStream fos = accountInfoFile.startWrite();
449         fos.write(accountsFileData);
450         accountInfoFile.finishWrite(fos);
451 
452         engine.clearAndReadState();
453 
454         SyncStorageEngine.AuthorityInfo aInfo = engine.getAuthority(0);
455         assertNotNull(aInfo);
456 
457         // Test service component read
458         List<PeriodicSync> syncs = engine.getPeriodicSyncs(
459                 new SyncStorageEngine.EndPoint(syncService1, 0));
460         assertEquals(1, syncs.size());
461         assertEquals(true, engine.getIsTargetServiceActive(syncService1, 0));
462     }
463 
464     @SmallTest
testComponentSettings()465     public void testComponentSettings() throws Exception {
466         EndPoint target1 = new EndPoint(syncService1, 0);
467         engine.updateOrAddPeriodicSync(target1, dayPoll, dayFuzz, Bundle.EMPTY);
468 
469         engine.setIsTargetServiceActive(target1.service, 0, true);
470         boolean active = engine.getIsTargetServiceActive(target1.service, 0);
471         assert(active);
472 
473         engine.setIsTargetServiceActive(target1.service, 1, false);
474         active = engine.getIsTargetServiceActive(target1.service, 1);
475         assert(!active);
476     }
477 
478     @MediumTest
479     /**
480      * V2 introduces flex time as well as service components.
481      * @throws Exception
482      */
testAuthorityParsingV2()483     public void testAuthorityParsingV2() throws Exception {
484         final Account account = new Account("account1", "type1");
485         final String authority1 = "auth1";
486         final String authority2 = "auth2";
487         final String authority3 = "auth3";
488 
489         EndPoint target1 = new EndPoint(account, authority1, 0);
490         EndPoint target2 = new EndPoint(account, authority2, 0);
491         EndPoint target3 = new EndPoint(account, authority3, 0);
492         EndPoint target4 = new EndPoint(account, authority3, 1);
493 
494         PeriodicSync sync1 = new PeriodicSync(account, authority1, Bundle.EMPTY, dayPoll, dayFuzz);
495         PeriodicSync sync2 = new PeriodicSync(account, authority2, Bundle.EMPTY, dayPoll, dayFuzz);
496         PeriodicSync sync3 = new PeriodicSync(account, authority3, Bundle.EMPTY, dayPoll, dayFuzz);
497         PeriodicSync sync1s = new PeriodicSync(account, authority1, Bundle.EMPTY, thousandSecs,
498                 thousandSecsFuzz);
499         PeriodicSync sync2s = new PeriodicSync(account, authority2, Bundle.EMPTY, thousandSecs,
500                 thousandSecsFuzz);
501         PeriodicSync sync3s = new PeriodicSync(account, authority3, Bundle.EMPTY, thousandSecs,
502                 thousandSecsFuzz);
503 
504         byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
505                 + "<accounts version=\"2\" >\n"
506                 + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" >"
507                 + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
508                 + "\n</authority>"
509                 + "<authority id=\"1\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth2\" >"
510                 + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
511                 + "\n</authority>"
512                 // No user defaults to user 0 - all users.
513                 + "<authority id=\"2\"            account=\"account1\" type=\"type1\" authority=\"auth3\" >"
514                 + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
515                 + "\n</authority>"
516                 + "<authority id=\"3\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth3\" >"
517                 + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
518                 + "\n</authority>"
519                 + "</accounts>").getBytes();
520 
521         File syncDir = getSyncDir();
522         syncDir.mkdirs();
523         AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
524         FileOutputStream fos = accountInfoFile.startWrite();
525         fos.write(accountsFileData);
526         accountInfoFile.finishWrite(fos);
527 
528         engine.clearAndReadState();
529 
530         List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
531         assertEquals("Got incorrect # of syncs", 1, syncs.size());
532         assertEquals(sync1, syncs.get(0));
533 
534         syncs = engine.getPeriodicSyncs(target2);
535         assertEquals(1, syncs.size());
536         assertEquals(sync2, syncs.get(0));
537 
538         syncs = engine.getPeriodicSyncs(target3);
539         assertEquals(1, syncs.size());
540         assertEquals(sync3, syncs.get(0));
541 
542         syncs = engine.getPeriodicSyncs(target4);
543 
544         assertEquals(1, syncs.size());
545         assertEquals(sync3, syncs.get(0));
546 
547         // Test empty periodic data.
548         accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
549                 + "<accounts version=\"2\">\n"
550                 + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
551                 + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
552                 + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
553                 + "</accounts>\n").getBytes();
554 
555         accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
556         fos = accountInfoFile.startWrite();
557         fos.write(accountsFileData);
558         accountInfoFile.finishWrite(fos);
559 
560         engine.clearAndReadState();
561 
562         syncs = engine.getPeriodicSyncs(target1);
563         assertEquals(0, syncs.size());
564 
565         syncs = engine.getPeriodicSyncs(target2);
566         assertEquals(0, syncs.size());
567 
568         syncs = engine.getPeriodicSyncs(target3);
569         assertEquals(0, syncs.size());
570 
571         accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
572                 + "<accounts version=\"2\">\n"
573                 + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n"
574                 + "<periodicSync period=\"1000\" />\n"
575                 + "</authority>"
576                 + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\">\n"
577                 + "<periodicSync period=\"1000\" />\n"
578                 + "</authority>"
579                 + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\">\n"
580                 + "<periodicSync period=\"1000\" />\n"
581                 + "</authority>"
582                 + "</accounts>\n").getBytes();
583 
584         accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
585         fos = accountInfoFile.startWrite();
586         fos.write(accountsFileData);
587         accountInfoFile.finishWrite(fos);
588 
589         engine.clearAndReadState();
590 
591         syncs = engine.getPeriodicSyncs(target1);
592         assertEquals(1, syncs.size());
593         assertEquals(sync1s, syncs.get(0));
594 
595         syncs = engine.getPeriodicSyncs(target2);
596         assertEquals(1, syncs.size());
597         assertEquals(sync2s, syncs.get(0));
598 
599         syncs = engine.getPeriodicSyncs(target3);
600         assertEquals(1, syncs.size());
601         assertEquals(sync3s, syncs.get(0));
602     }
603 
604     @MediumTest
testAuthorityParsing()605     public void testAuthorityParsing() throws Exception {
606         final Account account = new Account("account1", "type1");
607         final String authority1 = "auth1";
608         final String authority2 = "auth2";
609         final String authority3 = "auth3";
610         final Bundle extras = new Bundle();
611 
612         EndPoint target1 = new EndPoint(account, authority1, 0);
613         EndPoint target2 = new EndPoint(account, authority2, 0);
614         EndPoint target3 = new EndPoint(account, authority3, 0);
615         EndPoint target4 = new EndPoint(account, authority3, 1);
616 
617         PeriodicSync sync1 = new PeriodicSync(account, authority1, extras, (long) (60 * 60 * 24));
618         PeriodicSync sync2 = new PeriodicSync(account, authority2, extras, (long) (60 * 60 * 24));
619         PeriodicSync sync3 = new PeriodicSync(account, authority3, extras, (long) (60 * 60 * 24));
620         PeriodicSync sync1s = new PeriodicSync(account, authority1, extras, 1000);
621         PeriodicSync sync2s = new PeriodicSync(account, authority2, extras, 1000);
622         PeriodicSync sync3s = new PeriodicSync(account, authority3, extras, 1000);
623 
624         MockContentResolver mockResolver = new MockContentResolver();
625 
626         final TestContext testContext = new TestContext(mockResolver, getContext());
627 
628         byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
629                 + "<accounts>\n"
630                 + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
631                 + "<authority id=\"1\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
632                 + "<authority id=\"2\"            account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
633                 + "<authority id=\"3\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
634                 + "</accounts>\n").getBytes();
635 
636         File syncDir = getSyncDir();
637         syncDir.mkdirs();
638         AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
639         FileOutputStream fos = accountInfoFile.startWrite();
640         fos.write(accountsFileData);
641         accountInfoFile.finishWrite(fos);
642 
643         SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
644 
645         List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
646         assertEquals(1, syncs.size());
647         assertEquals("expected sync1: " + sync1.toString() + " == sync 2" + syncs.get(0).toString(), sync1, syncs.get(0));
648 
649         syncs = engine.getPeriodicSyncs(target2);
650         assertEquals(1, syncs.size());
651         assertEquals(sync2, syncs.get(0));
652 
653         syncs = engine.getPeriodicSyncs(target3);
654         assertEquals(1, syncs.size());
655         assertEquals(sync3, syncs.get(0));
656         syncs = engine.getPeriodicSyncs(target4);
657 
658 
659         assertEquals(1, syncs.size());
660         assertEquals(sync3, syncs.get(0));
661 
662         accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
663                 + "<accounts version=\"2\">\n"
664                 + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
665                 + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
666                 + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
667                 + "</accounts>\n").getBytes();
668 
669         accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
670         fos = accountInfoFile.startWrite();
671         fos.write(accountsFileData);
672         accountInfoFile.finishWrite(fos);
673 
674         engine.clearAndReadState();
675 
676         syncs = engine.getPeriodicSyncs(target1);
677         assertEquals(0, syncs.size());
678 
679         syncs = engine.getPeriodicSyncs(target2);
680         assertEquals(0, syncs.size());
681 
682         syncs = engine.getPeriodicSyncs(target3);
683         assertEquals(0, syncs.size());
684 
685         accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
686                 + "<accounts version=\"2\">\n"
687                 + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n"
688                 + "<periodicSync period=\"1000\" />\n"
689                 + "</authority>"
690                 + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\">\n"
691                 + "<periodicSync period=\"1000\" />\n"
692                 + "</authority>"
693                 + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\">\n"
694                 + "<periodicSync period=\"1000\" />\n"
695                 + "</authority>"
696                 + "</accounts>\n").getBytes();
697 
698         accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
699         fos = accountInfoFile.startWrite();
700         fos.write(accountsFileData);
701         accountInfoFile.finishWrite(fos);
702 
703         engine.clearAndReadState();
704 
705         syncs = engine.getPeriodicSyncs(target1);
706         assertEquals(1, syncs.size());
707         assertEquals(sync1s, syncs.get(0));
708 
709         syncs = engine.getPeriodicSyncs(target2);
710         assertEquals(1, syncs.size());
711         assertEquals(sync2s, syncs.get(0));
712 
713         syncs = engine.getPeriodicSyncs(target3);
714         assertEquals(1, syncs.size());
715         assertEquals(sync3s, syncs.get(0));
716     }
717 
718     @MediumTest
testListenForTicklesParsing()719     public void testListenForTicklesParsing() throws Exception {
720         byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
721                 + "<accounts>\n"
722                 + "<listenForTickles user=\"0\" enabled=\"false\" />"
723                 + "<listenForTickles user=\"1\" enabled=\"true\" />"
724                 + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
725                 + "<authority id=\"1\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
726                 + "</accounts>\n").getBytes();
727 
728         MockContentResolver mockResolver = new MockContentResolver();
729         final TestContext testContext = new TestContext(mockResolver, getContext());
730 
731         File syncDir = getSyncDir();
732         syncDir.mkdirs();
733         AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
734         FileOutputStream fos = accountInfoFile.startWrite();
735         fos.write(accountsFileData);
736         accountInfoFile.finishWrite(fos);
737 
738         SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
739 
740         assertEquals(false, engine.getMasterSyncAutomatically(0));
741         assertEquals(true, engine.getMasterSyncAutomatically(1));
742         assertEquals(true, engine.getMasterSyncAutomatically(2));
743 
744     }
745 
746     @MediumTest
testAuthorityRenaming()747     public void testAuthorityRenaming() throws Exception {
748         final Account account1 = new Account("acc1", "type1");
749         final Account account2 = new Account("acc2", "type2");
750         final String authorityContacts = "contacts";
751         final String authorityCalendar = "calendar";
752         final String authorityOther = "other";
753         final String authorityContactsNew = "com.android.contacts";
754         final String authorityCalendarNew = "com.android.calendar";
755 
756         MockContentResolver mockResolver = new MockContentResolver();
757 
758         final TestContext testContext = new TestContext(mockResolver, getContext());
759 
760         byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
761                 + "<accounts>\n"
762                 + "<authority id=\"0\" account=\"acc1\" type=\"type1\" authority=\"contacts\" />\n"
763                 + "<authority id=\"1\" account=\"acc1\" type=\"type1\" authority=\"calendar\" />\n"
764                 + "<authority id=\"2\" account=\"acc1\" type=\"type1\" authority=\"other\" />\n"
765                 + "<authority id=\"3\" account=\"acc2\" type=\"type2\" authority=\"contacts\" />\n"
766                 + "<authority id=\"4\" account=\"acc2\" type=\"type2\" authority=\"calendar\" />\n"
767                 + "<authority id=\"5\" account=\"acc2\" type=\"type2\" authority=\"other\" />\n"
768                 + "<authority id=\"6\" account=\"acc2\" type=\"type2\" enabled=\"false\""
769                 + " authority=\"com.android.calendar\" />\n"
770                 + "<authority id=\"7\" account=\"acc2\" type=\"type2\" enabled=\"false\""
771                 + " authority=\"com.android.contacts\" />\n"
772                 + "</accounts>\n").getBytes();
773 
774         File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
775         syncDir.mkdirs();
776         AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
777         FileOutputStream fos = accountInfoFile.startWrite();
778         fos.write(accountsFileData);
779         accountInfoFile.finishWrite(fos);
780 
781         SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
782 
783         assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityContacts));
784         assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityCalendar));
785         assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityOther));
786         assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityContactsNew));
787         assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityCalendarNew));
788 
789         assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContacts));
790         assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendar));
791         assertEquals(true, engine.getSyncAutomatically(account2, 0, authorityOther));
792         assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContactsNew));
793         assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendarNew));
794     }
795 
796     @SmallTest
testSyncableMigration()797     public void testSyncableMigration() throws Exception {
798         final Account account = new Account("acc", "type");
799 
800         MockContentResolver mockResolver = new MockContentResolver();
801 
802         final TestContext testContext = new TestContext(mockResolver, getContext());
803 
804         byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
805                 + "<accounts>\n"
806                 + "<authority id=\"0\" account=\"acc\" authority=\"other1\" />\n"
807                 + "<authority id=\"1\" account=\"acc\" type=\"type\" authority=\"other2\" />\n"
808                 + "<authority id=\"2\" account=\"acc\" type=\"type\" syncable=\"false\""
809                 + " authority=\"other3\" />\n"
810                 + "<authority id=\"3\" account=\"acc\" type=\"type\" syncable=\"true\""
811                 + " authority=\"other4\" />\n"
812                 + "</accounts>\n").getBytes();
813 
814         File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
815         syncDir.mkdirs();
816         AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
817         FileOutputStream fos = accountInfoFile.startWrite();
818         fos.write(accountsFileData);
819         accountInfoFile.finishWrite(fos);
820 
821         SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
822 
823         assertEquals(-1, engine.getIsSyncable(account, 0, "other1"));
824         assertEquals(1, engine.getIsSyncable(account, 0, "other2"));
825         assertEquals(0, engine.getIsSyncable(account, 0, "other3"));
826         assertEquals(1, engine.getIsSyncable(account, 0, "other4"));
827     }
828 
829     /**
830      * Verify that the API cannot cause a run-time reboot by passing in the empty string as an
831      * authority. The problem here is that
832      * {@link SyncStorageEngine#getOrCreateAuthorityLocked(account, provider)} would register
833      * an empty authority which causes a RTE in {@link SyncManager#scheduleReadyPeriodicSyncs()}.
834      * This is not strictly a SSE test, but it does depend on the SSE data structures.
835      */
836     @SmallTest
testExpectedIllegalArguments()837     public void testExpectedIllegalArguments() throws Exception {
838         try {
839             ContentResolver.setSyncAutomatically(account1, "", true);
840             fail("empty provider string should throw IllegalArgumentException");
841         } catch (IllegalArgumentException expected) {}
842 
843         try {
844             ContentResolver.addPeriodicSync(account1, "", Bundle.EMPTY, 84000L);
845             fail("empty provider string should throw IllegalArgumentException");
846         } catch (IllegalArgumentException expected) {}
847 
848         try {
849             ContentResolver.removePeriodicSync(account1, "", Bundle.EMPTY);
850             fail("empty provider string should throw IllegalArgumentException");
851         } catch (IllegalArgumentException expected) {}
852 
853         try {
854             ContentResolver.cancelSync(account1, "");
855             fail("empty provider string should throw IllegalArgumentException");
856         } catch (IllegalArgumentException expected) {}
857 
858         try {
859             ContentResolver.setIsSyncable(account1, "", 0);
860             fail("empty provider string should throw IllegalArgumentException");
861         } catch (IllegalArgumentException expected) {}
862 
863         try {
864             ContentResolver.cancelSync(account1, "");
865             fail("empty provider string should throw IllegalArgumentException");
866         } catch (IllegalArgumentException expected) {}
867 
868         try {
869             ContentResolver.requestSync(account1, "", Bundle.EMPTY);
870             fail("empty provider string should throw IllegalArgumentException");
871         } catch (IllegalArgumentException expected) {}
872 
873         try {
874             ContentResolver.getSyncStatus(account1, "");
875             fail("empty provider string should throw IllegalArgumentException");
876         } catch (IllegalArgumentException expected) {}
877 
878         // Make sure we aren't blocking null account/provider for those functions that use it
879         // to specify ALL accounts/providers.
880         ContentResolver.requestSync(null, null, Bundle.EMPTY);
881         ContentResolver.cancelSync(null, null);
882     }
883 }
884 
885 class TestContext extends ContextWrapper {
886 
887     ContentResolver mResolver;
888 
889     private final Context mRealContext;
890 
TestContext(ContentResolver resolver, Context realContext)891     public TestContext(ContentResolver resolver, Context realContext) {
892         super(new RenamingDelegatingContext(new MockContext(), realContext, "test."));
893         mRealContext = realContext;
894         mResolver = resolver;
895     }
896 
897     @Override
getResources()898     public Resources getResources() {
899         return mRealContext.getResources();
900     }
901 
902     @Override
getFilesDir()903     public File getFilesDir() {
904         return mRealContext.getFilesDir();
905     }
906 
907     @Override
enforceCallingOrSelfPermission(String permission, String message)908     public void enforceCallingOrSelfPermission(String permission, String message) {
909     }
910 
911     @Override
sendBroadcast(Intent intent)912     public void sendBroadcast(Intent intent) {
913     }
914 
915     @Override
getContentResolver()916     public ContentResolver getContentResolver() {
917         return mResolver;
918     }
919 }
920