Merge change 1017 into donut

* changes:
  Changes in mediaframework test for the OpenCore 2.0 integration. Changes included: 1) The default value for the metatData 2) Add the videoSizedChange callback to get the video dimension Added the test cases for the async reset callback test case for the Qualcomm new realease.
diff --git a/Android.mk b/Android.mk
index 42a6b31..ca76fb4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -85,8 +85,10 @@
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
 	core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadset.aidl \
+    core/java/android/content/IContentService.aidl \
 	core/java/android/content/ISyncAdapter.aidl \
 	core/java/android/content/ISyncContext.aidl \
+    core/java/android/content/ISyncStatusObserver.aidl \
 	core/java/android/content/pm/IPackageDataObserver.aidl \
 	core/java/android/content/pm/IPackageDeleteObserver.aidl \
 	core/java/android/content/pm/IPackageInstallObserver.aidl \
diff --git a/api/current.xml b/api/current.xml
index f01ed14..2bab739 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -92268,6 +92268,19 @@
  visibility="public"
 >
 </method>
+<method name="getBroadcastCookie"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
 <method name="getBroadcastItem"
  return="E"
  abstract="false"
@@ -92305,6 +92318,21 @@
 <parameter name="callback" type="E">
 </parameter>
 </method>
+<method name="onCallbackDied"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="E">
+</parameter>
+<parameter name="cookie" type="java.lang.Object">
+</parameter>
+</method>
 <method name="register"
  return="boolean"
  abstract="false"
@@ -92318,6 +92346,21 @@
 <parameter name="callback" type="E">
 </parameter>
 </method>
+<method name="register"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="E">
+</parameter>
+<parameter name="cookie" type="java.lang.Object">
+</parameter>
+</method>
 <method name="unregister"
  return="boolean"
  abstract="false"
diff --git a/core/java/android/backup/BackupService.java b/core/java/android/backup/BackupService.java
index d912d8c..6197182 100644
--- a/core/java/android/backup/BackupService.java
+++ b/core/java/android/backup/BackupService.java
@@ -22,6 +22,7 @@
 import android.backup.IBackupService;
 import android.content.Intent;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -59,23 +60,25 @@
      * The application is being asked to write any data changed since the
      * last time it performed a backup operation.  The state data recorded
      * during the last backup pass is provided in the oldStateFd file descriptor.
-     * If oldStateFd is negative, no old state is available and the application
-     * should perform a full backup.  In both cases, a representation of the
-     * final backup state after this pass should be written to the file pointed
+     * If oldState.getStatSize() is zero or negative, no old state is available
+     * and the application should perform a full backup.  In both cases, a representation
+     * of the final backup state after this pass should be written to the file pointed
      * to by the newStateFd file descriptor.
      *
-     * @param oldStateFd An open, read-only file descriptor pointing to the last
-     *                   backup state provided by the application.  May be negative,
-     *                   in which case no prior state is being provided and the
-     *                   application should perform a full backup.
-     * @param dataFd An open, read/write file descriptor pointing to the backup data
-     *               destination.  Typically the application will use backup helper
-     *               classes to write to this file.
-     * @param newStateFd An open, read/write file descriptor pointing to an empty
-     *                   file.  The application should record the final backup state
-     *                   here after writing the requested data to dataFd.
+     * @param oldState An open, read-only ParcelFileDescriptor pointing to the last backup
+     *                 state provided by the application.  May be empty or invalid, in which
+     *                 case no prior state is being provided and the application should
+     *                 perform a full backup.
+     * @param data An open, read/write ParcelFileDescriptor pointing to the backup data
+     *             destination.  Typically the application will use backup helper
+     *             classes to write to this file.
+     * @param newState An open, read/write ParcelFileDescriptor pointing to an empty
+     *                 file.  The application should record the final backup state
+     *                 here after writing the requested data to dataFd.
      */
-    public abstract void onBackup(int oldStateFd, int dataFd, int newStateFd);
+    public abstract void onBackup(ParcelFileDescriptor oldState,
+            ParcelFileDescriptor data,
+            ParcelFileDescriptor newState);
     
     /**
      * The application is being restored from backup, and should replace any
@@ -84,13 +87,13 @@
      * the restore is finished, the application should write a representation
      * of the final state to the newStateFd file descriptor, 
      *
-     * @param dataFd An open, read-only file descriptor pointing to a full snapshot
-     *               of the application's data.
-     * @param newStateFd An open, read/write file descriptor pointing to an empty
-     *                   file.  The application should record the final backup state
-     *                   here after restoring its data from dataFd.
+     * @param data An open, read-only ParcelFileDescriptor pointing to a full snapshot
+     *             of the application's data.
+     * @param newState An open, read/write ParcelFileDescriptor pointing to an empty
+     *                 file.  The application should record the final backup state
+     *                 here after restoring its data from dataFd.
      */
-    public abstract void onRestore(int dataFd, int newStateFd);
+    public abstract void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState);
 
 
     // ----- Core implementation -----
@@ -110,17 +113,19 @@
 
     // ----- IBackupService binder interface -----
     private class BackupServiceBinder extends IBackupService.Stub {
-        public void doBackup(int oldStateFd, int dataFd, int newStateFd)
-                throws RemoteException {
+        public void doBackup(ParcelFileDescriptor oldState,
+                ParcelFileDescriptor data,
+                ParcelFileDescriptor newState) throws RemoteException {
             // !!! TODO - real implementation; for now just invoke the callbacks directly
             Log.v("BackupServiceBinder", "doBackup() invoked");
-            BackupService.this.onBackup(oldStateFd, dataFd, newStateFd);
+            BackupService.this.onBackup(oldState, data, newState);
         }
 
-        public void doRestore(int dataFd, int newStateFd) throws RemoteException {
+        public void doRestore(ParcelFileDescriptor data,
+                ParcelFileDescriptor newState) throws RemoteException {
             // !!! TODO - real implementation; for now just invoke the callbacks directly
             Log.v("BackupServiceBinder", "doRestore() invoked");
-            BackupService.this.onRestore(dataFd, newStateFd);
+            BackupService.this.onRestore(data, newState);
         }
     }
 }
diff --git a/core/java/android/backup/IBackupService.aidl b/core/java/android/backup/IBackupService.aidl
index 24544bd..1bde8ea 100644
--- a/core/java/android/backup/IBackupService.aidl
+++ b/core/java/android/backup/IBackupService.aidl
@@ -16,38 +16,44 @@
 
 package android.backup;
 
+import android.os.ParcelFileDescriptor;
+ 
 /**
  * Interface presented by applications being asked to participate in the
  * backup & restore mechanism.  End user code does not typically implement
  * this interface; they subclass BackupService instead.
  *
  * {@hide}
- */
+ */ 
 interface IBackupService {
     /**
      * Request that the app perform an incremental backup.
      *
-     * @param oldStateFd Read-only file containing the description blob of the
+     * @param oldState Read-only file containing the description blob of the
      *        app's data state as of the last backup operation's completion.
+     *        This file is empty or invalid when a full backup is being
+     *        requested.
      *
-     * @param dataFd Read-write file, empty when onBackup() is called, that
+     * @param data Read-write file, empty when onBackup() is called, that
      *        is the data destination for this backup pass's incrementals.
      *
-     * @param newStateFd Read-write file, empty when onBackup() is called,
+     * @param newState Read-write file, empty when onBackup() is called,
      *        where the new state blob is to be recorded.
      */
-    void doBackup(int oldStateFd, int dataFd, int newStateFd);
+    void doBackup(in ParcelFileDescriptor oldState,
+            in ParcelFileDescriptor data,
+            in ParcelFileDescriptor newState);
 
     /**
      * Restore an entire data snapshot to the application.
      *
-     * @param dataFd Read-only file containing the full data snapshot of the
+     * @param data Read-only file containing the full data snapshot of the
      *        app's backup.  This is to be a <i>replacement</i> of the app's
      *        current data, not to be merged into it.
      *
-     * @param newStateFd Read-write file, empty when onRestore() is called,
+     * @param newState Read-write file, empty when onRestore() is called,
      *        that is to be written with the state description that holds after
      *        the restore has been completed.
      */
-    void doRestore(int dataFd, int newStateFd);
+    void doRestore(in ParcelFileDescriptor data, in ParcelFileDescriptor newState);
 }
diff --git a/core/java/android/content/ActiveSyncInfo.aidl b/core/java/android/content/ActiveSyncInfo.aidl
new file mode 100644
index 0000000..1142206
--- /dev/null
+++ b/core/java/android/content/ActiveSyncInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+parcelable ActiveSyncInfo;
diff --git a/core/java/android/content/ActiveSyncInfo.java b/core/java/android/content/ActiveSyncInfo.java
new file mode 100644
index 0000000..63be8d1
--- /dev/null
+++ b/core/java/android/content/ActiveSyncInfo.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcel;
+import android.os.Parcelable.Creator;
+
+/** @hide */
+public class ActiveSyncInfo {
+    public final int authorityId;
+    public final String account;
+    public final String authority;
+    public final long startTime;
+    
+    ActiveSyncInfo(int authorityId, String account, String authority,
+            long startTime) {
+        this.authorityId = authorityId;
+        this.account = account;
+        this.authority = authority;
+        this.startTime = startTime;
+    }
+    
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(authorityId);
+        parcel.writeString(account);
+        parcel.writeString(authority);
+        parcel.writeLong(startTime);
+    }
+
+    ActiveSyncInfo(Parcel parcel) {
+        authorityId = parcel.readInt();
+        account = parcel.readString();
+        authority = parcel.readString();
+        startTime = parcel.readLong();
+    }
+    
+    public static final Creator<ActiveSyncInfo> CREATOR = new Creator<ActiveSyncInfo>() {
+        public ActiveSyncInfo createFromParcel(Parcel in) {
+            return new ActiveSyncInfo(in);
+        }
+
+        public ActiveSyncInfo[] newArray(int size) {
+            return new ActiveSyncInfo[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0a71d57..74144fc 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -25,9 +25,13 @@
 import android.database.IContentObserver;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.text.TextUtils;
+import android.util.Config;
+import android.util.Log;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -85,8 +89,7 @@
      */
     public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     
-    public ContentResolver(Context context)
-    {
+    public ContentResolver(Context context) {
         mContext = context;
     }
 
@@ -605,7 +608,7 @@
             ContentObserver observer)
     {
         try {
-            ContentServiceNative.getDefault().registerContentObserver(uri, notifyForDescendents,
+            getContentService().registerContentObserver(uri, notifyForDescendents,
                     observer.getContentObserver());
         } catch (RemoteException e) {
         }
@@ -621,7 +624,7 @@
         try {
             IContentObserver contentObserver = observer.releaseContentObserver();
             if (contentObserver != null) {
-                ContentServiceNative.getDefault().unregisterContentObserver(
+                getContentService().unregisterContentObserver(
                         contentObserver);
             }
         } catch (RemoteException e) {
@@ -651,7 +654,7 @@
      */
     public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
         try {
-            ContentServiceNative.getDefault().notifyChange(
+            getContentService().notifyChange(
                     uri, observer == null ? null : observer.getContentObserver(),
                     observer != null && observer.deliverSelfNotifications(), syncToNetwork);
         } catch (RemoteException e) {
@@ -677,7 +680,7 @@
     public void startSync(Uri uri, Bundle extras) {
         validateSyncExtrasBundle(extras);
         try {
-            ContentServiceNative.getDefault().startSync(uri, extras);
+            getContentService().startSync(uri, extras);
         } catch (RemoteException e) {
         }
     }
@@ -718,7 +721,7 @@
 
     public void cancelSync(Uri uri) {
         try {
-            ContentServiceNative.getDefault().cancelSync(uri);
+            getContentService().cancelSync(uri);
         } catch (RemoteException e) {
         }
     }
@@ -779,6 +782,22 @@
         }
     }
 
+    /** @hide */
+    public static final String CONTENT_SERVICE_NAME = "content";
+    
+    /** @hide */
+    public static IContentService getContentService() {
+        if (sContentService != null) {
+            return sContentService;
+        }
+        IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
+        if (Config.LOGV) Log.v("ContentService", "default service binder = " + b);
+        sContentService = IContentService.Stub.asInterface(b);
+        if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService);
+        return sContentService;
+    }
+    
+    private static IContentService sContentService;
     private final Context mContext;
     private static final String TAG = "ContentResolver";
 }
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index b028868..6cd2c54 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -21,6 +21,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Config;
@@ -34,7 +35,7 @@
 /**
  * {@hide}
  */
-public final class ContentService extends ContentServiceNative {
+public final class ContentService extends IContentService.Stub {
     private static final String TAG = "ContentService";
     private Context mContext;
     private boolean mFactoryTest;
@@ -73,6 +74,21 @@
         }
     }
 
+    @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            // The content service only throws security exceptions, so let's
+            // log all others.
+            if (!(e instanceof SecurityException)) {
+                Log.e(TAG, "Content Service Crash", e);
+            }
+            throw e;
+        }
+    }
+
     /*package*/ ContentService(Context context, boolean factoryTest) {
         mContext = context;
         mFactoryTest = factoryTest;
@@ -204,9 +220,158 @@
         }
     }
 
+    public boolean getSyncProviderAutomatically(String providerName) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
+                "no permission to read the sync settings");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                return syncManager.getSyncStorageEngine().getSyncProviderAutomatically(
+                        null, providerName);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return false;
+    }
+
+    public void setSyncProviderAutomatically(String providerName, boolean sync) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+                "no permission to write the sync settings");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                syncManager.getSyncStorageEngine().setSyncProviderAutomatically(
+                        null, providerName, sync);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public boolean getListenForNetworkTickles() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
+                "no permission to read the sync settings");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                return syncManager.getSyncStorageEngine().getListenForNetworkTickles();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return false;
+    }
+    
+    public void setListenForNetworkTickles(boolean flag) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+                "no permission to write the sync settings");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                syncManager.getSyncStorageEngine().setListenForNetworkTickles(flag);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    public boolean isSyncActive(String account, String authority) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
+                "no permission to read the sync stats");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                return syncManager.getSyncStorageEngine().isSyncActive(
+                        account, authority);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return false;
+    }
+    
+    public ActiveSyncInfo getActiveSync() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
+                "no permission to read the sync stats");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                return syncManager.getSyncStorageEngine().getActiveSync();
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return null;
+    }
+    
+    public SyncStatusInfo getStatusByAuthority(String authority) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
+                "no permission to read the sync stats");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                return syncManager.getSyncStorageEngine().getStatusByAuthority(
+                        authority);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return null;
+    }
+    
+    public boolean isAuthorityPending(String account, String authority) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
+                "no permission to read the sync stats");
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                return syncManager.getSyncStorageEngine().isAuthorityPending(
+                        account, authority);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return false;
+    }
+    
+    public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                syncManager.getSyncStorageEngine().addStatusChangeListener(
+                        mask, callback);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+    
+    public void removeStatusChangeListener(ISyncStatusObserver callback) {
+        long identityToken = clearCallingIdentity();
+        try {
+            SyncManager syncManager = getSyncManager();
+            if (syncManager != null) {
+                syncManager.getSyncStorageEngine().removeStatusChangeListener(
+                        callback);
+            }
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+    
     public static IContentService main(Context context, boolean factoryTest) {
         ContentService service = new ContentService(context, factoryTest);
-        ServiceManager.addService("content", service);
+        ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
         return service;
     }
 
diff --git a/core/java/android/content/ContentServiceNative.java b/core/java/android/content/ContentServiceNative.java
deleted file mode 100644
index 364f9ee..0000000
--- a/core/java/android/content/ContentServiceNative.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.IContentObserver;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.ServiceManager;
-import android.os.Bundle;
-import android.util.Config;
-import android.util.Log;
-
-/**
- * {@hide}
- */
-abstract class ContentServiceNative extends Binder implements IContentService
-{
-    public ContentServiceNative()
-    {
-        attachInterface(this, descriptor);
-    }
-
-    /**
-     * Cast a Binder object into a content resolver interface, generating
-     * a proxy if needed.
-     */
-    static public IContentService asInterface(IBinder obj)
-    {
-        if (obj == null) {
-            return null;
-        }
-        IContentService in =
-            (IContentService)obj.queryLocalInterface(descriptor);
-        if (in != null) {
-            return in;
-        }
-
-        return new ContentServiceProxy(obj);
-    }
-
-    /**
-     * Retrieve the system's default/global content service.
-     */
-    static public IContentService getDefault()
-    {
-        if (gDefault != null) {
-            return gDefault;
-        }
-        IBinder b = ServiceManager.getService("content");
-        if (Config.LOGV) Log.v("ContentService", "default service binder = " + b);
-        gDefault = asInterface(b);
-        if (Config.LOGV) Log.v("ContentService", "default service = " + gDefault);
-        return gDefault;
-    }
-
-    @Override
-    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
-    {
-        try {
-        switch (code) {
-            case 5038: {
-                data.readString(); // ignore the interface token that service generated
-                Uri uri = Uri.parse(data.readString());
-                notifyChange(uri, null, false, false);
-                return true;
-            }
-            
-            case REGISTER_CONTENT_OBSERVER_TRANSACTION: {
-                Uri uri = Uri.CREATOR.createFromParcel(data);
-                boolean notifyForDescendents = data.readInt() != 0;
-                IContentObserver observer = IContentObserver.Stub.asInterface(data.readStrongBinder());
-                registerContentObserver(uri, notifyForDescendents, observer);
-                return true;
-            }
-
-            case UNREGISTER_CHANGE_OBSERVER_TRANSACTION: {
-                IContentObserver observer = IContentObserver.Stub.asInterface(data.readStrongBinder());
-                unregisterContentObserver(observer);
-                return true;
-            }
-
-            case NOTIFY_CHANGE_TRANSACTION: {
-                Uri uri = Uri.CREATOR.createFromParcel(data);
-                IContentObserver observer = IContentObserver.Stub.asInterface(data.readStrongBinder());
-                boolean observerWantsSelfNotifications = data.readInt() != 0;
-                boolean syncToNetwork = data.readInt() != 0;
-                notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork);
-                return true;
-            }
-
-            case START_SYNC_TRANSACTION: {
-                Uri url = null;
-                int hasUrl = data.readInt();
-                if (hasUrl != 0) {
-                    url = Uri.CREATOR.createFromParcel(data);
-                }
-                startSync(url, data.readBundle());
-                return true;
-            }
-
-            case CANCEL_SYNC_TRANSACTION: {
-                Uri url = null;
-                int hasUrl = data.readInt();
-                if (hasUrl != 0) {
-                    url = Uri.CREATOR.createFromParcel(data);
-                }
-                cancelSync(url);
-                return true;
-            }
-
-            default:
-                return super.onTransact(code, data, reply, flags);
-        }
-        } catch (Exception e) {
-            Log.e("ContentServiceNative", "Caught exception in transact", e);
-        }
-
-        return false;
-    }
-
-    public IBinder asBinder()
-    {
-        return this;
-    }
-
-    private static IContentService gDefault;
-}
-
-
-final class ContentServiceProxy implements IContentService
-{
-    public ContentServiceProxy(IBinder remote)
-    {
-        mRemote = remote;
-    }
-
-    public IBinder asBinder()
-    {
-        return mRemote;
-    }
-
-    public void registerContentObserver(Uri uri, boolean notifyForDescendents,
-            IContentObserver observer) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        uri.writeToParcel(data, 0);
-        data.writeInt(notifyForDescendents ? 1 : 0);
-        data.writeStrongInterface(observer);
-        mRemote.transact(REGISTER_CONTENT_OBSERVER_TRANSACTION, data, null, 0);
-        data.recycle();
-    }
-
-    public void unregisterContentObserver(IContentObserver observer) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        data.writeStrongInterface(observer);
-        mRemote.transact(UNREGISTER_CHANGE_OBSERVER_TRANSACTION, data, null, 0);
-        data.recycle();
-    }
-
-    public void notifyChange(Uri uri, IContentObserver observer,
-            boolean observerWantsSelfNotifications, boolean syncToNetwork)
-            throws RemoteException {
-        Parcel data = Parcel.obtain();
-        uri.writeToParcel(data, 0);
-        data.writeStrongInterface(observer);
-        data.writeInt(observerWantsSelfNotifications ? 1 : 0);
-        data.writeInt(syncToNetwork ? 1 : 0);
-        mRemote.transact(NOTIFY_CHANGE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
-        data.recycle();
-    }
-
-    public void startSync(Uri url, Bundle extras) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        if (url == null) {
-            data.writeInt(0);
-        } else {
-            data.writeInt(1);
-            url.writeToParcel(data, 0);
-        }
-        extras.writeToParcel(data, 0);
-        mRemote.transact(START_SYNC_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
-        data.recycle();
-    }
-
-    public void cancelSync(Uri url) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        if (url == null) {
-            data.writeInt(0);
-        } else {
-            data.writeInt(1);
-            url.writeToParcel(data, 0);
-        }
-        mRemote.transact(CANCEL_SYNC_TRANSACTION, data, null /* reply */, IBinder.FLAG_ONEWAY);
-        data.recycle();
-    }
-
-    private IBinder mRemote;
-}
-
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
new file mode 100644
index 0000000..8617d949
--- /dev/null
+++ b/core/java/android/content/IContentService.aidl
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.content.ActiveSyncInfo;
+import android.content.ISyncStatusObserver;
+import android.content.SyncStatusInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.database.IContentObserver;
+
+/**
+ * @hide
+ */
+interface IContentService {
+    void registerContentObserver(in Uri uri, boolean notifyForDescendentsn,
+            IContentObserver observer);
+    void unregisterContentObserver(IContentObserver observer);
+
+    void notifyChange(in Uri uri, IContentObserver observer,
+            boolean observerWantsSelfNotifications, boolean syncToNetwork);
+
+    void startSync(in Uri url, in Bundle extras);
+    void cancelSync(in Uri uri);
+    
+    /**
+     * Check if the provider should be synced when a network tickle is received
+     * @param providerName the provider whose setting we are querying
+     * @return true of the provider should be synced when a network tickle is received
+     */
+    boolean getSyncProviderAutomatically(String providerName);
+
+    /**
+     * Set whether or not the provider is synced when it receives a network tickle.
+     *
+     * @param providerName the provider whose behavior is being controlled
+     * @param sync true if the provider should be synced when tickles are received for it
+     */
+    void setSyncProviderAutomatically(String providerName, boolean sync);
+
+    void setListenForNetworkTickles(boolean flag);
+
+    boolean getListenForNetworkTickles();
+    
+    /**
+     * Returns true if there is currently a sync operation for the given
+     * account or authority in the pending list, or actively being processed.
+     */
+    boolean isSyncActive(String account, String authority);
+    
+    ActiveSyncInfo getActiveSync();
+    
+    /**
+     * Returns the status that matches the authority. If there are multiples accounts for
+     * the authority, the one with the latest "lastSuccessTime" status is returned.
+     * @param authority the authority whose row should be selected
+     * @return the SyncStatusInfo for the authority, or null if none exists
+     */
+    SyncStatusInfo getStatusByAuthority(String authority);
+
+    /**
+     * Return true if the pending status is true of any matching authorities.
+     */
+    boolean isAuthorityPending(String account, String authority);
+    
+    void addStatusChangeListener(int mask, ISyncStatusObserver callback);
+    
+    void removeStatusChangeListener(ISyncStatusObserver callback);
+}
diff --git a/core/java/android/content/IContentService.java b/core/java/android/content/IContentService.java
deleted file mode 100644
index a3047da..0000000
--- a/core/java/android/content/IContentService.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.IContentObserver;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.Bundle;
-
-/**
- * {@hide}
- */
-public interface IContentService extends IInterface
-{
-    public void registerContentObserver(Uri uri, boolean notifyForDescendentsn,
-            IContentObserver observer) throws RemoteException;
-    public void unregisterContentObserver(IContentObserver observer) throws RemoteException;
-
-    public void notifyChange(Uri uri, IContentObserver observer,
-            boolean observerWantsSelfNotifications, boolean syncToNetwork)
-            throws RemoteException;
-
-    public void startSync(Uri url, Bundle extras) throws RemoteException;
-    public void cancelSync(Uri uri) throws RemoteException;
-
-    static final String SERVICE_NAME = "content";
-
-    /* IPC constants */
-    static final String descriptor = "android.content.IContentService";
-
-    static final int REGISTER_CONTENT_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
-    static final int UNREGISTER_CHANGE_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
-    static final int NOTIFY_CHANGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
-    static final int START_SYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4;
-    static final int CANCEL_SYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
-}
-
diff --git a/core/java/android/content/ISyncStatusObserver.aidl b/core/java/android/content/ISyncStatusObserver.aidl
new file mode 100644
index 0000000..eb26845
--- /dev/null
+++ b/core/java/android/content/ISyncStatusObserver.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * @hide
+ */
+oneway interface ISyncStatusObserver {
+    void onStatusChanged(int which);
+}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 96470c3..ade5f3d 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -32,8 +32,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -43,18 +41,13 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcel;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.preference.Preference;
-import android.preference.PreferenceGroup;
-import android.provider.Sync;
 import android.provider.Settings;
-import android.provider.Sync.History;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.format.Time;
@@ -73,13 +66,12 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.PriorityQueue;
 import java.util.Random;
