Merge "Skip any number of leading ID3v2 tags (instead of just the first), also simplify/fix the find-consecutive-mp3-frames functionality." into froyo
diff --git a/CleanSpec.mk b/CleanSpec.mk
index de0fd2f..417f764 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -49,6 +49,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.xml b/api/current.xml
index aea02e5..6477fc4 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26785,59 +26785,6 @@
 </package>
 <package name="android.app.backup"
 >
-<class name="AbsoluteFileBackupHelper"
- extends="android.app.backup.FileBackupHelperBase"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.app.backup.BackupHelper">
-</implements>
-<constructor name="AbsoluteFileBackupHelper"
- type="android.app.backup.AbsoluteFileBackupHelper"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="files" type="java.lang.String...">
-</parameter>
-</constructor>
-<method name="performBackup"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="oldState" type="android.os.ParcelFileDescriptor">
-</parameter>
-<parameter name="data" type="android.app.backup.BackupDataOutput">
-</parameter>
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
-</parameter>
-</method>
-<method name="restoreEntity"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="android.app.backup.BackupDataInputStream">
-</parameter>
-</method>
-</class>
 <class name="BackupAgent"
  extends="android.content.ContextWrapper"
  abstract="true"
@@ -30050,8 +29997,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="thread" type="java.lang.Thread">
-</parameter>
 </method>
 <field name="LOG_SYNC_DETAILS"
  type="int"
@@ -30065,48 +30010,6 @@
 >
 </field>
 </class>
-<class name="ActiveSyncInfo"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getAccount"
- return="android.accounts.Account"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getAuthority"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getStartTime"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <class name="ActivityNotFoundException"
  extends="java.lang.RuntimeException"
  abstract="false"
@@ -32142,8 +32045,8 @@
 <parameter name="selectionArgs" type="java.lang.String[]">
 </parameter>
 </method>
-<method name="getActiveSync"
- return="android.content.ActiveSyncInfo"
+<method name="getCurrentSync"
+ return="android.content.SyncInfo"
  abstract="false"
  native="false"
  synchronized="false"
@@ -34728,17 +34631,6 @@
  visibility="public"
 >
 </field>
-<field name="STORAGE_SERVICE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;storage&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TELEPHONY_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -36195,6 +36087,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.util.Iterator">
+</implements>
 <method name="close"
  return="void"
  abstract="true"
@@ -36206,32 +36100,6 @@
  visibility="public"
 >
 </method>
-<method name="hasNext"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
-<method name="next"
- return="android.content.Entity"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
 <method name="reset"
  return="void"
  abstract="true"
@@ -36242,8 +36110,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
 </method>
 </interface>
 <class name="Intent"
@@ -41676,6 +41542,45 @@
 </parameter>
 </method>
 </class>
+<class name="SyncInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="account"
+ type="android.accounts.Account"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="authority"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="startTime"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="SyncResult"
  extends="java.lang.Object"
  abstract="false"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 74cfbfa..b07b690 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2148,31 +2148,7 @@
         }
 
         @Override public Drawable getApplicationIcon(ApplicationInfo info) {
-            final int icon = info.icon;
-            if (icon != 0) {
-                ResourceName name = new ResourceName(info, icon);
-                Drawable dr = getCachedIcon(name);
-                if (dr != null) {
-                    return dr;
-                }
-                try {
-                    Resources r = getResourcesForApplication(info);
-                    dr = r.getDrawable(icon);
-                    if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
-                            + Integer.toHexString(icon) + " from " + r
-                            + ": " + dr);
-                    putCachedIcon(name, dr);
-                    return dr;
-                } catch (NameNotFoundException e) {
-                    Log.w("PackageManager", "Failure retrieving resources for"
-                            + info.packageName);
-                } catch (RuntimeException e) {
-                    // If an exception was thrown, fall through to return
-                    // default icon.
-                    Log.w("PackageManager", "Failure retrieving app icon", e);
-                }
-            }
-            return getDefaultActivityIcon();
+            return info.loadIcon(this);
         }
 
         @Override public Drawable getApplicationIcon(String packageName)
@@ -2413,25 +2389,6 @@
             }
         }
 
-        private CharSequence getLabel(ResourceName name, ApplicationInfo app, int id) {
-            CharSequence cs = getCachedString(name);
-            if (cs != null) {
-                return cs;
-            }
-            try {
-                Resources r = getResourcesForApplication(app);
-                cs = r.getText(id);
-                putCachedString(name, cs);
-            } catch (NameNotFoundException e) {
-                Log.w("PackageManager", "Failure retrieving resources for"
-                        + app.packageName);
-            } catch (RuntimeException e) {
-                // If an exception was thrown, fall through to return null
-                Log.w("ApplicationInfo", "Failure retrieving activity name", e);
-            }
-            return cs;
-        }
-
         @Override
         public CharSequence getText(String packageName, int resid,
                 ApplicationInfo appInfo) {
@@ -2493,17 +2450,7 @@
 
         @Override
         public CharSequence getApplicationLabel(ApplicationInfo info) {
-            if (info.nonLocalizedLabel != null) {
-                return info.nonLocalizedLabel;
-            }
-            final int id = info.labelRes;
-            if (id != 0) {
-                CharSequence cs = getLabel(new ResourceName(info, id), info, id);
-                if (cs != null) {
-                    return cs;
-                }
-            }
-            return info.packageName;
+            return info.loadLabel(this);
         }
 
         @Override
diff --git a/core/java/android/app/backup/AbsoluteFileBackupHelper.java b/core/java/android/app/backup/AbsoluteFileBackupHelper.java
index 677224c..a4d99cf 100644
--- a/core/java/android/app/backup/AbsoluteFileBackupHelper.java
+++ b/core/java/android/app/backup/AbsoluteFileBackupHelper.java
@@ -26,7 +26,7 @@
  * Like FileBackupHelper, but takes absolute paths for the files instead of
  * subpaths of getFilesDir()
  *
- * STOPSHIP: document!
+ * @hide
  */
 public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper {
     private static final String TAG = "AbsoluteFileBackupHelper";
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index 14bc5dd..9dd7b9f 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -17,12 +17,10 @@
 package android.content;
 
 import android.accounts.Account;
-import android.net.TrafficStats;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
-import android.util.EventLog;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -113,12 +111,13 @@
         public void cancelSync(ISyncContext syncContext) {
             // synchronize to make sure that mSyncThread doesn't change between when we
             // check it and when we use it
+            final SyncThread syncThread;
             synchronized (mSyncThreadLock) {
-                if (mSyncThread != null
-                        && mSyncThread.mSyncContext.getSyncContextBinder()
-                        == syncContext.asBinder()) {
-                    onSyncCanceled(mSyncThread);
-                }
+                syncThread = mSyncThread;
+            }
+            if (syncThread != null
+                    && syncThread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
+                onSyncCanceled();
             }
         }
 
@@ -213,9 +212,14 @@
      * thread than the sync thread and so you must consider the multi-threaded implications
      * of the work that you do in this method.
      *
-     * @param thread the thread that is running the sync operation to cancel
      */
-    public void onSyncCanceled(Thread thread) {
-        thread.interrupt();
+    public void onSyncCanceled() {
+        final SyncThread syncThread;
+        synchronized (mSyncThreadLock) {
+            syncThread = mSyncThread;
+        }
+        if (syncThread != null) {
+            syncThread.interrupt();
+        }
     }
 }
diff --git a/core/java/android/content/ActiveSyncInfo.java b/core/java/android/content/ActiveSyncInfo.java
deleted file mode 100644
index b0c90a1..0000000
--- a/core/java/android/content/ActiveSyncInfo.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.accounts.Account;
-import android.os.Parcel;
-import android.os.Parcelable.Creator;
-
-/**
- * Information about the sync operation that is currently underway.
- */
-public class ActiveSyncInfo {
-    private final int authorityId;
-    private final Account account;
-    private final String authority;
-    private final long startTime;
-
-    /**
-     * Get the {@link Account} that is currently being synced.
-     * @return the account
-     */
-    public Account getAccount() {
-        return new Account(account.name, account.type);
-    }
-
-    /** @hide */
-    public int getAuthorityId() {
-        return authorityId;
-    }
-
-    /**
-     * Get the authority of the provider that is currently being synced.
-     * @return the authority
-     */
-    public String getAuthority() {
-        return authority;
-    }
-
-    /**
-     * Get the start time of the current sync operation. This is represented in elapsed real time.
-     * See {@link android.os.SystemClock#elapsedRealtime()}.
-     * @return the start time in milliseconds since boot
-     */
-    public long getStartTime() {
-        return startTime;
-    }
-
-    /** @hide */
-    ActiveSyncInfo(int authorityId, Account account, String authority,
-            long startTime) {
-        this.authorityId = authorityId;
-        this.account = account;
-        this.authority = authority;
-        this.startTime = startTime;
-    }
-
-    /** @hide */
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeInt(authorityId);
-        account.writeToParcel(parcel, 0);
-        parcel.writeString(authority);
-        parcel.writeLong(startTime);
-    }
-
-    /** @hide */
-    ActiveSyncInfo(Parcel parcel) {
-        authorityId = parcel.readInt();
-        account = new Account(parcel);
-        authority = parcel.readString();
-        startTime = parcel.readLong();
-    }
-
-    /** @hide */
-    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];
-        }
-    };
-}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index d3cc4df..1cb2353 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1179,11 +1179,11 @@
 
     /**
      * If a sync is active returns the information about it, otherwise returns false.
-     * @return the ActiveSyncInfo for the currently active sync or null if one is not active.
+     * @return the SyncInfo for the currently active sync or null if one is not active.
      */
