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