-import java.util.Observer;
-import java.util.Observable;
 
 /**
  * @hide
@@ -138,7 +130,6 @@
     volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
     volatile private boolean mDataConnectionIsConnected = false;
     volatile private boolean mStorageIsLow = false;
-    private Sync.Settings.QueryMap mSyncSettings;
 
     private final NotificationManager mNotificationMgr;
     private AlarmManager mAlarmService = null;
@@ -225,17 +216,6 @@
     private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM";
     private final SyncHandler mSyncHandler;
 
-    private static final String[] SYNC_ACTIVE_PROJECTION = new String[]{
-            Sync.Active.ACCOUNT,
-            Sync.Active.AUTHORITY,
-            Sync.Active.START_TIME,
-    };
-
-    private static final String[] SYNC_PENDING_PROJECTION = new String[]{
-            Sync.Pending.ACCOUNT,
-            Sync.Pending.AUTHORITY
-    };
-
     private static final int MAX_SYNC_POLL_DELAY_SECONDS = 36 * 60 * 60; // 36 hours
     private static final int MIN_SYNC_POLL_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
 
@@ -289,6 +269,14 @@
                 HANDLE_SYNC_ALARM_WAKE_LOCK);
         mHandleAlarmWakeLock.setReferenceCounted(false);
 
+        mSyncStorageEngine.addStatusChangeListener(
+                SyncStorageEngine.CHANGE_SETTINGS, new ISyncStatusObserver.Stub() {
+            public void onStatusChanged(int which) {
+                // force the sync loop to run if the settings change
+                sendCheckAlarmsMessage();
+            }
+        });
+        
         if (!factoryTest) {
             AccountMonitorListener listener = new AccountMonitorListener() {
                 public void onAccountsUpdated(String[] accounts) {
@@ -448,20 +436,10 @@
         return mActiveSyncContext;
     }
 
-    private Sync.Settings.QueryMap getSyncSettings() {
-        if (mSyncSettings == null) {
-            mSyncSettings = new Sync.Settings.QueryMap(mContext.getContentResolver(), true,
-                    new Handler());
-            mSyncSettings.addObserver(new Observer(){
-                public void update(Observable o, Object arg) {
-                    // force the sync loop to run if the settings change
-                    sendCheckAlarmsMessage();
-                }
-            });
-        }
-        return mSyncSettings;
+    public SyncStorageEngine getSyncStorageEngine() {
+        return mSyncStorageEngine;
     }
-
+    
     private void ensureContentResolver() {
         if (mContentResolver == null) {
             mContentResolver = mContext.getContentResolver();
@@ -574,15 +552,15 @@
 
         int source;
         if (uploadOnly) {
-            source = Sync.History.SOURCE_LOCAL;
+            source = SyncStorageEngine.SOURCE_LOCAL;
         } else if (force) {
-            source = Sync.History.SOURCE_USER;
+            source = SyncStorageEngine.SOURCE_USER;
         } else if (url == null) {
-            source = Sync.History.SOURCE_POLL;
+            source = SyncStorageEngine.SOURCE_POLL;
         } else {
             // this isn't strictly server, since arbitrary callers can (and do) request
             // a non-forced two-way sync on a specific url
-            source = Sync.History.SOURCE_SERVER;
+            source = SyncStorageEngine.SOURCE_SERVER;
         }
 
         List<String> names = new ArrayList<String>();
@@ -667,9 +645,7 @@
 
     public void updateHeartbeatTime() {
         mHeartbeatTime = SystemClock.elapsedRealtime();
-        ensureContentResolver();
-        mContentResolver.notifyChange(Sync.Active.CONTENT_URI,
-                null /* this change wasn't made through an observer */);
+        mSyncStorageEngine.reportActiveChange();
     }
 
     private void sendSyncAlarmMessage() {
@@ -876,7 +852,7 @@
         final String key;
         long earliestRunTime;
         long delay;
-        Long rowId = null;
+        SyncStorageEngine.PendingOperation pendingOperation;
 
         SyncOperation(String account, int source, String authority, Bundle extras, long delay) {
             this.account = account;
@@ -916,7 +892,7 @@
             sb.append(" when: ").append(earliestRunTime);
             sb.append(" delay: ").append(delay);
             sb.append(" key: {").append(key).append("}");
-            if (rowId != null) sb.append(" rowId: ").append(rowId);
+            if (pendingOperation != null) sb.append(" pendingOperation: ").append(pendingOperation);
             return sb.toString();
         }
 
@@ -999,242 +975,262 @@
 
     protected void dump(FileDescriptor fd, PrintWriter pw) {
         StringBuilder sb = new StringBuilder();
-        dumpSyncState(sb);
-        sb.append("\n");
+        dumpSyncState(pw, sb);
         if (isSyncEnabled()) {
-            dumpSyncHistory(sb);
+            dumpSyncHistory(pw, sb);
         }
-        pw.println(sb.toString());
     }
 
-    protected void dumpSyncState(StringBuilder sb) {
-        sb.append("sync enabled: ").append(isSyncEnabled()).append("\n");
-        sb.append("data connected: ").append(mDataConnectionIsConnected).append("\n");
-        sb.append("memory low: ").append(mStorageIsLow).append("\n");
+    static String formatTime(long time) {
+        Time tobj = new Time();
+        tobj.set(time);
+        return tobj.format("%Y-%m-%d %H:%M:%S");
+    }
+    
+    protected void dumpSyncState(PrintWriter pw, StringBuilder sb) {
+        pw.print("sync enabled: "); pw.println(isSyncEnabled());
+        pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
+        pw.print("memory low: "); pw.println(mStorageIsLow);
 
         final String[] accounts = mAccounts;
-        sb.append("accounts: ");
+        pw.print("accounts: ");
         if (accounts != null) {
-            sb.append(accounts.length);
+            pw.println(accounts.length);
         } else {
-            sb.append("none");
+            pw.println("none");
         }
-        sb.append("\n");
         final long now = SystemClock.elapsedRealtime();
-        sb.append("now: ").append(now).append("\n");
-        sb.append("uptime: ").append(DateUtils.formatElapsedTime(now/1000)).append(" (HH:MM:SS)\n");
-        sb.append("time spent syncing : ")
-                .append(DateUtils.formatElapsedTime(
-                        mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000))
-                .append(" (HH:MM:SS), sync ")
-                .append(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ")
-                .append("in progress").append("\n");
+        pw.print("now: "); pw.println(now);
+        pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
+                pw.println(" (HH:MM:SS)");
+        pw.print("time spent syncing: ");
+                pw.print(DateUtils.formatElapsedTime(
+                        mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
+                pw.print(" (HH:MM:SS), sync ");
+                pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
+                pw.println("in progress");
         if (mSyncHandler.mAlarmScheduleTime != null) {
-            sb.append("next alarm time: ").append(mSyncHandler.mAlarmScheduleTime)
-                    .append(" (")
-                    .append(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000))
-                    .append(" (HH:MM:SS) from now)\n");
+            pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime);
+                    pw.print(" (");
+                    pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000));
+                    pw.println(" (HH:MM:SS) from now)");
         } else {
-            sb.append("no alarm is scheduled (there had better not be any pending syncs)\n");
+            pw.println("no alarm is scheduled (there had better not be any pending syncs)");
         }
 
-        sb.append("active sync: ").append(mActiveSyncContext).append("\n");
+        pw.print("active sync: "); pw.println(mActiveSyncContext);
 
-        sb.append("notification info: ");
+        pw.print("notification info: ");
+        sb.setLength(0);
         mSyncHandler.mSyncNotificationInfo.toString(sb);
-        sb.append("\n");
+        pw.println(sb.toString());
 
         synchronized (mSyncQueue) {
-            sb.append("sync queue: ");
+            pw.print("sync queue: ");
+            sb.setLength(0);
             mSyncQueue.dump(sb);
+            pw.println(sb.toString());
         }
 
-        Cursor c = mSyncStorageEngine.query(Sync.Active.CONTENT_URI,
-                SYNC_ACTIVE_PROJECTION, null, null, null);
-        sb.append("\n");
-        try {
-            if (c.moveToNext()) {
-                final long durationInSeconds = (now - c.getLong(2)) / 1000;
-                sb.append("Active sync: ").append(c.getString(0))
-                        .append(" ").append(c.getString(1))
-                        .append(", duration is ")
-                        .append(DateUtils.formatElapsedTime(durationInSeconds)).append(".\n");
-            } else {
-                sb.append("No sync is in progress.\n");
-            }
-        } finally {
-            c.close();
-        }
-
-        c = mSyncStorageEngine.query(Sync.Pending.CONTENT_URI,
-                SYNC_PENDING_PROJECTION, null, null, "account, authority");
-        sb.append("\nPending Syncs\n");
-        try {
-            if (c.getCount() != 0) {
-                dumpSyncPendingHeader(sb);
-                while (c.moveToNext()) {
-                    dumpSyncPendingRow(sb, c);
-                }
-                dumpSyncPendingFooter(sb);
-            } else {
-                sb.append("none\n");
-            }
-        } finally {
-            c.close();
-        }
-
-        String currentAccount = null;
-        c = mSyncStorageEngine.query(Sync.Status.CONTENT_URI,
-                STATUS_PROJECTION, null, null, "account, authority");
-        sb.append("\nSync history by account and authority\n");
-        try {
-            while (c.moveToNext()) {
-                if (!TextUtils.equals(currentAccount, c.getString(0))) {
-                    if (currentAccount != null) {
-                        dumpSyncHistoryFooter(sb);
-                    }
-                    currentAccount = c.getString(0);
-                    dumpSyncHistoryHeader(sb, currentAccount);
-                }
-
-                dumpSyncHistoryRow(sb, c);
-            }
-            if (c.getCount() > 0) dumpSyncHistoryFooter(sb);
-        } finally {
-            c.close();
-        }
-    }
-
-    private void dumpSyncHistoryHeader(StringBuilder sb, String account) {
-        sb.append(" Account: ").append(account).append("\n");
-        sb.append("  ___________________________________________________________________________________________________________________________\n");
-        sb.append(" |                 |             num times synced           |   total  |         last success          |                     |\n");
-        sb.append(" | authority       | local |  poll | server |  user | total | duration |  source |               time  |   result if failing |\n");
-    }
-
-    private static String[] STATUS_PROJECTION = new String[]{
-            Sync.Status.ACCOUNT, // 0
-            Sync.Status.AUTHORITY, // 1
-            Sync.Status.NUM_SYNCS, // 2
-            Sync.Status.TOTAL_ELAPSED_TIME, // 3
-            Sync.Status.NUM_SOURCE_LOCAL, // 4
-            Sync.Status.NUM_SOURCE_POLL, // 5
-            Sync.Status.NUM_SOURCE_SERVER, // 6
-            Sync.Status.NUM_SOURCE_USER, // 7
-            Sync.Status.LAST_SUCCESS_SOURCE, // 8
-            Sync.Status.LAST_SUCCESS_TIME, // 9
-            Sync.Status.LAST_FAILURE_SOURCE, // 10
-            Sync.Status.LAST_FAILURE_TIME, // 11
-            Sync.Status.LAST_FAILURE_MESG // 12
-    };
-
-    private void dumpSyncHistoryRow(StringBuilder sb, Cursor c) {
-        boolean hasSuccess = !c.isNull(9);
-        boolean hasFailure = !c.isNull(11);
-        Time timeSuccess = new Time();
-        if (hasSuccess) timeSuccess.set(c.getLong(9));
-        Time timeFailure = new Time();
-        if (hasFailure) timeFailure.set(c.getLong(11));
-        sb.append(String.format(" | %-15s | %5d | %5d | %6d | %5d | %5d | %8s | %7s | %19s | %19s |\n",
-                c.getString(1),
-                c.getLong(4),
-                c.getLong(5),
-                c.getLong(6),
-                c.getLong(7),
-                c.getLong(2),
-                DateUtils.formatElapsedTime(c.getLong(3)/1000),
-                hasSuccess ? Sync.History.SOURCES[c.getInt(8)] : "",
-                hasSuccess ? timeSuccess.format("%Y-%m-%d %H:%M:%S") : "",
-                hasFailure ? History.mesgToString(c.getString(12)) : ""));
-    }
-
-    private void dumpSyncHistoryFooter(StringBuilder sb) {
-        sb.append(" |___________________________________________________________________________________________________________________________|\n");
-    }
-
-    private void dumpSyncPendingHeader(StringBuilder sb) {
-        sb.append(" ____________________________________________________\n");
-        sb.append(" | account                        | authority       |\n");
-    }
-
-    private void dumpSyncPendingRow(StringBuilder sb, Cursor c) {
-        sb.append(String.format(" | %-30s | %-15s |\n", c.getString(0), c.getString(1)));
-    }
-
-    private void dumpSyncPendingFooter(StringBuilder sb) {
-        sb.append(" |__________________________________________________|\n");
-    }
-
-    protected void dumpSyncHistory(StringBuilder sb) {
-        Cursor c = mSyncStorageEngine.query(Sync.History.CONTENT_URI, null, "event=?",
-                new String[]{String.valueOf(Sync.History.EVENT_STOP)},
-                Sync.HistoryColumns.EVENT_TIME + " desc");
-        try {
-            long numSyncsLastHour = 0, durationLastHour = 0;
-            long numSyncsLastDay = 0, durationLastDay = 0;
-            long numSyncsLastWeek = 0, durationLastWeek = 0;
-            long numSyncsLast4Weeks = 0, durationLast4Weeks = 0;
-            long numSyncsTotal = 0, durationTotal = 0;
-
-            long now = System.currentTimeMillis();
-            int indexEventTime = c.getColumnIndexOrThrow(Sync.History.EVENT_TIME);
-            int indexElapsedTime = c.getColumnIndexOrThrow(Sync.History.ELAPSED_TIME);
-            while (c.moveToNext()) {
-                long duration = c.getLong(indexElapsedTime);
-                long endTime = c.getLong(indexEventTime) + duration;
-                long millisSinceStart = now - endTime;
-                numSyncsTotal++;
-                durationTotal += duration;
-                if (millisSinceStart < MILLIS_IN_HOUR) {
-                    numSyncsLastHour++;
-                    durationLastHour += duration;
-                }
-                if (millisSinceStart < MILLIS_IN_DAY) {
-                    numSyncsLastDay++;
-                    durationLastDay += duration;
-                }
-                if (millisSinceStart < MILLIS_IN_WEEK) {
-                    numSyncsLastWeek++;
-                    durationLastWeek += duration;
-                }
-                if (millisSinceStart < MILLIS_IN_4WEEKS) {
-                    numSyncsLast4Weeks++;
-                    durationLast4Weeks += duration;
-                }
-            }
-            dumpSyncIntervalHeader(sb);
-            dumpSyncInterval(sb, "hour", MILLIS_IN_HOUR, numSyncsLastHour, durationLastHour);
-            dumpSyncInterval(sb, "day", MILLIS_IN_DAY, numSyncsLastDay, durationLastDay);
-            dumpSyncInterval(sb, "week", MILLIS_IN_WEEK, numSyncsLastWeek, durationLastWeek);
-            dumpSyncInterval(sb, "4 weeks",
-                    MILLIS_IN_4WEEKS, numSyncsLast4Weeks, durationLast4Weeks);
-            dumpSyncInterval(sb, "total", 0, numSyncsTotal, durationTotal);
-            dumpSyncIntervalFooter(sb);
-        } finally {
-            c.close();
-        }
-    }
-
-    private void dumpSyncIntervalHeader(StringBuilder sb) {
-        sb.append("Sync Stats\n");
-        sb.append(" ___________________________________________________________\n");
-        sb.append(" |          |        |   duration in sec   |               |\n");
-        sb.append(" | interval |  count |  average |    total | % of interval |\n");
-    }
-
-    private void dumpSyncInterval(StringBuilder sb, String label,
-            long interval, long numSyncs, long duration) {
-        sb.append(String.format(" | %-8s | %6d | %8.1f | %8.1f",
-                label, numSyncs, ((float)duration/numSyncs)/1000, (float)duration/1000));
-        if (interval > 0) {
-            sb.append(String.format(" | %13.2f |\n", ((float)duration/interval)*100.0));
+        ActiveSyncInfo active = mSyncStorageEngine.getActiveSync();
+        if (active != null) {
+            SyncStorageEngine.AuthorityInfo authority
+                    = mSyncStorageEngine.getAuthority(active.authorityId);
+            final long durationInSeconds = (now - active.startTime) / 1000;
+            pw.print("Active sync: ");
+                    pw.print(authority != null ? authority.account : "<no account>");
+                    pw.print(" ");
+                    pw.print(authority != null ? authority.authority : "<no account>");
+                    pw.print(", duration is ");
+                    pw.println(DateUtils.formatElapsedTime(durationInSeconds));
         } else {
-            sb.append(String.format(" | %13s |\n", "na"));
+            pw.println("No sync is in progress.");
+        }
+
+        ArrayList<SyncStorageEngine.PendingOperation> ops
+                = mSyncStorageEngine.getPendingOperations();
+        if (ops != null && ops.size() > 0) {
+            pw.println();
+            pw.println("Pending Syncs");
+            final int N = ops.size();
+            for (int i=0; i<N; i++) {
+                SyncStorageEngine.PendingOperation op = ops.get(i);
+                pw.print("  #"); pw.print(i); pw.print(": account=");
+                pw.print(op.account); pw.print(" authority=");
+                pw.println(op.authority);
+                if (op.extras != null && op.extras.size() > 0) {
+                    sb.setLength(0);
+                    SyncOperation.extrasToStringBuilder(op.extras, sb);
+                    pw.print("    extras: "); pw.println(sb.toString());
+                }
+            }
+        }
+
+        HashSet<String> processedAccounts = new HashSet<String>();
+        ArrayList<SyncStatusInfo> statuses
+                = mSyncStorageEngine.getSyncStatus();
+        if (statuses != null && statuses.size() > 0) {
+            pw.println();
+            pw.println("Sync Status");
+            final int N = statuses.size();
+            for (int i=0; i<N; i++) {
+                SyncStatusInfo status = statuses.get(i);
+                SyncStorageEngine.AuthorityInfo authority
+                        = mSyncStorageEngine.getAuthority(status.authorityId);
+                if (authority != null) {
+                    String curAccount = authority.account;
+                    
+                    if (processedAccounts.contains(curAccount)) {
+                        continue;
+                    }
+                    
+                    processedAccounts.add(curAccount);
+                    
+                    pw.print("  Account "); pw.print(authority.account);
+                    pw.println(":");
+                    for (int j=i; j<N; j++) {
+                        status = statuses.get(j);
+                        authority = mSyncStorageEngine.getAuthority(status.authorityId);
+                        if (!curAccount.equals(authority.account)) {
+                            continue;
+                        }
+                        pw.print("    "); pw.print(authority.authority);
+                        pw.println(":");
+                        pw.print("      count: local="); pw.print(status.numSourceLocal);
+                                pw.print(" poll="); pw.print(status.numSourcePoll);
+                                pw.print(" server="); pw.print(status.numSourceServer);
+                                pw.print(" user="); pw.print(status.numSourceUser);
+                                pw.print(" total="); pw.println(status.numSyncs);
+                        pw.print("      total duration: ");
+                                pw.println(DateUtils.formatElapsedTime(
+                                        status.totalElapsedTime/1000));
+                        if (status.lastSuccessTime != 0) {
+                            pw.print("      SUCCESS: source=");
+                                    pw.print(SyncStorageEngine.SOURCES[
+                                            status.lastSuccessSource]);
+                                    pw.print(" time=");
+                                    pw.println(formatTime(status.lastSuccessTime));
+                        } else {
+                            pw.print("      FAILURE: source=");
+                                    pw.print(SyncStorageEngine.SOURCES[
+                                            status.lastFailureSource]);
+                                    pw.print(" initialTime=");
+                                    pw.print(formatTime(status.initialFailureTime));
+                                    pw.print(" lastTime=");
+                                    pw.println(formatTime(status.lastFailureTime));
+                            pw.print("      message: "); pw.println(status.lastFailureMesg);
+                        }
+                    }
+                }
+            }
         }
     }
 
-    private void dumpSyncIntervalFooter(StringBuilder sb) {
-        sb.append(" |_________________________________________________________|\n");
+    private void dumpTimeSec(PrintWriter pw, long time) {
+        pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
+        pw.print('s');
+    }
+    
+    private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
+        pw.print("Success ("); pw.print(ds.successCount);
+        if (ds.successCount > 0) {
+            pw.print(" for "); dumpTimeSec(pw, ds.successTime);
+            pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
+        }
+        pw.print(") Failure ("); pw.print(ds.failureCount);
+        if (ds.failureCount > 0) {
+            pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
+            pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
+        }
+        pw.println(")");
+    }
+    
+    protected void dumpSyncHistory(PrintWriter pw, StringBuilder sb) {
+        SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
+        if (dses != null && dses[0] != null) {
+            pw.println();
+            pw.println("Sync Statistics");
+            pw.print("  Today:  "); dumpDayStatistic(pw, dses[0]);
+            int today = dses[0].day;
+            int i;
+            SyncStorageEngine.DayStats ds;
+            
+            // Print each day in the current week.
+            for (i=1; i<=6 && i < dses.length; i++) {
+                ds = dses[i];
+                if (ds == null) break;
+                int delta = today-ds.day;
+                if (delta > 6) break;
+                
+                pw.print("  Day-"); pw.print(delta); pw.print(":  ");
+                dumpDayStatistic(pw, ds);
+            }
+            
+            // Aggregate all following days into weeks and print totals.
+            int weekDay = today;
+            while (i < dses.length) {
+                SyncStorageEngine.DayStats aggr = null;
+                weekDay -= 7;
+                while (i < dses.length) {
+                    ds = dses[i];
+                    if (ds == null) {
+                        i = dses.length;
+                        break;
+                    }
+                    int delta = weekDay-ds.day;
+                    if (delta > 6) break;
+                    i++;
+                    
+                    if (aggr == null) {
+                        aggr = new SyncStorageEngine.DayStats(weekDay);
+                    }
+                    aggr.successCount += ds.successCount;
+                    aggr.successTime += ds.successTime;
+                    aggr.failureCount += ds.failureCount;
+                    aggr.failureTime += ds.failureTime;
+                }
+                if (aggr != null) {
+                    pw.print("  Week-"); pw.print((today-weekDay)/7); pw.print(": ");
+                    dumpDayStatistic(pw, aggr);
+                }
+            }
+        }
+        
+        ArrayList<SyncStorageEngine.SyncHistoryItem> items
+                = mSyncStorageEngine.getSyncHistory();
+        if (items != null && items.size() > 0) {
+            pw.println();
+            pw.println("Recent Sync History");
+            final int N = items.size();
+            for (int i=0; i<N; i++) {
+                SyncStorageEngine.SyncHistoryItem item = items.get(i);
+                SyncStorageEngine.AuthorityInfo authority
+                        = mSyncStorageEngine.getAuthority(item.authorityId);
+                pw.print("  #"); pw.print(i+1); pw.print(": ");
+                        pw.print(authority != null ? authority.account : "<no account>");
+                        pw.print(" ");
+                        pw.print(authority != null ? authority.authority : "<no account>");
+                Time time = new Time();
+                time.set(item.eventTime);
+                pw.print(" "); pw.print(SyncStorageEngine.SOURCES[item.source]);
+                        pw.print(" @ ");
+                        pw.print(formatTime(item.eventTime));
+                        pw.print(" for ");
+                        dumpTimeSec(pw, item.elapsedTime);
+                        pw.println();
+                if (item.event != SyncStorageEngine.EVENT_STOP
+                        || item.upstreamActivity !=0
+                        || item.downstreamActivity != 0) {
+                    pw.print("    event="); pw.print(item.event);
+                            pw.print(" upstreamActivity="); pw.print(item.upstreamActivity);
+                            pw.print(" downstreamActivity="); pw.println(item.downstreamActivity);
+                }
+                if (item.mesg != null
+                        && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
+                    pw.print("    mesg="); pw.println(item.mesg);
+                }
+            }
+        }
     }
 
     /**
@@ -1461,7 +1457,6 @@
             // found that is runnable (not disabled, etc). If that one is ready to run then
             // start it, otherwise just get out.
             SyncOperation syncOperation;
-            final Sync.Settings.QueryMap syncSettings = getSyncSettings();
             final ConnectivityManager connManager = (ConnectivityManager)
                     mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
             final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
@@ -1488,9 +1483,9 @@
                     final boolean force = syncOperation.extras.getBoolean(
                             ContentResolver.SYNC_EXTRAS_FORCE, false);
                     if (!force && (!backgroundDataSetting
-                            || !syncSettings.getListenForNetworkTickles()
-                            || !syncSettings.getSyncProviderAutomatically(
-                                    syncOperation.authority))) {
+                            || !mSyncStorageEngine.getListenForNetworkTickles()
+                            || !mSyncStorageEngine.getSyncProviderAutomatically(
+                                    null, syncOperation.authority))) {
                         if (isLoggable) {
                             Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation);
                         }
@@ -1616,7 +1611,7 @@
                     if (isLoggable) {
                         Log.v(TAG, "finished sync operation " + syncOperation);
                     }
-                    historyMessage = History.MESG_SUCCESS;
+                    historyMessage = SyncStorageEngine.MESG_SUCCESS;
                     // TODO: set these correctly when the SyncResult is extended to include it
                     downstreamActivity = 0;
                     upstreamActivity = 0;
@@ -1640,7 +1635,7 @@
                 } catch (RemoteException e) {
                     // we don't need to retry this in this case
                 }
-                historyMessage = History.MESG_CANCELED;
+                historyMessage = SyncStorageEngine.MESG_CANCELED;
                 downstreamActivity = 0;
                 upstreamActivity = 0;
             }
@@ -1675,14 +1670,22 @@
          *   If SyncResult.error() is true then it is safe to call this.
          */
         private int syncResultToErrorNumber(SyncResult syncResult) {
-            if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS;
-            if (syncResult.stats.numAuthExceptions > 0) return History.ERROR_AUTHENTICATION;
-            if (syncResult.stats.numIoExceptions > 0) return History.ERROR_IO;
-            if (syncResult.stats.numParseExceptions > 0) return History.ERROR_PARSE;
-            if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT;
-            if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS;
-            if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES;
-            if (syncResult.databaseError) return History.ERROR_INTERNAL;
+            if (syncResult.syncAlreadyInProgress)
+                return SyncStorageEngine.ERROR_SYNC_ALREADY_IN_PROGRESS;
+            if (syncResult.stats.numAuthExceptions > 0)
+                return SyncStorageEngine.ERROR_AUTHENTICATION;
+            if (syncResult.stats.numIoExceptions > 0)
+                return SyncStorageEngine.ERROR_IO;
+            if (syncResult.stats.numParseExceptions > 0)
+                return SyncStorageEngine.ERROR_PARSE;
+            if (syncResult.stats.numConflictDetectedExceptions > 0)
+                return SyncStorageEngine.ERROR_CONFLICT;
+            if (syncResult.tooManyDeletions)
+                return SyncStorageEngine.ERROR_TOO_MANY_DELETIONS;
+            if (syncResult.tooManyRetries)
+                return SyncStorageEngine.ERROR_TOO_MANY_RETRIES;
+            if (syncResult.databaseError)
+                return SyncStorageEngine.ERROR_INTERNAL;
             throw new IllegalStateException("we are not in an error state, " + syncResult);
         }
 
@@ -1904,7 +1907,8 @@
             final int source = syncOperation.syncSource;
             final long now = System.currentTimeMillis();
 
-            EventLog.writeEvent(2720, syncOperation.authority, Sync.History.EVENT_START, source);
+            EventLog.writeEvent(2720, syncOperation.authority,
+                    SyncStorageEngine.EVENT_START, source);
 
             return mSyncStorageEngine.insertStartSyncEvent(
                     syncOperation.account, syncOperation.authority, now, source);
@@ -1912,7 +1916,8 @@
 
         public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
                 int upstreamActivity, int downstreamActivity, long elapsedTime) {
-            EventLog.writeEvent(2720, syncOperation.authority, Sync.History.EVENT_STOP, syncOperation.syncSource);
+            EventLog.writeEvent(2720, syncOperation.authority,
+                    SyncStorageEngine.EVENT_STOP, syncOperation.syncSource);
 
             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, resultMessage,
                     downstreamActivity, upstreamActivity);
@@ -1921,18 +1926,6 @@
 
     static class SyncQueue {
         private SyncStorageEngine mSyncStorageEngine;
-        private final String[] COLUMNS = new String[]{
-                "_id",
-                "authority",
-                "account",
-                "extras",
-                "source"
-        };
-        private static final int COLUMN_ID = 0;
-        private static final int COLUMN_AUTHORITY = 1;
-        private static final int COLUMN_ACCOUNT = 2;
-        private static final int COLUMN_EXTRAS = 3;
-        private static final int COLUMN_SOURCE = 4;
 
         private static final boolean DEBUG_CHECK_DATA_CONSISTENCY = false;
 
@@ -1946,25 +1939,28 @@
 
         public SyncQueue(SyncStorageEngine syncStorageEngine) {
             mSyncStorageEngine = syncStorageEngine;
-            Cursor cursor = mSyncStorageEngine.getPendingSyncsCursor(COLUMNS);
-            try {
-                while (cursor.moveToNext()) {
-                    add(cursorToOperation(cursor),
-                            true /* this is being added from the database */);
-                }
-            } finally {
-                cursor.close();
-                if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */);
+            ArrayList<SyncStorageEngine.PendingOperation> ops
+                    = mSyncStorageEngine.getPendingOperations();
+            final int N = ops.size();
+            for (int i=0; i<N; i++) {
+                SyncStorageEngine.PendingOperation op = ops.get(i);
+                SyncOperation syncOperation = new SyncOperation(
+                        op.account, op.syncSource, op.authority, op.extras, 0);
+                syncOperation.pendingOperation = op;
+                add(syncOperation, op);
             }
+            
+            if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */);
         }
 
         public boolean add(SyncOperation operation) {
             return add(new SyncOperation(operation),
-                    false /* this is not coming from the database */);
+                    null /* this is not coming from the database */);
         }
 