-    public static ActiveSyncInfo getActiveSync() {
+    public static SyncInfo getCurrentSync() {
         try {
-            return getContentService().getActiveSync();
+            return getContentService().getCurrentSync();
         } catch (RemoteException e) {
             throw new RuntimeException("the ContentService should always be reachable", e);
         }
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 0477d6d..377e383 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -386,14 +386,14 @@
         return false;
     }
 
-    public ActiveSyncInfo getActiveSync() {
+    public SyncInfo getCurrentSync() {
         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();
+                return syncManager.getSyncStorageEngine().getCurrentSync();
             }
         } finally {
             restoreCallingIdentity(identityToken);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2b0e7e7..ee87290 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -417,7 +417,7 @@
     /**
      * Returns the absolute path to the directory on the external filesystem
      * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
-     * Environment.getExternalStorageDirectory()} where the application can
+     * Environment.getExternalStorageDirectory()}) where the application can
      * place persistent files it owns.  These files are private to the
      * applications, and not typically visible to the user as media.
      * 
@@ -439,7 +439,7 @@
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
      * private_file}
      *
-     * <p>If you install a non-null <var>type</var> to this function, the returned
+     * <p>If you supply a non-null <var>type</var> to this function, the returned
      * file will be a path to a sub-directory of the given type.  Though these files
      * are not automatically scanned by the media scanner, you can explicitly
      * add them to the media database with
@@ -450,7 +450,7 @@
      * Environment.getExternalStoragePublicDirectory()}, which provides
      * directories of media shared by all applications.  The
      * directories returned here are
-     * owned by the application, and its contents will be removed when the
+     * owned by the application, and their contents will be removed when the
      * application is uninstalled.  Unlike
      * {@link android.os.Environment#getExternalStoragePublicDirectory
      * Environment.getExternalStoragePublicDirectory()}, the directory
@@ -479,6 +479,7 @@
      * this method again when it is available.
      *
      * @see #getFilesDir
+     * @see android.os.Environment#getExternalStoragePublicDirectory
      */
     public abstract File getExternalFilesDir(String type);
     
@@ -1372,6 +1373,7 @@
     public static final String SENSOR_SERVICE = "sensor";
     
     /**
+     * @hide
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.os.storage.StorageManager} for accesssing system storage
      * functions.
diff --git a/core/java/android/content/CursorEntityIterator.java b/core/java/android/content/CursorEntityIterator.java
index 54619a3..18437e5 100644
--- a/core/java/android/content/CursorEntityIterator.java
+++ b/core/java/android/content/CursorEntityIterator.java
@@ -53,9 +53,9 @@
      * iterator is positioned in front of an element.
      *
      * @return {@code true} if there are more elements, {@code false} otherwise.
-     * @see #next
+     * @see EntityIterator#next()
      */
-    public final boolean hasNext() throws RemoteException {
+    public final boolean hasNext() {
         if (mIsClosed) {
             throw new IllegalStateException("calling hasNext() when the iterator is closed");
         }
@@ -70,9 +70,9 @@
      * @return the next object.
      * @throws java.util.NoSuchElementException
      *             if there are no more elements.
-     * @see #hasNext
+     * @see EntityIterator#hasNext()
      */
-    public Entity next() throws RemoteException {
+    public Entity next() {
         if (mIsClosed) {
             throw new IllegalStateException("calling next() when the iterator is closed");
         }
@@ -80,10 +80,18 @@
             throw new IllegalStateException("you may only call next() if hasNext() is true");
         }
 
-        return getEntityAndIncrementCursor(mCursor);
+        try {
+            return getEntityAndIncrementCursor(mCursor);
+        } catch (RemoteException e) {
+            throw new RuntimeException("caught a remote exception, this process will die soon", e);
+        }
     }
 
-    public final void reset() throws RemoteException {
+    public void remove() {
+        throw new UnsupportedOperationException("remove not supported by EntityIterators");
+    }
+
+    public final void reset() {
         if (mIsClosed) {
             throw new IllegalStateException("calling reset() when the iterator is closed");
         }
diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java
index 3cc1040..55c47ba 100644
--- a/core/java/android/content/EntityIterator.java
+++ b/core/java/android/content/EntityIterator.java
@@ -16,32 +16,20 @@
 
 package android.content;
 
-import android.os.RemoteException;
+import java.util.Iterator;
 
-public interface EntityIterator {
+/**
+ * A specialization of {@link Iterator} that allows iterating over a collection of
+ * {@link Entity} objects. In addition to the iteration functionality it also allows
+ * resetting the iterator back to the beginning and provides for an explicit {@link #close()}
+ * method to indicate that the iterator is no longer needed and that its resources
+ * can be released.
+ */
+public interface EntityIterator extends Iterator<Entity> {
     /**
-     * Returns whether there are more elements to iterate, i.e. whether the
-     * iterator is positioned in front of an element.
-     *
-     * @return {@code true} if there are more elements, {@code false} otherwise.
-     * @see #next
-     * @since Android 1.0
+     * Reset the iterator back to the beginning.
      */
-    public boolean hasNext() throws RemoteException;
-
-    /**
-     * Returns the next object in the iteration, i.e. returns the element in
-     * front of the iterator and advances the iterator by one position.
-     *
-     * @return the next object.
-     * @throws java.util.NoSuchElementException
-     *             if there are no more elements.
-     * @see #hasNext
-     * @since Android 1.0
-     */
-    public Entity next() throws RemoteException;
-
-    public void reset() throws RemoteException;
+    public void reset();
 
     /**
      * Indicates that this iterator is no longer needed and that any associated resources
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 2d906ed..a6368d5 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -17,7 +17,7 @@
 package android.content;
 
 import android.accounts.Account;
-import android.content.ActiveSyncInfo;
+import android.content.SyncInfo;
 import android.content.ISyncStatusObserver;
 import android.content.SyncAdapterType;
 import android.content.SyncStatusInfo;
@@ -104,7 +104,7 @@
      */
     boolean isSyncActive(in Account account, String authority);
 
-    ActiveSyncInfo getActiveSync();
+    SyncInfo getCurrentSync();
 
     /**
      * Returns the types of the SyncAdapters that are registered with the system.
diff --git a/core/java/android/content/ActiveSyncInfo.aidl b/core/java/android/content/SyncInfo.aidl
similarity index 95%
rename from core/java/android/content/ActiveSyncInfo.aidl
rename to core/java/android/content/SyncInfo.aidl
index 1142206..0737429 100644
--- a/core/java/android/content/ActiveSyncInfo.aidl
+++ b/core/java/android/content/SyncInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.content;
 
-parcelable ActiveSyncInfo;
+parcelable SyncInfo;
diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java
new file mode 100644
index 0000000..616b05f
--- /dev/null
+++ b/core/java/android/content/SyncInfo.java
@@ -0,0 +1,87 @@
+/*
+ * 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.accounts.Account;
+import android.os.Parcel;
+import android.os.Parcelable.Creator;
+
+/**
+ * Information about the sync operation that is currently underway.
+ */
+public class SyncInfo {
+    /** @hide */
+    public final int authorityId;
+
+    /**
+     * The {@link Account} that is currently being synced.
+     */
+    public final Account account;
+
+    /**
+     * The authority of the provider that is currently being synced.
+     */
+    public final String authority;
+
+    /**
+     * The start time of the current sync operation in milliseconds since boot.
+     * This is represented in elapsed real time.
+     * See {@link android.os.SystemClock#elapsedRealtime()}.
+     */
+    public final long startTime;
+
+    /** @hide */
+    SyncInfo(int authorityId, Account account, String authority,
+            long startTime) {
+        this.authorityId = authorityId;
+        this.account = account;
+        this.authority = authority;
+        this.startTime = startTime;
+    }
+
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(authorityId);
+        account.writeToParcel(parcel, 0);
+        parcel.writeString(authority);
+        parcel.writeLong(startTime);
+    }
+
+    /** @hide */
+    SyncInfo(Parcel parcel) {
+        authorityId = parcel.readInt();
+        account = new Account(parcel);
+        authority = parcel.readString();
+        startTime = parcel.readLong();
+    }
+
+    /** @hide */
+    public static final Creator<SyncInfo> CREATOR = new Creator<SyncInfo>() {
+        public SyncInfo createFromParcel(Parcel in) {
+            return new SyncInfo(in);
+        }
+
+        public SyncInfo[] newArray(int size) {
+            return new SyncInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 5c8ee18..455815f 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -398,10 +398,6 @@
         return minValue + random.nextInt((int)spread);
     }
 
-    public ActiveSyncContext getActiveSyncContext() {
-        return mActiveSyncContext;
-    }
-
     public SyncStorageEngine getSyncStorageEngine() {
         return mSyncStorageEngine;
     }
@@ -412,11 +408,6 @@
         }
     }
 
-    public Account getSyncingAccount() {
-        ActiveSyncContext activeSyncContext = mActiveSyncContext;
-        return (activeSyncContext != null) ? activeSyncContext.mSyncOperation.account : null;
-    }
-
     private void initializeSyncAdapter(Account account, String authority) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "initializeSyncAdapter: " + account + ", authority " + authority);
@@ -425,7 +416,8 @@
         RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
                 mSyncAdapters.getServiceInfo(syncAdapterType);
         if (syncAdapterInfo == null) {
-            Log.w(TAG, "can't find a sync adapter for " + syncAdapterType);
+            Log.w(TAG, "can't find a sync adapter for " + syncAdapterType + ", removing");
+            mSyncStorageEngine.removeAuthority(account, authority);
             return;
         }
 
@@ -466,6 +458,8 @@
                 }
             } catch (RemoteException e) {
                 // doesn't matter, we will retry again later
+                Log.d(TAG, "error while initializing: " + mAccount + ", authority " + mAuthority,
+                        e);
             } finally {
                 // give the sync adapter time to initialize before unbinding from it
                 // TODO: change this API to not rely on this timing, http://b/2500805
@@ -602,16 +596,12 @@
                         continue;
                     }
 
-                    // initialize the SyncAdapter if the isSyncable state is unknown
-                    if (isSyncable < 0) {
-                        initializeSyncAdapter(account, authority);
-                        continue;
-                    }
-
-                    final boolean syncAutomatically = masterSyncAutomatically
-                            && mSyncStorageEngine.getSyncAutomatically(account, authority);
+                    // always allow if the isSyncable state is unknown
                     boolean syncAllowed =
-                            ignoreSettings || (backgroundDataUsageAllowed && syncAutomatically);
+                            (isSyncable < 0)
+                            || ignoreSettings
+                            || (backgroundDataUsageAllowed && masterSyncAutomatically
+                                && mSyncStorageEngine.getSyncAutomatically(account, authority));
                     if (!syncAllowed) {
                         if (isLoggable) {
                             Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
@@ -1026,11 +1016,11 @@
             pw.println(sb.toString());
         }
 
-        ActiveSyncInfo active = mSyncStorageEngine.getActiveSync();
+        SyncInfo active = mSyncStorageEngine.getCurrentSync();
         if (active != null) {
             SyncStorageEngine.AuthorityInfo authority
-                    = mSyncStorageEngine.getAuthority(active.getAuthorityId());
-            final long durationInSeconds = (now - active.getStartTime()) / 1000;
+                    = 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(" ");
@@ -1061,7 +1051,7 @@
                 pw.println(op.expedited);
                 if (op.extras != null && op.extras.size() > 0) {
                     sb.setLength(0);
-                    SyncOperation.extrasToStringBuilder(op.extras, sb);
+                    SyncOperation.extrasToStringBuilder(op.extras, sb, false /* asKey */);
                     pw.print("    extras: "); pw.println(sb.toString());
                 }
             }
@@ -1376,8 +1366,14 @@
         }
 
         public void handleMessage(Message msg) {
+            Long earliestFuturePollTime = null;
             try {
                 waitUntilReadyToRun();
+                // Always do this first so that we be sure that any periodic syncs that
+                // are ready to run have been converted into pending syncs. This allows the
+                // logic that considers the next steps to take based on the set of pending syncs
+                // to also take into account the periodic syncs.
+                earliestFuturePollTime = scheduleReadyPeriodicSyncs();
                 switch (msg.what) {
                     case SyncHandler.MESSAGE_SYNC_FINISHED:
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -1486,48 +1482,90 @@
                 }
                 manageSyncNotification();
                 manageErrorNotification();
-                manageSyncAlarm();
+                manageSyncAlarm(earliestFuturePollTime);
                 mSyncTimeTracker.update();
             }
         }
 
-        private boolean isSyncAllowed(Account account, String authority, boolean ignoreSettings,
-                boolean backgroundDataUsageAllowed) {
-            Account[] accounts = mAccounts;
+        /**
+         * Turn any periodic sync operations that are ready to run into pending sync operations.
+         * @return the desired start time of the earliest future  periodic sync operation,
+         * in milliseconds since boot
+         */
+        private Long scheduleReadyPeriodicSyncs() {
+            final boolean backgroundDataUsageAllowed =
+                    getConnectivityManager().getBackgroundDataSetting();
+            Long earliestFuturePollTime = null;
+            if (!backgroundDataUsageAllowed || !mSyncStorageEngine.getMasterSyncAutomatically()) {
+                return earliestFuturePollTime;
+            }
+            final long nowAbsolute = System.currentTimeMillis();
+            ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
+            for (SyncStorageEngine.AuthorityInfo info : infos) {
+                // skip the sync if the account of this operation no longer exists
+                if (!ArrayUtils.contains(mAccounts, info.account)) {
+                    continue;
+                }
 
-            // skip the sync if the account of this operation no longer exists
-            if (!ArrayUtils.contains(accounts, account)) {
-                return false;
+                if (!mSyncStorageEngine.getSyncAutomatically(info.account, info.authority)) {
+                    continue;
+                }
+
+                if (mSyncStorageEngine.getIsSyncable(info.account, info.authority) == 0) {
+                    continue;
+                }
+
+                SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info);
+                for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) {
+                    final Bundle extras = info.periodicSyncs.get(i).first;
+                    final Long periodInSeconds = info.periodicSyncs.get(i).second;
+                    // find when this periodic sync was last scheduled to run
+                    final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
+                    // compute when this periodic sync should next run
+                    long nextPollTimeAbsolute = lastPollTimeAbsolute + periodInSeconds * 1000;
+                    // if it is ready to run then schedule it and mark it as having been scheduled
+                    if (nextPollTimeAbsolute <= nowAbsolute) {
+                        scheduleSyncOperation(
+                                new SyncOperation(info.account, SyncStorageEngine.SOURCE_PERIODIC,
+                                        info.authority, extras, 0 /* delay */));
+                        status.setPeriodicSyncTime(i, nowAbsolute);
+                    } else {
+                        // it isn't ready to run, remember this time if it is earlier than
+                        // earliestFuturePollTime
+                        if (earliestFuturePollTime == null
+                                || nextPollTimeAbsolute < earliestFuturePollTime) {
+                            earliestFuturePollTime = nextPollTimeAbsolute;
+                        }
+                    }
+                }
             }
 
-            // skip the sync if it isn't manual and auto sync is disabled
-            final boolean syncAutomatically =
-                    mSyncStorageEngine.getSyncAutomatically(account, authority)
-                            && mSyncStorageEngine.getMasterSyncAutomatically();
-            if (!(ignoreSettings || (backgroundDataUsageAllowed && syncAutomatically))) {
-                return false;
+            if (earliestFuturePollTime == null) {
+                return null;
             }
 
-            if (mSyncStorageEngine.getIsSyncable(account, authority) <= 0) {
-                // if not syncable or if the syncable is unknown (< 0), don't allow
-                return false;
-            }
-
-            return true;
+            // convert absolute time to elapsed time
+            return SystemClock.elapsedRealtime()
+                    + ((earliestFuturePollTime < nowAbsolute)
+                      ? 0
+                      : (earliestFuturePollTime - nowAbsolute));
         }
 
         private void runStateSyncing() {
             // if the sync timeout has been reached then cancel it
-
             ActiveSyncContext activeSyncContext = mActiveSyncContext;
 
             final long now = SystemClock.elapsedRealtime();
             if (now > activeSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC) {
-                SyncOperation nextSyncOperation;
+                Pair<SyncOperation, Long> nextOpAndRunTime;
                 synchronized (mSyncQueue) {
-                    nextSyncOperation = getNextReadyToRunSyncOperation(now);
+                    nextOpAndRunTime = mSyncQueue.nextOperation();
                 }
-                if (nextSyncOperation != null) {
+                SyncOperation curOp = activeSyncContext.mSyncOperation;
+                if (nextOpAndRunTime != null
+                        && nextOpAndRunTime.second <= now
+                        && !nextOpAndRunTime.first.account.equals(curOp.account)
+                        && !nextOpAndRunTime.first.authority.equals(curOp.authority)) {
                     Log.d(TAG, "canceling and rescheduling sync because it ran too long: "
                             + activeSyncContext.mSyncOperation);
                     scheduleSyncOperation(new SyncOperation(activeSyncContext.mSyncOperation));
@@ -1574,27 +1612,48 @@
             // found that is runnable (not disabled, etc). If that one is ready to run then
             // start it, otherwise just get out.
             SyncOperation op;
+            int syncableState;
             final boolean backgroundDataUsageAllowed =
                     getConnectivityManager().getBackgroundDataSetting();
+            final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically();
+
             synchronized (mSyncQueue) {
                 final long now = SystemClock.elapsedRealtime();
                 while (true) {
-                    op = getNextReadyToRunSyncOperation(now);
-                    if (op == null) {
+                    Pair<SyncOperation, Long> nextOpAndRunTime = mSyncQueue.nextOperation();
+                    if (nextOpAndRunTime == null || nextOpAndRunTime.second > now) {
                         if (isLoggable) {
-                            Log.v(TAG, "runStateIdle: no more sync operations, returning");
+                            Log.v(TAG, "runStateIdle: no more ready sync operations, returning");
                         }
                         return;
                     }
+                    op = nextOpAndRunTime.first;
 
-                    // we are either going to run this sync or drop it so go ahead and remove it
-                    // from the queue now
+                    // we are either going to run this sync or drop it so go ahead and
+                    // remove it from the queue now
                     mSyncQueue.remove(op);
 
-                    final boolean ignoreSettings = op.extras
-                            .getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
-                    if (!isSyncAllowed(op.account, op.authority, ignoreSettings,
-                            backgroundDataUsageAllowed)) {
+                    // drop the sync if the account of this operation no longer exists
+                    if (!ArrayUtils.contains(mAccounts, op.account)) {
+                        continue;
+                    }
+
+
+                    // drop this sync request if it isn't syncable, intializing the sync adapter
+                    // if the syncable state is set to "unknown"
+                    syncableState = mSyncStorageEngine.getIsSyncable(op.account, op.authority);
+                    if (syncableState == 0) {
+                        continue;
+                    }
+
+                    // skip the sync if it isn't manual and auto sync or
+                    // background data usage is disabled
+                    if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
+                            && (syncableState > 0)
+                            && (!masterSyncAutomatically
+                                || !backgroundDataUsageAllowed
+                                || !mSyncStorageEngine.getSyncAutomatically(
+                                       op.account, op.authority))) {
                         continue;
                     }
 
@@ -1608,6 +1667,19 @@
                 }
             }
 
+            // convert the op into an initialization sync if the syncable state is "unknown" and
+            // op isn't already an initialization sync. If it is marked syncable then convert
+            // this into a regular sync
+            final boolean initializeIsSet =
+                    op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
+            if (syncableState < 0 && !initializeIsSet) {
+                op.extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+                op = new SyncOperation(op);
+            } else if (syncableState > 0 && initializeIsSet) {
+                op.extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
+                op = new SyncOperation(op);
+            }
+
             // connect to the sync adapter
             SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
             RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
@@ -1643,74 +1715,6 @@
             // MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message
         }
 
-        private SyncOperation getNextPeriodicSyncOperation() {
-            final boolean backgroundDataUsageAllowed =
-                    getConnectivityManager().getBackgroundDataSetting();
-            SyncStorageEngine.AuthorityInfo best = null;
-            long bestPollTimeAbsolute = Long.MAX_VALUE;
-            Bundle bestExtras = null;
-            ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
-            for (SyncStorageEngine.AuthorityInfo info : infos) {
-                if (!isSyncAllowed(info.account, info.authority, false /* ignoreSettings */,
-                        backgroundDataUsageAllowed)) {
-                    continue;
-                }
-                SyncStatusInfo status = mSyncStorageEngine.getStatusByAccountAndAuthority(
-                        info.account, info.authority);
-                int i = 0;
-                for (Pair<Bundle, Long> periodicSync : info.periodicSyncs) {
-                    long lastPollTimeAbsolute = status != null ? status.getPeriodicSyncTime(i) : 0;
-                    final Bundle extras = periodicSync.first;
-                    final Long periodInSeconds = periodicSync.second;
-                    long nextPollTimeAbsolute = lastPollTimeAbsolute + periodInSeconds * 1000;
-                    if (nextPollTimeAbsolute < bestPollTimeAbsolute) {
-                        best = info;
-                        bestPollTimeAbsolute = nextPollTimeAbsolute;
-                        bestExtras = extras;
-                    }
-                    i++;
-                }
-            }
-
-            if (best == null) {
-                return null;
-            }
-
-            final long nowAbsolute = System.currentTimeMillis();
-            final SyncOperation syncOperation = new SyncOperation(best.account,
-                    SyncStorageEngine.SOURCE_PERIODIC,
-                    best.authority, bestExtras, 0 /* delay */);
-            syncOperation.earliestRunTime = SystemClock.elapsedRealtime()
-                    + (bestPollTimeAbsolute - nowAbsolute);
-            if (syncOperation.earliestRunTime < 0) {
-                syncOperation.earliestRunTime = 0;
-            }
-            return syncOperation;
-        }
-
-        public Pair<SyncOperation, Long> bestSyncOperationCandidate() {
-            Pair<SyncOperation, Long> nextOpAndRunTime = mSyncQueue.nextOperation();
-            SyncOperation nextOp = nextOpAndRunTime != null ? nextOpAndRunTime.first : null;
-            Long nextRunTime = nextOpAndRunTime != null ? nextOpAndRunTime.second : null;
-            SyncOperation pollOp = getNextPeriodicSyncOperation();
-            if (nextOp != null
-                    && (pollOp == null || nextOp.expedited
-                        || nextRunTime <= pollOp.earliestRunTime)) {
-                return nextOpAndRunTime;
-            } else if (pollOp != null) {
-                return Pair.create(pollOp, pollOp.earliestRunTime);
-            } else {
-                return null;
-            }
-        }
-
-        private SyncOperation getNextReadyToRunSyncOperation(long now) {
-            Pair<SyncOperation, Long> nextOpAndRunTime = bestSyncOperationCandidate();
-            return nextOpAndRunTime != null && nextOpAndRunTime.second <= now
-                    ? nextOpAndRunTime.first
-                    : null;
-        }
-
         private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) {
             mActiveSyncContext.mSyncAdapter = syncAdapter;
             final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
@@ -1757,6 +1761,15 @@
                     downstreamActivity = 0;
                     upstreamActivity = 0;
                     clearBackoffSetting(syncOperation);
+                    // if this was an initialization sync and the sync adapter is now
+                    // marked syncable then reschedule the sync. The next time it runs it
+                    // will be made into a regular sync.
+                    if (syncOperation.extras.getBoolean(
+                                ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
+                            && mSyncStorageEngine.getIsSyncable(
+                                syncOperation.account, syncOperation.authority) > 0) {
+                        scheduleSyncOperation(new SyncOperation(syncOperation));
+                    }
                 } else {
                     Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
                     // the operation failed so increase the backoff time
@@ -1919,7 +1932,7 @@
             }
         }
 
-        private void manageSyncAlarm() {
+        private void manageSyncAlarm(Long earliestFuturePollElapsedTime) {
             // in each of these cases the sync loop will be kicked, which will cause this
             // method to be called again
             if (!mDataConnectionIsConnected) return;
@@ -1935,8 +1948,16 @@
             ActiveSyncContext activeSyncContext = mActiveSyncContext;
             if (activeSyncContext == null) {
                 synchronized (mSyncQueue) {
-                    Pair<SyncOperation, Long> candidate = bestSyncOperationCandidate();
-                    alarmTime = candidate != null ? candidate.second : null;
+                    final Pair<SyncOperation, Long> candidate = mSyncQueue.nextOperation();
+                    if (earliestFuturePollElapsedTime == null && candidate == null) {
+                        alarmTime = null;
+                    } else if (earliestFuturePollElapsedTime == null) {
+                        alarmTime = candidate.second;
+                    } else if (candidate == null) {
+                        alarmTime = earliestFuturePollElapsedTime;
+                    } else {
+                        alarmTime = Math.min(earliestFuturePollElapsedTime, candidate.second);
+                    }
                 }
             } else {
                 final long notificationTime =
@@ -2077,7 +2098,7 @@
                                 SyncStorageEngine.EVENT_STOP, syncOperation.syncSource,
                                 syncOperation.account.name.hashCode());
 
-            mSyncStorageEngine.stopSyncEvent(rowId, syncOperation.extras, elapsedTime,
+            mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
                     resultMessage, downstreamActivity, upstreamActivity);
         }
     }
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index ec601de..a7d036f 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -80,7 +80,7 @@
         sb.append("authority: ").append(authority);
         sb.append(" account: ").append(account);
         sb.append(" extras: ");
-        extrasToStringBuilder(extras, sb);
+        extrasToStringBuilder(extras, sb, false /* asKey */);
         sb.append(" syncSource: ").append(syncSource);
         sb.append(" when: ").append(earliestRunTime);
         sb.append(" expedited: ").append(expedited);
@@ -92,13 +92,19 @@
         sb.append("authority: ").append(authority);
         sb.append(" account: ").append(account);
         sb.append(" extras: ");
-        extrasToStringBuilder(extras, sb);
+        extrasToStringBuilder(extras, sb, true /* asKey */);
         return sb.toString();
     }
 
-    public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
+    public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb, boolean asKey) {
         sb.append("[");
         for (String key : bundle.keySet()) {
+            // if we are writing this as a key don't consider whether this
+            // is an initialization sync or not when computing the key since
+            // we set this flag appropriately when dispatching the sync request.
+            if (asKey && ContentResolver.SYNC_EXTRAS_INITIALIZE.equals(key)) {
+                continue;
+            }
             sb.append(key).append("=").append(bundle.get(key)).append(" ");
         }
         sb.append("]");
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 432277f..28baa0d 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
 import android.util.Pair;
@@ -120,14 +121,16 @@
 
     /**
      * Find the operation that should run next. Operations are sorted by their earliestRunTime,
-     * prioritizing expedited operations. The earliestRunTime is adjusted by the sync adapter's
-     * backoff and delayUntil times, if any.
+     * prioritizing first those with a syncable state of "unknown" that aren't retries then
+     * expedited operations.
+     * The earliestRunTime is adjusted by the sync adapter's backoff and delayUntil times, if any.
      * @return the operation that should run next and when it should run. The time may be in
      * the future. It is expressed in milliseconds since boot.
      */
     public Pair<SyncOperation, Long> nextOperation() {
         SyncOperation best = null;
         long bestRunTime = 0;
+        boolean bestSyncableIsUnknownAndNotARetry = false;
         for (SyncOperation op : mOperationsMap.values()) {
             long opRunTime = op.earliestRunTime;
             if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
@@ -137,12 +140,23 @@
                         Math.max(opRunTime, delayUntil),
                         backoff != null ? backoff.first : 0);
             }
-            // if the expedited state of both ops are the same then compare their runtime.
-            // Otherwise the candidate is only better than the current best if the candidate
-            // is expedited.
+            // we know a sync is a retry if the intialization flag is set, since that will only
+            // be set by the sync dispatching code, thus if it is set it must have already been
+            // dispatched
+            final boolean syncableIsUnknownAndNotARetry =
+                    !op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
+                    && mSyncStorageEngine.getIsSyncable(op.account, op.authority) < 0;
+            // if the unsyncable state differs, make the current the best if it is unsyncable
+            // else, if the expedited state differs, make the current the best if it is expedited
+            // else, make the current the best if it is earlier than the best
             if (best == null
-                    || (best.expedited == op.expedited ? opRunTime < bestRunTime : op.expedited)) {
+                    || ((bestSyncableIsUnknownAndNotARetry == syncableIsUnknownAndNotARetry)
+                        ? (best.expedited == op.expedited
+                           ? opRunTime < bestRunTime
+                           : op.expedited)
+                        : syncableIsUnknownAndNotARetry)) {
                 best = op;
+                bestSyncableIsUnknownAndNotARetry = syncableIsUnknownAndNotARetry;
                 bestRunTime = opRunTime;
             }
         }
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 6a959ae..984c070 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -59,7 +59,6 @@
  */
 public class SyncStorageEngine extends Handler {
     private static final String TAG = "SyncManager";
-    private static final boolean DEBUG = false;
     private static final boolean DEBUG_FILE = false;
 
     private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
@@ -230,7 +229,7 @@
     private final ArrayList<PendingOperation> mPendingOperations =
             new ArrayList<PendingOperation>();
 
-    private ActiveSyncInfo mActiveSync;
+    private SyncInfo mCurrentSync;
 
     private final SparseArray<SyncStatusInfo> mSyncStatus =
             new SparseArray<SyncStatusInfo>();
@@ -241,6 +240,8 @@
     private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
             = new RemoteCallbackList<ISyncStatusObserver>();
 
+    private int mNextAuthorityId = 0;
+
     // We keep 4 weeks of stats.
     private final DayStats[] mDayStats = new DayStats[7*4];
     private final Calendar mCal;
@@ -301,7 +302,11 @@
         readStatusLocked();
         readPendingOperationsLocked();
         readStatisticsLocked();
-        readLegacyAccountInfoLocked();
+        readAndDeleteLegacyAccountInfoLocked();
+        writeAccountInfoLocked();
+        writeStatusLocked();
+        writePendingOperationsLocked();
+        writeStatisticsLocked();
     }
 
     public static SyncStorageEngine newTestInstance(Context context) {
@@ -365,7 +370,9 @@
             mChangeListeners.finishBroadcast();
         }
 
-        if (DEBUG) Log.v(TAG, "reportChange " + which + " to: " + reports);
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "reportChange " + which + " to: " + reports);
+        }
 
         if (reports != null) {
             int i = reports.size();
@@ -402,15 +409,19 @@
     }
 
     public void setSyncAutomatically(Account account, String providerName, boolean sync) {
-        boolean wasEnabled;
+        Log.d(TAG, "setSyncAutomatically: " + account + ", provider " + providerName
+                + " -> " + sync);
         synchronized (mAuthorities) {
             AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
-            wasEnabled = authority.enabled;
+            if (authority.enabled == sync) {
+                Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
+                return;
+            }
             authority.enabled = sync;
             writeAccountInfoLocked();
         }
 
-        if (!wasEnabled && sync) {
+        if (sync) {
             ContentResolver.requestSync(account, providerName, new Bundle());
         }
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
@@ -440,7 +451,6 @@
     }
 
     public void setIsSyncable(Account account, String providerName, int syncable) {
-        int oldState;
         if (syncable > 1) {
             syncable = 1;
         } else if (syncable < -1) {
@@ -449,12 +459,15 @@
         Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
         synchronized (mAuthorities) {
             AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
-            oldState = authority.syncable;
+            if (authority.syncable == syncable) {
+                Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
+                return;
+            }
             authority.syncable = syncable;
             writeAccountInfoLocked();
         }
 
-        if (oldState <= 0 && syncable > 0) {
+        if (syncable > 0) {
             ContentResolver.requestSync(account, providerName, new Bundle());
         }
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
@@ -550,49 +563,60 @@
                     + " -> period " + period + ", extras " + extras);
         }
         synchronized (mAuthorities) {
-            AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
-            if (add) {
-                boolean alreadyPresent = false;
-                for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
-                    Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
-                    final Bundle existingExtras = syncInfo.first;
-                    if (equals(existingExtras, extras)) {
-                        if (syncInfo.second == period) {
-                            return;
+            try {
+                AuthorityInfo authority =
+                        getOrCreateAuthorityLocked(account, providerName, -1, false);
+                if (add) {
+                    // add this periodic sync if one with the same extras doesn't already
+                    // exist in the periodicSyncs array
+                    boolean alreadyPresent = false;
+                    for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
+                        Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
+                        final Bundle existingExtras = syncInfo.first;
+                        if (equals(existingExtras, extras)) {
+                            if (syncInfo.second == period) {
+                                return;
+                            }
+                            authority.periodicSyncs.set(i, Pair.create(extras, period));
+                            alreadyPresent = true;
+                            break;
                         }
-                        authority.periodicSyncs.set(i, Pair.create(extras, period));
-                        alreadyPresent = true;
-                        break;
+                    }
+                    // if we added an entry to the periodicSyncs array also add an entry to
+                    // the periodic syncs status to correspond to it
+                    if (!alreadyPresent) {
+                        authority.periodicSyncs.add(Pair.create(extras, period));
+                        SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
+                        status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
+                    }
+                } else {
+                    // remove any periodic syncs that match the authority and extras
+                    SyncStatusInfo status = mSyncStatus.get(authority.ident);
+                    boolean changed = false;
+                    Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
+                    int i = 0;
+                    while (iterator.hasNext()) {
+                        Pair<Bundle, Long> syncInfo = iterator.next();
+                        if (equals(syncInfo.first, extras)) {
+                            iterator.remove();
+                            changed = true;
+                            // if we removed an entry from the periodicSyncs array also
+                            // remove the corresponding entry from the status
+                            if (status != null) {
+                                status.removePeriodicSyncTime(i);
+                            }
+                        } else {
+                            i++;
+                        }
+                    }
+                    if (!changed) {
+                        return;
                     }
                 }
-                if (!alreadyPresent) {
-                    authority.periodicSyncs.add(Pair.create(extras, period));
-                    SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
-                    status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
-                }
-            } else {
-                SyncStatusInfo status = mSyncStatus.get(authority.ident);
-                boolean changed = false;
-                Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
-                int i = 0;
-                while (iterator.hasNext()) {
-                    Pair<Bundle, Long> syncInfo = iterator.next();
-                    if (equals(syncInfo.first, extras)) {
-                        iterator.remove();
-                        changed = true;
-                        if (status != null) {
-                            status.removePeriodicSyncTime(i);
-                        }
-                    } else {
-                        i++;
-                    }
-                }
-                if (!changed) {
-                    return;
-                }
+            } finally {
+                writeAccountInfoLocked();
+                writeStatusLocked();
             }
-            writeAccountInfoLocked();
-            writeStatusLocked();
         }
 
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
@@ -622,13 +646,14 @@
     }
 
     public void setMasterSyncAutomatically(boolean flag) {
-        boolean old;
         synchronized (mAuthorities) {
-            old = mMasterSyncAutomatically;
+            if (mMasterSyncAutomatically == flag) {
+                return;
+            }
             mMasterSyncAutomatically = flag;
             writeAccountInfoLocked();
         }
-        if (!old && flag) {
+        if (flag) {
             ContentResolver.requestSync(null, null, new Bundle());
         }
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
@@ -651,7 +676,7 @@
 
     public void removeAuthority(Account account, String authority) {
         synchronized (mAuthorities) {
-            removeAuthorityLocked(account, authority);
+            removeAuthorityLocked(account, authority, true /* doWrite */);
         }
     }
 
@@ -678,8 +703,8 @@
                 }
             }
 
-            if (mActiveSync != null) {
-                AuthorityInfo ainfo = getAuthority(mActiveSync.getAuthorityId());
+            if (mCurrentSync != null) {
+                AuthorityInfo ainfo = getAuthority(mCurrentSync.authorityId);
                 if (ainfo != null && ainfo.account.equals(account)
                         && ainfo.authority.equals(authority)) {
                     return true;
@@ -692,10 +717,12 @@
 
     public PendingOperation insertIntoPending(PendingOperation op) {
         synchronized (mAuthorities) {
-            if (DEBUG) Log.v(TAG, "insertIntoPending: account=" + op.account
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "insertIntoPending: account=" + op.account
                     + " auth=" + op.authority
                     + " src=" + op.syncSource
                     + " extras=" + op.extras);
+            }
 
             AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
                     op.authority,
@@ -721,10 +748,12 @@
     public boolean deleteFromPending(PendingOperation op) {
         boolean res = false;
         synchronized (mAuthorities) {
-            if (DEBUG) Log.v(TAG, "deleteFromPending: account=" + op.account
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                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) {
@@ -737,7 +766,7 @@
                 AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
                         "deleteFromPending");
                 if (authority != null) {
-                    if (DEBUG) Log.v(TAG, "removing - " + authority);
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority);
                     final int N = mPendingOperations.size();
                     boolean morePending = false;
                     for (int i=0; i<N; i++) {
@@ -750,7 +779,7 @@
                     }
 
                     if (!morePending) {
-                        if (DEBUG) Log.v(TAG, "no more pending!");
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!");
                         SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
                         status.pending = false;
                     }
@@ -767,7 +796,9 @@
     public int clearPending() {
         int num;
         synchronized (mAuthorities) {
-            if (DEBUG) Log.v(TAG, "clearPending");
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "clearPending");
+            }
             num = mPendingOperations.size();
             mPendingOperations.clear();
             final int N = mSyncStatus.size();
@@ -806,14 +837,16 @@
      */
     public void doDatabaseCleanup(Account[] accounts) {
         synchronized (mAuthorities) {
-            if (DEBUG) Log.w(TAG, "Updating for new accounts...");
+            if (Log.isLoggable(TAG, Log.VERBOSE)) 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);
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.w(TAG, "Account removed: " + acc.account);
+                    }
                     for (AuthorityInfo auth : acc.authorities.values()) {
                         removing.put(auth.ident, auth);
                     }
@@ -859,12 +892,14 @@
     public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
         synchronized (mAuthorities) {
             if (activeSyncContext != null) {
-                if (DEBUG) Log.v(TAG, "setActiveSync: account="
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "setActiveSync: account="
                         + activeSyncContext.mSyncOperation.account
                         + " auth=" + activeSyncContext.mSyncOperation.authority
                         + " src=" + activeSyncContext.mSyncOperation.syncSource
                         + " extras=" + activeSyncContext.mSyncOperation.extras);
-                if (mActiveSync != null) {
+                }
+                if (mCurrentSync != null) {
                     Log.w(TAG, "setActiveSync called with existing active sync!");
                 }
                 AuthorityInfo authority = getAuthorityLocked(
@@ -874,12 +909,12 @@
                 if (authority == null) {
                     return;
                 }
-                mActiveSync = new ActiveSyncInfo(authority.ident,
+                mCurrentSync = new SyncInfo(authority.ident,
                         authority.account, authority.authority,
                         activeSyncContext.mStartTime);
             } else {
-                if (DEBUG) Log.v(TAG, "setActiveSync: null");
-                mActiveSync = null;
+                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "setActiveSync: null");
+                mCurrentSync = null;
             }
         }
 
@@ -900,8 +935,10 @@
             long now, int source) {
         long id;
         synchronized (mAuthorities) {
-            if (DEBUG) Log.v(TAG, "insertStartSyncEvent: account=" + accountName
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "insertStartSyncEvent: account=" + accountName
                     + " auth=" + authorityName + " source=" + source);
+            }
             AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
                     "insertStartSyncEvent");
             if (authority == null) {
@@ -919,7 +956,7 @@
                 mSyncHistory.remove(mSyncHistory.size()-1);
             }
             id = item.historyId;
-            if (DEBUG) Log.v(TAG, "returning historyId " + id);
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id);
         }
 
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
@@ -944,10 +981,12 @@
         return true;
     }
 
-    public void stopSyncEvent(long historyId, Bundle extras, long elapsedTime, String resultMessage,
+    public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
             long downstreamActivity, long upstreamActivity) {
         synchronized (mAuthorities) {
-            if (DEBUG) Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
+            }
             SyncHistoryItem item = null;
             int i = mSyncHistory.size();
             while (i > 0) {
@@ -989,14 +1028,6 @@
                     break;
                 case SOURCE_PERIODIC:
                     status.numSourcePeriodic++;
-                    AuthorityInfo authority = mAuthorities.get(item.authorityId);
-                    for (int periodicSyncIndex = 0;
-                            periodicSyncIndex < authority.periodicSyncs.size();
-                            periodicSyncIndex++) {
-                        if (equals(extras, authority.periodicSyncs.get(periodicSyncIndex).first)) {
-                            status.setPeriodicSyncTime(periodicSyncIndex, item.eventTime);
-                        }
-                    }
                     break;
             }
 
@@ -1063,9 +1094,9 @@
      * 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() {
+    public SyncInfo getCurrentSync() {
         synchronized (mAuthorities) {
-            return mActiveSync;
+            return mCurrentSync;
         }
     }
 
@@ -1261,18 +1292,14 @@
         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++;
-                }
+                ident = mNextAuthorityId;
+                mNextAuthorityId++;
+                doWrite = true;
             }
-            if (DEBUG) Log.v(TAG, "created a new AuthorityInfo for " + accountName
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "created a new AuthorityInfo for " + accountName
                     + ", provider " + authorityName);
+            }
             authority = new AuthorityInfo(accountName, authorityName, ident);
             account.authorities.put(authorityName, authority);
             mAuthorities.put(ident, authority);
@@ -1284,13 +1311,15 @@
         return authority;
     }
 
-    private void removeAuthorityLocked(Account account, String authorityName) {
+    private void removeAuthorityLocked(Account account, String authorityName, boolean doWrite) {
         AccountInfo accountInfo = mAccounts.get(account);
         if (accountInfo != null) {
             final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
             if (authorityInfo != null) {
                 mAuthorities.remove(authorityInfo.ident);
-                writeAccountInfoLocked();
+                if (doWrite) {
+                    writeAccountInfoLocked();
+                }
             }
         }
     }
@@ -1340,7 +1369,11 @@
             readStatusLocked();
             readPendingOperationsLocked();
             readStatisticsLocked();
-            readLegacyAccountInfoLocked();
+            readAndDeleteLegacyAccountInfoLocked();
+            writeAccountInfoLocked();
+            writeStatusLocked();
+            writePendingOperationsLocked();
+            writeStatisticsLocked();
         }
     }
 
@@ -1348,7 +1381,7 @@
      * Read all account information back in to the initial engine state.
      */
     private void readAccountInfoLocked() {
-        boolean writeNeeded = false;
+        int highestAuthorityId = -1;
         FileInputStream fis = null;
         try {
             fis = mAccountInfoFile.openRead();
@@ -1370,11 +1403,14 @@
                 } catch (NumberFormatException e) {
                     version = 0;
                 }
-                if (version < ACCOUNTS_VERSION) {
-                    writeNeeded = true;
+                String nextIdString = parser.getAttributeValue(null, "nextAuthorityId");
+                try {
+                    int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
+                    mNextAuthorityId = Math.max(mNextAuthorityId, id);
+                } catch (NumberFormatException e) {
+                    // don't care
                 }
-                mMasterSyncAutomatically = listen == null
-                            || Boolean.parseBoolean(listen);
+                mMasterSyncAutomatically = listen == null || Boolean.parseBoolean(listen);
                 eventType = parser.next();
                 AuthorityInfo authority = null;
                 Pair<Bundle, Long> periodicSync = null;
@@ -1385,6 +1421,9 @@
                             if ("authority".equals(tagName)) {
                                 authority = parseAuthority(parser, version);
                                 periodicSync = null;
+                                if (authority.ident > highestAuthorityId) {
+                                    highestAuthorityId = authority.ident;
+                                }
                             }
                         } else if (parser.getDepth() == 3) {
                             if ("periodicSync".equals(tagName) && authority != null) {
@@ -1407,6 +1446,7 @@
             else Log.w(TAG, "Error reading accounts", e);
             return;
         } finally {
+            mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId);
             if (fis != null) {
                 try {
                     fis.close();
@@ -1415,13 +1455,7 @@
             }
         }
 
-        if (maybeMigrateSettingsForRenamedAuthorities()) {
-            writeNeeded = true;
-        }
-
-        if (writeNeeded) {
-            writeAccountInfoLocked();
-        }
+        maybeMigrateSettingsForRenamedAuthorities();
     }
 
     /**
@@ -1463,7 +1497,8 @@
         }
 
         for (AuthorityInfo authorityInfo : authoritiesToRemove) {
-            removeAuthorityLocked(authorityInfo.account, authorityInfo.authority);
+            removeAuthorityLocked(authorityInfo.account, authorityInfo.authority,
+                    false /* doWrite */);
             writeNeeded = true;
         }
 
@@ -1593,6 +1628,7 @@
 
             out.startTag(null, "accounts");
             out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
+            out.attribute(null, "nextAuthorityId", Integer.toString(mNextAuthorityId));
             if (!mMasterSyncAutomatically) {
                 out.attribute(null, "listen-for-tickles", "false");
             }
@@ -1675,7 +1711,7 @@
      * erase it.  Note that we don't deal with pending operations, active
      * sync, or history.
      */
-    private void readLegacyAccountInfoLocked() {
+    private void readAndDeleteLegacyAccountInfoLocked() {
         // Look for old database to initialize from.
         File file = mContext.getDatabasePath("syncmanager.db");
         if (!file.exists()) {
@@ -1792,8 +1828,6 @@
 
             db.close();
 
-            writeAccountInfoLocked();
-            writeStatusLocked();
             (new File(path)).delete();
         }
     }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 480504d..0a04e5b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -16,6 +16,9 @@
 
 package android.content.pm;
 
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Printer;
@@ -496,7 +499,7 @@
      */
     public CharSequence loadDescription(PackageManager pm) {
         if (descriptionRes != 0) {
-            CharSequence label = pm.getText(packageName, descriptionRes, null);
+            CharSequence label = pm.getText(packageName, descriptionRes, this);
             if (label != null) {
                 return label;
             }
@@ -514,4 +517,31 @@
                 FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS |
                 FLAG_SUPPORTS_SCREEN_DENSITIES);
     }
+    
+    /**
+     * @hide
+     */
+    @Override protected Drawable loadDefaultIcon(PackageManager pm) {
+        if ((flags & FLAG_EXTERNAL_STORAGE) != 0
+                && isPackageUnavailable(pm)) {
+            return Resources.getSystem().getDrawable(
+                    com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+        }
+        return pm.getDefaultActivityIcon();
+    }
+    
+    private boolean isPackageUnavailable(PackageManager pm) {
+        try {
+            return pm.getPackageInfo(packageName, 0) == null;
+        } catch (NameNotFoundException ex) {
+            return true;
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    @Override protected ApplicationInfo getApplicationInfo() {
+        return this;
+    }
 }
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 338c62b6..cafe372 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -99,24 +99,6 @@
         return name;
     }
     
-    @Override public Drawable loadIcon(PackageManager pm) {
-        ApplicationInfo ai = applicationInfo;
-        Drawable dr;
-        if (icon != 0) {
-            dr = pm.getDrawable(packageName, icon, ai);
-            if (dr != null) {
-                return dr;
-            }
-        }
-        if (ai.icon != 0) {
-            dr = pm.getDrawable(packageName, ai.icon, ai);
-            if (dr != null) {
-                return dr;
-            }
-        }
-        return pm.getDefaultActivityIcon();
-    }
-    
     /**
      * Return the icon resource identifier to use for this component.  If
      * the component defines an icon, that is used; else, the application
@@ -155,7 +137,7 @@
         dest.writeInt(enabled ? 1 : 0);
         dest.writeInt(exported ? 1 : 0);
     }
-
+    
     protected ComponentInfo(Parcel source) {
         super(source);
         applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
@@ -164,4 +146,18 @@
         enabled = (source.readInt() != 0);
         exported = (source.readInt() != 0);
     }
+    
+    /**
+     * @hide
+     */
+    @Override protected Drawable loadDefaultIcon(PackageManager pm) {
+        return applicationInfo.loadIcon(pm);
+    }
+    
+    /**
+     * @hide
+     */
+    @Override protected ApplicationInfo getApplicationInfo() {
+        return applicationInfo;
+    }
 }
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index d666263..14c0680 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -103,7 +103,7 @@
             return nonLocalizedLabel;
         }
         if (labelRes != 0) {
-            CharSequence label = pm.getText(packageName, labelRes, null);
+            CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo());
             if (label != null) {
                 return label.toString().trim();
             }
@@ -123,15 +123,31 @@
      * the PackageManager from which you originally retrieved this item.
      * 
      * @return Returns a Drawable containing the item's icon.  If the
-     * item does not have an icon, the default activity icon is returned.
+     * item does not have an icon, the item's default icon is returned
+     * such as the default activity icon.
      */
     public Drawable loadIcon(PackageManager pm) {
         if (icon != 0) {
-            Drawable dr = pm.getDrawable(packageName, icon, null);
+            Drawable dr = pm.getDrawable(packageName, icon, getApplicationInfo());
             if (dr != null) {
                 return dr;
             }
         }
+        return loadDefaultIcon(pm);
+    }
+    
+    /**
+     * Retrieve the default graphical icon associated with this item.
+     * 
+     * @param pm A PackageManager from which the icon can be loaded; usually
+     * the PackageManager from which you originally retrieved this item.
+     * 
+     * @return Returns a Drawable containing the item's default icon
+     * such as the default activity icon.
+     * 
+     * @hide
+     */
+    protected Drawable loadDefaultIcon(PackageManager pm) {
         return pm.getDefaultActivityIcon();
     }
     
@@ -152,7 +168,7 @@
         if (metaData != null) {
             int resid = metaData.getInt(name);
             if (resid != 0) {
-                return pm.getXml(packageName, resid, null);
+                return pm.getXml(packageName, resid, getApplicationInfo());
             }
         }
         return null;
@@ -182,7 +198,7 @@
         dest.writeInt(icon);
         dest.writeBundle(metaData);
     }
-
+    
     protected PackageItemInfo(Parcel source) {
         name = source.readString();
         packageName = source.readString();
@@ -193,6 +209,18 @@
         metaData = source.readBundle();
     }
 
+    /**
+     * Get the ApplicationInfo for the application to which this item belongs,
+     * if available, otherwise returns null.
+     * 
+     * @return Returns the ApplicationInfo of this item, or null if not known.
+     * 
+     * @hide
+     */
+    protected ApplicationInfo getApplicationInfo() {
+        return null;
+    }
+
     public static class DisplayNameComparator
             implements Comparator<PackageItemInfo> {
         public DisplayNameComparator(PackageManager pm) {
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index a35940f..74e756b 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -163,8 +163,6 @@
      * item does not have an icon, the default activity icon is returned.
      */
     public Drawable loadIcon(PackageManager pm) {
-        ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
-        ApplicationInfo ai = ci.applicationInfo;
         Drawable dr;
         if (resolvePackageName != null && icon != 0) {
             dr = pm.getDrawable(resolvePackageName, icon, null);
@@ -172,6 +170,8 @@
                 return dr;
             }
         }
+        ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
+        ApplicationInfo ai = ci.applicationInfo;
         if (icon != 0) {
             dr = pm.getDrawable(ci.packageName, icon, ai);
             if (dr != null) {
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index e5bc6e3..4101ab4 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -54,12 +54,12 @@
     static final int MATCH_GROUP_PATH = 5;
 
     static Pattern sAddressPattern = Pattern.compile(
-            /* scheme    */ "(?:(http|HTTP|https|HTTPS|file|FILE)\\:\\/\\/)?" +
+            /* scheme    */ "(?:(http|https|file)\\:\\/\\/)?" +
             /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
             /* host      */ "([-" + GOOD_IRI_CHAR + "%_]+(?:\\.[-" + GOOD_IRI_CHAR + "%_]+)*|\\[[0-9a-fA-F:\\.]+\\])?" +
             /* port      */ "(?:\\:([0-9]*))?" +
             /* path      */ "(\\/?[^#]*)?" +
-            /* anchor    */ ".*");
+            /* anchor    */ ".*", Pattern.CASE_INSENSITIVE);
 
     /** parses given uriString. */
     public WebAddress(String address) throws ParseException {
@@ -79,7 +79,7 @@
         String t;
         if (m.matches()) {
             t = m.group(MATCH_GROUP_SCHEME);
-            if (t != null) mScheme = t;
+            if (t != null) mScheme = t.toLowerCase();
             t = m.group(MATCH_GROUP_AUTHORITY);
             if (t != null) mAuthInfo = t;
             t = m.group(MATCH_GROUP_HOST);
diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java
index 84b6487..a31639f 100644
--- a/core/java/android/net/http/RequestQueue.java
+++ b/core/java/android/net/http/RequestQueue.java
@@ -238,6 +238,8 @@
             mContext.registerReceiver(mProxyChangeReceiver,
                                       new IntentFilter(Proxy.PROXY_CHANGE_ACTION));
         }
+        // we need to resample the current proxy setup
+        setProxyConfig();
     }
 
     /**
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index eab1627..f7e7d39 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -68,6 +68,15 @@
      * happened.  You can determine its current state with
      * {@link #getExternalStorageState()}.
      * 
+     * <p>Applications should not directly use this top-level directory, in
+     * order to avoid polluting the user's root namespace.  Any files that are
+     * private to the application should be placed in a directory returned
+     * by {@link android.content.Context#getExternalFilesDir
+     * Context.getExternalFilesDir}, which the system will take care of deleting
+     * if the application is uninstalled.  Other shared files should be placed
+     * in one of the directories returned by
+     * {@link #getExternalStoragePublicDirectory}.
+     * 
      * <p>Here is an example of typical code to monitor the state of
      * external storage:</p>
      * 
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index 194fe33..dc0d864 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -24,7 +24,6 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteException;
 import android.net.Uri;
-import android.os.RemoteException;
 import android.pim.vcard.exception.VCardException;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
@@ -522,10 +521,6 @@
                     }
                 }
             }
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, String.format("RemoteException at id %s (%s)",
-                    contactId, e.getMessage()));
-            return "";
         } finally {
             if (entityIterator != null) {
                 entityIterator.close();
diff --git a/core/java/android/provider/Applications.java b/core/java/android/provider/Applications.java
index 0b0ce58..7aabc50 100644
--- a/core/java/android/provider/Applications.java
+++ b/core/java/android/provider/Applications.java
@@ -16,67 +16,112 @@
 
 package android.provider;
 
-import android.app.SearchManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.database.Cursor;
 import android.net.Uri;
-import android.widget.SimpleCursorAdapter;
+
+import java.util.List;
 
 /**
- * <p>The Applications provider gives information about installed applications.</p>
- * 
- * <p>This provider provides the following columns:
- * 
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ * The Applications provider gives information about installed applications.
  *
- *     <thead>
- *     <tr><th>Column Name</th> <th>Description</th> </tr>
- *     </thead>
- *
- * <tbody>
- * <tr><th>{@link SearchManager#SUGGEST_COLUMN_TEXT_1}</th>
- *     <td>The application name.</td>
- * </tr>
- * 
- * <tr><th>{@link SearchManager#SUGGEST_COLUMN_INTENT_COMPONENT}</th>
- *     <td>The component to be used when forming the intent.</td>
- * </tr>
- * 
- * <tr><th>{@link SearchManager#SUGGEST_COLUMN_ICON_1}</th>
- *     <td>The application's icon resource id, prepended by its package name and
- *         separated by a colon, e.g., "com.android.alarmclock:2130837524". The
- *         package name is required for an activity interpreting this value to
- *         be able to correctly access the icon drawable, for example, in an override of
- *         {@link SimpleCursorAdapter#setViewImage(android.widget.ImageView, String)}.</td>
- * </tr>
- * 
- * <tr><th>{@link SearchManager#SUGGEST_COLUMN_ICON_2}</th>
- *     <td><i>Unused - column provided to conform to the {@link SearchManager} stipulation
- *            that all providers provide either both or neither of
- *            {@link SearchManager#SUGGEST_COLUMN_ICON_1} and
- *            {@link SearchManager#SUGGEST_COLUMN_ICON_2}.</td>
- * </tr>
- * 
- * @hide pending API council approval - should be unhidden at the same time as
- *       {@link SearchManager#SUGGEST_COLUMN_INTENT_COMPONENT}
+ * @hide Only used by ApplicationsProvider so far.
  */
 public class Applications {
-    private static final String TAG = "Applications";
 
     /**
      * The content authority for this provider.
-     *
-     * @hide
      */
     public static final String AUTHORITY = "applications";
 
     /**
      * The content:// style URL for this provider
-     *
-     * @hide
      */
     public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
 
     /**
+     * The content path for application component URIs.
+     */
+    public static final String APPLICATION_PATH = "applications";
+
+    /**
+     * The content path for application search.
+     */
+    public static final String SEARCH_PATH = "search";
+
+    private static final String APPLICATION_SUB_TYPE = "vnd.android.application";
+
+    /**
+     * The MIME type for a single application item.
+     */
+    public static final String APPLICATION_ITEM_TYPE =
+            ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + APPLICATION_SUB_TYPE;
+
+    /**
+     * The MIME type for a list of application items.
+     */
+    public static final String APPLICATION_DIR_TYPE =
+            ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + APPLICATION_SUB_TYPE;
+
+    /**
      * no public constructor since this is a utility class
      */
     private Applications() {}
+
+    /**
+     * Gets a cursor with application search results.
+     * See {@link ApplicationColumns} for the columns available in the returned cursor.
+     */
+    public static Cursor search(ContentResolver resolver, String query) {
+        Uri searchUri = CONTENT_URI.buildUpon().appendPath(SEARCH_PATH).appendPath(query).build();
+        return resolver.query(searchUri, null, null, null, null);
+    }
+
+    /**
+     * Gets the application component name from an application URI.
+     *
+     * @param appUri A URI of the form
+     * "content://applications/applications/&lt;packageName&gt;/&lt;className&gt;".
+     * @return The component name for the application, or
+     * <code>null</code> if the given URI was <code>null</code>
+     * or malformed.
+     */
+    public static ComponentName uriToComponentName(Uri appUri) {
+        if (appUri == null) return null;
+        if (!ContentResolver.SCHEME_CONTENT.equals(appUri.getScheme())) return null;
+        if (!AUTHORITY.equals(appUri.getAuthority())) return null;
+        List<String> pathSegments = appUri.getPathSegments();
+        if (pathSegments.size() != 3) return null;
+        if (!APPLICATION_PATH.equals(pathSegments.get(0))) return null;
+        String packageName = pathSegments.get(1);
+        String name = pathSegments.get(2);
+        return new ComponentName(packageName, name);
+    }
+
+    /**
+     * Gets the URI for an application component.
+     *
+     * @param packageName The name of the application's package.
+     * @param className The class name of the application.
+     * @return A URI of the form
+     * "content://applications/applications/&lt;packageName&gt;/&lt;className&gt;".
+     */
+    public static Uri componentNameToUri(String packageName, String className) {
+        return Applications.CONTENT_URI.buildUpon()
+                .appendEncodedPath(APPLICATION_PATH)
+                .appendPath(packageName)
+                .appendPath(className)
+                .build();
+    }
+
+    /**
+     * The columns in application cursors, like those returned by
+     * {@link Applications#search(ContentResolver, String)}.
+     */
+    public interface ApplicationColumns extends BaseColumns {
+        public static final String NAME = "name";
+        public static final String ICON = "icon";
+        public static final String URI = "uri";
+    }
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d615fd0..8ef2aeb 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1688,11 +1688,12 @@
             // bail out before bad things happen
             if (mDataChanged) return;
 
-            if (mAdapter != null && mItemCount > 0 &&
-                    mClickMotionPosition != INVALID_POSITION &&
-                    mClickMotionPosition < mAdapter.getCount() && sameWindow()) {
-                performItemClick(mChild, mClickMotionPosition, getAdapter().getItemId(
-                        mClickMotionPosition));
+            final ListAdapter adapter = mAdapter;
+            final int motionPosition = mClickMotionPosition;
+            if (adapter != null && mItemCount > 0 &&
+                    motionPosition != INVALID_POSITION &&
+                    motionPosition < adapter.getCount() && sameWindow()) {
+                performItemClick(mChild, motionPosition, adapter.getItemId(motionPosition));
             }
         }
     }
@@ -2118,8 +2119,8 @@
                                     mPendingCheckForTap : mPendingCheckForLongPress);
                         }
                         mLayoutMode = LAYOUT_NORMAL;
-                        mTouchMode = TOUCH_MODE_TAP;
-                        if (!mDataChanged) {
+                        if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
+                            mTouchMode = TOUCH_MODE_TAP;
                             setSelectedPositionInt(mMotionPosition);
                             layoutChildren();
                             child.setPressed(true);
@@ -2141,12 +2142,12 @@
                                     mTouchMode = TOUCH_MODE_REST;
                                 }
                             }, ViewConfiguration.getPressedStateDuration());
+                        } else {
+                            mTouchMode = TOUCH_MODE_REST;
                         }
                         return true;
-                    } else {
-                        if (!mDataChanged) {
-                            post(performClick);
-                        }
+                    } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
+                        post(performClick);
                     }
                 }
                 mTouchMode = TOUCH_MODE_REST;
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
index ec79a50..333257e 100644
--- a/core/java/com/android/internal/widget/ContactHeaderWidget.java
+++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java
@@ -269,9 +269,19 @@
                             bindContactInfo(cursor);
                             Uri lookupUri = Contacts.getLookupUri(cursor.getLong(ContactQuery._ID),
                                     cursor.getString(ContactQuery.LOOKUP_KEY));
