Pending op fix didn't merge properly from KLP-dev.

Manually merge in Cl c/342668. Add another test to ensure that it
WAI.

Change-Id: Iae603328d525cc71e4e31ad30a13384efc50f823
diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java
index 713e599..1722f76 100644
--- a/services/java/com/android/server/content/SyncManager.java
+++ b/services/java/com/android/server/content/SyncManager.java
@@ -547,6 +547,7 @@
         if (isLoggable) {
             Log.d(TAG, "one off sync for: " + cname + " " + extras.toString());
         }
+
         Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
         if (expedited) {
             runtimeMillis = -1; // this means schedule at the front of the queue
diff --git a/services/java/com/android/server/content/SyncStorageEngine.java b/services/java/com/android/server/content/SyncStorageEngine.java
index f843a62..1d8ca5a 100644
--- a/services/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/java/com/android/server/content/SyncStorageEngine.java
@@ -362,6 +362,12 @@
                 periodicSyncs.add(defaultSync);
             }
         }
+
+        @Override
+        public String toString() {
+            return target + ", enabled=" + enabled + ", syncable=" + syncable + ", backoff="
+                    + backoffTime + ", delay=" + delayUntil;
+        }
     }
 
     public static class SyncHistoryItem {
@@ -1084,7 +1090,7 @@
             pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras,
                     op.expedited);
             mPendingOperations.add(pop);
-            writePendingOperationsLocked();
+            appendPendingOperationLocked(pop);
 
             SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
             status.pending = true;
@@ -2416,6 +2422,9 @@
         }
         try {
             fis = mPendingFile.openRead();
+            if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
+                Log.v(TAG_FILE, "Reading " + mPendingFile.getBaseFile());
+            }
             XmlPullParser parser;
             parser = Xml.newPullParser();
             parser.setInput(fis, null);
@@ -2427,12 +2436,11 @@
             }
             if (eventType == XmlPullParser.END_DOCUMENT) return; // Nothing to read.
 