-        private boolean add(SyncOperation operation, boolean fromDatabase) {
-            if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase);
+        private boolean add(SyncOperation operation,
+                SyncStorageEngine.PendingOperation pop) {
+            if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
 
             // If this operation is expedited then set its earliestRunTime to be immediately
             // before the head of the list, or not if none are in the list.
@@ -1996,7 +1992,7 @@
 
             if (existingOperation != null
                     && operation.earliestRunTime >= existingOperation.earliestRunTime) {
-                if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase);
+                if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
                 return false;
             }
 
@@ -2004,26 +2000,17 @@
                 removeByKey(operationKey);
             }
 
-            if (operation.rowId == null) {
-                byte[] extrasData = null;
-                Parcel parcel = Parcel.obtain();
-                try {
-                    operation.extras.writeToParcel(parcel, 0);
-                    extrasData = parcel.marshall();
-                } finally {
-                    parcel.recycle();
-                }
-                ContentValues values = new ContentValues();
-                values.put("account", operation.account);
-                values.put("authority", operation.authority);
-                values.put("source", operation.syncSource);
-                values.put("extras", extrasData);
-                Uri pendingUri = mSyncStorageEngine.insertIntoPending(values);
-                operation.rowId = pendingUri == null ? null : ContentUris.parseId(pendingUri);
-                if (operation.rowId == null) {
+            operation.pendingOperation = pop;
+            if (operation.pendingOperation == null) {
+                pop = new SyncStorageEngine.PendingOperation(
+                                operation.account, operation.syncSource,
+                                operation.authority, operation.extras);
+                pop = mSyncStorageEngine.insertIntoPending(pop);
+                if (pop == null) {
                     throw new IllegalStateException("error adding pending sync operation "
                             + operation);
                 }
+                operation.pendingOperation = pop;
             }
 
             if (DEBUG_CHECK_DATA_CONSISTENCY) {
@@ -2033,7 +2020,7 @@
             }
             mOpsByKey.put(operationKey, operation);
             mOpsByWhen.add(operation);
-            if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase);
+            if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
             return true;
         }
 
@@ -2045,7 +2032,7 @@
                         "unable to find " + operationToRemove + " in mOpsByWhen");
             }
 
-            if (mSyncStorageEngine.deleteFromPending(operationToRemove.rowId) != 1) {
+            if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
                 throw new IllegalStateException("unable to find pending row for "
                         + operationToRemove);
             }
@@ -2065,7 +2052,7 @@
                 throw new IllegalStateException("unable to find " + operation + " in mOpsByKey");
             }
 
-            if (mSyncStorageEngine.deleteFromPending(operation.rowId) != 1) {
+            if (!mSyncStorageEngine.deleteFromPending(operation.pendingOperation)) {
                 throw new IllegalStateException("unable to find pending row for " + operation);
             }
 
@@ -2087,7 +2074,7 @@
                             "unable to find " + syncOperation + " in mOpsByWhen");
                 }
 
-                if (mSyncStorageEngine.deleteFromPending(syncOperation.rowId) != 1) {
+                if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
                     throw new IllegalStateException("unable to find pending row for "
                             + syncOperation);
                 }
@@ -2128,48 +2115,29 @@
             }
 
             if (checkDatabase) {
-                // check that the DB contains the same rows as the in-memory data structures
-                Cursor cursor = mSyncStorageEngine.getPendingSyncsCursor(COLUMNS);
-                try {
-                    if (mOpsByKey.size() != cursor.getCount()) {
-                        StringBuilder sb = new StringBuilder();
-                        DatabaseUtils.dumpCursor(cursor, sb);
+                final int N = mSyncStorageEngine.getPendingOperationCount();
+                if (mOpsByKey.size() != N) {
+                    ArrayList<SyncStorageEngine.PendingOperation> ops
+                            = mSyncStorageEngine.getPendingOperations();
+                    StringBuilder sb = new StringBuilder();
+                    for (int i=0; i<N; i++) {
+                        SyncStorageEngine.PendingOperation op = ops.get(i);
+                        sb.append("#");
+                        sb.append(i);
+                        sb.append(": account=");
+                        sb.append(op.account);
+                        sb.append(" syncSource=");
+                        sb.append(op.syncSource);
+                        sb.append(" authority=");
+                        sb.append(op.authority);
                         sb.append("\n");
-                        dump(sb);
-                        throw new IllegalStateException("DB size mismatch: "
-                                + mOpsByKey .size() + " != " + cursor.getCount() + "\n"
-                                + sb.toString());
                     }
-                } finally {
-                    cursor.close();
+                    dump(sb);
+                    throw new IllegalStateException("DB size mismatch: "
+                            + mOpsByKey.size() + " != " + N + "\n"
+                            + sb.toString());
                 }
             }
         }
-
-        private SyncOperation cursorToOperation(Cursor cursor) {
-            byte[] extrasData = cursor.getBlob(COLUMN_EXTRAS);
-            Bundle extras;
-            Parcel parcel = Parcel.obtain();
-            try {
-                parcel.unmarshall(extrasData, 0, extrasData.length);
-                parcel.setDataPosition(0);
-                extras = parcel.readBundle();
-            } catch (RuntimeException e) {
-                // A RuntimeException is thrown if we were unable to parse the parcel.
-                // Create an empty parcel in this case.
-                extras = new Bundle();
-            } finally {
-                parcel.recycle();
-            }
-
-            SyncOperation syncOperation = new SyncOperation(
-                    cursor.getString(COLUMN_ACCOUNT),
-                    cursor.getInt(COLUMN_SOURCE),
-                    cursor.getString(COLUMN_AUTHORITY),
-                    extras,
-                    0 /* delay */);
-            syncOperation.rowId = cursor.getLong(COLUMN_ID);
-            return syncOperation;
-        }
     }
 }