-                            startPhotoQuery(cursor.getLong(ContactQuery.PHOTO_ID),
-                                    lookupUri, false /* don't reset query handler */);
-                            invalidate();
+
+                            final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
+
+                            if (photoId == 0) {
+                                mPhotoView.setImageBitmap(loadPlaceholderPhoto(null));
+                                if (cookie != null && cookie instanceof Uri) {
+                                    mPhotoView.assignContactUri((Uri) cookie);
+                                }
+                                invalidate();
+                            } else {
+                                startPhotoQuery(photoId, lookupUri,
+                                        false /* don't reset query handler */);
+                            }
                         } else {
                             // shouldn't really happen
                             setDisplayName(null, null);
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp
index a3be309..f60fe6d 100755
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/core/jni/android_location_GpsLocationProvider.cpp
@@ -41,6 +41,7 @@
 static const GpsInterface* sGpsInterface = NULL;
 static const GpsXtraInterface* sGpsXtraInterface = NULL;
 static const AGpsInterface* sAGpsInterface = NULL;
+static const GpsPrivacyInterface* sGpsPrivacyInterface = NULL;
 static const GpsNiInterface* sGpsNiInterface = NULL;
 static const GpsDebugInterface* sGpsDebugInterface = NULL;
 
@@ -223,9 +224,15 @@
         sAGpsInterface->init(&sAGpsCallbacks);
 
     if (!sGpsNiInterface)
-       sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+        sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
     if (sGpsNiInterface)
-       sGpsNiInterface->init(&sGpsNiCallbacks);
+        sGpsNiInterface->init(&sGpsNiCallbacks);
+
+    // Clear privacy lock while enabled
+    if (!sGpsPrivacyInterface)
+        sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE);
+    if (sGpsPrivacyInterface)
+        sGpsPrivacyInterface->set_privacy_lock(0);
 
     if (!sGpsDebugInterface)
        sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
