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