diff --git a/core/java/android/content/SyncProvider.java b/core/java/android/content/SyncProvider.java
deleted file mode 100644
index 6ddd046..0000000
--- a/core/java/android/content/SyncProvider.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2007 The Android Open Source Project
-package android.content;
-
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * ContentProvider that tracks the sync data and overall sync
- * history on the device.
- * 
- * @hide
- */
-public class SyncProvider extends ContentProvider {
-    public SyncProvider() {
-    }
-
-    private SyncStorageEngine mSyncStorageEngine;
-
-    @Override
-    public boolean onCreate() {
-        mSyncStorageEngine = SyncStorageEngine.getSingleton();
-        return true;
-    }
-
-    @Override
-    public Cursor query(Uri url, String[] projectionIn,
-                        String selection, String[] selectionArgs, String sort) {
-        return mSyncStorageEngine.query(url, projectionIn, selection, selectionArgs, sort);
-    }
-
-    @Override
-    public Uri insert(Uri url, ContentValues initialValues) {
-        return mSyncStorageEngine.insert(true /* the caller is the provider */,
-                url, initialValues);
-    }
-
-    @Override
-    public int delete(Uri url, String where, String[] whereArgs) {
-        return mSyncStorageEngine.delete(true /* the caller is the provider */,
-                url, where, whereArgs);
-    }
-
-    @Override
-    public int update(Uri url, ContentValues initialValues, String where, String[] whereArgs) {
-        return mSyncStorageEngine.update(true /* the caller is the provider */, 
-                url, initialValues, where, whereArgs);
-    }
-
-    @Override
-    public String getType(Uri url) {
-        return mSyncStorageEngine.getType(url);
-    }
-}
diff --git a/core/java/android/content/SyncStatusInfo.aidl b/core/java/android/content/SyncStatusInfo.aidl
new file mode 100644
index 0000000..b188c0b
--- /dev/null
+++ b/core/java/android/content/SyncStatusInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+parcelable SyncStatusInfo;
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
new file mode 100644
index 0000000..6687fcb
--- /dev/null
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/** @hide */
+public class SyncStatusInfo implements Parcelable {
+    static final int VERSION = 1;
+    
+    public final int authorityId;
+    public long totalElapsedTime;
+    public int numSyncs;
+    public int numSourcePoll;
+    public int numSourceServer;
+    public int numSourceLocal;
+    public int numSourceUser;
+    public long lastSuccessTime;
+    public int lastSuccessSource;
+    public long lastFailureTime;
+    public int lastFailureSource;
+    public String lastFailureMesg;
+    public long initialFailureTime;
+    public boolean pending;
+    
+    SyncStatusInfo(int authorityId) {
+        this.authorityId = authorityId;
+    }
+
+    public int getLastFailureMesgAsInt(int def) {
+        try {
+            if (lastFailureMesg != null) {
+                return Integer.parseInt(lastFailureMesg);
+            }
+        } catch (NumberFormatException e) {
+        }
+        return def;
+    }
+    
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(VERSION);
+        parcel.writeInt(authorityId);
+        parcel.writeLong(totalElapsedTime);
+        parcel.writeInt(numSyncs);
+        parcel.writeInt(numSourcePoll);
+        parcel.writeInt(numSourceServer);
+        parcel.writeInt(numSourceLocal);
+        parcel.writeInt(numSourceUser);
+        parcel.writeLong(lastSuccessTime);
+        parcel.writeInt(lastSuccessSource);
+        parcel.writeLong(lastFailureTime);
+        parcel.writeInt(lastFailureSource);
+        parcel.writeString(lastFailureMesg);
+        parcel.writeLong(initialFailureTime);
+        parcel.writeInt(pending ? 1 : 0);
+    }
+
+    SyncStatusInfo(Parcel parcel) {
+        int version = parcel.readInt();
+        if (version != VERSION) {
+            Log.w("SyncStatusInfo", "Unknown version: " + version);
+        }
+        authorityId = parcel.readInt();
+        totalElapsedTime = parcel.readLong();
+        numSyncs = parcel.readInt();
+        numSourcePoll = parcel.readInt();
+        numSourceServer = parcel.readInt();
+        numSourceLocal = parcel.readInt();
+        numSourceUser = parcel.readInt();
+        lastSuccessTime = parcel.readLong();
+        lastSuccessSource = parcel.readInt();
+        lastFailureTime = parcel.readLong();
+        lastFailureSource = parcel.readInt();
+        lastFailureMesg = parcel.readString();
+        initialFailureTime = parcel.readLong();
+        pending = parcel.readInt() != 0;
+    }
+    
+    public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
+        public SyncStatusInfo createFromParcel(Parcel in) {
+            return new SyncStatusInfo(in);
+        }
+
+        public SyncStatusInfo[] newArray(int size) {
+            return new SyncStatusInfo[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 282f6e7..1587462 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -1,125 +1,282 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.content;
 
-import android.Manifest;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.provider.Sync;
-import android.text.TextUtils;
-import android.util.Config;
-import android.util.Log;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Iterator;
+import java.util.TimeZone;
 
 /**
- * ContentProvider that tracks the sync data and overall sync
+ * Singleton that tracks the sync data and overall sync
  * history on the device.
  * 
  * @hide
  */
-public class SyncStorageEngine {
+public class SyncStorageEngine extends Handler {
     private static final String TAG = "SyncManager";
+    private static final boolean DEBUG = false;
+    private static final boolean DEBUG_FILE = false;
+    
+    // @VisibleForTesting
+    static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
 
-    private static final String DATABASE_NAME = "syncmanager.db";
-    private static final int DATABASE_VERSION = 10;
+    /** Enum value for a sync start event. */
+    public static final int EVENT_START = 0;
 
-    private static final int STATS = 1;
-    private static final int STATS_ID = 2;
-    private static final int HISTORY = 3;
-    private static final int HISTORY_ID = 4;
-    private static final int SETTINGS = 5;
-    private static final int PENDING = 7;
-    private static final int ACTIVE = 8;
-    private static final int STATUS = 9;
+    /** Enum value for a sync stop event. */
+    public static final int EVENT_STOP = 1;
 
-    private static final UriMatcher sURLMatcher =
-            new UriMatcher(UriMatcher.NO_MATCH);
+    // TODO: i18n -- grab these out of resources.
+    /** String names for the sync event types. */
+    public static final String[] EVENTS = { "START", "STOP" };
 
-    private static final HashMap<String,String> HISTORY_PROJECTION_MAP;
-    private static final HashMap<String,String> PENDING_PROJECTION_MAP;
-    private static final HashMap<String,String> ACTIVE_PROJECTION_MAP;
-    private static final HashMap<String,String> STATUS_PROJECTION_MAP;
+    /** Enum value for a server-initiated sync. */
+    public static final int SOURCE_SERVER = 0;
 
-    private final Context mContext;
-    private final SQLiteOpenHelper mOpenHelper;
-    private static SyncStorageEngine sSyncStorageEngine = null;
+    /** Enum value for a local-initiated sync. */
+    public static final int SOURCE_LOCAL = 1;
+    /**
+     * Enum value for a poll-based sync (e.g., upon connection to
+     * network)
+     */
+    public static final int SOURCE_POLL = 2;
 
-    static {
-        sURLMatcher.addURI("sync", "stats", STATS);
-        sURLMatcher.addURI("sync", "stats/#", STATS_ID);
-        sURLMatcher.addURI("sync", "history", HISTORY);
-        sURLMatcher.addURI("sync", "history/#", HISTORY_ID);
-        sURLMatcher.addURI("sync", "settings", SETTINGS);
-        sURLMatcher.addURI("sync", "status", STATUS);
-        sURLMatcher.addURI("sync", "active", ACTIVE);
-        sURLMatcher.addURI("sync", "pending", PENDING);
+    /** Enum value for a user-initiated sync. */
+    public static final int SOURCE_USER = 3;
 
-        HashMap<String,String> map;
-        PENDING_PROJECTION_MAP = map = new HashMap<String,String>();
-        map.put(Sync.History._ID, Sync.History._ID);
-        map.put(Sync.History.ACCOUNT, Sync.History.ACCOUNT);
-        map.put(Sync.History.AUTHORITY, Sync.History.AUTHORITY);
+    // TODO: i18n -- grab these out of resources.
+    /** String names for the sync source types. */
+    public static final String[] SOURCES = { "SERVER",
+                                             "LOCAL",
+                                             "POLL",
+                                             "USER" };
 
-        ACTIVE_PROJECTION_MAP = map = new HashMap<String,String>();
-        map.put(Sync.History._ID, Sync.History._ID);
-        map.put(Sync.History.ACCOUNT, Sync.History.ACCOUNT);
-        map.put(Sync.History.AUTHORITY, Sync.History.AUTHORITY);
-        map.put("startTime", "startTime");
+    // Error types
+    public static final int ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
+    public static final int ERROR_AUTHENTICATION = 2;
+    public static final int ERROR_IO = 3;
+    public static final int ERROR_PARSE = 4;
+    public static final int ERROR_CONFLICT = 5;
+    public static final int ERROR_TOO_MANY_DELETIONS = 6;
+    public static final int ERROR_TOO_MANY_RETRIES = 7;
+    public static final int ERROR_INTERNAL = 8;
 
-        HISTORY_PROJECTION_MAP = map = new HashMap<String,String>();
-        map.put(Sync.History._ID, "history._id as _id");
-        map.put(Sync.History.ACCOUNT, "stats.account as account");
-        map.put(Sync.History.AUTHORITY, "stats.authority as authority");
-        map.put(Sync.History.EVENT, Sync.History.EVENT);
-        map.put(Sync.History.EVENT_TIME, Sync.History.EVENT_TIME);
-        map.put(Sync.History.ELAPSED_TIME, Sync.History.ELAPSED_TIME);
-        map.put(Sync.History.SOURCE, Sync.History.SOURCE);
-        map.put(Sync.History.UPSTREAM_ACTIVITY, Sync.History.UPSTREAM_ACTIVITY);
-        map.put(Sync.History.DOWNSTREAM_ACTIVITY, Sync.History.DOWNSTREAM_ACTIVITY);
-        map.put(Sync.History.MESG, Sync.History.MESG);
+    // The MESG column will contain one of these or one of the Error types.
+    public static final String MESG_SUCCESS = "success";
+    public static final String MESG_CANCELED = "canceled";
 
-        STATUS_PROJECTION_MAP = map = new HashMap<String,String>();
-        map.put(Sync.Status._ID, "status._id as _id");
-        map.put(Sync.Status.ACCOUNT, "stats.account as account");
-        map.put(Sync.Status.AUTHORITY, "stats.authority as authority");
-        map.put(Sync.Status.TOTAL_ELAPSED_TIME, Sync.Status.TOTAL_ELAPSED_TIME);
-        map.put(Sync.Status.NUM_SYNCS, Sync.Status.NUM_SYNCS);
-        map.put(Sync.Status.NUM_SOURCE_LOCAL, Sync.Status.NUM_SOURCE_LOCAL);
-        map.put(Sync.Status.NUM_SOURCE_POLL, Sync.Status.NUM_SOURCE_POLL);
-        map.put(Sync.Status.NUM_SOURCE_SERVER, Sync.Status.NUM_SOURCE_SERVER);
-        map.put(Sync.Status.NUM_SOURCE_USER, Sync.Status.NUM_SOURCE_USER);
-        map.put(Sync.Status.LAST_SUCCESS_SOURCE, Sync.Status.LAST_SUCCESS_SOURCE);
-        map.put(Sync.Status.LAST_SUCCESS_TIME, Sync.Status.LAST_SUCCESS_TIME);
-        map.put(Sync.Status.LAST_FAILURE_SOURCE, Sync.Status.LAST_FAILURE_SOURCE);
-        map.put(Sync.Status.LAST_FAILURE_TIME, Sync.Status.LAST_FAILURE_TIME);
-        map.put(Sync.Status.LAST_FAILURE_MESG, Sync.Status.LAST_FAILURE_MESG);
-        map.put(Sync.Status.PENDING, Sync.Status.PENDING);
+    public static final int CHANGE_SETTINGS = 1<<0;
+    public static final int CHANGE_PENDING = 1<<1;
+    public static final int CHANGE_ACTIVE = 1<<2;
+    public static final int CHANGE_STATUS = 1<<3;
+    public static final int CHANGE_ALL = 0x7fffffff;
+    
+    public static final int MAX_HISTORY = 15;
+    
+    private static final int MSG_WRITE_STATUS = 1;
+    private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
+    
+    private static final int MSG_WRITE_STATISTICS = 2;
+    private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
+    
+    public static class PendingOperation {
+        final String account;
+        final int syncSource;
+        final String authority;
+        final Bundle extras;        // note: read-only.
+        
+        int authorityId;
+        byte[] flatExtras;
+        
+        PendingOperation(String account, int source,
+                String authority, Bundle extras) {
+            this.account = account;
+            this.syncSource = source;
+            this.authority = authority;
+            this.extras = extras != null ? new Bundle(extras) : extras;
+            this.authorityId = -1;
+        }
+
+        PendingOperation(PendingOperation other) {
+            this.account = other.account;
+            this.syncSource = other.syncSource;
+            this.authority = other.authority;
+            this.extras = other.extras;
+            this.authorityId = other.authorityId;
+        }
     }
+    
+    static class AccountInfo {
+        final String account;
+        final HashMap<String, AuthorityInfo> authorities =
+                new HashMap<String, AuthorityInfo>();
+        
+        AccountInfo(String account) {
+            this.account = account;
+        }
+    }
+    
+    public static class AuthorityInfo {
+        final String account;
+        final String authority;
+        final int ident;
+        boolean enabled;
+        
+        AuthorityInfo(String account, String authority, int ident) {
+            this.account = account;
+            this.authority = authority;
+            this.ident = ident;
+            enabled = true;
+        }
+    }
+    
+    public static class SyncHistoryItem {
+        int authorityId;
+        int historyId;
+        long eventTime;
+        long elapsedTime;
+        int source;
+        int event;
+        long upstreamActivity;
+        long downstreamActivity;
+        String mesg;
+    }
+    
+    public static class DayStats {
+        public final int day;
+        public int successCount;
+        public long successTime;
+        public int failureCount;
+        public long failureTime;
+        
+        public DayStats(int day) {
+            this.day = day;
+        }
+    }
+    
+    // Primary list of all syncable authorities.  Also our global lock.
+    private final SparseArray<AuthorityInfo> mAuthorities =
+            new SparseArray<AuthorityInfo>();
+    
+    private final HashMap<String, AccountInfo> mAccounts =
+        new HashMap<String, AccountInfo>();
 
-    private static final String[] STATS_ACCOUNT_PROJECTION =
-            new String[] { Sync.Stats.ACCOUNT };
-
-    private static final int MAX_HISTORY_EVENTS_TO_KEEP = 5000;
-
-    private static final String SELECT_INITIAL_FAILURE_TIME_QUERY_STRING = ""
-            + "SELECT min(a) "
-            + "FROM ("
-            + "  SELECT initialFailureTime AS a "
-            + "  FROM status "
-            + "  WHERE stats_id=? AND a IS NOT NULL "
-            + "    UNION "
-            + "  SELECT ? AS a"
-            + " )";
-
+    private final ArrayList<PendingOperation> mPendingOperations =
+            new ArrayList<PendingOperation>();
+    
+    private ActiveSyncInfo mActiveSync;
+    
+    private final SparseArray<SyncStatusInfo> mSyncStatus =
+            new SparseArray<SyncStatusInfo>();
+    
+    private final ArrayList<SyncHistoryItem> mSyncHistory =
+            new ArrayList<SyncHistoryItem>();
+    
+    private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
+            = new RemoteCallbackList<ISyncStatusObserver>();
+    
+    // We keep 4 weeks of stats.
+    private final DayStats[] mDayStats = new DayStats[7*4];
+    private final Calendar mCal;
+    private int mYear;
+    private int mYearInDays;
+    
+    private final Context mContext;
+    private static volatile SyncStorageEngine sSyncStorageEngine = null;
+    
+    /**
+     * This file contains the core engine state: all accounts and the
+     * settings for them.  It must never be lost, and should be changed
+     * infrequently, so it is stored as an XML file.
+     */
+    private final AtomicFile mAccountInfoFile;
+    
+    /**
+     * This file contains the current sync status.  We would like to retain
+     * it across boots, but its loss is not the end of the world, so we store
+     * this information as binary data.
+     */
+    private final AtomicFile mStatusFile;
+    
+    /**
+     * This file contains sync statistics.  This is purely debugging information
+     * so is written infrequently and can be thrown away at any time.
+     */
+    private final AtomicFile mStatisticsFile;
+    
+    /**
+     * This file contains the pending sync operations.  It is a binary file,
+     * which must be updated every time an operation is added or removed,
+     * so we have special handling of it.
+     */
+    private final AtomicFile mPendingFile;
+    private static final int PENDING_FINISH_TO_WRITE = 4;
+    private int mNumPendingFinished = 0;
+    
+    private int mNextHistoryId = 0;
+    private boolean mListenForTickles = true;
+    
     private SyncStorageEngine(Context context) {
         mContext = context;
-        mOpenHelper = new SyncStorageEngine.DatabaseHelper(context);
         sSyncStorageEngine = this;
+        
+        mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
+        
+        File dataDir = Environment.getDataDirectory();
+        File systemDir = new File(dataDir, "system");
+        File syncDir = new File(systemDir, "sync");
+        mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
+        mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
+        mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
+        mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
+        
+        readAccountInfoLocked();
+        readStatusLocked();
+        readPendingOperationsLocked();
+        readStatisticsLocked();
+        readLegacyAccountInfoLocked();
     }
 
     public static SyncStorageEngine newTestInstance(Context context) {
@@ -140,619 +297,1249 @@
         return sSyncStorageEngine;
     }
 
-    private class DatabaseHelper extends SQLiteOpenHelper {
-        DatabaseHelper(Context context) {
-            super(context, DATABASE_NAME, null, DATABASE_VERSION);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE pending ("
-                    + "_id INTEGER PRIMARY KEY,"
-                    + "authority TEXT NOT NULL,"
-                    + "account TEXT NOT NULL,"
-                    + "extras BLOB NOT NULL,"
-                    + "source INTEGER NOT NULL"
-                    + ");");
-
-            db.execSQL("CREATE TABLE stats (" +
-                       "_id INTEGER PRIMARY KEY," +
-                       "account TEXT, " +
-                       "authority TEXT, " +
-                       "syncdata TEXT, " +
-                       "UNIQUE (account, authority)" +
-                       ");");
-
-            db.execSQL("CREATE TABLE history (" +
-                       "_id INTEGER PRIMARY KEY," +
-                       "stats_id INTEGER," +
-                       "eventTime INTEGER," +
-                       "elapsedTime INTEGER," +
-                       "source INTEGER," +
-                       "event INTEGER," +
-                       "upstreamActivity INTEGER," +
-                       "downstreamActivity INTEGER," +
-                       "mesg TEXT);");
-
-            db.execSQL("CREATE TABLE status ("
-                    + "_id INTEGER PRIMARY KEY,"
-                    + "stats_id INTEGER NOT NULL,"
-                    + "totalElapsedTime INTEGER NOT NULL DEFAULT 0,"
-                    + "numSyncs INTEGER NOT NULL DEFAULT 0,"
-                    + "numSourcePoll INTEGER NOT NULL DEFAULT 0,"
-                    + "numSourceServer INTEGER NOT NULL DEFAULT 0,"
-                    + "numSourceLocal INTEGER NOT NULL DEFAULT 0,"
-                    + "numSourceUser INTEGER NOT NULL DEFAULT 0,"
-                    + "lastSuccessTime INTEGER,"
-                    + "lastSuccessSource INTEGER,"
-                    + "lastFailureTime INTEGER,"
-                    + "lastFailureSource INTEGER,"
-                    + "lastFailureMesg STRING,"
-                    + "initialFailureTime INTEGER,"
-                    + "pending INTEGER NOT NULL DEFAULT 0);");
-
-            db.execSQL("CREATE TABLE active ("
-                    + "_id INTEGER PRIMARY KEY,"
-                    + "authority TEXT,"
-                    + "account TEXT,"
-                    + "startTime INTEGER);");
-
-            db.execSQL("CREATE INDEX historyEventTime ON history (eventTime)");
-
-            db.execSQL("CREATE TABLE settings (" +
-                       "name TEXT PRIMARY KEY," +
-                       "value TEXT);");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion == 9 && newVersion == 10) {
-                Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
-                        + newVersion + ", which will preserve old data");
-                db.execSQL("ALTER TABLE status ADD COLUMN initialFailureTime INTEGER");
-                return;
+    @Override public void handleMessage(Message msg) {
+        if (msg.what == MSG_WRITE_STATUS) {
+            synchronized (mAccounts) {
+                writeStatusLocked();
             }
-
-            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
-                    + newVersion + ", which will destroy all old data");
-            db.execSQL("DROP TABLE IF EXISTS pending");
-            db.execSQL("DROP TABLE IF EXISTS stats");
-            db.execSQL("DROP TABLE IF EXISTS history");
-            db.execSQL("DROP TABLE IF EXISTS settings");
-            db.execSQL("DROP TABLE IF EXISTS active");
-            db.execSQL("DROP TABLE IF EXISTS status");
-            onCreate(db);
-        }
-
-        @Override
-        public void onOpen(SQLiteDatabase db) {
-            if (!db.isReadOnly()) {
-                db.delete("active", null, null);
-                db.insert("active", "account", null);
+        } else if (msg.what == MSG_WRITE_STATISTICS) {
+            synchronized (mAccounts) {
+                writeStatisticsLocked();
             }
         }
     }
-
-    protected void doDatabaseCleanup(String[] accounts) {
-        HashSet<String> currentAccounts = new HashSet<String>();
-        for (String account : accounts) currentAccounts.add(account);
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        Cursor cursor = db.query("stats", STATS_ACCOUNT_PROJECTION,
-                null /* where */, null /* where args */, Sync.Stats.ACCOUNT,
-                null /* having */, null /* order by */);
-        try {
-            while (cursor.moveToNext()) {
-                String account = cursor.getString(0);
-                if (TextUtils.isEmpty(account)) {
+    
+    public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+        synchronized (mAuthorities) {
+            mChangeListeners.register(callback, mask);
+        }
+    }
+    
+    public void removeStatusChangeListener(ISyncStatusObserver callback) {
+        synchronized (mAuthorities) {
+            mChangeListeners.unregister(callback);
+        }
+    }
+    
+    private void reportChange(int which) {
+        ArrayList<ISyncStatusObserver> reports = null;
+        synchronized (mAuthorities) {
+            int i = mChangeListeners.beginBroadcast();
+            while (i > 0) {
+                i--;
+                Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
+                if ((which & mask.intValue()) == 0) {
                     continue;
                 }
-                if (!currentAccounts.contains(account)) {
-                    String where = Sync.Stats.ACCOUNT + "=?";
-                    int numDeleted;
-                    numDeleted = db.delete("stats", where, new String[]{account});
-                    if (Config.LOGD) {
-                        Log.d(TAG, "deleted " + numDeleted
-                                + " records from stats table"
-                                + " for account " + account);
+                if (reports == null) {
+                    reports = new ArrayList<ISyncStatusObserver>(i);
+                }
+                reports.add(mChangeListeners.getBroadcastItem(i));
+            }
+        }
+        
+        if (DEBUG) Log.v(TAG, "reportChange " + which + " to: " + reports);
+        
+        if (reports != null) {
+            int i = reports.size();
+            while (i > 0) {
+                i--;
+                try {
+                    reports.get(i).onStatusChanged(which);
+                } catch (RemoteException e) {
+                    // The remote callback list will take care of this for us.
+                }
+            }
+        }
+    }
+    
+    public boolean getSyncProviderAutomatically(String account, String providerName) {
+        synchronized (mAuthorities) {
+            if (account != null) {
+                AuthorityInfo authority = getAuthorityLocked(account, providerName,
+                        "getSyncProviderAutomatically");
+                return authority != null ? authority.enabled : false;
+            }
+            
+            int i = mAuthorities.size();
+            while (i > 0) {
+                i--;
+                AuthorityInfo authority = mAuthorities.get(i);
+                if (authority.authority.equals(providerName)
+                        && authority.enabled) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    public void setSyncProviderAutomatically(String account, String providerName, boolean sync) {
+        synchronized (mAuthorities) {
+            if (account != null) {
+                AuthorityInfo authority = getAuthorityLocked(account, providerName,
+                        "setSyncProviderAutomatically");
+                if (authority != null) {
+                    authority.enabled = sync;
+                }
+            } else {
+                int i = mAuthorities.size();
+                while (i > 0) {
+                    i--;
+                    AuthorityInfo authority = mAuthorities.get(i);
+                    if (authority.account.equals(account)
+                            && authority.authority.equals(providerName)) {
+                        authority.enabled = sync;
                     }
                 }
             }
-        } finally {
-            cursor.close();
+            writeAccountInfoLocked();
         }
-    }
-
-    protected void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
-        if (activeSyncContext != null) {
-            updateActiveSync(activeSyncContext.mSyncOperation.account,
-                    activeSyncContext.mSyncOperation.authority, activeSyncContext.mStartTime);
-        } else {
-            // we indicate that the sync is not active by passing null for all the parameters
-            updateActiveSync(null, null, null);
-        }
-    }
-
-    private int updateActiveSync(String account, String authority, Long startTime) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        ContentValues values = new ContentValues();
-        values.put("account", account);
-        values.put("authority", authority);
-        values.put("startTime", startTime);
-        int numChanges = db.update("active", values, null, null);
-        if (numChanges > 0) {
-            mContext.getContentResolver().notifyChange(Sync.Active.CONTENT_URI,
-                    null /* this change wasn't made through an observer */);
-        }
-        return numChanges;
-    }
-
-    /**
-     * Implements the {@link ContentProvider#query} method
-     */
-    public Cursor query(Uri url, String[] projectionIn,
-            String selection, String[] selectionArgs, String sort) {
-        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-
-        // Generate the body of the query
-        int match = sURLMatcher.match(url);
-        String groupBy = null;
-        switch (match) {
-            case STATS:
-                qb.setTables("stats");
-                break;
-            case STATS_ID:
-                qb.setTables("stats");
-                qb.appendWhere("_id=");
-                qb.appendWhere(url.getPathSegments().get(1));
-                break;
-            case HISTORY:
-                // join the stats and history tables, so the caller can get
-                // the account and authority information as part of this query.
-                qb.setTables("stats, history");
-                qb.setProjectionMap(HISTORY_PROJECTION_MAP);
-                qb.appendWhere("stats._id = history.stats_id");
-                break;
-            case ACTIVE:
-                qb.setTables("active");
-                qb.setProjectionMap(ACTIVE_PROJECTION_MAP);
-                qb.appendWhere("account is not null");
-                break;
-            case PENDING:
-                qb.setTables("pending");
-                qb.setProjectionMap(PENDING_PROJECTION_MAP);
-                groupBy = "account, authority";
-                break;
-            case STATUS:
-                // join the stats and status tables, so the caller can get
-                // the account and authority information as part of this query.
-                qb.setTables("stats, status");
-                qb.setProjectionMap(STATUS_PROJECTION_MAP);
-                qb.appendWhere("stats._id = status.stats_id");
-                break;
-            case HISTORY_ID:
-                // join the stats and history tables, so the caller can get
-                // the account and authority information as part of this query.
-                qb.setTables("stats, history");
-                qb.setProjectionMap(HISTORY_PROJECTION_MAP);
-                qb.appendWhere("stats._id = history.stats_id");
-                qb.appendWhere("AND history._id=");
-                qb.appendWhere(url.getPathSegments().get(1));
-                break;
-            case SETTINGS:
-                qb.setTables("settings");
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown URL " + url);
-        }
-
-        if (match == SETTINGS) {
-            mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
-                    "no permission to read the sync settings");
-        } else {
-            mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
-                    "no permission to read the sync stats");
-        }
-        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-        Cursor c = qb.query(db, projectionIn, selection, selectionArgs, groupBy, null, sort);
-        c.setNotificationUri(mContext.getContentResolver(), url);
-        return c;
-    }
-
-    /**
-     * Implements the {@link ContentProvider#insert} method
-     * @param callerIsTheProvider true if this is being called via the
-     *  {@link ContentProvider#insert} in method rather than directly.
-     * @throws UnsupportedOperationException if callerIsTheProvider is true and the url isn't
-     *   for the Settings table.
-     */
-    public Uri insert(boolean callerIsTheProvider, Uri url, ContentValues values) {
-        String table;
-        long rowID;
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        final int match = sURLMatcher.match(url);
-        checkCaller(callerIsTheProvider, match);
-        switch (match) {
-            case SETTINGS:
-                mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
-                        "no permission to write the sync settings");
-                table = "settings";
-                rowID = db.replace(table, null, values);
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown URL " + url);
-        }
-
-
-        if (rowID > 0) {
-            mContext.getContentResolver().notifyChange(url, null /* observer */);
-            return Uri.parse("content://sync/" + table + "/" + rowID);
-        }
-
-        return null;
-    }
-
-    private static void checkCaller(boolean callerIsTheProvider, int match) {
-        if (callerIsTheProvider && match != SETTINGS) {
-            throw new UnsupportedOperationException(
-                    "only the settings are modifiable via the ContentProvider interface");
-        }
-    }
-
-    /**
-     * Implements the {@link ContentProvider#delete} method
-     * @param callerIsTheProvider true if this is being called via the
-     *  {@link ContentProvider#delete} in method rather than directly.
-     * @throws UnsupportedOperationException if callerIsTheProvider is true and the url isn't
-     *   for the Settings table.
-     */
-    public int delete(boolean callerIsTheProvider, Uri url, String where, String[] whereArgs) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        int match = sURLMatcher.match(url);
-
-        int numRows;
-        switch (match) {
-            case SETTINGS:
-                mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
-                        "no permission to write the sync settings");
-                numRows = db.delete("settings", where, whereArgs);
-                break;
-            default:
-                throw new UnsupportedOperationException("Cannot delete URL: " + url);
-        }
-
-        if (numRows > 0) {
-            mContext.getContentResolver().notifyChange(url, null /* observer */);
-        }
-        return numRows;
-    }
-
-    /**
-     * Implements the {@link ContentProvider#update} method
-     * @param callerIsTheProvider true if this is being called via the
-     *  {@link ContentProvider#update} in method rather than directly.
-     * @throws UnsupportedOperationException if callerIsTheProvider is true and the url isn't
-     *   for the Settings table.
-     */
-    public int update(boolean callerIsTheProvider, Uri url, ContentValues initialValues,
-            String where, String[] whereArgs) {
-        switch (sURLMatcher.match(url)) {
-            case SETTINGS:
-                throw new UnsupportedOperationException("updating url " + url
-                        + " is not allowed, use insert instead");
-            default:
-                throw new UnsupportedOperationException("Cannot update URL: " + url);
-        }
-    }
-
-    /**
-     * Implements the {@link ContentProvider#getType} method
-     */
-    public String getType(Uri url) {
-        int match = sURLMatcher.match(url);
-        switch (match) {
-            case SETTINGS:
-                return "vnd.android.cursor.dir/sync-settings";
-            default:
-                throw new IllegalArgumentException("Unknown URL");
-        }
-    }
-
-    protected Uri insertIntoPending(ContentValues values) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        try {
-            db.beginTransaction();
-            long rowId = db.insert("pending", Sync.Pending.ACCOUNT, values);
-            if (rowId < 0) return null;
-            String account = values.getAsString(Sync.Pending.ACCOUNT);
-            String authority = values.getAsString(Sync.Pending.AUTHORITY);
-
-            long statsId = createStatsRowIfNecessary(account, authority);
-            createStatusRowIfNecessary(statsId);
-
-            values.clear();
-            values.put(Sync.Status.PENDING, 1);
-            int numUpdatesStatus = db.update("status", values, "stats_id=" + statsId, null);
-
-            db.setTransactionSuccessful();
-
-            mContext.getContentResolver().notifyChange(Sync.Pending.CONTENT_URI,
-                    null /* no observer initiated this change */);
-            if (numUpdatesStatus > 0) {
-                mContext.getContentResolver().notifyChange(Sync.Status.CONTENT_URI,
-                        null /* no observer initiated this change */);
-            }
-            return ContentUris.withAppendedId(Sync.Pending.CONTENT_URI, rowId);
-        } finally {
-            db.endTransaction();
-        }
-    }
-
-    int deleteFromPending(long rowId) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
-            String account;
-            String authority;
-            Cursor c = db.query("pending",
-                    new String[]{Sync.Pending.ACCOUNT, Sync.Pending.AUTHORITY},
-                    "_id=" + rowId, null, null, null, null);
-            try {
-                if (c.getCount() != 1) {
-                    return 0;
-                }
-                c.moveToNext();
-                account = c.getString(0);
-                authority = c.getString(1);
-            } finally {
-                c.close();
-            }
-            db.delete("pending", "_id=" + rowId, null /* no where args */);
-            final String[] accountAuthorityWhereArgs = new String[]{account, authority};
-            boolean isPending = 0 < DatabaseUtils.longForQuery(db,
-                    "SELECT COUNT(*) FROM PENDING WHERE account=? AND authority=?",
-                    accountAuthorityWhereArgs);
-            if (!isPending) {
-                long statsId = createStatsRowIfNecessary(account, authority);
-                db.execSQL("UPDATE status SET pending=0 WHERE stats_id=" + statsId);
-            }
-            db.setTransactionSuccessful();
-
-            mContext.getContentResolver().notifyChange(Sync.Pending.CONTENT_URI,
-                    null /* no observer initiated this change */);
-            if (!isPending) {
-                mContext.getContentResolver().notifyChange(Sync.Status.CONTENT_URI,
-                        null /* no observer initiated this change */);
-            }
-            return 1;
-        } finally {
-            db.endTransaction();
-        }
-    }
-
-    int clearPending() {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
-            int numChanges = db.delete("pending", null, null /* no where args */);
-            if (numChanges > 0) {
-                db.execSQL("UPDATE status SET pending=0");
-                mContext.getContentResolver().notifyChange(Sync.Pending.CONTENT_URI,
-                        null /* no observer initiated this change */);
-                mContext.getContentResolver().notifyChange(Sync.Status.CONTENT_URI,
-                        null /* no observer initiated this change */);
-            }
-            db.setTransactionSuccessful();
-            return numChanges;
-        } finally {
-            db.endTransaction();
-        }
-    }
-
-    /**
-     * Returns a cursor over all the pending syncs in no particular order. This cursor is not
-     * "live", in that if changes are made to the pending table any observers on this cursor
-     * will not be notified.
-     * @param projection Return only these columns. If null then all columns are returned.
-     * @return the cursor of pending syncs
-     */
-    public Cursor getPendingSyncsCursor(String[] projection) {
-        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-        return db.query("pending", projection, null, null, null, null, null);
-    }
-
-    // @VisibleForTesting
-    static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
-
-    private boolean purgeOldHistoryEvents(long now) {
-        // remove events that are older than MILLIS_IN_4WEEKS
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        int numDeletes = db.delete("history", "eventTime<" + (now - MILLIS_IN_4WEEKS), null);
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            if (numDeletes > 0) {
-                Log.v(TAG, "deleted " + numDeletes + " old event(s) from the sync history");
-            }
-        }
-
-        // keep only the last MAX_HISTORY_EVENTS_TO_KEEP history events
-        numDeletes += db.delete("history", "eventTime < (select min(eventTime) from "
-                + "(select eventTime from history order by eventTime desc limit ?))",
-                new String[]{String.valueOf(MAX_HISTORY_EVENTS_TO_KEEP)});
         
-        return numDeletes > 0;
+        reportChange(CHANGE_SETTINGS);
     }
 
-    public long insertStartSyncEvent(String account, String authority, long now, int source) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        long statsId = createStatsRowIfNecessary(account, authority);
+    public void setListenForNetworkTickles(boolean flag) {
+        synchronized (mAuthorities) {
+            mListenForTickles = flag;
+            writeAccountInfoLocked();
+        }
+        reportChange(CHANGE_SETTINGS);
+    }
 
-        purgeOldHistoryEvents(now);
-        ContentValues values = new ContentValues();
-        values.put(Sync.History.STATS_ID, statsId);
-        values.put(Sync.History.EVENT_TIME, now);
-        values.put(Sync.History.SOURCE, source);
-        values.put(Sync.History.EVENT, Sync.History.EVENT_START);
-        long rowId = db.insert("history", null, values);
-        mContext.getContentResolver().notifyChange(Sync.History.CONTENT_URI, null /* observer */);
-        mContext.getContentResolver().notifyChange(Sync.Status.CONTENT_URI, null /* observer */);
-        return rowId;
+    public boolean getListenForNetworkTickles() {
+        synchronized (mAuthorities) {
+            return mListenForTickles;
+        }
+    }
+    
+    public AuthorityInfo getAuthority(String account, String authority) {
+        synchronized (mAuthorities) {
+            return getAuthorityLocked(account, authority, null);
+        }
+    }
+    
+    public AuthorityInfo getAuthority(int authorityId) {
+        synchronized (mAuthorities) {
+            return mAuthorities.get(authorityId);
+        }
+    }
+    
+    /**
+     * Returns true if there is currently a sync operation for the given
+     * account or authority in the pending list, or actively being processed.
+     */
+    public boolean isSyncActive(String account, String authority) {
+        synchronized (mAuthorities) {
+            int i = mPendingOperations.size();
+            while (i > 0) {
+                i--;
+                // TODO(fredq): this probably shouldn't be considering
+                // pending operations.
+                PendingOperation op = mPendingOperations.get(i);
+                if (op.account.equals(account) && op.authority.equals(authority)) {
+                    return true;
+                }
+            }
+            
+            if (mActiveSync != null) {
+                AuthorityInfo ainfo = getAuthority(mActiveSync.authorityId);
+                if (ainfo != null && ainfo.account.equals(account)
+                        && ainfo.authority.equals(authority)) {
+                    return true;
+                }
+            }
+        }
+        
+        return false;
+    }
+    
+    public PendingOperation insertIntoPending(PendingOperation op) {
+        synchronized (mAuthorities) {
+            if (DEBUG) Log.v(TAG, "insertIntoPending: account=" + op.account
+                    + " auth=" + op.authority
+                    + " src=" + op.syncSource
+                    + " extras=" + op.extras);
+            
+            AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
+                    op.authority,
+                    -1 /* desired identifier */,
+                    true /* write accounts to storage */);
+            if (authority == null) {
+                return null;
+            }
+            
+            op = new PendingOperation(op);
+            op.authorityId = authority.ident;
+            mPendingOperations.add(op);
+            appendPendingOperationLocked(op);
+            
+            SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
+            status.pending = true;
+        }
+        
+        reportChange(CHANGE_PENDING);
+        return op;
+    }
+
+    public boolean deleteFromPending(PendingOperation op) {
+        boolean res = false;
+        synchronized (mAuthorities) {
+            if (DEBUG) Log.v(TAG, "deleteFromPending: account=" + op.account
+                    + " auth=" + op.authority
+                    + " src=" + op.syncSource
+                    + " extras=" + op.extras);
+            if (mPendingOperations.remove(op)) {
+                if (mPendingOperations.size() == 0
+                        || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
+                    writePendingOperationsLocked();
+                    mNumPendingFinished = 0;
+                } else {
+                    mNumPendingFinished++;
+                }
+                
+                AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
+                        "deleteFromPending");
+                if (authority != null) {
+                    if (DEBUG) Log.v(TAG, "removing - " + authority);
+                    final int N = mPendingOperations.size();
+                    boolean morePending = false;
+                    for (int i=0; i<N; i++) {
+                        PendingOperation cur = mPendingOperations.get(i);
+                        if (cur.account.equals(op.account)
+                                && cur.authority.equals(op.authority)) {
+                            morePending = true;
+                            break;
+                        }
+                    }
+                    
+                    if (!morePending) {
+                        if (DEBUG) Log.v(TAG, "no more pending!");
+                        SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
+                        status.pending = false;
+                    }
+                }
+                
+                res = true;
+            }
+        }
+        
+        reportChange(CHANGE_PENDING);
+        return res;
+    }
+
+    public int clearPending() {
+        int num;
+        synchronized (mAuthorities) {
+            if (DEBUG) Log.v(TAG, "clearPending");
+            num = mPendingOperations.size();
+            mPendingOperations.clear();
+            final int N = mSyncStatus.size();
+            for (int i=0; i<N; i++) {
+                mSyncStatus.get(i).pending = false;
+            }
+            writePendingOperationsLocked();
+        }
+        reportChange(CHANGE_PENDING);
+        return num;
+    }
+
+    /**
+     * Return a copy of the current array of pending operations.  The
+     * PendingOperation objects are the real objects stored inside, so that
+     * they can be used with deleteFromPending().
+     */
+    public ArrayList<PendingOperation> getPendingOperations() {
+        synchronized (mAuthorities) {
+            return new ArrayList<PendingOperation>(mPendingOperations);
+        }
+    }
+    
+    /**
+     * Return the number of currently pending operations.
+     */
+    public int getPendingOperationCount() {
+        synchronized (mAuthorities) {
+            return mPendingOperations.size();
+        }
+    }
+    
+    /**
+     * Called when the set of account has changed, given the new array of
+     * active accounts.
+     */
+    public void doDatabaseCleanup(String[] accounts) {
+        synchronized (mAuthorities) {
+            if (DEBUG) Log.w(TAG, "Updating for new accounts...");
+            SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
+            Iterator<AccountInfo> accIt = mAccounts.values().iterator();
+            while (accIt.hasNext()) {
+                AccountInfo acc = accIt.next();
+                if (!ArrayUtils.contains(accounts, acc.account)) {
+                    // This account no longer exists...
+                    if (DEBUG) Log.w(TAG, "Account removed: " + acc.account);
+                    for (AuthorityInfo auth : acc.authorities.values()) {
+                        removing.put(auth.ident, auth);
+                    }
+                    accIt.remove();
+                }
+            }
+            
+            // Clean out all data structures.
+            int i = removing.size();
+            if (i > 0) {
+                while (i > 0) {
+                    i--;
+                    int ident = removing.keyAt(i);
+                    mAuthorities.remove(ident);
+                    int j = mSyncStatus.size();
+                    while (j > 0) {
+                        j--;
+                        if (mSyncStatus.keyAt(j) == ident) {
+                            mSyncStatus.remove(mSyncStatus.keyAt(j));
+                        }
+                    }
+                    j = mSyncHistory.size();
+                    while (j > 0) {
+                        j--;
+                        if (mSyncHistory.get(j).authorityId == ident) {
+                            mSyncHistory.remove(j);
+                        }
+                    }
+                }
+                writeAccountInfoLocked();
+                writeStatusLocked();
+                writePendingOperationsLocked();
+                writeStatisticsLocked();
+            }
+        }
+    }
+
+    /**
+     * Called when the currently active sync is changing (there can only be
+     * one at a time).  Either supply a valid ActiveSyncContext with information
+     * about the sync, or null to stop the currently active sync.
+     */
+    public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
+        synchronized (mAuthorities) {
+            if (activeSyncContext != null) {
+                if (DEBUG) Log.v(TAG, "setActiveSync: account="
+                        + activeSyncContext.mSyncOperation.account
+                        + " auth=" + activeSyncContext.mSyncOperation.authority
+                        + " src=" + activeSyncContext.mSyncOperation.syncSource
+                        + " extras=" + activeSyncContext.mSyncOperation.extras);
+                if (mActiveSync != null) {
+                    Log.w(TAG, "setActiveSync called with existing active sync!");
+                }
+                AuthorityInfo authority = getAuthorityLocked(
+                        activeSyncContext.mSyncOperation.account,
+                        activeSyncContext.mSyncOperation.authority,
+                        "setActiveSync");
+                if (authority == null) {
+                    return;
+                }
+                mActiveSync = new ActiveSyncInfo(authority.ident,
+                        authority.account, authority.authority,
+                        activeSyncContext.mStartTime);
+            } else {
+                if (DEBUG) Log.v(TAG, "setActiveSync: null");
+                mActiveSync = null;
+            }
+        }
+        
+        reportChange(CHANGE_ACTIVE);
+    }
+
+    /**
+     * To allow others to send active change reports, to poke clients.
+     */
+    public void reportActiveChange() {
+        reportChange(CHANGE_ACTIVE);
+    }
+    
+    /**
+     * Note that sync has started for the given account and authority.
+     */
+    public long insertStartSyncEvent(String accountName, String authorityName,
+            long now, int source) {
+        long id;
+        synchronized (mAuthorities) {
+            if (DEBUG) Log.v(TAG, "insertStartSyncEvent: account=" + accountName
+                    + " auth=" + authorityName + " source=" + source);
+            AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
+                    "insertStartSyncEvent");
+            if (authority == null) {
+                return -1;
+            }
+            SyncHistoryItem item = new SyncHistoryItem();
+            item.authorityId = authority.ident;
+            item.historyId = mNextHistoryId++;
+            if (mNextHistoryId < 0) mNextHistoryId = 0;
+            item.eventTime = now;
+            item.source = source;
+            item.event = EVENT_START;
+            mSyncHistory.add(0, item);
+            while (mSyncHistory.size() > MAX_HISTORY) {
+                mSyncHistory.remove(mSyncHistory.size()-1);
+            }
+            id = item.historyId;
+            if (DEBUG) Log.v(TAG, "returning historyId " + id);
+        }
+        
+        reportChange(CHANGE_STATUS);
+        return id;
     }
 
     public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
             long downstreamActivity, long upstreamActivity) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
