- make the SyncManager add periodic syncs when it upgrades from a
  version of the accounts.xml file that pre-dated periodic syncs,
  e.g. eclair or early froyo. http://b/2515823
- make the AccountManagerService dump() use a getAccounts call that
  doesn't check the GET_ACCOUNTS permission to make it useful
  in "adb bugreport"
- add some logging to SyncManager to help track down a problem

Change-Id: Icb646909074e2d327d71f6bb39cf06b6fac29e77
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 6d0a266..16b603c 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -1699,7 +1699,7 @@
                 }
             }
         } else {
-            Account[] accounts = getAccounts(null /* type */);
+            Account[] accounts = getAccountsByType(null /* type */);
             fout.println("Accounts: " + accounts.length);
             for (Account account : accounts) {
                 fout.println("  " + account);
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 85769a6..840a714 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -416,6 +416,9 @@
     }
 
     private void initializeSyncAdapter(Account account, String authority) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "initializeSyncAdapter: " + account + ", authority " + authority);
+        }
         SyncAdapterType syncAdapterType = SyncAdapterType.newKey(authority, account.type);
         RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
                 mSyncAdapters.getServiceInfo(syncAdapterType);
@@ -427,9 +430,11 @@
         Intent intent = new Intent();
         intent.setAction("android.content.SyncAdapter");
         intent.setComponent(syncAdapterInfo.componentName);
