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 17package android.content; 18 19import com.android.internal.os.AtomicFile; 20 21import android.accounts.Account; 22import android.os.Bundle; 23import android.test.AndroidTestCase; 24import android.test.RenamingDelegatingContext; 25import android.test.mock.MockContentResolver; 26import android.test.mock.MockContext; 27import android.test.suitebuilder.annotation.LargeTest; 28import android.test.suitebuilder.annotation.MediumTest; 29import android.test.suitebuilder.annotation.SmallTest; 30 31import java.io.File; 32import java.io.FileOutputStream; 33import java.util.List; 34 35public class SyncStorageEngineTest extends AndroidTestCase { 36 37 private File getSyncDir() { 38 return new File(new File(getContext().getFilesDir(), "system"), "sync"); 39 } 40 41 /** 42 * Test that we handle the case of a history row being old enough to purge before the 43 * correcponding sync is finished. This can happen if the clock changes while we are syncing. 44 * 45 */ 46 // TODO: this test causes AidlTest to fail. Omit for now 47 // @SmallTest 48 public void testPurgeActiveSync() throws Exception { 49 final Account account = new Account("a@example.com", "example.type"); 50 final String authority = "testprovider"; 51 52 MockContentResolver mockResolver = new MockContentResolver(); 53 54 SyncStorageEngine engine = SyncStorageEngine.newTestInstance( 55 new TestContext(mockResolver, getContext())); 56 57 long time0 = 1000; 58 long historyId = engine.insertStartSyncEvent( 59 account, 0, authority, time0, SyncStorageEngine.SOURCE_LOCAL, 60 false /* initialization */); 61 long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2; 62 engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0); 63 } 64 65 /** 66 * Test that we can create, remove and retrieve periodic syncs 67 */ 68 @MediumTest 69 public void testPeriodics() throws Exception { 70 final Account account1 = new Account("a@example.com", "example.type"); 71 final Account account2 = new Account("b@example.com", "example.type.2"); 72 final String authority = "testprovider"; 73 final Bundle extras1 = new Bundle(); 74 extras1.putString("a", "1"); 75 final Bundle extras2 = new Bundle(); 76 extras2.putString("a", "2"); 77 final int period1 = 200; 78 final int period2 = 1000; 79 80 PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1); 81 PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1); 82 PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2); 83 PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2); 84 85 MockContentResolver mockResolver = new MockContentResolver(); 86 87 SyncStorageEngine engine = SyncStorageEngine.newTestInstance( 88 new TestContext(mockResolver, getContext())); 89 90 removePeriodicSyncs(engine, account1, 0, authority); 91 removePeriodicSyncs(engine, account2, 0, authority); 92 removePeriodicSyncs(engine, account1, 1, authority); 93 94 // this should add two distinct periodic syncs for account1 and one for account2 95 engine.addPeriodicSync(sync1.account, 0, sync1.authority, sync1.extras, sync1.period); 96 engine.addPeriodicSync(sync2.account, 0, sync2.authority, sync2.extras, sync2.period); 97 engine.addPeriodicSync(sync3.account, 0, sync3.authority, sync3.extras, sync3.period); 98 engine.addPeriodicSync(sync4.account, 0, sync4.authority, sync4.extras, sync4.period); 99 // add a second user 100 engine.addPeriodicSync(sync2.account, 1, sync2.authority, sync2.extras, sync2.period); 101 102 List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority); 103 104 assertEquals(2, syncs.size()); 105 106 assertEquals(sync1, syncs.get(0)); 107 assertEquals(sync3, syncs.get(1)); 108 109 engine.removePeriodicSync(sync1.account, 0, sync1.authority, sync1.extras); 110 111 syncs = engine.getPeriodicSyncs(account1, 0, authority); 112 assertEquals(1, syncs.size()); 113 assertEquals(sync3, syncs.get(0)); 114 115 syncs = engine.getPeriodicSyncs(account2, 0, authority); 116 assertEquals(1, syncs.size()); 117 assertEquals(sync4, syncs.get(0)); 118 119 syncs = engine.getPeriodicSyncs(sync2.account, 1, sync2.authority); 120 assertEquals(1, syncs.size()); 121 assertEquals(sync2, syncs.get(0)); 122 } 123 124 private void removePeriodicSyncs(SyncStorageEngine engine, Account account, int userId, 125 String authority) { 126 engine.setIsSyncable(account, userId, authority, 127 engine.getIsSyncable(account, 0, authority)); 128 List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, userId, authority); 129 for (PeriodicSync sync : syncs) { 130 engine.removePeriodicSync(sync.account, userId, sync.authority, sync.extras); 131 } 132 } 133 134 @LargeTest 135 public void testAuthorityPersistence() throws Exception { 136 final Account account1 = new Account("a@example.com", "example.type"); 137 final Account account2 = new Account("b@example.com", "example.type.2"); 138 final String authority1 = "testprovider1"; 139 final String authority2 = "testprovider2"; 140 final Bundle extras1 = new Bundle(); 141 extras1.putString("a", "1"); 142 final Bundle extras2 = new Bundle(); 143 extras2.putString("a", "2"); 144 extras2.putLong("b", 2); 145 extras2.putInt("c", 1); 146 extras2.putBoolean("d", true); 147 extras2.putDouble("e", 1.2); 148 extras2.putFloat("f", 4.5f); 149 extras2.putParcelable("g", account1); 150 final int period1 = 200; 151 final int period2 = 1000; 152 153 PeriodicSync sync1 = new PeriodicSync(account1, authority1, extras1, period1); 154 PeriodicSync sync2 = new PeriodicSync(account1, authority1, extras2, period1); 155 PeriodicSync sync3 = new PeriodicSync(account1, authority2, extras1, period1); 156 PeriodicSync sync4 = new PeriodicSync(account1, authority2, extras2, period2); 157 PeriodicSync sync5 = new PeriodicSync(account2, authority1, extras1, period1); 158 159 MockContentResolver mockResolver = new MockContentResolver(); 160 161 SyncStorageEngine engine = SyncStorageEngine.newTestInstance( 162 new TestContext(mockResolver, getContext())); 163 164 removePeriodicSyncs(engine, account1, 0, authority1); 165 removePeriodicSyncs(engine, account2, 0, authority1); 166 removePeriodicSyncs(engine, account1, 0, authority2); 167 removePeriodicSyncs(engine, account2, 0, authority2); 168 169 engine.setMasterSyncAutomatically(false, 0); 170 171 engine.setIsSyncable(account1, 0, authority1, 1); 172 engine.setSyncAutomatically(account1, 0, authority1, true); 173 174 engine.setIsSyncable(account2, 0, authority1, 1); 175 engine.setSyncAutomatically(account2, 0, authority1, true); 176 177 engine.setIsSyncable(account1, 0, authority2, 1); 178 engine.setSyncAutomatically(account1, 0, authority2, false); 179 180 engine.setIsSyncable(account2, 0, authority2, 0); 181 engine.setSyncAutomatically(account2, 0, authority2, true); 182 183 engine.addPeriodicSync(sync1.account, 0, sync1.authority, sync1.extras, sync1.period); 184 engine.addPeriodicSync(sync2.account, 0, sync2.authority, sync2.extras, sync2.period); 185 engine.addPeriodicSync(sync3.account, 0, sync3.authority, sync3.extras, sync3.period); 186 engine.addPeriodicSync(sync4.account, 0, sync4.authority, sync4.extras, sync4.period); 187 engine.addPeriodicSync(sync5.account, 0, sync5.authority, sync5.extras, sync5.period); 188 189 engine.writeAllState(); 190 engine.clearAndReadState(); 191 192 List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority1); 193 assertEquals(2, syncs.size()); 194 assertEquals(sync1, syncs.get(0)); 195 assertEquals(sync2, syncs.get(1)); 196 197 syncs = engine.getPeriodicSyncs(account1, 0, authority2); 198 assertEquals(2, syncs.size()); 199 assertEquals(sync3, syncs.get(0)); 200 assertEquals(sync4, syncs.get(1)); 201 202 syncs = engine.getPeriodicSyncs(account2, 0, authority1); 203 assertEquals(1, syncs.size()); 204 assertEquals(sync5, syncs.get(0)); 205 206 assertEquals(true, engine.getSyncAutomatically(account1, 0, authority1)); 207 assertEquals(true, engine.getSyncAutomatically(account2, 0, authority1)); 208 assertEquals(false, engine.getSyncAutomatically(account1, 0, authority2)); 209 assertEquals(true, engine.getSyncAutomatically(account2, 0, authority2)); 210 211 assertEquals(1, engine.getIsSyncable(account1, 0, authority1)); 212 assertEquals(1, engine.getIsSyncable(account2, 0, authority1)); 213 assertEquals(1, engine.getIsSyncable(account1, 0, authority2)); 214 assertEquals(0, engine.getIsSyncable(account2, 0, authority2)); 215 } 216 217 @MediumTest 218 public void testAuthorityParsing() throws Exception { 219 final Account account = new Account("account1", "type1"); 220 final String authority1 = "auth1"; 221 final String authority2 = "auth2"; 222 final String authority3 = "auth3"; 223 final Bundle extras = new Bundle(); 224 PeriodicSync sync1 = new PeriodicSync(account, authority1, extras, (long) (60 * 60 * 24)); 225 PeriodicSync sync2 = new PeriodicSync(account, authority2, extras, (long) (60 * 60 * 24)); 226 PeriodicSync sync3 = new PeriodicSync(account, authority3, extras, (long) (60 * 60 * 24)); 227 PeriodicSync sync1s = new PeriodicSync(account, authority1, extras, 1000); 228 PeriodicSync sync2s = new PeriodicSync(account, authority2, extras, 1000); 229 PeriodicSync sync3s = new PeriodicSync(account, authority3, extras, 1000); 230 231 MockContentResolver mockResolver = new MockContentResolver(); 232 233 final TestContext testContext = new TestContext(mockResolver, getContext()); 234 235 byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 236 + "<accounts>\n" 237 + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" 238 + "<authority id=\"1\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n" 239 + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" 240 + "<authority id=\"3\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" 241 + "</accounts>\n").getBytes(); 242 243 File syncDir = getSyncDir(); 244 syncDir.mkdirs(); 245 AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); 246 FileOutputStream fos = accountInfoFile.startWrite(); 247 fos.write(accountsFileData); 248 accountInfoFile.finishWrite(fos); 249 250 SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); 251 252 List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, 0, authority1); 253 assertEquals(1, syncs.size()); 254 assertEquals(sync1, syncs.get(0)); 255 256 syncs = engine.getPeriodicSyncs(account, 0, authority2); 257 assertEquals(1, syncs.size()); 258 assertEquals(sync2, syncs.get(0)); 259 260 syncs = engine.getPeriodicSyncs(account, 0, authority3); 261 assertEquals(1, syncs.size()); 262 assertEquals(sync3, syncs.get(0)); 263 264 syncs = engine.getPeriodicSyncs(account, 1, authority3); 265 assertEquals(1, syncs.size()); 266 assertEquals(sync3, syncs.get(0)); 267 268 accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 269 + "<accounts version=\"2\">\n" 270 + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" 271 + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n" 272 + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" 273 + "</accounts>\n").getBytes(); 274 275 accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); 276 fos = accountInfoFile.startWrite(); 277 fos.write(accountsFileData); 278 accountInfoFile.finishWrite(fos); 279 280 engine.clearAndReadState(); 281 282 syncs = engine.getPeriodicSyncs(account, 0, authority1); 283 assertEquals(0, syncs.size()); 284 285 syncs = engine.getPeriodicSyncs(account, 0, authority2); 286 assertEquals(0, syncs.size()); 287 288 syncs = engine.getPeriodicSyncs(account, 0, authority3); 289 assertEquals(0, syncs.size()); 290 291 accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 292 + "<accounts version=\"2\">\n" 293 + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n" 294 + "<periodicSync period=\"1000\" />\n" 295 + "</authority>" 296 + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\">\n" 297 + "<periodicSync period=\"1000\" />\n" 298 + "</authority>" 299 + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\">\n" 300 + "<periodicSync period=\"1000\" />\n" 301 + "</authority>" 302 + "</accounts>\n").getBytes(); 303 304 accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); 305 fos = accountInfoFile.startWrite(); 306 fos.write(accountsFileData); 307 accountInfoFile.finishWrite(fos); 308 309 engine.clearAndReadState(); 310 311 syncs = engine.getPeriodicSyncs(account, 0, authority1); 312 assertEquals(1, syncs.size()); 313 assertEquals(sync1s, syncs.get(0)); 314 315 syncs = engine.getPeriodicSyncs(account, 0, authority2); 316 assertEquals(1, syncs.size()); 317 assertEquals(sync2s, syncs.get(0)); 318 319 syncs = engine.getPeriodicSyncs(account, 0, authority3); 320 assertEquals(1, syncs.size()); 321 assertEquals(sync3s, syncs.get(0)); 322 } 323 324 @MediumTest 325 public void testListenForTicklesParsing() throws Exception { 326 byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 327 + "<accounts>\n" 328 + "<listenForTickles user=\"0\" enabled=\"false\" />" 329 + "<listenForTickles user=\"1\" enabled=\"true\" />" 330 + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" 331 + "<authority id=\"1\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" 332 + "</accounts>\n").getBytes(); 333 334 MockContentResolver mockResolver = new MockContentResolver(); 335 final TestContext testContext = new TestContext(mockResolver, getContext()); 336 337 File syncDir = getSyncDir(); 338 syncDir.mkdirs(); 339 AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); 340 FileOutputStream fos = accountInfoFile.startWrite(); 341 fos.write(accountsFileData); 342 accountInfoFile.finishWrite(fos); 343 344 SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); 345 346 assertEquals(false, engine.getMasterSyncAutomatically(0)); 347 assertEquals(true, engine.getMasterSyncAutomatically(1)); 348 assertEquals(true, engine.getMasterSyncAutomatically(2)); 349 350 } 351 352 @MediumTest 353 public void testAuthorityRenaming() throws Exception { 354 final Account account1 = new Account("acc1", "type1"); 355 final Account account2 = new Account("acc2", "type2"); 356 final String authorityContacts = "contacts"; 357 final String authorityCalendar = "calendar"; 358 final String authorityOther = "other"; 359 final String authorityContactsNew = "com.android.contacts"; 360 final String authorityCalendarNew = "com.android.calendar"; 361 362 MockContentResolver mockResolver = new MockContentResolver(); 363 364 final TestContext testContext = new TestContext(mockResolver, getContext()); 365 366 byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 367 + "<accounts>\n" 368 + "<authority id=\"0\" account=\"acc1\" type=\"type1\" authority=\"contacts\" />\n" 369 + "<authority id=\"1\" account=\"acc1\" type=\"type1\" authority=\"calendar\" />\n" 370 + "<authority id=\"2\" account=\"acc1\" type=\"type1\" authority=\"other\" />\n" 371 + "<authority id=\"3\" account=\"acc2\" type=\"type2\" authority=\"contacts\" />\n" 372 + "<authority id=\"4\" account=\"acc2\" type=\"type2\" authority=\"calendar\" />\n" 373 + "<authority id=\"5\" account=\"acc2\" type=\"type2\" authority=\"other\" />\n" 374 + "<authority id=\"6\" account=\"acc2\" type=\"type2\" enabled=\"false\"" 375 + " authority=\"com.android.calendar\" />\n" 376 + "<authority id=\"7\" account=\"acc2\" type=\"type2\" enabled=\"false\"" 377 + " authority=\"com.android.contacts\" />\n" 378 + "</accounts>\n").getBytes(); 379 380 File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync"); 381 syncDir.mkdirs(); 382 AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); 383 FileOutputStream fos = accountInfoFile.startWrite(); 384 fos.write(accountsFileData); 385 accountInfoFile.finishWrite(fos); 386 387 SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); 388 389 assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityContacts)); 390 assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityCalendar)); 391 assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityOther)); 392 assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityContactsNew)); 393 assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityCalendarNew)); 394 395 assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContacts)); 396 assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendar)); 397 assertEquals(true, engine.getSyncAutomatically(account2, 0, authorityOther)); 398 assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContactsNew)); 399 assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendarNew)); 400 } 401 402 @SmallTest 403 public void testSyncableMigration() throws Exception { 404 final Account account = new Account("acc", "type"); 405 406 MockContentResolver mockResolver = new MockContentResolver(); 407 408 final TestContext testContext = new TestContext(mockResolver, getContext()); 409 410 byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 411 + "<accounts>\n" 412 + "<authority id=\"0\" account=\"acc\" authority=\"other1\" />\n" 413 + "<authority id=\"1\" account=\"acc\" type=\"type\" authority=\"other2\" />\n" 414 + "<authority id=\"2\" account=\"acc\" type=\"type\" syncable=\"false\"" 415 + " authority=\"other3\" />\n" 416 + "<authority id=\"3\" account=\"acc\" type=\"type\" syncable=\"true\"" 417 + " authority=\"other4\" />\n" 418 + "</accounts>\n").getBytes(); 419 420 File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync"); 421 syncDir.mkdirs(); 422 AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); 423 FileOutputStream fos = accountInfoFile.startWrite(); 424 fos.write(accountsFileData); 425 accountInfoFile.finishWrite(fos); 426 427 SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); 428 429 assertEquals(-1, engine.getIsSyncable(account, 0, "other1")); 430 assertEquals(1, engine.getIsSyncable(account, 0, "other2")); 431 assertEquals(0, engine.getIsSyncable(account, 0, "other3")); 432 assertEquals(1, engine.getIsSyncable(account, 0, "other4")); 433 } 434} 435 436class TestContext extends ContextWrapper { 437 438 ContentResolver mResolver; 439 440 private final Context mRealContext; 441 442 public TestContext(ContentResolver resolver, Context realContext) { 443 super(new RenamingDelegatingContext(new MockContext(), realContext, "test.")); 444 mRealContext = realContext; 445 mResolver = resolver; 446 } 447 448 @Override 449 public File getFilesDir() { 450 return mRealContext.getFilesDir(); 451 } 452 453 @Override 454 public void enforceCallingOrSelfPermission(String permission, String message) { 455 } 456 457 @Override 458 public void sendBroadcast(Intent intent) { 459 } 460 461 @Override 462 public ContentResolver getContentResolver() { 463 return mResolver; 464 } 465} 466