-            ContentValues values = new ContentValues();
-            values.put(Sync.History.ELAPSED_TIME, elapsedTime);
-            values.put(Sync.History.EVENT, Sync.History.EVENT_STOP);
-            values.put(Sync.History.MESG, resultMessage);
-            values.put(Sync.History.DOWNSTREAM_ACTIVITY, downstreamActivity);
-            values.put(Sync.History.UPSTREAM_ACTIVITY, upstreamActivity);
-
-            int count = db.update("history", values, "_id=?",
-                    new String[]{Long.toString(historyId)});
-            // We think that count should always be 1 but don't want to change this until after
-            // launch.
-            if (count > 0) {
-                int source = (int) DatabaseUtils.longForQuery(db,
-                        "SELECT source FROM history WHERE _id=" + historyId, null);
-                long eventTime = DatabaseUtils.longForQuery(db,
-                        "SELECT eventTime FROM history WHERE _id=" + historyId, null);
-                long statsId = DatabaseUtils.longForQuery(db,
-                        "SELECT stats_id FROM history WHERE _id=" + historyId, null);
-
-                createStatusRowIfNecessary(statsId);
-
-                // update the status table to reflect this sync
-                StringBuilder sb = new StringBuilder();
-                ArrayList<String> bindArgs = new ArrayList<String>();
-                sb.append("UPDATE status SET");
-                sb.append(" numSyncs=numSyncs+1");
-                sb.append(", totalElapsedTime=totalElapsedTime+" + elapsedTime);
-                switch (source) {
-                    case Sync.History.SOURCE_LOCAL:
-                        sb.append(", numSourceLocal=numSourceLocal+1");
-                        break;
-                    case Sync.History.SOURCE_POLL:
-                        sb.append(", numSourcePoll=numSourcePoll+1");
-                        break;
-                    case Sync.History.SOURCE_USER:
-                        sb.append(", numSourceUser=numSourceUser+1");
-                        break;
-                    case Sync.History.SOURCE_SERVER:
-                        sb.append(", numSourceServer=numSourceServer+1");
-                        break;
+        synchronized (mAuthorities) {
+            if (DEBUG) Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
+            SyncHistoryItem item = null;
+            int i = mSyncHistory.size();
+            while (i > 0) {
+                i--;
+                item = mSyncHistory.get(i);
+                if (item.historyId == historyId) {
+                    break;
                 }
-
-                final String statsIdString = String.valueOf(statsId);
-                final long lastSyncTime = (eventTime + elapsedTime);
-                if (Sync.History.MESG_SUCCESS.equals(resultMessage)) {
-                    // - if successful, update the successful columns
-                    sb.append(", lastSuccessTime=" + lastSyncTime);
-                    sb.append(", lastSuccessSource=" + source);
-                    sb.append(", lastFailureTime=null");
-                    sb.append(", lastFailureSource=null");
-                    sb.append(", lastFailureMesg=null");
-                    sb.append(", initialFailureTime=null");
-                } else if (!Sync.History.MESG_CANCELED.equals(resultMessage)) {
-                    sb.append(", lastFailureTime=" + lastSyncTime);
-                    sb.append(", lastFailureSource=" + source);
-                    sb.append(", lastFailureMesg=?");
-                    bindArgs.add(resultMessage);
-                    long initialFailureTime = DatabaseUtils.longForQuery(db,
-                            SELECT_INITIAL_FAILURE_TIME_QUERY_STRING, 
-                            new String[]{statsIdString, String.valueOf(lastSyncTime)});
-                    sb.append(", initialFailureTime=" + initialFailureTime);
-                }
-                sb.append(" WHERE stats_id=?");
-                bindArgs.add(statsIdString);
-                db.execSQL(sb.toString(), bindArgs.toArray());
-                db.setTransactionSuccessful();
-                mContext.getContentResolver().notifyChange(Sync.History.CONTENT_URI,
-                        null /* observer */);
-                mContext.getContentResolver().notifyChange(Sync.Status.CONTENT_URI,
-                        null /* observer */);
+                item = null;
             }
-        } finally {
-            db.endTransaction();
+            
+            if (item == null) {
+                Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
+                return;
+            }
+            
+            item.elapsedTime = elapsedTime;
+            item.event = EVENT_STOP;
+            item.mesg = resultMessage;
+            item.downstreamActivity = downstreamActivity;
+            item.upstreamActivity = upstreamActivity;
+            
+            SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
+            
+            status.numSyncs++;
+            status.totalElapsedTime += elapsedTime;
+            switch (item.source) {
+                case SOURCE_LOCAL:
+                    status.numSourceLocal++;
+                    break;
+                case SOURCE_POLL:
+                    status.numSourcePoll++;
+                    break;
+                case SOURCE_USER:
+                    status.numSourceUser++;
+                    break;
+                case SOURCE_SERVER:
+                    status.numSourceServer++;
+                    break;
+            }
+            
+            boolean writeStatisticsNow = false;
+            int day = getCurrentDay();
+            if (mDayStats[0] == null) {
+                mDayStats[0] = new DayStats(day);
+            } else if (day != mDayStats[0].day) {
+                System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
+                mDayStats[0] = new DayStats(day);
+                writeStatisticsNow = true;
+            } else if (mDayStats[0] == null) {
+            }
+            final DayStats ds = mDayStats[0];
+            
+            final long lastSyncTime = (item.eventTime + elapsedTime);
+            boolean writeStatusNow = false;
+            if (MESG_SUCCESS.equals(resultMessage)) {
+                // - if successful, update the successful columns
+                if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
+                    writeStatusNow = true;
+                }
+                status.lastSuccessTime = lastSyncTime;
+                status.lastSuccessSource = item.source;
+                status.lastFailureTime = 0;
+                status.lastFailureSource = -1;
+                status.lastFailureMesg = null;
+                status.initialFailureTime = 0;
+                ds.successCount++;
+                ds.successTime += elapsedTime;
+            } else if (!MESG_CANCELED.equals(resultMessage)) {
+                if (status.lastFailureTime == 0) {
+                    writeStatusNow = true;
+                }
+                status.lastFailureTime = lastSyncTime;
+                status.lastFailureSource = item.source;
+                status.lastFailureMesg = resultMessage;
+                if (status.initialFailureTime == 0) {
+                    status.initialFailureTime = lastSyncTime;
+                }
+                ds.failureCount++;
+                ds.failureTime += elapsedTime;
+            }
+            
+            if (writeStatusNow) {
+                writeStatusLocked();
+            } else if (!hasMessages(MSG_WRITE_STATUS)) {
+                sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
+                        WRITE_STATUS_DELAY);
+            }
+            if (writeStatisticsNow) {
+                writeStatisticsLocked();
+            } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
+                sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
+                        WRITE_STATISTICS_DELAY);
+            }            
+        }
+        
+        reportChange(CHANGE_STATUS);
+    }
+
+    /**
+     * Return the currently active sync information, or null if there is no
+     * active sync.  Note that the returned object is the real, live active
+     * sync object, so be careful what you do with it.
+     */
+    public ActiveSyncInfo getActiveSync() {
+        synchronized (mAuthorities) {
+            return mActiveSync;
+        }
+    }
+    
+    /**
+     * Return an array of the current sync status for all authorities.  Note
+     * that the objects inside the array are the real, live status objects,
+     * so be careful what you do with them.
+     */
+    public ArrayList<SyncStatusInfo> getSyncStatus() {
+        synchronized (mAuthorities) {
+            final int N = mSyncStatus.size();
+            ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
+            for (int i=0; i<N; i++) {
+                ops.add(mSyncStatus.valueAt(i));
+            }
+            return ops;
+        }
+    }
+    
+    /**
+     * Returns the status that matches the authority. If there are multiples accounts for
+     * the authority, the one with the latest "lastSuccessTime" status is returned.
+     * @param authority the authority whose row should be selected
+     * @return the SyncStatusInfo for the authority, or null if none exists
+     */
+    public SyncStatusInfo getStatusByAuthority(String authority) {
+        synchronized (mAuthorities) {
+            SyncStatusInfo best = null;
+            final int N = mSyncStatus.size();
+            for (int i=0; i<N; i++) {
+                SyncStatusInfo cur = mSyncStatus.get(i);
+                AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
+                if (ainfo != null && ainfo.authority.equals(authority)) {
+                    if (best == null) {
+                        best = cur;
+                    } else if (best.lastSuccessTime > cur.lastSuccessTime) {
+                        best = cur;
+                    }
+                }
+            }
+            return best;
+        }
+    }
+    
+    /**
+     * Return true if the pending status is true of any matching authorities.
+     */
+    public boolean isAuthorityPending(String account, String authority) {
+        synchronized (mAuthorities) {
+            final int N = mSyncStatus.size();
+            for (int i=0; i<N; i++) {
+                SyncStatusInfo cur = mSyncStatus.get(i);
+                AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
+                if (ainfo == null) {
+                    continue;
+                }
+                if (account != null && !ainfo.account.equals(account)) {
+                    continue;
+                }
+                if (ainfo.authority.equals(authority) && cur.pending) {
+                    return true;
+                }
+            }
+            return false;
         }
     }
 
     /**
+     * Return an array of the current sync status for all authorities.  Note
+     * that the objects inside the array are the real, live status objects,
+     * so be careful what you do with them.
+     */
+    public ArrayList<SyncHistoryItem> getSyncHistory() {
+        synchronized (mAuthorities) {
+            final int N = mSyncHistory.size();
+            ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
+            for (int i=0; i<N; i++) {
+                items.add(mSyncHistory.get(i));
+            }
+            return items;
+        }
+    }
+    
+    /**
+     * Return an array of the current per-day statistics.  Note
+     * that the objects inside the array are the real, live status objects,
+     * so be careful what you do with them.
+     */
+    public DayStats[] getDayStatistics() {
+        synchronized (mAuthorities) {
+            DayStats[] ds = new DayStats[mDayStats.length];
+            System.arraycopy(mDayStats, 0, ds, 0, ds.length);
+            return ds;
+        }
+    }
+    
+    /**
      * If sync is failing for any of the provider/accounts then determine the time at which it
      * started failing and return the earliest time over all the provider/accounts. If none are
      * failing then return 0.
      */
     public long getInitialSyncFailureTime() {
-        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-        // Join the settings for a provider with the status so that we can easily
-        // check if each provider is enabled for syncing. We also join in the overall
-        // enabled flag ("listen_for_tickles") to each row so that we don't need to
-        // make a separate DB lookup to access it.
-        Cursor c = db.rawQuery(""
-                + "SELECT initialFailureTime, s1.value, s2.value "
-                + "FROM status "
-                + "LEFT JOIN stats ON status.stats_id=stats._id "
-                + "LEFT JOIN settings as s1 ON 'sync_provider_' || authority=s1.name "
-                + "LEFT JOIN settings as s2 ON s2.name='listen_for_tickles' "
-                + "where initialFailureTime is not null "
-                + "  AND lastFailureMesg!=" + Sync.History.ERROR_TOO_MANY_DELETIONS
-                + "  AND lastFailureMesg!=" + Sync.History.ERROR_AUTHENTICATION
-                + "  AND lastFailureMesg!=" + Sync.History.ERROR_SYNC_ALREADY_IN_PROGRESS
-                + "  AND authority!='subscribedfeeds' "
-                + " ORDER BY initialFailureTime", null);
+        synchronized (mAuthorities) {
+            if (!mListenForTickles) {
+                return 0;
+            }
+            
+            long oldest = 0;
+            int i = mSyncStatus.size();
+            while (i > 0) {
+                i--;
+                SyncStatusInfo stats = mSyncStatus.valueAt(i);
+                AuthorityInfo authority = mAuthorities.get(stats.authorityId);
+                if (authority != null && authority.enabled) {
+                    if (oldest == 0 || stats.initialFailureTime < oldest) {
+                        oldest = stats.initialFailureTime;
+                    }
+                }
+            }
+            
+            return oldest;
+        }
+    }
+    
+    private int getCurrentDay() {
+        mCal.setTimeInMillis(System.currentTimeMillis());
+        final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
+        if (mYear != mCal.get(Calendar.YEAR)) {
+            mYear = mCal.get(Calendar.YEAR);
+            mCal.clear();
+            mCal.set(Calendar.YEAR, mYear);
+            mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
+        }
+        return dayOfYear + mYearInDays;
+    }
+    
+    /**
+     * Retrieve an authority, returning null if one does not exist.
+     * 
+     * @param accountName The name of the account for the authority.
+     * @param authorityName The name of the authority itself.
+     * @param tag If non-null, this will be used in a log message if the
+     * requested authority does not exist.
+     */
+    private AuthorityInfo getAuthorityLocked(String accountName, String authorityName,
+            String tag) {
+        AccountInfo account = mAccounts.get(accountName);
+        if (account == null) {
+            if (tag != null) {
+                Log.w(TAG, tag + ": unknown account " + accountName);
+            }
+            return null;
+        }
+        AuthorityInfo authority = account.authorities.get(authorityName);
+        if (authority == null) {
+            if (tag != null) {
+                Log.w(TAG, tag + ": unknown authority " + authorityName);
+            }
+            return null;
+        }
+        
+        return authority;
+    }
+    
+    private AuthorityInfo getOrCreateAuthorityLocked(String accountName,
+            String authorityName, int ident, boolean doWrite) {
+        AccountInfo account = mAccounts.get(accountName);
+        if (account == null) {
+            account = new AccountInfo(accountName);
+            mAccounts.put(accountName, account);
+        }
+        AuthorityInfo authority = account.authorities.get(authorityName);
+        if (authority == null) {
+            if (ident < 0) {
+                // Look for a new identifier for this authority.
+                final int N = mAuthorities.size();
+                ident = 0;
+                for (int i=0; i<N; i++) {
+                    if (mAuthorities.valueAt(i).ident > ident) {
+                        break;
+                    }
+                    ident++;
+                }
+            }
+            authority = new AuthorityInfo(accountName, authorityName, ident);
+            account.authorities.put(authorityName, authority);
+            mAuthorities.put(ident, authority);
+            if (doWrite) {
+                writeAccountInfoLocked();
+            }
+        }
+        
+        return authority;
+    }
+    
+    private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
+        SyncStatusInfo status = mSyncStatus.get(authorityId);
+        if (status == null) {
+            status = new SyncStatusInfo(authorityId);
+            mSyncStatus.put(authorityId, status);
+        }
+        return status;
+    }
+    
+    /**
+     * Read all account information back in to the initial engine state.
+     */
+    private void readAccountInfoLocked() {
+        FileInputStream fis = null;
         try {
+            fis = mAccountInfoFile.openRead();
+            if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, null);
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.START_TAG) {
+                eventType = parser.next();
+            }
+            String tagName = parser.getName();
+            if ("accounts".equals(tagName)) {
+                String listen = parser.getAttributeValue(
+                        null, "listen-for-tickles");
+                mListenForTickles = listen == null
+                            || Boolean.parseBoolean(listen);
+                eventType = parser.next();
+                do {
+                    if (eventType == XmlPullParser.START_TAG
+                            && parser.getDepth() == 2) {
+                        tagName = parser.getName();
+                        if ("authority".equals(tagName)) {
+                            int id = -1;
+                            try {
+                                id = Integer.parseInt(parser.getAttributeValue(
+                                        null, "id"));
+                            } catch (NumberFormatException e) {
+                            } catch (NullPointerException e) {
+                            }
+                            if (id >= 0) {
+                                String accountName = parser.getAttributeValue(
+                                        null, "account");
+                                String authorityName = parser.getAttributeValue(
+                                        null, "authority");
+                                String enabled = parser.getAttributeValue(
+                                        null, "enabled");
+                                AuthorityInfo authority = mAuthorities.get(id); 
+                                if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
+                                        + accountName + " auth=" + authorityName
+                                        + " enabled=" + enabled);
+                                if (authority == null) {
+                                    if (DEBUG_FILE) Log.v(TAG, "Creating entry");
+                                    authority = getOrCreateAuthorityLocked(
+                                        accountName, authorityName, id, false);
+                                }
+                                if (authority != null) {
+                                    authority.enabled = enabled == null
+                                            || Boolean.parseBoolean(enabled);
+                                } else {
+                                    Log.w(TAG, "Failure adding authority: account="
+                                            + accountName + " auth=" + authorityName
+                                            + " enabled=" + enabled);
+                                }
+                            }
+                        }
+                    }
+                    eventType = parser.next();
+                } while (eventType != XmlPullParser.END_DOCUMENT);
+            }
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "Error reading accounts", e);
+        } catch (java.io.IOException e) {
+            if (fis == null) Log.i(TAG, "No initial accounts");
+            else Log.w(TAG, "Error reading accounts", e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (java.io.IOException e1) {
+                }
+            }
+        }
+    }
+    
+    /**
+     * Write all account information to the account file.
+     */
+    private void writeAccountInfoLocked() {
+        if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
+        FileOutputStream fos = null;
+        
+        try {
+            fos = mAccountInfoFile.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, "accounts");
+            if (!mListenForTickles) {
+                out.attribute(null, "listen-for-tickles", "false");
+            }
+            
+            final int N = mAuthorities.size();
+            for (int i=0; i<N; i++) {
+                AuthorityInfo authority = mAuthorities.get(i);
+                out.startTag(null, "authority");
+                out.attribute(null, "id", Integer.toString(authority.ident));
+                out.attribute(null, "account", authority.account);
+                out.attribute(null, "authority", authority.authority);
+                if (!authority.enabled) {
+                    out.attribute(null, "enabled", "false");
+                }
+                out.endTag(null, "authority");
+            }
+            
+            out.endTag(null, "accounts");
+            
+            out.endDocument();
+            
+            mAccountInfoFile.finishWrite(fos);
+        } catch (java.io.IOException e1) {
+            Log.w(TAG, "Error writing accounts", e1);
+            if (fos != null) {
+                mAccountInfoFile.failWrite(fos);
+            }
+        }
+    }
+    
+    static int getIntColumn(Cursor c, String name) {
+        return c.getInt(c.getColumnIndex(name));
+    }
+    
+    static long getLongColumn(Cursor c, String name) {
+        return c.getLong(c.getColumnIndex(name));
+    }
+    
+    /**
+     * Load sync engine state from the old syncmanager database, and then
+     * erase it.  Note that we don't deal with pending operations, active
+     * sync, or history.
+     */
+    private void readLegacyAccountInfoLocked() {
+        // Look for old database to initialize from.
+        File file = mContext.getDatabasePath("syncmanager.db");
+        if (!file.exists()) {
+            return;
+        }
+        String path = file.getPath();
+        SQLiteDatabase db = null;
+        try {
+            db = SQLiteDatabase.openDatabase(path, null,
+                    SQLiteDatabase.OPEN_READONLY);
+        } catch (SQLiteException e) {
+        }
+        
+        if (db != null) {
+            // Copy in all of the status information, as well as accounts.
+            if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
+            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+            qb.setTables("stats, status");
+            HashMap<String,String> map = new HashMap<String,String>();
+            map.put("_id", "status._id as _id");
+            map.put("account", "stats.account as account");
+            map.put("authority", "stats.authority as authority");
+            map.put("totalElapsedTime", "totalElapsedTime");
+            map.put("numSyncs", "numSyncs");
+            map.put("numSourceLocal", "numSourceLocal");
+            map.put("numSourcePoll", "numSourcePoll");
+            map.put("numSourceServer", "numSourceServer");
+            map.put("numSourceUser", "numSourceUser");
+            map.put("lastSuccessSource", "lastSuccessSource");
+            map.put("lastSuccessTime", "lastSuccessTime");
+            map.put("lastFailureSource", "lastFailureSource");
+            map.put("lastFailureTime", "lastFailureTime");
+            map.put("lastFailureMesg", "lastFailureMesg");
+            map.put("pending", "pending");
+            qb.setProjectionMap(map);
+            qb.appendWhere("stats._id = status.stats_id");
+            Cursor c = qb.query(db, null, null, null, null, null, null);
             while (c.moveToNext()) {
-                // these settings default to true, so if they are null treat them as enabled
-                final String providerEnabledString = c.getString(1);
-                if (providerEnabledString != null && !Boolean.parseBoolean(providerEnabledString)) {
-                    continue;
+                String accountName = c.getString(c.getColumnIndex("account"));
+                String authorityName = c.getString(c.getColumnIndex("authority"));
+                AuthorityInfo authority = this.getOrCreateAuthorityLocked(
+                        accountName, authorityName, -1, false);
+                if (authority != null) {
+                    int i = mSyncStatus.size();
+                    boolean found = false;
+                    SyncStatusInfo st = null;
+                    while (i > 0) {
+                        i--;
+                        st = mSyncStatus.get(i);
+                        if (st.authorityId == authority.ident) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        st = new SyncStatusInfo(authority.ident);
+                        mSyncStatus.put(authority.ident, st);
+                    }
+                    st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
+                    st.numSyncs = getIntColumn(c, "numSyncs");
+                    st.numSourceLocal = getIntColumn(c, "numSourceLocal");
+                    st.numSourcePoll = getIntColumn(c, "numSourcePoll");
+                    st.numSourceServer = getIntColumn(c, "numSourceServer");
+                    st.numSourceUser = getIntColumn(c, "numSourceUser");
+                    st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
+                    st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
+                    st.lastFailureSource = getIntColumn(c, "lastFailureSource");
+                    st.lastFailureTime = getLongColumn(c, "lastFailureTime");
+                    st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
+                    st.pending = getIntColumn(c, "pending") != 0;
                 }
-                final String allEnabledString = c.getString(2);
-                if (allEnabledString != null && !Boolean.parseBoolean(allEnabledString)) {
-                    continue;
-                }
-                return c.getLong(0);
             }
-        } finally {
+            
             c.close();
-        }
-        return 0;
-    }
-
-    private void createStatusRowIfNecessary(long statsId) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        boolean statusExists = 0 != DatabaseUtils.longForQuery(db,
-                "SELECT count(*) FROM status WHERE stats_id=" + statsId, null);
-        if (!statusExists) {
-            ContentValues values = new ContentValues();
-            values.put("stats_id", statsId);
-            db.insert("status", null, values);
-        }
-    }
-
-    private long createStatsRowIfNecessary(String account, String authority) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        StringBuilder where = new StringBuilder();
-        where.append(Sync.Stats.ACCOUNT + "= ?");
-        where.append(" and " + Sync.Stats.AUTHORITY + "= ?");
-        Cursor cursor = query(Sync.Stats.CONTENT_URI,
-                Sync.Stats.SYNC_STATS_PROJECTION,
-                where.toString(), new String[] { account, authority },
-                null /* order */);
-        try {
-            long id;
-            if (cursor.moveToFirst()) {
-                id = cursor.getLong(cursor.getColumnIndexOrThrow(Sync.Stats._ID));
-            } else {
-                ContentValues values = new ContentValues();
-                values.put(Sync.Stats.ACCOUNT, account);
-                values.put(Sync.Stats.AUTHORITY, authority);
-                id = db.insert("stats", null, values);
+            
+            // Retrieve the settings.
+            qb = new SQLiteQueryBuilder();
+            qb.setTables("settings");
+            c = qb.query(db, null, null, null, null, null, null);
+            while (c.moveToNext()) {
+                String name = c.getString(c.getColumnIndex("name"));
+                String value = c.getString(c.getColumnIndex("value"));
+                if (name == null) continue;
+                if (name.equals("listen_for_tickles")) {
+                    setListenForNetworkTickles(value == null
+                            || Boolean.parseBoolean(value));
+                } else if (name.startsWith("sync_provider_")) {
+                    String provider = name.substring("sync_provider_".length(),
+                            name.length());
+                    setSyncProviderAutomatically(null, provider,
+                            value == null || Boolean.parseBoolean(value));
+                }
             }
-            return id;
+            
+            c.close();
+            
+            db.close();
+            
+            writeAccountInfoLocked();
+            writeStatusLocked();
+            (new File(path)).delete();
+        }
+    }
+    
+    public static final int STATUS_FILE_END = 0;
+    public static final int STATUS_FILE_ITEM = 100;
+    
+    /**
+     * Read all sync status back in to the initial engine state.
+     */
+    private void readStatusLocked() {
+        if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
+        try {
+            byte[] data = mStatusFile.readFully();
+            Parcel in = Parcel.obtain();
+            in.unmarshall(data, 0, data.length);
+            in.setDataPosition(0);
+            int token;
+            while ((token=in.readInt()) != STATUS_FILE_END) {
+                if (token == STATUS_FILE_ITEM) {
+                    SyncStatusInfo status = new SyncStatusInfo(in);
+                    if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
+                        status.pending = false;
+                        if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
+                                + status.authorityId);
+                        mSyncStatus.put(status.authorityId, status);
+                    }
+                } else {
+                    // Ooops.
+                    Log.w(TAG, "Unknown status token: " + token);
+                    break;
+                }
+            }
+        } catch (java.io.IOException e) {
+            Log.i(TAG, "No initial status");
+        }
+    }
+    
+    /**
+     * Write all sync status to the sync status file.
+     */
+    private void writeStatusLocked() {
+        if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
+        
+        // The file is being written, so we don't need to have a scheduled
+        // write until the next change.
+        removeMessages(MSG_WRITE_STATUS);
+        
+        FileOutputStream fos = null;
+        try {
+            fos = mStatusFile.startWrite();
+            Parcel out = Parcel.obtain();
+            final int N = mSyncStatus.size();
+            for (int i=0; i<N; i++) {
+                SyncStatusInfo status = mSyncStatus.valueAt(i);
+                out.writeInt(STATUS_FILE_ITEM);
+                status.writeToParcel(out, 0);
+            }
+            out.writeInt(STATUS_FILE_END);
+            fos.write(out.marshall());
+            out.recycle();
+            
+            mStatusFile.finishWrite(fos);
+        } catch (java.io.IOException e1) {
+            Log.w(TAG, "Error writing status", e1);
+            if (fos != null) {
+                mStatusFile.failWrite(fos);
+            }
+        }
+    }
+    
+    public static final int PENDING_OPERATION_VERSION = 1;
+    
+    /**
+     * Read all pending operations back in to the initial engine state.
+     */
+    private void readPendingOperationsLocked() {
+        if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
+        try {
+            byte[] data = mPendingFile.readFully();
+            Parcel in = Parcel.obtain();
+            in.unmarshall(data, 0, data.length);
+            in.setDataPosition(0);
+            final int SIZE = in.dataSize();
+            while (in.dataPosition() < SIZE) {
+                int version = in.readInt();
+                if (version != PENDING_OPERATION_VERSION) {
+                    Log.w(TAG, "Unknown pending operation version "
+                            + version + "; dropping all ops");
+                    break;
+                }
+                int authorityId = in.readInt();
+                int syncSource = in.readInt();
+                byte[] flatExtras = in.createByteArray();
+                AuthorityInfo authority = mAuthorities.get(authorityId);
+                if (authority != null) {
+                    Bundle extras = null;
+                    if (flatExtras != null) {
+                        extras = unflattenBundle(flatExtras);
+                    }
+                    PendingOperation op = new PendingOperation(
+                            authority.account, syncSource,
+                            authority.authority, extras);
+                    op.authorityId = authorityId;
+                    op.flatExtras = flatExtras;
+                    if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
+                            + " auth=" + op.authority
+                            + " src=" + op.syncSource
+                            + " extras=" + op.extras);
+                    mPendingOperations.add(op);
+                }
+            }
+        } catch (java.io.IOException e) {
+            Log.i(TAG, "No initial pending operations");
+        }
+    }
+    
+    private void writePendingOperationLocked(PendingOperation op, Parcel out) {
+        out.writeInt(PENDING_OPERATION_VERSION);
+        out.writeInt(op.authorityId);
+        out.writeInt(op.syncSource);
+        if (op.flatExtras == null && op.extras != null) {
+            op.flatExtras = flattenBundle(op.extras);
+        }
+        out.writeByteArray(op.flatExtras);
+    }
+    
+    /**
+     * Write all currently pending ops to the pending ops file.
+     */
+    private void writePendingOperationsLocked() {
+        final int N = mPendingOperations.size();
+        FileOutputStream fos = null;
+        try {
+            if (N == 0) {
+                if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
+                mPendingFile.truncate();
+                return;
+            }
+            
+            if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
+            fos = mPendingFile.startWrite();
+        
+            Parcel out = Parcel.obtain();
+            for (int i=0; i<N; i++) {
+                PendingOperation op = mPendingOperations.get(i);
+                writePendingOperationLocked(op, out);
+            }
+            fos.write(out.marshall());
+            out.recycle();
+            
+            mPendingFile.finishWrite(fos);
+        } catch (java.io.IOException e1) {
+            Log.w(TAG, "Error writing pending operations", e1);
+            if (fos != null) {
+                mPendingFile.failWrite(fos);
+            }
+        }
+    }
+    
+    /**
+     * Append the given operation to the pending ops file; if unable to,
+     * write all pending ops.
+     */
+    private void appendPendingOperationLocked(PendingOperation op) {
+        if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
+        FileOutputStream fos = null;
+        try {
+            fos = mPendingFile.openAppend();
+        } catch (java.io.IOException e) {
+            if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
+            writePendingOperationsLocked();
+            return;
+        }
+        
+        try {
+            Parcel out = Parcel.obtain();
+            writePendingOperationLocked(op, out);
+            fos.write(out.marshall());
+            out.recycle();
+        } catch (java.io.IOException e1) {
+            Log.w(TAG, "Error writing pending operations", e1);
         } finally {
-            cursor.close();
+            try {
+                fos.close();
+            } catch (java.io.IOException e2) {
+            }
+        }
+    }
+    
+    static private byte[] flattenBundle(Bundle bundle) {
+        byte[] flatData = null;
+        Parcel parcel = Parcel.obtain();
+        try {
+            bundle.writeToParcel(parcel, 0);
+            flatData = parcel.marshall();
+        } finally {
+            parcel.recycle();
+        }
+        return flatData;
+    }
+    
+    static private Bundle unflattenBundle(byte[] flatData) {
+        Bundle bundle;
+        Parcel parcel = Parcel.obtain();
+        try {
+            parcel.unmarshall(flatData, 0, flatData.length);
+            parcel.setDataPosition(0);
+            bundle = parcel.readBundle();
+        } catch (RuntimeException e) {
+            // A RuntimeException is thrown if we were unable to parse the parcel.
+            // Create an empty parcel in this case.
+            bundle = new Bundle();
+        } finally {
+            parcel.recycle();
+        }
+        return bundle;
+    }
+    
+    public static final int STATISTICS_FILE_END = 0;
+    public static final int STATISTICS_FILE_ITEM_OLD = 100;
+    public static final int STATISTICS_FILE_ITEM = 101;
+    
+    /**
+     * Read all sync statistics back in to the initial engine state.
+     */
+    private void readStatisticsLocked() {
+        try {
+            byte[] data = mStatisticsFile.readFully();
+            Parcel in = Parcel.obtain();
+            in.unmarshall(data, 0, data.length);
+            in.setDataPosition(0);
+            int token;
+            int index = 0;
+            while ((token=in.readInt()) != STATISTICS_FILE_END) {
+                if (token == STATISTICS_FILE_ITEM
+                        || token == STATISTICS_FILE_ITEM_OLD) {
+                    int day = in.readInt();
+                    if (token == STATISTICS_FILE_ITEM_OLD) {
+                        day = day - 2009 + 14245;  // Magic!
+                    }
+                    DayStats ds = new DayStats(day);
+                    ds.successCount = in.readInt();
+                    ds.successTime = in.readLong();
+                    ds.failureCount = in.readInt();
+                    ds.failureTime = in.readLong();
+                    if (index < mDayStats.length) {
+                        mDayStats[index] = ds;
+                        index++;
+                    }
+                } else {
+                    // Ooops.
+                    Log.w(TAG, "Unknown stats token: " + token);
+                    break;
+                }
+            }
+        } catch (java.io.IOException e) {
+            Log.i(TAG, "No initial statistics");
+        }
+    }
+    
+    /**
+     * Write all sync statistics to the sync status file.
+     */
+    private void writeStatisticsLocked() {
+        if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
+        
+        // The file is being written, so we don't need to have a scheduled
+        // write until the next change.
+        removeMessages(MSG_WRITE_STATISTICS);
+        
+        FileOutputStream fos = null;
+        try {
+            fos = mStatisticsFile.startWrite();
+            Parcel out = Parcel.obtain();
+            final int N = mDayStats.length;
+            for (int i=0; i<N; i++) {
+                DayStats ds = mDayStats[i];
+                if (ds == null) {
+                    break;
+                }
+                out.writeInt(STATISTICS_FILE_ITEM);
+                out.writeInt(ds.day);
+                out.writeInt(ds.successCount);
+                out.writeLong(ds.successTime);
+                out.writeInt(ds.failureCount);
+                out.writeLong(ds.failureTime);
+            }
+            out.writeInt(STATISTICS_FILE_END);
+            fos.write(out.marshall());
+            out.recycle();
+            
+            mStatisticsFile.finishWrite(fos);
+        } catch (java.io.IOException e1) {
+            Log.w(TAG, "Error writing stats", e1);
+            if (fos != null) {
+                mStatisticsFile.failWrite(fos);
+            }
         }
     }
 }
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 5ea5aae..9cf7a22 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -16,10 +16,20 @@
 
 package android.os;
 