-        mContext.bindService(intent, new InitializerServiceConnection(account, authority, mContext,
+        if (!mContext.bindService(intent, new InitializerServiceConnection(account, authority, mContext,
                 mMainHandler),
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND);
+                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND)) {
+            Log.w(TAG, "initializeSyncAdapter: failed to bind to " + intent);
+        }
     }
 
     private static class InitializerServiceConnection implements ServiceConnection {
@@ -452,6 +457,9 @@
             try {
                 if (!mInitialized) {
                     mInitialized = true;
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "calling initialize: " + mAccount + ", authority " + mAuthority);
+                    }
                     ISyncAdapter.Stub.asInterface(service).initialize(mAccount, mAuthority);
                 }
             } catch (RemoteException e) {
@@ -1307,7 +1315,6 @@
         // it if sync is still failing
         private boolean mErrorNotificationInstalled = false;
         private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
-
         public void onBootCompleted() {
             mBootCompleted = true;
             if (mReadyToRunLatch != null) {
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 240da72..0ec24530 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -121,6 +121,9 @@
 
     private static final boolean SYNC_ENABLED_DEFAULT = false;
 
+    // the version of the accounts xml file format
+    private static final int ACCOUNTS_VERSION = 1;
+
     public static class PendingOperation {
         final Account account;
         final int syncSource;
@@ -1322,6 +1325,7 @@
      * Read all account information back in to the initial engine state.
      */
     private void readAccountInfoLocked() {
+        boolean writeNeeded = false;
         FileInputStream fis = null;
         try {
             fis = mAccountInfoFile.openRead();
@@ -1336,6 +1340,16 @@
             if ("accounts".equals(tagName)) {
                 String listen = parser.getAttributeValue(
                         null, "listen-for-tickles");
+                String versionString = parser.getAttributeValue(null, "version");
+                int version;
+                try {
+                    version = (versionString == null) ? 0 : Integer.parseInt(versionString);
+                } catch (NumberFormatException e) {
+                    version = 0;
+                }
+                if (version < ACCOUNTS_VERSION) {
+                    writeNeeded = true;
+                }
                 mMasterSyncAutomatically = listen == null
                             || Boolean.parseBoolean(listen);
                 eventType = parser.next();
@@ -1346,7 +1360,7 @@
                         tagName = parser.getName();
                         if (parser.getDepth() == 2) {
                             if ("authority".equals(tagName)) {
-                                authority = parseAuthority(parser);
+                                authority = parseAuthority(parser, version);
                                 periodicSync = null;
                             }
                         } else if (parser.getDepth() == 3) {
@@ -1364,9 +1378,11 @@
             }
         } catch (XmlPullParserException e) {
             Log.w(TAG, "Error reading accounts", e);
+            return;
         } catch (java.io.IOException e) {
             if (fis == null) Log.i(TAG, "No initial accounts");
             else Log.w(TAG, "Error reading accounts", e);
+            return;
         } finally {
             if (fis != null) {
                 try {
@@ -1375,9 +1391,13 @@
                 }
             }
         }
+
+        if (writeNeeded) {
+            writeAccountInfoLocked();
+        }
     }
 
-    private AuthorityInfo parseAuthority(XmlPullParser parser) {
+    private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
         AuthorityInfo authority = null;
         int id = -1;
         try {
@@ -1406,8 +1426,14 @@
                 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
                 authority = getOrCreateAuthorityLocked(
                         new Account(accountName, accountType), authorityName, id, false);
-                // clear this since we will read these later on
-                authority.periodicSyncs.clear();
+                // If the version is 0 then we are upgrading from a file format that did not
+                // know about periodic syncs. In that case don't clear the list since we
+                // want the default, which is a daily periodioc sync.
+                // Otherwise clear out this default list since we will populate it later with
+                // the periodic sync descriptions that are read from the configuration file.
+                if (version > 0) {
+                    authority.periodicSyncs.clear();
+                }
             }
             if (authority != null) {
                 authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
@@ -1443,6 +1469,7 @@
         }
         final Pair<Bundle, Long> periodicSync = Pair.create(extras, period);
         authority.periodicSyncs.add(periodicSync);
+
         return periodicSync;
     }
 
@@ -1491,6 +1518,7 @@
             out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             out.startTag(null, "accounts");
+            out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
             if (!mMasterSyncAutomatically) {
                 out.attribute(null, "listen-for-tickles", "false");
             }
@@ -1505,7 +1533,7 @@
                 out.attribute(null, "authority", authority.authority);
                 if (!authority.enabled) {
                     out.attribute(null, "enabled", "false");
-                }
+                }   
                 if (authority.syncable < 0) {
                     out.attribute(null, "syncable", "unknown");
                 } else if (authority.syncable == 0) {
diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
index 1505a7c..7028d1a 100644
--- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
+++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
@@ -16,6 +16,8 @@
 
 package android.content;
 
+import com.android.internal.os.AtomicFile;
+
 import android.test.AndroidTestCase;
 import android.test.RenamingDelegatingContext;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -26,6 +28,7 @@
 
 import java.util.List;
 import java.io.File;
+import java.io.FileOutputStream;
 
 public class SyncStorageEngineTest extends AndroidTestCase {
 
@@ -193,6 +196,109 @@
         assertEquals(1, engine.getIsSyncable(account1, authority2));
         assertEquals(0, engine.getIsSyncable(account2, authority2));
     }
+
+    @SmallTest
+    public void testAuthorityParsing() throws Exception {
+        final Account account = new Account("account1", "type1");
+        final String authority1 = "auth1";
+        final String authority2 = "auth2";
+        final String authority3 = "auth3";
+        final Bundle extras = new Bundle();
+        PeriodicSync sync1 = new PeriodicSync(account, authority1, extras, (long) (60 * 60 * 24));
+        PeriodicSync sync2 = new PeriodicSync(account, authority2, extras, (long) (60 * 60 * 24));
+        PeriodicSync sync3 = new PeriodicSync(account, authority3, extras, (long) (60 * 60 * 24));
+        PeriodicSync sync1s = new PeriodicSync(account, authority1, extras, 1000);
+        PeriodicSync sync2s = new PeriodicSync(account, authority2, extras, 1000);
+        PeriodicSync sync3s = new PeriodicSync(account, authority3, extras, 1000);
+
+        MockContentResolver mockResolver = new MockContentResolver();
+
+        final TestContext testContext = new TestContext(mockResolver, getContext());
+        SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
+
+        byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                + "<accounts>\n"
+                + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
+                + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
+                + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
+                + "</accounts>\n").getBytes();
+
+        File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
+        syncDir.mkdirs();
+        AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
+        FileOutputStream fos = accountInfoFile.startWrite();
+        fos.write(accountsFileData);
+        accountInfoFile.finishWrite(fos);
+
+        engine.clearAndReadState();
+
+        List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, authority1);
+        assertEquals(1, syncs.size());
+        assertEquals(sync1, syncs.get(0));
+
+        syncs = engine.getPeriodicSyncs(account, authority2);
+        assertEquals(1, syncs.size());
+        assertEquals(sync2, syncs.get(0));
+
+        syncs = engine.getPeriodicSyncs(account, authority3);
+        assertEquals(1, syncs.size());
+        assertEquals(sync3, syncs.get(0));
+
+        accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                + "<accounts version=\"1\">\n"
+                + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
+                + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
+                + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
+                + "</accounts>\n").getBytes();
+
+        accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
+        fos = accountInfoFile.startWrite();
+        fos.write(accountsFileData);
+        accountInfoFile.finishWrite(fos);
+
+        engine.clearAndReadState();
+
+        syncs = engine.getPeriodicSyncs(account, authority1);
+        assertEquals(0, syncs.size());
+
+        syncs = engine.getPeriodicSyncs(account, authority2);
+        assertEquals(0, syncs.size());
+
+        syncs = engine.getPeriodicSyncs(account, authority3);
+        assertEquals(0, syncs.size());
+
+        accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                + "<accounts version=\"1\">\n"
+                + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n"
+                + "<periodicSync period=\"1000\" />\n"
+                + "</authority>"
+                + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\">\n"
+                + "<periodicSync period=\"1000\" />\n"
+                + "</authority>"
+                + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\">\n"
+                + "<periodicSync period=\"1000\" />\n"
+                + "</authority>"
+                + "</accounts>\n").getBytes();
+
+        accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
+        fos = accountInfoFile.startWrite();
+        fos.write(accountsFileData);
+        accountInfoFile.finishWrite(fos);
+
+        engine.clearAndReadState();
+
+        syncs = engine.getPeriodicSyncs(account, authority1);
+        assertEquals(1, syncs.size());
+        assertEquals(sync1s, syncs.get(0));
+
+        syncs = engine.getPeriodicSyncs(account, authority2);
+        assertEquals(1, syncs.size());
+        assertEquals(sync2s, syncs.get(0));
+
+        syncs = engine.getPeriodicSyncs(account, authority3);
+        assertEquals(1, syncs.size());
+        assertEquals(sync3s, syncs.get(0));
+    }
 }
 
 class TestContext extends ContextWrapper {