@@ -235,6 +242,12 @@
 
 static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj)
 {
+    // Enable privacy lock while disabled
+    if (!sGpsPrivacyInterface)
+        sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE);
+    if (sGpsPrivacyInterface)
+        sGpsPrivacyInterface->set_privacy_lock(1);
+
     pthread_mutex_lock(&sEventMutex);
     sPendingCallbacks |= kDisableRequest;
     pthread_cond_signal(&sEventCond);
@@ -476,12 +489,10 @@
 static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
       jint notifId, jint response)
 {
-    if (!sGpsNiInterface) {
+    if (!sGpsNiInterface)
         sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
-    }
-    if (sGpsNiInterface) {
+    if (sGpsNiInterface)
         sGpsNiInterface->respond(notifId, response);
-    }
 }
 
 static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj)
diff --git a/core/res/res/drawable-hdpi/sym_app_on_sd_unavailable_icon.png b/core/res/res/drawable-hdpi/sym_app_on_sd_unavailable_icon.png
new file mode 100644
index 0000000..d915d41
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_app_on_sd_unavailable_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_app_on_sd_unavailable_icon.png b/core/res/res/drawable-mdpi/sym_app_on_sd_unavailable_icon.png
new file mode 100644
index 0000000..4730668
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_app_on_sd_unavailable_icon.png
Binary files differ
diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
index 48fe765..f840512 100644
--- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
+++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
@@ -51,7 +51,7 @@
         long historyId = engine.insertStartSyncEvent(
                 account, authority, time0, SyncStorageEngine.SOURCE_LOCAL);
         long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