+import com.android.internal.util.TypedProperties;
+
+import android.util.Config;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.io.Reader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 
 import org.apache.harmony.dalvik.ddmc.Chunk;
 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
@@ -721,5 +731,198 @@
             count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK_RANGE];
             return count;
         }
-    };
+    }
+
+
+    /**
+     * A Map of typed debug properties.
+     */
+    private static final TypedProperties debugProperties;
+
+    /*
+     * Load the debug properties from the standard files into debugProperties.
+     */
+    static {
+        if (Config.DEBUG) {
+            final String TAG = "DebugProperties";
+            final String[] files = { "/system/debug.prop", "/debug.prop", "/data/debug.prop" };
+            final TypedProperties tp = new TypedProperties();
+
+            // Read the properties from each of the files, if present.
+            for (String file : files) {
+                Reader r;
+                try {
+                    r = new FileReader(file);
+                } catch (FileNotFoundException ex) {
+                    // It's ok if a file is missing.
+                    continue;
+                }
+
+                try {
+                    tp.load(r);
+                } catch (Exception ex) {
+                    throw new RuntimeException("Problem loading " + file, ex);
+                } finally {
+                    try {
+                        r.close();
+                    } catch (IOException ex) {
+                        // Ignore this error.
+                    }
+                }
+            }
+
+            debugProperties = tp.isEmpty() ? null : tp;
+        } else {
+            debugProperties = null;
+        }
+    }
+
+
+    /**
+     * Returns true if the type of the field matches the specified class.
+     * Handles the case where the class is, e.g., java.lang.Boolean, but
+     * the field is of the primitive "boolean" type.  Also handles all of
+     * the java.lang.Number subclasses.
+     */
+    private static boolean fieldTypeMatches(Field field, Class<?> cl) {
+        Class<?> fieldClass = field.getType();
+        if (fieldClass == cl) {
+            return true;
+        }
+        Field primitiveTypeField;
+        try {
+            /* All of the classes we care about (Boolean, Integer, etc.)
+             * have a Class field called "TYPE" that points to the corresponding
+             * primitive class.
+             */
+            primitiveTypeField = cl.getField("TYPE");
+        } catch (NoSuchFieldException ex) {
+            return false;
+        }
+        try {
+            return fieldClass == (Class<?>) primitiveTypeField.get(null);
+        } catch (IllegalAccessException ex) {
+            return false;
+        }
+    }
+
+
+    /**
+     * Looks up the property that corresponds to the field, and sets the field's value
+     * if the types match.
+     */
+    private static void modifyFieldIfSet(final Field field, final TypedProperties properties,
+                                         final String propertyName) {
+        if (field.getType() == java.lang.String.class) {
+            int stringInfo = properties.getStringInfo(propertyName);
+            switch (stringInfo) {
+                case TypedProperties.STRING_SET:
+                    // Handle as usual below.
+                    break;
+                case TypedProperties.STRING_NULL:
+                    try {
+                        field.set(null, null);  // null object for static fields; null string
+                    } catch (IllegalAccessException ex) {
+                        throw new IllegalArgumentException(
+                            "Cannot set field for " + propertyName, ex);
+                    }
+                    return;
+                case TypedProperties.STRING_NOT_SET:
+                    return;
+                case TypedProperties.STRING_TYPE_MISMATCH:
+                    throw new IllegalArgumentException(
+                        "Type of " + propertyName + " " +
+                        " does not match field type (" + field.getType() + ")");
+                default:
+                    throw new IllegalStateException(
+                        "Unexpected getStringInfo(" + propertyName + ") return value " +
+                        stringInfo);
+            }
+        }
+        Object value = properties.get(propertyName);
+        if (value != null) {
+            if (!fieldTypeMatches(field, value.getClass())) {
+                throw new IllegalArgumentException(
+                    "Type of " + propertyName + " (" + value.getClass() + ") " +
+                    " does not match field type (" + field.getType() + ")");
+            }
+            try {
+                field.set(null, value);  // null object for static fields
+            } catch (IllegalAccessException ex) {
+                throw new IllegalArgumentException(
+                    "Cannot set field for " + propertyName, ex);
+            }
+        }
+    }
+
+
+    /**
+     * Reflectively sets static fields of a class based on internal debugging
+     * properties.  This method is a no-op if android.util.Config.DEBUG is
+     * false.
+     * <p>
+     * <strong>NOTE TO APPLICATION DEVELOPERS</strong>: Config.DEBUG will
+     * always be false in release builds.  This API is typically only useful
+     * for platform developers.
+     * </p>
+     * Class setup: define a class whose only fields are non-final, static
+     * primitive types (except for "char") or Strings.  In a static block
+     * after the field definitions/initializations, pass the class to
+     * this method, Debug.setFieldsOn().  Example:
+     * <pre>
+     * package com.example;
+     *
+     * import android.os.Debug;
+     *
+     * public class MyDebugVars {
+     *    public static String s = "a string";
+     *    public static String s2 = "second string";
+     *    public static String ns = null;
+     *    public static boolean b = false;
+     *    public static int i = 5;
+     *    public static float f = 0.1f;
+     *    public static double d = 0.5d;
+     *
+     *    // This MUST appear AFTER all fields are defined and initialized!
+     *    static {
+     *        Debug.setFieldsOn(MyDebugVars.class);
+     *    }
+     * }
+     * </pre>
+     * setFieldsOn() may override the value of any field in the class based
+     * on internal properties that are fixed at boot time.
+     * <p>
+     * These properties are only set during platform debugging, and are not
+     * meant to be used as a general-purpose properties store.
+     *
+     * {@hide}
+     *
+     * @param cl The class to (possibly) modify
+     * @throws IllegalArgumentException if any fields are final or non-static,
+     *         or if the type of the field does not match the type of
+     *         the internal debugging property value.
+     */
+    public static void setFieldsOn(Class<?> cl) {
+        if (Config.DEBUG) {
+            if (debugProperties != null) {
+                /* Only look for fields declared directly by the class,
+                 * so we don't mysteriously change static fields in superclasses.
+                 */
+                for (Field field : cl.getDeclaredFields()) {
+                    final String propertyName = cl.getName() + "." + field.getName();
+                    boolean isStatic = Modifier.isStatic(field.getModifiers());
+                    boolean isFinal = Modifier.isFinal(field.getModifiers());
+                    if (!isStatic || isFinal) {
+                        throw new IllegalArgumentException(propertyName +
+                            " must be static and non-final");
+                    }
+                    modifyFieldIfSet(field, debugProperties, propertyName);
+                }
+            }
+        } else {
+            Log.w("android.os.Debug",
+                  "setFieldsOn(" + (cl == null ? "null" : cl.getName()) +
+                  ") called in non-DEBUG build");
+        }
+    }
 }
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 63f6dff..23c0a7bf 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -49,25 +49,35 @@
 public class RemoteCallbackList<E extends IInterface> {
     /*package*/ HashMap<IBinder, Callback> mCallbacks
             = new HashMap<IBinder, Callback>();
-    private IInterface[] mActiveBroadcast;
+    private Object[] mActiveBroadcast;
     private boolean mKilled = false;
     
     private final class Callback implements IBinder.DeathRecipient {
         final E mCallback;
+        final Object mCookie;
         
-        Callback(E callback) {
+        Callback(E callback, Object cookie) {
             mCallback = callback;
+            mCookie = cookie;
         }
         
         public void binderDied() {
             synchronized (mCallbacks) {
                 mCallbacks.remove(mCallback.asBinder());
             }
-            onCallbackDied(mCallback);
+            onCallbackDied(mCallback, mCookie);
         }
     }
     
     /**
+     * Simple version of {@link RemoteCallbackList#register(E, Object)}
+     * that does not take a cookie object.
+     */
+    public boolean register(E callback) {
+        return register(callback, null);
+    }
+    
+    /**
      * Add a new callback to the list.  This callback will remain in the list
      * until a corresponding call to {@link #unregister} or its hosting process
      * goes away.  If the callback was already registered (determined by
@@ -81,6 +91,8 @@
      * Most services will want to check for null before calling this with
      * an object given from a client, so that clients can't crash the
      * service with bad data.
+     * @param cookie Optional additional data to be associated with this
+     * callback.
      * 
      * @return Returns true if the callback was successfully added to the list.
      * Returns false if it was not added, either because {@link #kill} had
@@ -90,14 +102,14 @@
      * @see #kill
      * @see #onCallbackDied
      */
-    public boolean register(E callback) {
+    public boolean register(E callback, Object cookie) {
         synchronized (mCallbacks) {
             if (mKilled) {
                 return false;
             }
             IBinder binder = callback.asBinder();
             try {
-                Callback cb = new Callback(callback);
+                Callback cb = new Callback(callback, cookie);
                 binder.linkToDeath(cb, 0);
                 mCallbacks.put(binder, cb);
                 return true;
@@ -154,17 +166,28 @@
     }
     
     /**
+     * Old version of {@link #onCallbackDied(E, Object)} that
+     * does not provide a cookie.
+     */
+    public void onCallbackDied(E callback) {
+    }
+    
+    /**
      * Called when the process hosting a callback in the list has gone away.
-     * The default implementation does nothing.
+     * The default implementation calls {@link #onCallbackDied(E)}
+     * for backwards compatibility.
      * 
      * @param callback The callback whose process has died.  Note that, since
      * its process has died, you can not make any calls on to this interface.
      * You can, however, retrieve its IBinder and compare it with another
      * IBinder to see if it is the same object.
+     * @param cookie The cookie object original provided to
+     * {@link #register(E, Object)}.
      * 
      * @see #register
      */
-    public void onCallbackDied(E callback) {
+    public void onCallbackDied(E callback, Object cookie) {
+        onCallbackDied(callback);
     }
     
     /**
@@ -203,13 +226,13 @@
             if (N <= 0) {
                 return 0;
             }
-            IInterface[] active = mActiveBroadcast;
+            Object[] active = mActiveBroadcast;
             if (active == null || active.length < N) {
-                mActiveBroadcast = active = new IInterface[N];
+                mActiveBroadcast = active = new Object[N];
             }
             int i=0;
             for (Callback cb : mCallbacks.values()) {
-                active[i++] = cb.mCallback;
+                active[i++] = cb;
             }
             return i;
         }
@@ -237,7 +260,17 @@
      * @see #beginBroadcast
      */
     public E getBroadcastItem(int index) {
-        return (E)mActiveBroadcast[index];
+        return ((Callback)mActiveBroadcast[index]).mCallback;
+    }
+    
+    /**
+     * Retrieve the cookie associated with the item
+     * returned by {@link #getBroadcastItem(int)}.
+     * 
+     * @see #getBroadcastItem
+     */
+    public Object getBroadcastCookie(int index) {
+        return ((Callback)mActiveBroadcast[index]).mCookie;
     }
     
     /**
@@ -248,7 +281,7 @@
      * @see #beginBroadcast
      */
     public void finishBroadcast() {
-        IInterface[] active = mActiveBroadcast;
+        Object[] active = mActiveBroadcast;
         if (active != null) {
             final int N = active.length;
             for (int i=0; i<N; i++) {
diff --git a/core/java/android/provider/Sync.java b/core/java/android/provider/Sync.java
deleted file mode 100644
index 628852f..0000000
--- a/core/java/android/provider/Sync.java
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import android.content.ContentQueryMap;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Handler;
-
-import java.util.Map;
-
-/**
- * The Sync provider stores information used in managing the syncing of the device,
- * including the history and pending syncs.
- * 
- * @hide
- */
-public final class Sync {
-    // utility class
-    private Sync() {}
-
-    /**
-     * The content url for this provider.
-     */
-    public static final Uri CONTENT_URI = Uri.parse("content://sync");
-
-    /**
-     * Columns from the stats table.
-     */
-    public interface StatsColumns {
-        /**
-         * The sync account.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ACCOUNT = "account";
-
-        /**
-         * The content authority (contacts, calendar, etc.).
-         * <P>Type: TEXT</P>
-         */
-        public static final String AUTHORITY = "authority";
-    }
-
-    /**
-     * Provides constants and utility methods to access and use the stats table.
-     */
-    public static final class Stats implements BaseColumns, StatsColumns {
-
-        // utility class
-        private Stats() {}
-
-        /**
-         * The content url for this table.
-         */
-        public static final Uri CONTENT_URI =
-                Uri.parse("content://sync/stats");
-
-        /** Projection for the _id column in the stats table. */
-        public static final String[] SYNC_STATS_PROJECTION = {_ID};
-    }
-
-    /**
-     * Columns from the history table.
-     */
-    public interface HistoryColumns {
-        /**
-         * The ID of the stats row corresponding to this event.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String STATS_ID = "stats_id";
-
-        /**
-         * The source of the sync event (LOCAL, POLL, USER, SERVER).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String SOURCE = "source";
-
-        /**
-         * The type of sync event (START, STOP).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String EVENT = "event";
-
-        /**
-         * The time of the event.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String EVENT_TIME = "eventTime";
-
-        /**
-         * How long this event took. This is only valid if the EVENT is EVENT_STOP.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String ELAPSED_TIME = "elapsedTime";
-
-        /**
-         * Any additional message associated with this event.
-         * <P>Type: TEXT</P>
-         */
-        public static final String MESG = "mesg";
-
-        /**
-         * How much activity was performed sending data to the server. This is sync adapter
-         * specific, but usually is something like how many record update/insert/delete attempts
-         * were carried out. This is only valid if the EVENT is EVENT_STOP.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String UPSTREAM_ACTIVITY = "upstreamActivity";
-
-        /**
-         * How much activity was performed while receiving data from the server.
-         * This is sync adapter specific, but usually is something like how many
-         * records were received from the server. This is only valid if the
-         * EVENT is EVENT_STOP.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String DOWNSTREAM_ACTIVITY = "downstreamActivity";
-    }
-
-    /**
-     * Columns from the history table.
-     */
-    public interface StatusColumns {
-        /**
-         * How many syncs were completed for this account and authority.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String NUM_SYNCS = "numSyncs";
-
-        /**
-         * How long all the events for this account and authority took.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String TOTAL_ELAPSED_TIME = "totalElapsedTime";
-
-        /**
-         * The number of syncs with SOURCE_POLL.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String NUM_SOURCE_POLL = "numSourcePoll";
-
-        /**
-         * The number of syncs with SOURCE_SERVER.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String NUM_SOURCE_SERVER = "numSourceServer";
-
-        /**
-         * The number of syncs with SOURCE_LOCAL.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String NUM_SOURCE_LOCAL = "numSourceLocal";
-
-        /**
-         * The number of syncs with SOURCE_USER.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String NUM_SOURCE_USER = "numSourceUser";
-
-        /**
-         * The time in ms that the last successful sync ended. Will be null if
-         * there are no successful syncs. A successful sync is defined as one having
-         * MESG=MESG_SUCCESS.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String LAST_SUCCESS_TIME = "lastSuccessTime";
-
-        /**
-         * The SOURCE of the last successful sync. Will be null if
-         * there are no successful syncs. A successful sync is defined
-         * as one having MESG=MESG_SUCCESS.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String LAST_SUCCESS_SOURCE = "lastSuccessSource";
-
-        /**
-         * The end time in ms of the last sync that failed since the last successful sync.
-         * Will be null if there are no syncs or if the last one succeeded. A failed
-         * sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String LAST_FAILURE_TIME = "lastFailureTime";
-
-        /**
-         * The SOURCE of the last sync that failed since the last successful sync.
-         * Will be null if there are no syncs or if the last one succeeded. A failed
-         * sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String LAST_FAILURE_SOURCE = "lastFailureSource";
-
-        /**
-         * The MESG of the last sync that failed since the last successful sync.
-         * Will be null if there are no syncs or if the last one succeeded. A failed
-         * sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
-         * <P>Type: STRING</P>
-         */
-        public static final String LAST_FAILURE_MESG = "lastFailureMesg";
-
-        /**
-         * Is set to 1 if a sync is pending, 0 if not.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String PENDING = "pending";
-    }
-
-    /**
-     * Provides constants and utility methods to access and use the history
-     * table.
-     */
-    public static class History implements BaseColumns,
-                                                 StatsColumns,
-                                                 HistoryColumns {
-
-        /**
-         * The content url for this table.
-         */
-        public static final Uri CONTENT_URI =
-                Uri.parse("content://sync/history");
-
-        /** Enum value for a sync start event. */
-        public static final int EVENT_START = 0;
-
-        /** Enum value for a sync stop event. */
-        public static final int EVENT_STOP = 1;
-
-        // TODO: i18n -- grab these out of resources.
-        /** String names for the sync event types. */
-        public static final String[] EVENTS = { "START", "STOP" };
-
-        /** Enum value for a server-initiated sync. */
-        public static final int SOURCE_SERVER = 0;
-
-        /** Enum value for a local-initiated sync. */
-        public static final int SOURCE_LOCAL = 1;
-        /**
-         * Enum value for a poll-based sync (e.g., upon connection to
-         * network)
-         */
-        public static final int SOURCE_POLL = 2;
-
-        /** Enum value for a user-initiated sync. */
-        public static final int SOURCE_USER = 3;
-
-        // TODO: i18n -- grab these out of resources.
-        /** String names for the sync source types. */
-        public static final String[] SOURCES = { "SERVER",
-                                                 "LOCAL",
-                                                 "POLL",
-                                                 "USER" };
-
-        // Error types
-        public static final int ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
-        public static final int ERROR_AUTHENTICATION = 2;
-        public static final int ERROR_IO = 3;
-        public static final int ERROR_PARSE = 4;
-        public static final int ERROR_CONFLICT = 5;
-        public static final int ERROR_TOO_MANY_DELETIONS = 6;
-        public static final int ERROR_TOO_MANY_RETRIES = 7;
-        public static final int ERROR_INTERNAL = 8;
-
-        // The MESG column will contain one of these or one of the Error types.
-        public static final String MESG_SUCCESS = "success";
-        public static final String MESG_CANCELED = "canceled";
-
-        private static final String FINISHED_SINCE_WHERE_CLAUSE = EVENT + "=" + EVENT_STOP
-                + " AND " + EVENT_TIME + ">? AND " + ACCOUNT + "=? AND " + AUTHORITY + "=?";
-
-        public static String mesgToString(String mesg) {
-            if (MESG_SUCCESS.equals(mesg)) return mesg;
-            if (MESG_CANCELED.equals(mesg)) return mesg;
-            switch (Integer.parseInt(mesg)) {
-                case ERROR_SYNC_ALREADY_IN_PROGRESS: return "already in progress";
-                case ERROR_AUTHENTICATION: return "bad authentication";
-                case ERROR_IO: return "network error";
-                case ERROR_PARSE: return "parse error";
-                case ERROR_CONFLICT: return "conflict detected";
-                case ERROR_TOO_MANY_DELETIONS: return "too many deletions";
-                case ERROR_TOO_MANY_RETRIES: return "too many retries";
-                case ERROR_INTERNAL: return "internal error";
-                default: return "unknown error";
-            }
-        }
-
-        // utility class
-        private History() {}
-
-        /**
-         * returns a cursor that queries the sync history in descending event time order
-         * @param contentResolver the ContentResolver to use for the query
-         * @return the cursor on the History table
-         */
-        public static Cursor query(ContentResolver contentResolver) {
-            return contentResolver.query(CONTENT_URI, null, null, null, EVENT_TIME + " desc");
-        }
-
-        public static boolean hasNewerSyncFinished(ContentResolver contentResolver,
-                String account, String authority, long when) {
-            Cursor c = contentResolver.query(CONTENT_URI, new String[]{_ID},
-                    FINISHED_SINCE_WHERE_CLAUSE,
-                    new String[]{Long.toString(when), account, authority}, null);
-            try {
-              return c.getCount() > 0;
-            } finally {
-                c.close();
-            }
-        }
-    }
-
-    /**
-     * Provides constants and utility methods to access and use the authority history
-     * table, which contains information about syncs aggregated by account and authority.
-     * All the HistoryColumns except for EVENT are present, plus the AuthorityHistoryColumns.
-     */
-    public static class Status extends History implements StatusColumns {
-
-        /**
-         * The content url for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://sync/status");
-
-        // utility class
-        private Status() {}
-
-        /**
-         * returns a cursor that queries the authority sync history in descending event order of
-         * ACCOUNT, AUTHORITY
-         * @param contentResolver the ContentResolver to use for the query
-         * @return the cursor on the AuthorityHistory table
-         */
-        public static Cursor query(ContentResolver contentResolver) {
-            return contentResolver.query(CONTENT_URI, null, null, null, ACCOUNT + ", " + AUTHORITY);
-        }
-
-        public static class QueryMap extends ContentQueryMap {
-            public QueryMap(ContentResolver contentResolver,
-                    boolean keepUpdated,
-                    Handler handlerForUpdateNotifications) {
-                super(contentResolver.query(CONTENT_URI, null, null, null, null),
-                        _ID, keepUpdated, handlerForUpdateNotifications);
-            }
-
-            public ContentValues get(String account, String authority) {
-                Map<String, ContentValues> rows = getRows();
-                for (ContentValues values : rows.values()) {
-                    if (values.getAsString(ACCOUNT).equals(account)
-                            && values.getAsString(AUTHORITY).equals(authority)) {
-                        return values;
-                    }
-                }
-                return null;
-            }
-        }
-    }
-
-    /**
-     * Provides constants and utility methods to access and use the pending syncs table
-     */
-    public static final class Pending implements BaseColumns,
-                                                 StatsColumns {
-
-        /**
-         * The content url for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://sync/pending");
-
-        // utility class
-        private Pending() {}
-
-        public static class QueryMap extends ContentQueryMap {
-            public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
-                    Handler handlerForUpdateNotifications) {
-                super(contentResolver.query(CONTENT_URI, null, null, null, null), _ID, keepUpdated,
-                        handlerForUpdateNotifications);
-            }
-
-            public boolean isPending(String account, String authority) {
-                Map<String, ContentValues> rows = getRows();
-                for (ContentValues values : rows.values()) {
-                    if (values.getAsString(ACCOUNT).equals(account)
-                            && values.getAsString(AUTHORITY).equals(authority)) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Columns from the history table.
-     */
-    public interface ActiveColumns {
-        /**
-         * The wallclock time of when the active sync started.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String START_TIME = "startTime";
-    }
-
-    /**
-     * Provides constants and utility methods to access and use the pending syncs table
-     */
-    public static final class Active implements BaseColumns,
-                                                StatsColumns,
-                                                ActiveColumns {
-
-        /**
-         * The content url for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://sync/active");
-
-        // utility class
-        private Active() {}
-
-        public static class QueryMap extends ContentQueryMap {
-            public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
-                    Handler handlerForUpdateNotifications) {
-                super(contentResolver.query(CONTENT_URI, null, null, null, null), _ID, keepUpdated,
-                        handlerForUpdateNotifications);
-            }
-
-            public ContentValues getActiveSyncInfo() {
-                Map<String, ContentValues> rows = getRows();
-                for (ContentValues values : rows.values()) {
-                    return values;
-                }
-                return null;
-            }
-
-            public String getSyncingAccount() {
-                ContentValues values = getActiveSyncInfo();
-                return (values == null) ? null : values.getAsString(ACCOUNT);
-            }
-
-            public String getSyncingAuthority() {
-                ContentValues values = getActiveSyncInfo();
-                return (values == null) ? null : values.getAsString(AUTHORITY);
-            }
-
-            public long getSyncStartTime() {
-                ContentValues values = getActiveSyncInfo();
-                return (values == null) ? -1 : values.getAsLong(START_TIME);
-            }
-        }
-    }
-
-    /**
-     * Columns in the settings table, which holds key/value pairs of settings.
-     */
-    public interface SettingsColumns {
-        /**
-         * The key of the setting
-         * <P>Type: TEXT</P>
-         */
-        public static final String KEY = "name";
-
-        /**
-         * The value of the settings
-         * <P>Type: TEXT</P>
-         */
-        public static final String VALUE = "value";
-    }
-
-    /**
-     * Provides constants and utility methods to access and use the settings
-     * table.
-     */
-    public static final class Settings implements BaseColumns, SettingsColumns {
-        /**
-         * The Uri of the settings table. This table behaves a little differently than
-         * normal tables. Updates are not allowed, only inserts, and inserts cause a replace
-         * to be performed, which first deletes the row if it is already present.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://sync/settings");
-
-        /** controls whether or not the device listens for sync tickles */
-        public static final String SETTING_LISTEN_FOR_TICKLES = "listen_for_tickles";
-
-        /** controls whether or not the individual provider is synced when tickles are received */
-        public static final String SETTING_SYNC_PROVIDER_PREFIX = "sync_provider_";
-
-        /** query column project */
-        private static final String[] PROJECTION = { KEY, VALUE };
-
-        /**
-         * Convenience function for updating a single settings value as a
-         * boolean. This will either create a new entry in the table if the
-         * given name does not exist, or modify the value of the existing row
-         * with that name.  Note that internally setting values are always
-         * stored as strings, so this function converts the given value to a
-         * string before storing it.
-         *
-         * @param contentResolver the ContentResolver to use to access the settings table
-         * @param name The name of the setting to modify.
-         * @param val The new value for the setting.
-         */
-        static private void putBoolean(ContentResolver contentResolver, String name, boolean val) {
-            ContentValues values = new ContentValues();
-            values.put(KEY, name);
-            values.put(VALUE, Boolean.toString(val));
-            // this insert is translated into an update by the underlying Sync provider
-            contentResolver.insert(CONTENT_URI, values);
-        }
-
-        /**
-         * Convenience function for getting a setting value as a boolean without using the
-         * QueryMap for light-weight setting querying.
-         * @param contentResolver The ContentResolver for querying the setting.
-         * @param name The name of the setting to query
-         * @param def The default value for the setting.
-         * @return The value of the setting.
-         */
-        static public boolean getBoolean(ContentResolver contentResolver,
-                String name, boolean def) {
-            Cursor cursor = contentResolver.query(
-                    CONTENT_URI,
-                    PROJECTION,
-                    KEY + "=?",
-                    new String[] { name },
-                    null);
-            try {
-                if (cursor != null && cursor.moveToFirst()) {
-                    return Boolean.parseBoolean(cursor.getString(1));
-                }
-            } finally {
-                if (cursor != null) cursor.close();
-            }
-            return def;
-        }
-
-        /**
-         * A convenience method to set whether or not the provider is synced when
-         * it receives a network tickle.
-         *
-         * @param contentResolver the ContentResolver to use to access the settings table
-         * @param providerName the provider whose behavior is being controlled
-         * @param sync true if the provider should be synced when tickles are received for it
-         */
-        static public void setSyncProviderAutomatically(ContentResolver contentResolver,
-                String providerName, boolean sync) {
-            putBoolean(contentResolver, SETTING_SYNC_PROVIDER_PREFIX + providerName, sync);
-        }
-
-        /**
-         * A convenience method to set whether or not the device should listen to tickles.
-         *
-         * @param contentResolver the ContentResolver to use to access the settings table
-         * @param flag true if it should listen.
-         */
-        static public void setListenForNetworkTickles(ContentResolver contentResolver,
-                boolean flag) {
-            putBoolean(contentResolver, SETTING_LISTEN_FOR_TICKLES, flag);
-        }
-
-        public static class QueryMap extends ContentQueryMap {
-            private ContentResolver mContentResolver;
-
-            public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
-                    Handler handlerForUpdateNotifications) {
-                super(contentResolver.query(CONTENT_URI, null, null, null, null), KEY, keepUpdated,
-                        handlerForUpdateNotifications);
-                mContentResolver = contentResolver;
-            }
-
-            /**
-             * Check if the provider should be synced when a network tickle is received
-             * @param providerName the provider whose setting we are querying
-             * @return true of the provider should be synced when a network tickle is received
-             */
-            public boolean getSyncProviderAutomatically(String providerName) {
-                return getBoolean(SETTING_SYNC_PROVIDER_PREFIX + providerName, true);
-            }
-
-            /**
-             * Set whether or not the provider is synced when it receives a network tickle.
-             *
-             * @param providerName the provider whose behavior is being controlled
-             * @param sync true if the provider should be synced when tickles are received for it
-             */
-            public void setSyncProviderAutomatically(String providerName, boolean sync) {
-                Settings.setSyncProviderAutomatically(mContentResolver, providerName, sync);
-            }
-
-            /**
-             * Set whether or not the device should listen for tickles.
-             *
-             * @param flag true if it should listen.
-             */
-            public void setListenForNetworkTickles(boolean flag) {
-                Settings.setListenForNetworkTickles(mContentResolver, flag);
-            }
-
-            /**
-             * Check if the device should listen to tickles.
-
-             * @return true if it should
-             */
-            public boolean getListenForNetworkTickles() {
-                return getBoolean(SETTING_LISTEN_FOR_TICKLES, true);
-            }
-
-            /**
-             * Convenience function for retrieving a single settings value
-             * as a boolean.
-             *
-             * @param name The name of the setting to retrieve.
-             * @param def Value to return if the setting is not defined.
-             * @return The setting's current value, or 'def' if it is not defined.
-             */
-            private boolean getBoolean(String name, boolean def) {
-                ContentValues values = getValues(name);
-                return values != null ? values.getAsBoolean(VALUE) : def;
-            }
-        }
-    }
-}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index b408f27..767c7e7 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1908,6 +1908,7 @@
                     // User clicked on whitespace, or stopped a fling. It is a scroll.
                     createScrollingCache();
                     mTouchMode = TOUCH_MODE_SCROLL;
+                    mMotionCorrection = 0;
                     motionPosition = findMotionRow(y);
                     reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                 }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 11fab8f..9ec8347 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1534,14 +1534,14 @@
             case FOCUS_LEFT:
                 if (selectedPosition > startOfRowPos) {
                     mLayoutMode = LAYOUT_MOVE_SELECTION;
-                    setSelectionInt(selectedPosition - 1);
+                    setSelectionInt(Math.max(0, selectedPosition - 1));
                     moved = true;
                 }
                 break;
             case FOCUS_RIGHT:
                 if (selectedPosition < endOfRowPos) {
                     mLayoutMode = LAYOUT_MOVE_SELECTION;
-                    setSelectionInt(selectedPosition + 1);
+                    setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
                     moved = true;
                 }
                 break;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 652e30c..02fc7c6 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -283,7 +283,7 @@
 
         final View child = getChildAt(0);
         int width = getMeasuredWidth();
-        if (child.getMeasuredHeight() < width) {
+        if (child.getMeasuredWidth() < width) {
             final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
             int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, mPaddingTop
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index de0c982..c6b01876 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4837,10 +4837,11 @@
     }
 
     private boolean compressText(float width) {
-        if (width > 0.0f && mLayout != null && getLineCount() == 1 && !mUserSetTextScaleX) {
+        // Only compress the text if it hasn't been compressed by the previous pass
+        if (width > 0.0f && mLayout != null && getLineCount() == 1 && !mUserSetTextScaleX &&
+                mTextPaint.getTextScaleX() == 1.0f) {
             final float textWidth = mLayout.getLineWidth(0);
-            final float overflow = (textWidth - width) / width;
-
+            final float overflow = (textWidth + 1.0f - width) / width;
             if (overflow > 0.0f && overflow <= Marquee.MARQUEE_DELTA_MAX) {
                 mTextPaint.setTextScaleX(1.0f - overflow - 0.005f);
                 post(new Runnable() {
@@ -5843,9 +5844,10 @@
                 final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft() -
                         textView.getCompoundPaddingRight();
                 final float lineWidth = textView.mLayout.getLineWidth(0);
-                mGhostStart = lineWidth - textWidth + textWidth / 3.0f;
+                final float gap = textWidth / 3.0f;
+                mGhostStart = lineWidth - textWidth + gap;
                 mMaxScroll = mGhostStart + textWidth;
-                mGhostOffset = lineWidth + textWidth / 3.0f;
+                mGhostOffset = lineWidth + gap;
                 mFadeStop = lineWidth + textWidth / 6.0f;
                 mMaxFadeScroll = mGhostStart + lineWidth + lineWidth;
 
diff --git a/core/java/com/android/internal/os/AtomicFile.java b/core/java/com/android/internal/os/AtomicFile.java
new file mode 100644
index 0000000..ca0345f
--- /dev/null
+++ b/core/java/com/android/internal/os/AtomicFile.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.FileUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Helper class for performing atomic operations on a file, by creating a
+ * backup file until a write has successfully completed.
+ */
+public class AtomicFile {
+    private final File mBaseName;
+    private final File mBackupName;
+    
+    public AtomicFile(File baseName) {
+        mBaseName = baseName;
+        mBackupName = new File(baseName.getPath() + ".bak");
+    }
+    
+    public File getBaseFile() {
+        return mBaseName;
+    }
+    
+    public FileOutputStream startWrite() throws IOException {
+        // Rename the current file so it may be used as a backup during the next read
+        if (mBaseName.exists()) {
+            if (!mBaseName.renameTo(mBackupName)) {
+                mBackupName.delete();
+                if (!mBaseName.renameTo(mBackupName)) {
+                    Log.w("AtomicFile", "Couldn't rename file " + mBaseName
+                            + " to backup file " + mBackupName);
+                }
+            }
+        }
+        FileOutputStream str = null;
+        try {
+            str = new FileOutputStream(mBaseName);
+        } catch (FileNotFoundException e) {
+            File parent = mBaseName.getParentFile();
+            if (!parent.mkdir()) {
+                throw new IOException("Couldn't create directory " + mBaseName);
+            }
+            FileUtils.setPermissions(
+                parent.getPath(),
+                FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+                -1, -1);
+            try {
+                str = new FileOutputStream(mBaseName);
+            } catch (FileNotFoundException e2) {
+                throw new IOException("Couldn't create " + mBaseName);
+            }
+        }
+        return str;
+    }
+    
+    public void finishWrite(FileOutputStream str) {
+        if (str != null) {
+            try {
+                str.close();
+                mBackupName.delete();
+            } catch (IOException e) {
+                Log.w("AtomicFile", "finishWrite: Got exception:", e);
+            }
+        }
+    }
+    
+    public void failWrite(FileOutputStream str) {
+        if (str != null) {
+            try {
+                str.close();
+                mBaseName.delete();
+                mBackupName.renameTo(mBaseName);
+            } catch (IOException e) {
+                Log.w("AtomicFile", "failWrite: Got exception:", e);
+            }
+        }
+    }
+    
+    public FileOutputStream openAppend() throws IOException {
+        try {
+            return new FileOutputStream(mBaseName, true);
+        } catch (FileNotFoundException e) {
+            throw new IOException("Couldn't append " + mBaseName);
+        }
+    }
+    
+    public void truncate() throws IOException {
+        try {
+            FileOutputStream fos = new FileOutputStream(mBaseName);
+            fos.close();
+        } catch (FileNotFoundException e) {
+            throw new IOException("Couldn't append " + mBaseName);
+        } catch (IOException e) {
+        }
+    }
+    
+    public FileInputStream openRead() throws FileNotFoundException {
+        if (mBackupName.exists()) {
+            mBaseName.delete();
+            mBackupName.renameTo(mBaseName);
+        }
+        return new FileInputStream(mBaseName);
+    }
+    
+    public byte[] readFully() throws IOException {
+        FileInputStream stream = openRead();
+        try {
+            int pos = 0;
+            int avail = stream.available();
+            byte[] data = new byte[avail];
+            while (true) {
+                int amt = stream.read(data, pos, data.length-pos);
+                //Log.i("foo", "Read " + amt + " bytes at " + pos
+                //        + " of avail " + data.length);
+                if (amt <= 0) {
+                    //Log.i("foo", "**** FINISHED READING: pos=" + pos
+                    //        + " len=" + data.length);
+                    return data;
+                }
+                pos += amt;
+                avail = stream.available();
+                if (avail > data.length-pos) {
+                    byte[] newData = new byte[pos+avail];
+                    System.arraycopy(data, 0, newData, 0, pos);
+                    data = newData;
+                }
+            }
+        } finally {
+            stream.close();
+        }
+    }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index aa8fd74..7ebc896 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1012,9 +1012,6 @@
                 android:excludeFromRecents="true">
         </activity>
 
-        <provider android:name=".content.SyncProvider"
-                android:authorities="sync" android:multiprocess="false" />
-
         <service android:name="com.android.server.LoadAverageService"
             android:exported="true" />
 
diff --git a/docs/html/guide/developing/tools/aidl.jd b/docs/html/guide/developing/tools/aidl.jd
index 96e4fec..f370a80 100644
--- a/docs/html/guide/developing/tools/aidl.jd
+++ b/docs/html/guide/developing/tools/aidl.jd
@@ -1,6 +1,25 @@
 page.title=Designing a Remote Interface Using AIDL
 @jd:body
 
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>In this document</h2>
+<ol>
+  <li><a href="#implementing">Implementing IPC Using AIDL</a>
+    <ol>
+      <li><a href="#aidlsyntax">Create an .aidl File</a></li>
+      <li><a href="#implementtheinterface">Implementing the Interface</a></li>
+      <li><a href="#exposingtheinterface">Exposing Your Interface to Clients</a></li>
+      <li><a href="#parcelable">Pass by value Parameters using Parcelables</a></li>
+    </ol>
+  </li>
+  <li><a href="#calling">Calling an IPC Method</a></li>
+</ol>
+</div>
+</div>
+
+
 <p>Since each application runs in its own process, and you can write a service that
 runs in a different process from your Application's UI, sometimes you need to pass objects
 between processes.  On the Android platform, one process can not normally access the memory
@@ -10,7 +29,7 @@
 <p>The code to do that marshalling is tedious to write, so we provide the AIDL tool to do it
 for you.</p>
 
-<p> AIDL (Android Interface Definition Language) is an <a
+<p>AIDL (Android Interface Definition Language) is an <a
 href="http://en.wikipedia.org/wiki/Interface_description_language">IDL</a>
 language used to generate code that enables two processes on an Android-powered device
 to talk using interprocess communication (IPC). If you have code
@@ -20,12 +39,9 @@
 <p>The AIDL IPC mechanism
     is interface-based, similar to COM or Corba, but lighter weight. It uses a proxy
     class to pass values between the client and the implementation. </p>
-<p>This page includes the following main topics: </p>
-<ul>
-    <li><a href="#implementing">Implementing  IPC Using AIDL</a></li>
-    <li><a href="#calling">Calling an .aidl (IPC) Class  </a></li>
-</ul>
-<h2>Implementing IPC Using AIDL <a name="implementing"></a></h2>
+
+
+<h2 id="implementing">Implementing IPC Using AIDL</h2>
 <p>Follow these steps to implement an IPC service using AIDL.</p>
 <ol>
     <li><strong><a href="#aidlsyntax">Create your .aidl file</a> </strong>- This
@@ -46,7 +62,8 @@
         Service.onBind(Intent)} to return an instance of your class that implements your
         interface. </li>
 </ol>
-<h3>Create an .aidl File  <a name="aidlsyntax"></a></h3>
+
+<h3 id="aidlsyntax">Create an .aidl File</h3>
 <p>AIDL is a simple syntax that lets you declare an interface with one or more
     methods, that can take parameters and return values. These parameters and return
     values can be of any type, even other AIDL-generated interfaces.  <em>However, it
@@ -117,7 +134,7 @@
     int getCustomerList(in String branch, out String[] customerList);
 }</pre>
 
-<h3>Implementing the Interface <a name="implementtheinterface"></a></h3>
+<h3 id="implementtheinterface">Implementing the Interface</h3>
 <p>AIDL generates an interface file for you with the same name as your .aidl
     file. If you are using the Eclipse plugin, AIDL will automatically be run as part of
     the build process (you don't need to run AIDL first and then build your project).
@@ -152,7 +169,7 @@
     <li>Only methods are supported; you cannot declare static fields in an AIDL interface.</li>
 </ul>
 
-<h3>Exposing Your Interface to Clients<a name="exposingtheinterface" id="exposingtheinterface"></a></h3>
+<h3 id="exposingtheinterface">Exposing Your Interface to Clients</h3>
 <p>Now that you've got your interface implementation, you need to expose it to clients.
     This is known as &quot;publishing your service.&quot; To publish a service,
     inherit {@link android.app.Service Service} and implement {@link android.app.Service#onBind
@@ -165,8 +182,8 @@
     exposing_a_service}
 }</pre>
 
-<a name="parcelable"></a>
-<h3>Pass by value Parameters using Parcelables</h3>
+
+<h3 id="parcelable">Pass by value Parameters using Parcelables</h3>
 
 <p>If you have a class that you would like to send from one process to another through
 an AIDL interface, you can do that.  You must ensure that the code for your class is available
@@ -181,25 +198,12 @@
 value in a parcel into your object.</li>
 <li>Add a static field called <code>CREATOR</code> to your class which is an object implementing
 the {@link android.os.Parcelable.Creator Parcelable.Creator} interface.</li>
-<li>Last but not least:
-<ul>
-<li>If you are developing with Eclipse/ADT, follow these steps:
-<ol type="a">
-<li>In the Package Explorer view, right-click on the project.</li>
-<li>Choose <strong>Android Tools</strong> > <strong>Create Aidl preprocess file
-for Parcelable classes</strong>.</li>
-<li>This will create a file called "project.aidl" in the root of the project.
-The file will be automatically used when compiling an aidl file that uses the
-parcelable classes.</li>
-</ol>
-</li>
-<li>If you are developing with Ant or are using a custom build process, create an aidl file
+<li>Last but not least, create an aidl file
 that declares your parcelable class (as shown below). If you are using a custom build process,
 do not add the aidl file to your build. Similar to a header file in C, the aidl file isn't 
 compiled.</li>
-</ul>
-</li>
-</ul>
+</ol>
+
 <p>AIDL will use these methods and fields in the code it generates to marshall and unmarshall
 your objects.</p>
 <p>Here is an example of how the {@link android.graphics.Rect} class implements the
@@ -269,7 +273,7 @@
 <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> for more
 on how to keep your application secure from malware.</p>
 
-<h2>Calling an IPC Method <a name="calling"></a></h2>
+<h2 id="calling">Calling an IPC Method</h2>
 <p>Here are the steps a calling class should make to call your remote interface: </p>
 <ol>
     <li>Declare a variable of the interface type that your .aidl file defined. </li>
diff --git a/docs/html/guide/topics/ui/custom-components.jd b/docs/html/guide/topics/ui/custom-components.jd
index eccc2ca..76d1034 100644
--- a/docs/html/guide/topics/ui/custom-components.jd
+++ b/docs/html/guide/topics/ui/custom-components.jd
@@ -34,7 +34,7 @@
 {@link android.widget.TextSwitcher TextSwitcher}. </p>
 
 <p>Among the layouts available are {@link android.widget.LinearLayout LinearLayout}, 
-{@link android.widget.FrameLayout FrameLayout}, {@link android.widget.AbsoluteLayout AbsoluteLayout}, 
+{@link android.widget.FrameLayout FrameLayout}, {@link android.widget.RelativeLayout RelativeLayout}, 
 and others. For more examples, see <a href="layout-objects.html">Common Layout Objects</a>.</p>
 
 <p>If none of the prebuilt widgets or layouts meets your needs, you can create your own View subclass. 
diff --git a/docs/html/guide/topics/ui/how-android-draws.jd b/docs/html/guide/topics/ui/how-android-draws.jd
index a511005..21f9833 100644
--- a/docs/html/guide/topics/ui/how-android-draws.jd
+++ b/docs/html/guide/topics/ui/how-android-draws.jd
@@ -70,8 +70,8 @@
     enclose its content (plus padding).</li>
    </ul>
   <p>There are subclasses of LayoutParams for different subclasses of ViewGroup.
-   For example, AbsoluteLayout has its own subclass of LayoutParams which adds
-   an X and Y value.
+   For example, RelativeLayout has its own subclass of LayoutParams, which includes
+   the ability to center child Views horizontally and vertically.
    </p>
    
    <p>
diff --git a/docs/html/guide/topics/ui/index.jd b/docs/html/guide/topics/ui/index.jd
index 6bd1d15..ef23672 100644
--- a/docs/html/guide/topics/ui/index.jd
+++ b/docs/html/guide/topics/ui/index.jd
@@ -116,7 +116,7 @@
 
 <p>There are a variety of ways in which you can layout your views. Using more and different kinds of view groups,
 you can structure child views and view groups in an infinite number of ways.
-Some pre-defined view groups offered by Android (called layouts) include LinearLayout, RelativeLayout, AbsoluteLayout,
+Some pre-defined view groups offered by Android (called layouts) include LinearLayout, RelativeLayout,
 TableLayout, GridLayout and others. Each offers a unique set of layout parameters that are used to define the
 positions of child views and layout structure.</p>
 <p>To learn about some of the different kinds of view groups used for a layout, 
diff --git a/docs/html/guide/topics/ui/layout-objects.jd b/docs/html/guide/topics/ui/layout-objects.jd
index cf85fd6..bb13a188 100644
--- a/docs/html/guide/topics/ui/layout-objects.jd
+++ b/docs/html/guide/topics/ui/layout-objects.jd
@@ -10,7 +10,6 @@
     <li><a href="#framelayout">FrameLayout</a></li>
     <li><a href="#linearlayout">LinearLayout</a></li>
     <li><a href="#tablelayout">TableLayout</a></li>
-    <li><a href="#absolutelayout">AbsoluteLayout</a></li>
     <li><a href="#relativelayout">RelativeLayout</a></li>
     <li><a href="#viewgroupsummary">Summary of Important View Groups</a></li>
   </ol>
@@ -143,16 +142,6 @@
 TableLayout</a> tutorial.</p>
 
 
-<h2 id="absolutelayout">AbsoluteLayout</h2>
-<p>{@link android.widget.AbsoluteLayout} enables child views to specify
-    their own exact x/y coordinates on the screen. Coordinates <em>(0,0)</em> is the upper left
-    corner, and values increase as you move down and to the right. Margins are not
-    supported, and overlapping elements are allowed (although not recommended). We
-    generally recommend against using AbsoluteLayout unless you have good reasons
-    to use it, because it is fairly rigid and does not adjust to different types of
-    displays. </p>
-
-
 <h2 id="relativelayout">RelativeLayout</h2>
 <p>{@link android.widget.RelativeLayout} lets child views specify their
     position relative to the parent view or to each other (specified by ID). So you can
@@ -232,11 +221,6 @@
         <th scope="col">Description</th>
     </tr>
     <tr>
-        <td>{@link android.widget.AbsoluteLayout AbsoluteLayout}<br /></td>
-        <td>Enables you to specify the location of child objects relative to the
-            parent in exact measurements (for example, pixels). </td>
-    </tr>
-    <tr>
         <td>{@link android.widget.FrameLayout FrameLayout}</td>
         <td>Layout that acts as a view frame to display
             a single object. </td>
diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
index df599c7..8b3bedf 100644
--- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
+++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
@@ -9,7 +9,6 @@
 import android.util.Config;
 import android.util.EventLog;
 import android.app.IntentService;
-import android.provider.Sync;
 import android.provider.SubscribedFeeds;
 import android.provider.SyncConstValue;
 import android.database.Cursor;
@@ -17,7 +16,7 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.os.Bundle;
-import android.os.Debug;
+import android.os.RemoteException;
 import android.text.TextUtils;
 import android.net.Uri;
 
@@ -105,10 +104,6 @@
 
     private void handleTickle(Context context, String account, String feed) {
         Cursor c = null;
-        Sync.Settings.QueryMap syncSettings =
-                new Sync.Settings.QueryMap(context.getContentResolver(),
-                        false /* don't keep updated */,
-                        null /* not needed since keep updated is false */);
         final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? "
                 + "and " + SubscribedFeeds.Feeds.FEED + "= ?";
         try {
@@ -124,9 +119,14 @@
                 String authority = c.getString(c.getColumnIndexOrThrow(
                         SubscribedFeeds.Feeds.AUTHORITY));
                 EventLog.writeEvent(LOG_TICKLE, authority);
-                if (!syncSettings.getSyncProviderAutomatically(authority)) {
-                    Log.d(TAG, "supressing tickle since provider " + authority
-                            + " is configured to not sync automatically");
+                try {
+                    if (!ContentResolver.getContentService()
+                            .getSyncProviderAutomatically(authority)) {
+                        Log.d(TAG, "supressing tickle since provider " + authority
+                                + " is configured to not sync automatically");
+                        continue;
+                    }
+                } catch (RemoteException e) {
                     continue;
                 }
                 Uri uri = Uri.parse("content://" + authority);
@@ -137,7 +137,6 @@
             }
         } finally {
             if (c != null) c.deactivate();
-            syncSettings.close();
         }
     }
 
diff --git a/preloaded-classes b/preloaded-classes
index 7d719e9..0520e41 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -76,8 +76,6 @@
 android.content.ContentQueryMap$1
 android.content.ContentResolver
 android.content.ContentResolver$CursorWrapperInner
-android.content.ContentServiceNative
-android.content.ContentServiceProxy
 android.content.ContentValues
 android.content.Context
 android.content.ContextWrapper
@@ -85,6 +83,8 @@
 android.content.DialogInterface$OnCancelListener
 android.content.DialogInterface$OnDismissListener
 android.content.IContentProvider
+android.content.IContentService
+android.content.IContentService$Stub
 android.content.Intent
 android.content.Intent$1
 android.content.IntentFilter
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 0f95318..04cd53f 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -26,15 +26,19 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 
 import android.backup.IBackupManager;
 
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.lang.String;
 import java.util.HashSet;
 import java.util.List;
@@ -57,6 +61,8 @@
     private HashSet<ServiceInfo> mPendingBackups = new HashSet<ServiceInfo>();
     private final Object mQueueLock = new Object();
 
+    private File mStateDir;
+    private File mDataDir;
     
     // ----- Handler that runs the actual backup process asynchronously -----
 
@@ -99,12 +105,54 @@
                         if (mTargetService != null) {
                             try {
                                 Log.d(TAG, "invoking doBackup() on " + backupIntent);
-                                // !!! TODO: set up files
-                                mTargetService.doBackup(-1, -1, -1);
+
+                                File savedStateName = new File(mStateDir, service.packageName);
+                                File backupDataName = new File(mDataDir, service.packageName + ".data");
+                                File newStateName = new File(mStateDir, service.packageName + ".new");
+                                
+                                ParcelFileDescriptor savedState =
+                                        ParcelFileDescriptor.open(savedStateName,
+                                                ParcelFileDescriptor.MODE_READ_ONLY |
+                                                ParcelFileDescriptor.MODE_CREATE);
+                                
+                                backupDataName.delete();
+                                ParcelFileDescriptor backupData =
+                                        ParcelFileDescriptor.open(backupDataName,
+                                                ParcelFileDescriptor.MODE_READ_WRITE |
+                                                ParcelFileDescriptor.MODE_CREATE);
+
+                                newStateName.delete();
+                                ParcelFileDescriptor newState =
+                                        ParcelFileDescriptor.open(newStateName,
+                                                ParcelFileDescriptor.MODE_READ_WRITE |
+                                                ParcelFileDescriptor.MODE_CREATE);
+
+                                // Run the target's backup pass
+                                try {
+                                    mTargetService.doBackup(savedState, backupData, newState);
+                                } finally {
+                                    savedState.close();
+                                    backupData.close();
+                                    newState.close();
+                                }
+
+                                // !!! TODO: Now propagate the newly-backed-up data to the transport
+                                
+                                // !!! TODO: After successful transport, delete the now-stale data
+                                // and juggle the files so that next time the new state is passed
+                                backupDataName.delete();
+                                newStateName.renameTo(savedStateName);
+                                
+                            } catch (FileNotFoundException fnf) {
+                                Log.d(TAG, "File not found on backup: ");
+                                fnf.printStackTrace();
                             } catch (RemoteException e) {
                                 Log.d(TAG, "Remote target " + backupIntent
                                         + " threw during backup:");
                                 e.printStackTrace();
+                            } catch (Exception e) {
+                                Log.w(TAG, "Final exception guard in backup: ");
+                                e.printStackTrace();
                             }
                             mContext.unbindService(this);
                         }
@@ -138,6 +186,11 @@
         mContext = context;
         mPackageManager = context.getPackageManager();
 
+        // Set up our bookkeeping
+        mStateDir = new File(Environment.getDataDirectory(), "backup");
+        mStateDir.mkdirs();
+        mDataDir = Environment.getDownloadCacheDirectory();
+        
         // Identify the backup participants
         // !!! TODO: also watch package-install to keep this up to date
         List<ResolveInfo> services = mPackageManager.queryIntentServices(
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 760988d..493bd09 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -35,7 +35,6 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.provider.Sync;
 import android.util.EventLog;
 import android.util.Log;
 
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 321b17e..3e75db1 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -2078,13 +2078,16 @@
             int pos = mAppTokens.size() - 1;
             int curGroup = 0;
             int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            boolean findingBehind = false;
             boolean haveGroup = false;
             boolean lastFullscreen = false;
             while (pos >= 0) {
                 AppWindowToken wtoken = mAppTokens.get(pos);
                 pos--;
-                // if we're about to tear down this window, don't use it for orientation
-                if (!wtoken.hidden && wtoken.hiddenRequested) {
+                // if we're about to tear down this window and not seek for
+                // the behind activity, don't use it for orientation
+                if (!findingBehind
+                        && (!wtoken.hidden && wtoken.hiddenRequested)) {
                     continue;
                 }
 
@@ -2108,10 +2111,12 @@
                     }
                 }
                 int or = wtoken.requestedOrientation;
-                // If this application is fullscreen, then just take whatever
+                // If this application is fullscreen, and didn't explicitly say
+                // to use the orientation behind it, then just take whatever
                 // orientation it has and ignores whatever is under it.
                 lastFullscreen = wtoken.appFullscreen;
-                if (lastFullscreen) {
+                if (lastFullscreen 
+                        && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
                     return or;
                 }
                 // If this application has requested an explicit orientation,
@@ -2123,6 +2128,7 @@
                         or == ActivityInfo.SCREEN_ORIENTATION_USER) {
                     return or;
                 }
+                findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
             }
             return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     }
diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
index 6636a97..2524966 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -186,6 +186,9 @@
     
     if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0)
         env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
+    else
+        env->SetIntField(obj, gFieldIds.mBatteryStatus,
+                         gConstants.statusUnknown);
     
     if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0)
         env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
diff --git a/test-runner/android/test/SyncBaseInstrumentation.java b/test-runner/android/test/SyncBaseInstrumentation.java
index c1d2507..772d75c 100644
--- a/test-runner/android/test/SyncBaseInstrumentation.java
+++ b/test-runner/android/test/SyncBaseInstrumentation.java
@@ -18,12 +18,10 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContentValues;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.SystemClock;
-import android.provider.Sync;
 import android.net.Uri;
-import java.util.Map;
 
 /**
  * If you would like to test sync a single provider with an
@@ -75,11 +73,11 @@
     }
 
     protected void cancelSyncsandDisableAutoSync() {
-        Sync.Settings.QueryMap mSyncSettings =
-                new Sync.Settings.QueryMap(mContentResolver, true, null);
-        mSyncSettings.setListenForNetworkTickles(false);
+        try {
+            ContentResolver.getContentService().setListenForNetworkTickles(false);
+        } catch (RemoteException e) {
+        }
         mContentResolver.cancelSync(null);
-        mSyncSettings.close();
     }
 
     /**
@@ -88,34 +86,11 @@
      * @return
      */
     private boolean isSyncActive(String account, String authority) {
-        Sync.Pending.QueryMap pendingQueryMap = null;
-        Sync.Active.QueryMap activeQueryMap = null;
         try {
-            pendingQueryMap = new Sync.Pending.QueryMap(mContentResolver, false, null);
-            activeQueryMap = new Sync.Active.QueryMap(mContentResolver, false, null);
-
-            if (pendingQueryMap.isPending(account, authority)) {
-                return true;
-            }
-            if (isActiveInActiveQueryMap(activeQueryMap, account, authority)) {
-                return true;
-            }
+            return ContentResolver.getContentService().isSyncActive(account,
+                    authority);
+        } catch (RemoteException e) {
             return false;
-        } finally {
-            activeQueryMap.close();
-            pendingQueryMap.close();
         }
     }
-
-    private boolean isActiveInActiveQueryMap(Sync.Active.QueryMap activemap, String account,
-                                             String authority) {
-        Map<String, ContentValues> rows = activemap.getRows();
-        for (ContentValues values : rows.values()) {
-            if (values.getAsString("account").equals(account)
-                    && values.getAsString("authority").equals(authority)) {
-                return true;
-            }
-        }
-        return false;
-    }
 }
diff --git a/tests/CoreTests/android/content/SyncStorageEngineTest.java b/tests/CoreTests/android/content/SyncStorageEngineTest.java
index 36805b1..dee6e38 100644
--- a/tests/CoreTests/android/content/SyncStorageEngineTest.java
+++ b/tests/CoreTests/android/content/SyncStorageEngineTest.java
@@ -20,7 +20,6 @@
 import android.test.RenamingDelegatingContext;
 import android.test.mock.MockContext;
 import android.test.mock.MockContentResolver;
-import android.provider.Sync;
 
 public class SyncStorageEngineTest extends AndroidTestCase {
 
@@ -39,7 +38,7 @@
 
         long time0 = 1000;
         long historyId = engine.insertStartSyncEvent(
-                account, authority, time0, Sync.History.SOURCE_LOCAL);
+                account, authority, time0, SyncStorageEngine.SOURCE_LOCAL);
         long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
         engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
     }
diff --git a/tests/DumpRenderTree/run_page_cycler.py b/tests/DumpRenderTree/assets/run_page_cycler.py
similarity index 92%
rename from tests/DumpRenderTree/run_page_cycler.py
rename to tests/DumpRenderTree/assets/run_page_cycler.py
index 9a099b5..2325047 100755
--- a/tests/DumpRenderTree/run_page_cycler.py
+++ b/tests/DumpRenderTree/assets/run_page_cycler.py
@@ -56,10 +56,11 @@
   run_load_test_cmd_postfix = " -w com.android.dumprendertree/.LayoutTestsAutoRunner"
 
   # Call LoadTestsAutoTest::runTest.
-  run_load_test_cmd = run_load_test_cmd_prefix + " -e class com.android.dumprendertree.LoadTestsAutoTest#runTest -e path \"" + path + "\" -e timeout " + timeout_ms + run_load_test_cmd_postfix
+  run_load_test_cmd = run_load_test_cmd_prefix + " -e class com.android.dumprendertree.LoadTestsAutoTest#runPageCyclerTest -e path \"" + path + "\" -e timeout " + timeout_ms + run_load_test_cmd_postfix
 
   (adb_output, adb_error) = subprocess.Popen(run_load_test_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
-  if adb_output.find('INSTRUMENTATION_FAILED') != -1:
+  if adb_output.find('INSTRUMENTATION_FAILED') != -1 or \
+      adb_output.find('Process crashed.') != -1:
     logging.error("Error happened : " + adb_output)
     sys.exit(1)
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 39eae02..562b1f3 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -16,24 +16,11 @@
 
 package com.android.dumprendertree;
 
-import android.app.Activity;
 import android.app.Instrumentation;
-import android.app.Instrumentation.ActivityMonitor;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Intent;
-
-import android.util.Log;
-import android.view.KeyEvent;
-import android.webkit.WebSettings;
-
 import android.os.Bundle;
-import android.os.Message;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import com.android.dumprendertree.TestShellActivity;
+import android.util.Log;
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
@@ -141,6 +128,7 @@
     private Vector<String> mTestList;
     private boolean mRebaselineResults;
     private String mTestPathPrefix;
+    private boolean mFinished;
     
     public LayoutTestsAutoTest() {
       super("com.android.dumprendertree", TestShellActivity.class);
@@ -290,6 +278,7 @@
         activity.setCallback(new TestShellCallback() {
             public void finished() {
                 synchronized (LayoutTestsAutoTest.this) {
+                    mFinished = true;
                     LayoutTestsAutoTest.this.notifyAll();
                 }
             }         
@@ -306,6 +295,7 @@
             resultFile = getAndroidExpectedResultFile(expectedResultFile);
         }
         
+        mFinished = false;
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setClass(activity, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
@@ -316,9 +306,11 @@
       
         // Wait until done.
         synchronized (this) {
-            try {
-                this.wait();
-            } catch (InterruptedException e) { }
+            while(!mFinished){
+                try {
+                    this.wait();
+                } catch (InterruptedException e) { }
+            }
         }
         
         if (!mRebaselineResults) {
@@ -478,7 +470,7 @@
                 byte[] buf = new byte[2048];
                 int len;
 
-                while ((len = in.read(buf)) > 0 ) {
+                while ((len = in.read(buf)) >= 0 ) {
                     out.write(buf, 0, len);
                 }
                 out.close();
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index b064dbb..5cb5155 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -16,52 +16,34 @@
 
 package com.android.dumprendertree;
 
-import android.app.Activity;
 import android.app.Instrumentation;
-import android.app.Instrumentation.ActivityMonitor;
 import android.content.Intent;
 
 import android.util.Log;
 
 import android.os.Bundle;
+import android.os.Debug;
+import android.os.Debug.MemoryInfo;
 import android.test.ActivityInstrumentationTestCase2;
 
 import com.android.dumprendertree.TestShellActivity;
 import com.android.dumprendertree.TestShellCallback;
 
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-
-class StreamPipe extends Thread {
-    InputStream in;
-    OutputStream out;
-    
-    StreamPipe(InputStream in, OutputStream out) {
-        this.in = in;
-        this.out = out;
-    }
-    
-    public void run() {
-        try {
-            byte[] buf = new byte[1024];
-            int nofb = this.in.read(buf);
-            while (nofb != -1) {
-                this.out.write(buf, 0, nofb);
-                nofb = this.in.read(buf);
-            }          
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-}
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
 
 public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShellActivity> {
 
     private final static String LOGTAG = "LoadTest";
     private final static String LOAD_TEST_RESULT = "/sdcard/load_test_result.txt";
-    
+    private boolean mFinished;
+    static final String LOAD_TEST_RUNNER_FILES[] = {
+        "run_page_cycler.py"
+  };
+
     public LoadTestsAutoTest() {
         super("com.android.dumprendertree", TestShellActivity.class);
     }
@@ -74,17 +56,17 @@
         bundle.putBoolean(file, result);
         inst.sendStatus(0, bundle);
     }
-    
+
     // Invokes running of layout tests
     // and waits till it has finished running.
-    public void runTest() {
+    public void runPageCyclerTest() {
         LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation();
 
         if (runner.mTestPath == null) {
             Log.e(LOGTAG, "No test specified");
             return;
         }
-        
+
         TestShellActivity activity = (TestShellActivity) getActivity();
 
         // Run tests
@@ -92,48 +74,69 @@
 
         // TODO(fqian): let am instrumentation pass in the command line, currently
         // am instrument does not allow spaces in the command.
-        runPostShellCommand("/system/bin/dumpsys meminfo");
-        
+        dumpMemoryInfo();
+
         // Kill activity
         activity.finish();
     }
 
-    private void runPostShellCommand(String cmd) {
-        if (cmd == null || cmd.length() == 0)
-            return;
-        
+    private void dumpMemoryInfo() {
         try {
-            // Call dumpsys meminfo
-            Process proc = Runtime.getRuntime().exec(cmd);
-            // Append output to LOAD_TEST_RESULT
-            InputStream input = proc.getInputStream();
-            InputStream error = proc.getErrorStream();
-            FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true);
+            Log.v(LOGTAG, "Dumping memory information.");
 
-            StreamPipe p_in = new StreamPipe(input, out);
-            StreamPipe p_err = new StreamPipe(error, System.err);
-            
-            p_in.start();
-            p_err.start();
-            
-            proc.waitFor();
+            FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true);
+            PrintStream ps = new PrintStream(out);
+
+            MemoryInfo mi = new MemoryInfo();
+            Debug.getMemoryInfo(mi);
+
+            //try to fake the dumpsys format
+            //this will eventually be changed to XML
+            String format = "%15s:%9d%9d%9d%9d";
+            String pss =
+              String.format(format, "(Pss)",
+                  mi.nativePss, mi.dalvikPss, mi.otherPss,
+                  mi.nativePss + mi.dalvikPss + mi.otherPss);
+            String sd =
+              String.format(format, "(shared dirty)",
+                  mi.nativeSharedDirty, mi.dalvikSharedDirty, mi.otherSharedDirty,
+                  mi.nativeSharedDirty + mi.dalvikSharedDirty + mi.otherSharedDirty);
+            String pd =
+              String.format(format, "(priv dirty)",
+                  mi.nativePrivateDirty, mi.dalvikPrivateDirty, mi.otherPrivateDirty,
+                  mi.nativePrivateDirty + mi.dalvikPrivateDirty + mi.otherPrivateDirty);
+
+            ps.print("\n\n\n");
+            ps.println("** MEMINFO in pid 0 [com.android.dumprendertree] **");
+            ps.println("                   native   dalvik    other    total");
+            ps.println("           size:    12060     5255      N/A    17315");
+            ps.println("      allocated:    12060     5255      N/A    17315");
+            ps.println("           free:    12060     5255      N/A    17315");
+            ps.println(pss);
+            ps.println(sd);
+            ps.println(pd);
+            ps.print("\n\n\n");
+            ps.flush();
+            ps.close();
+            out.flush();
+            out.close();
         } catch (IOException e) {
             Log.e(LOGTAG, e.getMessage());
-        } catch (InterruptedException e) {
-            Log.e(LOGTAG, e.getMessage());
-        }      
+        }
     }
-    
+
     // A convenient method to be called by another activity.
     private void runTestAndWaitUntilDone(TestShellActivity activity, String url, int timeout) {
         activity.setCallback(new TestShellCallback() {
             public void finished() {
                 synchronized (LoadTestsAutoTest.this) {
+                    mFinished = true;
                     LoadTestsAutoTest.this.notifyAll();
                 }
-            }         
+            }
         });
-        
+
+        mFinished = false;
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setClass(activity, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
@@ -141,12 +144,41 @@
         intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
         intent.putExtra(TestShellActivity.RESULT_FILE, LOAD_TEST_RESULT);
         activity.startActivity(intent);
-        
+
         // Wait until done.
         synchronized (this) {
-            try {
-                this.wait();
-            } catch (InterruptedException e) { }
+            while(!mFinished) {
+                try {
+                    this.wait();
+                } catch (InterruptedException e) { }
+            }
         }
-    }   
+    }
+
+    public void copyRunnerAssetsToCache() {
+        try {
+            String out_dir = getActivity().getApplicationContext()
+                .getCacheDir().getPath() + "/";
+
+            for( int i=0; i< LOAD_TEST_RUNNER_FILES.length; i++) {
+                InputStream in = getActivity().getAssets().open(
+                        LOAD_TEST_RUNNER_FILES[i]);
+                OutputStream out = new FileOutputStream(
+                        out_dir + LOAD_TEST_RUNNER_FILES[i]);
+
+                byte[] buf = new byte[2048];
+                int len;
+
+                while ((len = in.read(buf)) >= 0 ) {
+                    out.write(buf, 0, len);
+                }
+                out.close();
+                in.close();
+            }
+        }catch (IOException e) {
+          e.printStackTrace();
+        }
+
+    }
+
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index bf8a3b3..404d101 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -165,7 +165,8 @@
             if (mDialogStrings != null)
                 os.write(mDialogStrings.toString().getBytes());
             mDialogStrings = null;
-            os.write(webkitData.getBytes());
+            if (webkitData != null)
+                os.write(webkitData.getBytes());
             os.flush();
             os.close();
         } catch (IOException ex) {
diff --git a/tools/layoutlib/.gitignore b/tools/layoutlib/.gitignore
new file mode 100644
index 0000000..d44a17d
--- /dev/null
+++ b/tools/layoutlib/.gitignore
@@ -0,0 +1,3 @@
+api/bin
+bridge/bin
+create/bin
diff --git a/tools/layoutlib/bridge/.gitignore b/tools/layoutlib/bridge/.gitignore
deleted file mode 100644
index efa6632..0000000
--- a/tools/layoutlib/bridge/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-bin/*
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
index 727d6f2..cfab90a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
@@ -17,7 +17,6 @@
 package com.android.layoutlib.bridge;
 
 import android.content.ContentResolver;
-import android.content.ContentServiceNative;
 import android.content.Context;
 import android.content.IContentProvider;
 import android.database.ContentObserver;
@@ -51,9 +50,6 @@
     
     /**
      * Stub for the layoutlib bridge content resolver.
-     * <p/>
-     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
-     * which returns null and would make the call crash. Instead we do nothing.
      */
     @Override
     public void registerContentObserver(Uri uri, boolean notifyForDescendents,
@@ -63,9 +59,6 @@
     
     /**
      * Stub for the layoutlib bridge content resolver.
-     * <p/>
-     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
-     * which returns null and would make the call crash. Instead we do nothing.
      */
     @Override
     public void unregisterContentObserver(ContentObserver observer) {
@@ -74,9 +67,6 @@
     
     /**
      * Stub for the layoutlib bridge content resolver.
-     * <p/>
-     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
-     * which returns null and would make the call crash. Instead we do nothing.
      */
     @Override
     public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
@@ -85,9 +75,6 @@
     
     /**
      * Stub for the layoutlib bridge content resolver.
-     * <p/>
-     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
-     * which returns null and would make the call crash. Instead we do nothing.
      */
     @Override
     public void startSync(Uri uri, Bundle extras) {
@@ -96,9 +83,6 @@
     
     /**
      * Stub for the layoutlib bridge content resolver.
-     * <p/>
-     * The super implementation accesses the {@link ContentServiceNative#getDefault()}
-     * which returns null and would make the call crash. Instead we do nothing.
      */
     @Override
     public void cancelSync(Uri uri) {