-            String tagName = parser.getName();
             do {
                 PendingOperation pop = null;
                 if (eventType == XmlPullParser.START_TAG) {
                     try {
-                        tagName = parser.getName();
+                        String tagName = parser.getName();
                         if (parser.getDepth() == 1 && "op".equals(tagName)) {
                             // Verify version.
                             String versionString =
@@ -2556,17 +2564,11 @@
             fos = mPendingFile.startWrite();
             XmlSerializer out = new FastXmlSerializer();
             out.setOutput(fos, "utf-8");
-            out.startDocument(null, true);
-            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
-            out.startTag(null, "pending");
-            out.attribute(null, "version", Integer.toString(PENDING_OPERATION_VERSION));
 
             for (int i = 0; i < N; i++) {
                 PendingOperation pop = mPendingOperations.get(i);
                 writePendingOperationLocked(pop, out);
              }
-             out.endTag(null, "pending");
              out.endDocument();
              mPendingFile.finishWrite(fos);
         } catch (java.io.IOException e1) {
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index d328a30..0354ffd 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -46,6 +46,7 @@
 public class SyncStorageEngineTest extends AndroidTestCase {
 
     protected Account account1;
+    protected Account account2;
     protected ComponentName syncService1;
     protected String authority1 = "testprovider";
     protected Bundle defaultBundle;
@@ -67,6 +68,7 @@
     @Override
     public void setUp() {
         account1 = new Account("a@example.com", "example.type");
+        account2 = new Account("b@example.com", "example.type");
         syncService1 = new ComponentName("com.example", "SyncService");
         // Default bundle.
         defaultBundle = new Bundle();
@@ -98,7 +100,7 @@
                 SyncOperation.REASON_PERIODIC,
                 SyncStorageEngine.SOURCE_LOCAL,
                 authority,
-                null, time0, 0 /* flex*/, 0, 0, true);
+                Bundle.EMPTY, time0, 0 /* flex*/, 0, 0, true);
         long historyId = engine.insertStartSyncEvent(op, time0);
         long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
         engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
@@ -108,11 +110,11 @@
      * Test persistence of pending operations.
      */
     @MediumTest
-    public void testPending() throws Exception {
+    public void testAppendPending() throws Exception {
         SyncOperation sop = new SyncOperation(account1,
                 DEFAULT_USER,
                 SyncOperation.REASON_PERIODIC,
-                SyncStorageEngine.SOURCE_LOCAL, authority1, null,
+                SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
                 0 /* runtime */, 0 /* flex */, 0 /* backoff */, 0 /* delayuntil */,
                 true /* expedited */);
         engine.insertIntoPending(sop);
@@ -134,6 +136,77 @@
     }
 
     /**
+     * Verify {@link com.android.server.content.SyncStorageEngine#writePendingOperationsLocked()}
+     */
+    public void testWritePendingOperationsLocked() throws Exception {
+        SyncOperation sop = new SyncOperation(account1,
+                DEFAULT_USER,
+                SyncOperation.REASON_IS_SYNCABLE,
+                SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
+                1000L /* runtime */, 57L /* flex */, 0 /* backoff */, 0 /* delayuntil */,
+                true /* expedited */);
+        SyncOperation sop1 = new SyncOperation(account2,
+                DEFAULT_USER,
+                SyncOperation.REASON_PERIODIC,
+                SyncStorageEngine.SOURCE_LOCAL, authority1, defaultBundle,
+                0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
+                false /* expedited */);
+        SyncOperation deleted = new SyncOperation(account2,
+                DEFAULT_USER,
+                SyncOperation.REASON_SYNC_AUTO,
+                SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
+                0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
+                false /* expedited */);
+        engine.insertIntoPending(sop);
+        engine.insertIntoPending(sop1);
+        engine.insertIntoPending(deleted);
+
+        SyncStorageEngine.PendingOperation popDeleted = engine.getPendingOperations().get(2);
+        // Free verifying, going to delete it anyway.
+        assertEquals(deleted.target.account, popDeleted.target.account);
+        assertEquals(deleted.target.provider, popDeleted.target.provider);
+        assertEquals(deleted.target.service, popDeleted.target.service);
+        assertEquals(deleted.target.userId, popDeleted.target.userId);
+        assertEquals(deleted.reason, popDeleted.reason);
+        assertEquals(deleted.syncSource, popDeleted.syncSource);
+        assertEquals(deleted.expedited, popDeleted.expedited);
+        assert(android.content.PeriodicSync.syncExtrasEquals(deleted.extras, popDeleted.extras));
+        // Delete one to force write-all
+        engine.deleteFromPending(popDeleted);
+        assertEquals("Delete of pending op failed.", 2, engine.getPendingOperationCount());
+        // If there's dirty pending data (which there is because we deleted a pending op) this
+        // re-writes the entire file.
+        engine.writeAllState();
+
+        engine.clearAndReadState();
+
+        // Validate state read back out.
+        assertEquals("Delete of pending op failed.", 2, engine.getPendingOperationCount());
+
+        List<SyncStorageEngine.PendingOperation> pops = engine.getPendingOperations();
+
+        SyncStorageEngine.PendingOperation popRetrieved = pops.get(0);
+        assertEquals(sop.target.account, popRetrieved.target.account);
+        assertEquals(sop.target.provider, popRetrieved.target.provider);
+        assertEquals(sop.target.service, popRetrieved.target.service);
+        assertEquals(sop.target.userId, popRetrieved.target.userId);
+        assertEquals(sop.reason, popRetrieved.reason);
+        assertEquals(sop.syncSource, popRetrieved.syncSource);
+        assertEquals(sop.expedited, popRetrieved.expedited);
+        assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
+
+        popRetrieved = pops.get(1);
+        assertEquals(sop1.target.account, popRetrieved.target.account);
+        assertEquals(sop1.target.provider, popRetrieved.target.provider);
+        assertEquals(sop1.target.service, popRetrieved.target.service);
+        assertEquals(sop1.target.userId, popRetrieved.target.userId);
+        assertEquals(sop1.reason, popRetrieved.reason);
+        assertEquals(sop1.syncSource, popRetrieved.syncSource);
+        assertEquals(sop1.expedited, popRetrieved.expedited);
+        assert(android.content.PeriodicSync.syncExtrasEquals(sop1.extras, popRetrieved.extras));
+    }
+
+    /**
      * Test that we can create, remove and retrieve periodic syncs. Backwards compatibility -
      * periodic syncs with no flex time are no longer used.
      */