-        engine.stopSyncEvent(historyId, new Bundle(), time1 - time0, "yay", 0, 0);
+        engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
     }
 
     /**
diff --git a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
index 1b3cdcc..5968e83 100644
--- a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
+++ b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
@@ -21,7 +21,6 @@
 import android.content.EntityIterator;
 import android.database.Cursor;
 import android.net.Uri;
-import android.pim.vcard.VCardComposer;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
@@ -69,6 +68,10 @@
         return mIterator.next();
     }
 
+    public void remove() {
+        throw new UnsupportedOperationException("remove not supported");
+    }
+
     public void reset() {
         mIterator = mEntityList.iterator();
     }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 842176e..27add0a 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -395,6 +395,7 @@
     mVideoTimeUs = 0;
 
     mSeeking = false;
+    mSeekNotificationSent = false;
     mSeekTimeUs = 0;
 
     mUri.setTo("");
@@ -686,11 +687,20 @@
 
 status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
     mSeeking = true;
+    mSeekNotificationSent = false;
     mSeekTimeUs = timeUs;
     mFlags &= ~AT_EOS;
 
     seekAudioIfNecessary_l();
 
+    if (!(mFlags & PLAYING)) {
+        LOGV("seeking while paused, sending SEEK_COMPLETE notification"
+             " immediately.");
+
+        notifyListener_l(MEDIA_SEEK_COMPLETE);
+        mSeekNotificationSent = true;
+    }
+
     return OK;
 }
 
@@ -701,6 +711,7 @@
         mWatchForAudioSeekComplete = true;
         mWatchForAudioEOS = true;
         mSeeking = false;
+        mSeekNotificationSent = false;
     }
 }
 
@@ -869,7 +880,7 @@
             mAudioPlayer->seekTo(timeUs);
             mWatchForAudioSeekComplete = true;
             mWatchForAudioEOS = true;
-        } else {
+        } else if (!mSeekNotificationSent) {
             // If we're playing video only, report seek complete now,
             // otherwise audio player will notify us later.
             notifyListener_l(MEDIA_SEEK_COMPLETE);
@@ -877,6 +888,7 @@
 
         mFlags |= FIRST_FRAME;
         mSeeking = false;
+        mSeekNotificationSent = false;
     }
 
     if (mFlags & FIRST_FRAME) {
@@ -984,7 +996,11 @@
 
     if (mWatchForAudioSeekComplete && !mAudioPlayer->isSeeking()) {
         mWatchForAudioSeekComplete = false;
-        notifyListener_l(MEDIA_SEEK_COMPLETE);
+
+        if (!mSeekNotificationSent) {
+            notifyListener_l(MEDIA_SEEK_COMPLETE);
+            mSeekNotificationSent = true;
+        }
     }
 
     status_t finalStatus;
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index fe84b38..2ed8ef1 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -144,6 +144,8 @@
         meta->setInt64(kKeyDuration, durationUs);
     }
 
+    meta->setCString(kKeyDecoderComponent, "AACDecoder");
+
     return meta;
 }
 
diff --git a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
index 553be87..7728597 100644
--- a/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/AMRNBDecoder.cpp
@@ -105,6 +105,8 @@
         meta->setInt64(kKeyDuration, durationUs);
     }
 
+    meta->setCString(kKeyDecoderComponent, "AMRNBDecoder");
+
     return meta;
 }
 
diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
index b6d7ea3..f349671 100644
--- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
@@ -138,6 +138,8 @@
         meta->setInt64(kKeyDuration, durationUs);
     }
 
+    meta->setCString(kKeyDecoderComponent, "AMRNBEncoder");
+
     return meta;
 }
 
diff --git a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
index c9d38c0..c17c100 100644
--- a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
+++ b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
@@ -110,6 +110,8 @@
         meta->setInt64(kKeyDuration, durationUs);
     }
 
+    meta->setCString(kKeyDecoderComponent, "AMRWBDecoder");
+
     return meta;
 }
 
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index 4dc96be..efcb476 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -107,6 +107,8 @@
         meta->setInt64(kKeyDuration, durationUs);
     }
 
+    meta->setCString(kKeyDecoderComponent, "MP3Decoder");
+
     return meta;
 }
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index c985dff..9e8a674 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -132,6 +132,7 @@
     int64_t mVideoTimeUs;
 
     bool mSeeking;
+    bool mSeekNotificationSent;
     int64_t mSeekTimeUs;
 
     bool mWatchForAudioSeekComplete;
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 57944fb..5cf61bd 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -177,6 +177,7 @@
     void systemReady() {
         // check our power situation now that it is safe to display the shutdown dialog.
         shutdownIfNoPower();
+        shutdownIfOverTemp();
     }
 
     private final void shutdownIfNoPower() {
@@ -190,6 +191,17 @@
         }
     }
 
+    private final void shutdownIfOverTemp() {
+        // shut down gracefully if temperature is too high (> 68.0C)
+        // wait until the system has booted before attempting to display the shutdown dialog.
+        if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) {
+            Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
+            intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(intent);
+        }
+    }
+
     private native void native_update();
 
     private synchronized final void update() {
@@ -199,6 +211,7 @@
         long dischargeDuration = 0;
 
         shutdownIfNoPower();
+        shutdownIfOverTemp();
 
         mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
         if (mAcOnline) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 7cd058a..8788cd5 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -5464,6 +5464,17 @@
         boolean dataDirExists = getDataPathForPackage(pkg).exists();
         res.name = pkgName;
         synchronized(mPackages) {
+            if (mSettings.mRenamedPackages.containsKey(pkgName)) {
+                // A package with the same name is already installed, though
+                // it has been renamed to an older name.  The package we
+                // are trying to install should be installed as an update to
+                // the existing one, but that has not been requested, so bail.
+                Slog.w(TAG, "Attempt to re-install " + pkgName
+                        + " without first uninstalling package running as "
+                        + mSettings.mRenamedPackages.get(pkgName));
+                res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+                return;
+            }
             if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
                 // Don't allow installation over an existing package with the same name.
                 Slog.w(TAG, "Attempt to re-install " + pkgName
@@ -5595,7 +5606,7 @@
                 PackageInstalledInfo restoreRes = new PackageInstalledInfo();
                 restoreRes.removedInfo = new PackageRemovedInfo();
                 // Parse old package
-                parseFlags |= ~PackageManager.INSTALL_REPLACE_EXISTING;
+                parseFlags &= ~PackageManager.INSTALL_REPLACE_EXISTING;
                 scanPackageLI(restoreFile, parseFlags, scanMode);
                 synchronized (mPackages) {
                     updatePermissionsLP(deletedPackage.packageName, deletedPackage,
@@ -6278,7 +6289,7 @@
             Log.i(TAG, "Removing non-system package:"+p.packageName);
             // Kill application pre-emptively especially for apps on sd.
             killApplication(packageName, p.applicationInfo.uid);
-            ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
+            ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo);
         }
         return ret;
     }
@@ -7605,9 +7616,10 @@
         }
 
         void setFlags(int pkgFlags) {
-            this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
-            (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
-            (pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+            this.pkgFlags = pkgFlags & (
+                    ApplicationInfo.FLAG_SYSTEM |
+                    ApplicationInfo.FLAG_FORWARD_LOCK |
+                    ApplicationInfo.FLAG_EXTERNAL_STORAGE);
         }
     }
 
@@ -9606,7 +9618,7 @@
                }
                // Parse package
                int parseFlags = PackageParser.PARSE_CHATTY |
-               PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+                       PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
                PackageParser pp = new PackageParser(codePath);
                pp.setSeparateProcesses(mSeparateProcesses);
                final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
@@ -9682,7 +9694,7 @@
                if (res) {
                    pkgList.add(pkgName);
                } else {
-                   Slog.e(TAG, "Failed to delete pkg  from sdcard : " + pkgName);
+                   Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
                    failedList.add(args);
                }
            }