Merge "Don't long press if preventDefault is called" into jb-dev
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4506546..2ed93f4 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -580,7 +580,8 @@
             IBinder b = data.readStrongBinder();
             IApplicationThread app = ApplicationThreadNative.asInterface(b);
             String name = data.readString();
-            ContentProviderHolder cph = getContentProvider(app, name);
+            boolean stable = data.readInt() != 0;
+            ContentProviderHolder cph = getContentProvider(app, name, stable);
             reply.writeNoException();
             if (cph != null) {
                 reply.writeInt(1);
@@ -617,12 +618,30 @@
             return true;
         }
 
+        case REF_CONTENT_PROVIDER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder b = data.readStrongBinder();
+            int stable = data.readInt();
+            int unstable = data.readInt();
+            boolean res = refContentProvider(b, stable, unstable);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
+
+        case UNSTABLE_PROVIDER_DIED_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder b = data.readStrongBinder();
+            unstableProviderDied(b);
+            reply.writeNoException();
+            return true;
+        }
+
         case REMOVE_CONTENT_PROVIDER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder b = data.readStrongBinder();
-            IApplicationThread app = ApplicationThreadNative.asInterface(b);
-            String name = data.readString();
-            removeContentProvider(app, name);
+            boolean stable = data.readInt() != 0;
+            removeContentProvider(b, stable);
             reply.writeNoException();
             return true;
         }
@@ -2314,13 +2333,13 @@
         reply.recycle();
     }
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
-                                                    String name) throws RemoteException
-    {
+            String name, boolean stable) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(caller != null ? caller.asBinder() : null);
         data.writeString(name);
+        data.writeInt(stable ? 1 : 0);
         mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
@@ -2352,7 +2371,7 @@
         return cph;
     }
     public void publishContentProviders(IApplicationThread caller,
-                                        List<ContentProviderHolder> providers) throws RemoteException
+            List<ContentProviderHolder> providers) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -2364,14 +2383,38 @@
         data.recycle();
         reply.recycle();
     }
-    
-    public void removeContentProvider(IApplicationThread caller,
-            String name) throws RemoteException {
+    public boolean refContentProvider(IBinder connection, int stable, int unstable)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
-        data.writeString(name);
+        data.writeStrongBinder(connection);
+        data.writeInt(stable);
+        data.writeInt(unstable);
+        mRemote.transact(REF_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() != 0;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+    public void unstableProviderDied(IBinder connection) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(connection);
+        mRemote.transact(UNSTABLE_PROVIDER_DIED_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(connection);
+        data.writeInt(stable ? 1 : 0);
         mRemote.transact(REMOVE_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 33e639e..a457e3c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -140,6 +140,7 @@
     private static final boolean DEBUG_CONFIGURATION = false;
     private static final boolean DEBUG_SERVICE = false;
     private static final boolean DEBUG_MEMORY_TRIM = false;
+    private static final boolean DEBUG_PROVIDER = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
     private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -210,6 +211,8 @@
         = new HashMap<IBinder, ProviderRefCount>();
     final HashMap<IBinder, ProviderClientRecord> mLocalProviders
         = new HashMap<IBinder, ProviderClientRecord>();
+    final HashMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
+            = new HashMap<ComponentName, ProviderClientRecord>();
 
     final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
         = new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
@@ -284,20 +287,19 @@
         }
     }
 
-    final class ProviderClientRecord implements IBinder.DeathRecipient {
-        final String mName;
+    final class ProviderClientRecord {
+        final String[] mNames;
         final IContentProvider mProvider;
         final ContentProvider mLocalProvider;
+        final IActivityManager.ContentProviderHolder mHolder;
 
-        ProviderClientRecord(String name, IContentProvider provider,
-                ContentProvider localProvider) {
-            mName = name;
+        ProviderClientRecord(String[] names, IContentProvider provider,
+                ContentProvider localProvider,
+                IActivityManager.ContentProviderHolder holder) {
+            mNames = names;
             mProvider = provider;
             mLocalProvider = localProvider;
-        }
-
-        public void binderDied() {
-            removeDeadProvider(mName, mProvider);
+            mHolder = holder;
         }
     }
 
@@ -1061,6 +1063,11 @@
             pw.flush();
         }
 
+        @Override
+        public void unstableProviderDied(IBinder provider) {
+            queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
+        }
+
         private void printRow(PrintWriter pw, String format, Object...objs) {
             pw.println(String.format(format, objs));
         }
@@ -1125,6 +1132,7 @@
         public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
         public static final int TRIM_MEMORY             = 140;
         public static final int DUMP_PROVIDER           = 141;
+        public static final int UNSTABLE_PROVIDER_DIED  = 142;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -1170,6 +1178,7 @@
                     case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
                     case TRIM_MEMORY: return "TRIM_MEMORY";
                     case DUMP_PROVIDER: return "DUMP_PROVIDER";
+                    case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
                 }
             }
             return Integer.toString(code);
@@ -1337,7 +1346,7 @@
                     break;
                 case REMOVE_PROVIDER:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove");
-                    completeRemoveProvider((IContentProvider)msg.obj);
+                    completeRemoveProvider((ProviderRefCount)msg.obj);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case ENABLE_JIT:
@@ -1377,6 +1386,9 @@
                     handleTrimMemory(msg.arg1);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
+                case UNSTABLE_PROVIDER_DIED:
+                    handleUnstableProviderDied((IBinder)msg.obj, false);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
         }
@@ -2867,10 +2879,24 @@
     }
 
     private static final class ProviderRefCount {
-        public int count;
+        public final IActivityManager.ContentProviderHolder holder;
+        public final ProviderClientRecord client;
+        public int stableCount;
+        public int unstableCount;
 
-        ProviderRefCount(int pCount) {
-            count = pCount;
+        // When this is set, the stable and unstable ref counts are 0 and
+        // we have a pending operation scheduled to remove the ref count
+        // from the activity manager.  On the activity manager we are still
+        // holding an unstable ref, though it is not reflected in the counts
+        // here.
+        public boolean removePending;
+
+        ProviderRefCount(IActivityManager.ContentProviderHolder inHolder,
+                ProviderClientRecord inClient, int sCount, int uCount) {
+            holder = inHolder;
+            client = inClient;
+            stableCount = sCount;
+            unstableCount = uCount;
         }
     }
 
@@ -4080,15 +4106,6 @@
                 Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
             }
 
-            try {
-                mInstrumentation.onCreate(data.instrumentationArgs);
-            }
-            catch (Exception e) {
-                throw new RuntimeException(
-                    "Exception thrown in onCreate() of "
-                    + data.instrumentationName + ": " + e.toString(), e);
-            }
-
         } else {
             mInstrumentation = new Instrumentation();
         }
@@ -4119,6 +4136,17 @@
                 }
             }
 
+            // Do this after providers, since instrumentation tests generally start their
+            // test thread at this point, and we don't want that racing.
+            try {
+                mInstrumentation.onCreate(data.instrumentationArgs);
+            }
+            catch (Exception e) {
+                throw new RuntimeException(
+                    "Exception thrown in onCreate() of "
+                    + data.instrumentationName + ": " + e.toString(), e);
+            }
+
             try {
                 mInstrumentation.callApplicationOnCreate(app);
             } catch (Exception e) {
@@ -4159,12 +4187,9 @@
             buf.append(": ");
             buf.append(cpi.name);
             Log.i(TAG, buf.toString());
-            IContentProvider cp = installProvider(context, null, cpi,
-                    false /*noisy*/, true /*noReleaseNeeded*/);
-            if (cp != null) {
-                IActivityManager.ContentProviderHolder cph =
-                        new IActivityManager.ContentProviderHolder(cpi);
-                cph.provider = cp;
+            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
+                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
+            if (cph != null) {
                 cph.noReleaseNeeded = true;
                 results.add(cph);
             }
@@ -4177,8 +4202,8 @@
         }
     }
 
-    public final IContentProvider acquireProvider(Context c, String name) {
-        IContentProvider provider = acquireExistingProvider(c, name);
+    public final IContentProvider acquireProvider(Context c, String name, boolean stable) {
+        IContentProvider provider = acquireExistingProvider(c, name, stable);
         if (provider != null) {
             return provider;
         }
@@ -4192,7 +4217,7 @@
         IActivityManager.ContentProviderHolder holder = null;
         try {
             holder = ActivityManagerNative.getDefault().getContentProvider(
-                    getApplicationThread(), name);
+                    getApplicationThread(), name, stable);
         } catch (RemoteException ex) {
         }
         if (holder == null) {
@@ -4202,23 +4227,79 @@
 
         // Install provider will increment the reference count for us, and break
         // any ties in the race.
-        provider = installProvider(c, holder.provider, holder.info,
-                true /*noisy*/, holder.noReleaseNeeded);
-        if (holder.provider != null && provider != holder.provider) {
-            if (localLOGV) {
-                Slog.v(TAG, "acquireProvider: lost the race, releasing extraneous "
-                        + "reference to the content provider");
-            }
-            try {
-                ActivityManagerNative.getDefault().removeContentProvider(
-                        getApplicationThread(), name);
-            } catch (RemoteException ex) {
-            }
-        }
-        return provider;
+        holder = installProvider(c, holder, holder.info,
+                true /*noisy*/, holder.noReleaseNeeded, stable);
+        return holder.provider;
     }
 
-    public final IContentProvider acquireExistingProvider(Context c, String name) {
+    private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
+        if (stable) {
+            prc.stableCount += 1;
+            if (prc.stableCount == 1) {
+                // We are acquiring a new stable reference on the provider.
+                int unstableDelta;
+                if (prc.removePending) {
+                    // We have a pending remove operation, which is holding the
+                    // last unstable reference.  At this point we are converting
+                    // that unstable reference to our new stable reference.
+                    unstableDelta = -1;
+                    // Cancel the removal of the provider.
+                    if (DEBUG_PROVIDER) {
+                        Slog.v(TAG, "incProviderRef: stable "
+                                + "snatched provider from the jaws of death");
+                    }
+                    prc.removePending = false;
+                    mH.removeMessages(H.REMOVE_PROVIDER, prc);
+                } else {
+                    unstableDelta = 0;
+                }
+                try {
+                    if (DEBUG_PROVIDER) {
+                        Slog.v(TAG, "incProviderRef Now stable - "
+                                + prc.holder.info.name + ": unstableDelta="
+                                + unstableDelta);
+                    }
+                    ActivityManagerNative.getDefault().refContentProvider(
+                            prc.holder.connection, 1, unstableDelta);
+                } catch (RemoteException e) {
+                    //do nothing content provider object is dead any way
+                }
+            }
+        } else {
+            prc.unstableCount += 1;
+            if (prc.unstableCount == 1) {
+                // We are acquiring a new unstable reference on the provider.
+                if (prc.removePending) {
+                    // Oh look, we actually have a remove pending for the
+                    // provider, which is still holding the last unstable
+                    // reference.  We just need to cancel that to take new
+                    // ownership of the reference.
+                    if (DEBUG_PROVIDER) {
+                        Slog.v(TAG, "incProviderRef: unstable "
+                                + "snatched provider from the jaws of death");
+                    }
+                    prc.removePending = false;
+                    mH.removeMessages(H.REMOVE_PROVIDER, prc);
+                } else {
+                    // First unstable ref, increment our count in the
+                    // activity manager.
+                    try {
+                        if (DEBUG_PROVIDER) {
+                            Slog.v(TAG, "incProviderRef: Now unstable - "
+                                    + prc.holder.info.name);
+                        }
+                        ActivityManagerNative.getDefault().refContentProvider(
+                                prc.holder.connection, 0, 1);
+                    } catch (RemoteException e) {
+                        //do nothing content provider object is dead any way
+                    }
+                }
+            }
+        }
+    }
+
+    public final IContentProvider acquireExistingProvider(Context c, String name,
+            boolean stable) {
         synchronized (mProviderMap) {
             ProviderClientRecord pr = mProviderMap.get(name);
             if (pr == null) {
@@ -4232,23 +4313,14 @@
             // provider is not reference counted and never needs to be released.
             ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
             if (prc != null) {
-                prc.count += 1;
-                if (prc.count == 1) {
-                    if (localLOGV) {
-                        Slog.v(TAG, "acquireExistingProvider: "
-                                + "snatched provider from the jaws of death");
-                    }
-                    // Because the provider previously had a reference count of zero,
-                    // it was scheduled to be removed.  Cancel that.
-                    mH.removeMessages(H.REMOVE_PROVIDER, provider);
-                }
+                incProviderRefLocked(prc, stable);
             }
             return provider;
         }
     }
 
-    public final boolean releaseProvider(IContentProvider provider) {
-        if(provider == null) {
+    public final boolean releaseProvider(IContentProvider provider, boolean stable) {
+        if (provider == null) {
             return false;
         }
 
@@ -4260,55 +4332,98 @@
                 return false;
             }
 
-            if (prc.count == 0) {
-                if (localLOGV) Slog.v(TAG, "releaseProvider: ref count already 0, how?");
-                return false;
+            boolean lastRef = false;
+            if (stable) {
+                if (prc.stableCount == 0) {
+                    if (DEBUG_PROVIDER) Slog.v(TAG,
+                            "releaseProvider: stable ref count already 0, how?");
+                    return false;
+                }
+                prc.stableCount -= 1;
+                if (prc.stableCount == 0) {
+                    // What we do at this point depends on whether there are
+                    // any unstable refs left: if there are, we just tell the
+                    // activity manager to decrement its stable count; if there
+                    // aren't, we need to enqueue this provider to be removed,
+                    // and convert to holding a single unstable ref while
+                    // doing so.
+                    lastRef = prc.unstableCount == 0;
+                    try {
+                        if (DEBUG_PROVIDER) {
+                            Slog.v(TAG, "releaseProvider: No longer stable w/lastRef="
+                                    + lastRef + " - " + prc.holder.info.name);
+                        }
+                        ActivityManagerNative.getDefault().refContentProvider(
+                                prc.holder.connection, -1, lastRef ? 1 : 0);
+                    } catch (RemoteException e) {
+                        //do nothing content provider object is dead any way
+                    }
+                }
+            } else {
+                if (prc.unstableCount == 0) {
+                    if (DEBUG_PROVIDER) Slog.v(TAG,
+                            "releaseProvider: unstable ref count already 0, how?");
+                    return false;
+                }
+                prc.unstableCount -= 1;
+                if (prc.unstableCount == 0) {
+                    // If this is the last reference, we need to enqueue
+                    // this provider to be removed instead of telling the
+                    // activity manager to remove it at this point.
+                    lastRef = prc.stableCount == 0;
+                    if (!lastRef) {
+                        try {
+                            if (DEBUG_PROVIDER) {
+                                Slog.v(TAG, "releaseProvider: No longer unstable - "
+                                        + prc.holder.info.name);
+                            }
+                            ActivityManagerNative.getDefault().refContentProvider(
+                                    prc.holder.connection, 0, -1);
+                        } catch (RemoteException e) {
+                            //do nothing content provider object is dead any way
+                        }
+                    }
+                }
             }
 
-            prc.count -= 1;
-            if (prc.count == 0) {
-                // Schedule the actual remove asynchronously, since we don't know the context
-                // this will be called in.
-                // TODO: it would be nice to post a delayed message, so
-                // if we come back and need the same provider quickly
-                // we will still have it available.
-                Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider);
-                mH.sendMessage(msg);
+            if (lastRef) {
+                if (!prc.removePending) {
+                    // Schedule the actual remove asynchronously, since we don't know the context
+                    // this will be called in.
+                    // TODO: it would be nice to post a delayed message, so
+                    // if we come back and need the same provider quickly
+                    // we will still have it available.
+                    if (DEBUG_PROVIDER) {
+                        Slog.v(TAG, "releaseProvider: Enqueueing pending removal - "
+                                + prc.holder.info.name);
+                    }
+                    prc.removePending = true;
+                    Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc);
+                    mH.sendMessage(msg);
+                } else {
+                    Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
+                }
             }
             return true;
         }
     }
 
-    public final IContentProvider acquireUnstableProvider(Context c, String name) {
-        return acquireProvider(c, name);
-    }
-
-    public final boolean releaseUnstableProvider(IContentProvider provider) {
-        return releaseProvider(provider);
-    }
-
-    final void completeRemoveProvider(IContentProvider provider) {
-        IBinder jBinder = provider.asBinder();
-        String remoteProviderName = null;
-        synchronized(mProviderMap) {
-            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
-            if (prc == null) {
-                // Either no release is needed (so we shouldn't be here) or the
-                // provider was already released.
-                if (localLOGV) Slog.v(TAG, "completeRemoveProvider: release not needed");
-                return;
-            }
-
-            if (prc.count != 0) {
+    final void completeRemoveProvider(ProviderRefCount prc) {
+        synchronized (mProviderMap) {
+            if (!prc.removePending) {
                 // There was a race!  Some other client managed to acquire
                 // the provider before the removal was completed.
                 // Abort the removal.  We will do it later.
-                if (localLOGV) Slog.v(TAG, "completeRemoveProvider: lost the race, "
+                if (DEBUG_PROVIDER) Slog.v(TAG, "completeRemoveProvider: lost the race, "
                         + "provider still in use");
                 return;
             }
 
-            mProviderRefCountMap.remove(jBinder);
+            final IBinder jBinder = prc.holder.provider.asBinder();
+            ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
+            if (existingPrc == prc) {
+                mProviderRefCountMap.remove(jBinder);
+            }
 
             Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
             while (iter.hasNext()) {
@@ -4316,41 +4431,70 @@
                 IBinder myBinder = pr.mProvider.asBinder();
                 if (myBinder == jBinder) {
                     iter.remove();
-                    if (pr.mLocalProvider == null) {
-                        myBinder.unlinkToDeath(pr, 0);
-                        if (remoteProviderName == null) {
-                            remoteProviderName = pr.mName;
+                }
+            }
+        }
+
+        try {
+            if (DEBUG_PROVIDER) {
+                Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
+                        + "removeContentProvider(" + prc.holder.info.name + ")");
+            }
+            ActivityManagerNative.getDefault().removeContentProvider(
+                    prc.holder.connection, false);
+        } catch (RemoteException e) {
+            //do nothing content provider object is dead any way
+        }
+    }
+
+    final void handleUnstableProviderDied(IBinder provider, boolean fromClient) {
+        synchronized(mProviderMap) {
+            ProviderRefCount prc = mProviderRefCountMap.get(provider);
+            if (prc != null) {
+                if (DEBUG_PROVIDER) Slog.v(TAG, "Cleaning up dead provider "
+                        + provider + " " + prc.holder.info.name);
+                mProviderRefCountMap.remove(provider);
+                if (prc.client != null && prc.client.mNames != null) {
+                    for (String name : prc.client.mNames) {
+                        ProviderClientRecord pr = mProviderMap.get(name);
+                        if (pr != null && pr.mProvider.asBinder() == provider) {
+                            Slog.i(TAG, "Removing dead content provider: " + name);
+                            mProviderMap.remove(name);
                         }
                     }
                 }
+                if (fromClient) {
+                    // We found out about this due to execution in our client
+                    // code.  Tell the activity manager about it now, to ensure
+                    // that the next time we go to do anything with the provider
+                    // it knows it is dead (so we don't race with its death
+                    // notification).
+                    try {
+                        ActivityManagerNative.getDefault().unstableProviderDied(
+                                prc.holder.connection);
+                    } catch (RemoteException e) {
+                        //do nothing content provider object is dead any way
+                    }
+                }
             }
         }
-
-        if (remoteProviderName != null) {
-            try {
-                if (localLOGV) {
-                    Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
-                            + "removeContentProvider(" + remoteProviderName + ")");
-                }
-                ActivityManagerNative.getDefault().removeContentProvider(
-                        getApplicationThread(), remoteProviderName);
-            } catch (RemoteException e) {
-                //do nothing content provider object is dead any way
-            }
-        }
     }
 
-    final void removeDeadProvider(String name, IContentProvider provider) {
-        synchronized(mProviderMap) {
-            ProviderClientRecord pr = mProviderMap.get(name);
-            if (pr != null && pr.mProvider.asBinder() == provider.asBinder()) {
-                Slog.i(TAG, "Removing dead content provider: " + name);
-                ProviderClientRecord removed = mProviderMap.remove(name);
-                if (removed != null) {
-                    removed.mProvider.asBinder().unlinkToDeath(removed, 0);
-                }
+    private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
+            ContentProvider localProvider,IActivityManager.ContentProviderHolder holder) {
+        String names[] = PATTERN_SEMICOLON.split(holder.info.authority);
+        ProviderClientRecord pcr = new ProviderClientRecord(names, provider,
+                localProvider, holder);
+        for (int i = 0; i < names.length; i++) {
+            ProviderClientRecord existing = mProviderMap.get(names[i]);
+            if (existing != null) {
+                Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+                        + " already published as " + names[i]);
+            } else {
+                mProviderMap.put(names[i], pcr);
             }
         }
+        return pcr;
     }
 
     /**
@@ -4367,12 +4511,13 @@
      * and returns the existing provider.  This can happen due to concurrent
      * attempts to acquire the same provider.
      */
-    private IContentProvider installProvider(Context context,
-            IContentProvider provider, ProviderInfo info,
-            boolean noisy, boolean noReleaseNeeded) {
+    private IActivityManager.ContentProviderHolder installProvider(Context context,
+            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
+            boolean noisy, boolean noReleaseNeeded, boolean stable) {
         ContentProvider localProvider = null;
-        if (provider == null) {
-            if (noisy) {
+        IContentProvider provider;
+        if (holder == null) {
+            if (DEBUG_PROVIDER || noisy) {
                 Slog.d(TAG, "Loading provider " + info.authority + ": "
                         + info.name);
             }
@@ -4409,7 +4554,7 @@
                           info.applicationInfo.sourceDir);
                     return null;
                 }
-                if (false) Slog.v(
+                if (DEBUG_PROVIDER) Slog.v(
                     TAG, "Instantiating local provider " + info.name);
                 // XXX Need to create the correct context for this provider.
                 localProvider.attachInfo(c, info);
@@ -4421,76 +4566,72 @@
                 }
                 return null;
             }
-        } else if (localLOGV) {
-            Slog.v(TAG, "Installing external provider " + info.authority + ": "
+        } else {
+            provider = holder.provider;
+            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                     + info.name);
         }
 
-        synchronized (mProviderMap) {
-            // There is a possibility that this thread raced with another thread to
-            // add the provider.  If we find another thread got there first then we
-            // just get out of the way and return the original provider.
-            IBinder jBinder = provider.asBinder();
-            String names[] = PATTERN_SEMICOLON.split(info.authority);
-            for (int i = 0; i < names.length; i++) {
-                ProviderClientRecord pr = mProviderMap.get(names[i]);
-                if (pr != null) {
-                    if (localLOGV) {
-                        Slog.v(TAG, "installProvider: lost the race, "
-                                + "using existing named provider");
-                    }
-                    provider = pr.mProvider;
-                } else {
-                    pr = new ProviderClientRecord(names[i], provider, localProvider);
-                    if (localProvider == null) {
-                        try {
-                            jBinder.linkToDeath(pr, 0);
-                        } catch (RemoteException e) {
-                            // Provider already dead.  Bail out of here without making
-                            // any changes to the provider map or other data structures.
-                            return null;
-                        }
-                    }
-                    mProviderMap.put(names[i], pr);
-                }
-            }
+        IActivityManager.ContentProviderHolder retHolder;
 
+        synchronized (mProviderMap) {
+            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
+                    + " / " + info.name);
+            IBinder jBinder = provider.asBinder();
             if (localProvider != null) {
-                ProviderClientRecord pr = mLocalProviders.get(jBinder);
+                ComponentName cname = new ComponentName(info.packageName, info.name);
+                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                 if (pr != null) {
-                    if (localLOGV) {
+                    if (DEBUG_PROVIDER) {
                         Slog.v(TAG, "installProvider: lost the race, "
                                 + "using existing local provider");
                     }
                     provider = pr.mProvider;
                 } else {
-                    pr = new ProviderClientRecord(null, provider, localProvider);
+                    holder = new IActivityManager.ContentProviderHolder(info);
+                    holder.provider = provider;
+                    holder.noReleaseNeeded = true;
+                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                     mLocalProviders.put(jBinder, pr);
+                    mLocalProvidersByName.put(cname, pr);
                 }
-            }
-
-            if (!noReleaseNeeded) {
+                retHolder = pr.mHolder;
+            } else {
                 ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                 if (prc != null) {
-                    if (localLOGV) {
-                        Slog.v(TAG, "installProvider: lost the race, incrementing ref count");
+                    if (DEBUG_PROVIDER) {
+                        Slog.v(TAG, "installProvider: lost the race, updating ref count");
                     }
-                    prc.count += 1;
-                    if (prc.count == 1) {
-                        if (localLOGV) {
-                            Slog.v(TAG, "installProvider: "
-                                    + "snatched provider from the jaws of death");
+                    // We need to transfer our new reference to the existing
+                    // ref count, releasing the old one...  but only if
+                    // release is needed (that is, it is not running in the
+                    // system process).
+                    if (!noReleaseNeeded) {
+                        incProviderRefLocked(prc, stable);
+                        try {
+                            ActivityManagerNative.getDefault().removeContentProvider(
+                                    holder.connection, stable);
+                        } catch (RemoteException e) {
+                            //do nothing content provider object is dead any way
                         }
-                        // Because the provider previously had a reference count of zero,
-                        // it was scheduled to be removed.  Cancel that.
-                        mH.removeMessages(H.REMOVE_PROVIDER, provider);
                     }
                 } else {
-                    mProviderRefCountMap.put(jBinder, new ProviderRefCount(1));
+                    ProviderClientRecord client = installProviderAuthoritiesLocked(
+                            provider, localProvider, holder);
+                    if (noReleaseNeeded) {
+                        prc = new ProviderRefCount(holder, client, 1000, 1000);
+                    } else {
+                        prc = stable
+                                ? new ProviderRefCount(holder, client, 1, 0)
+                                : new ProviderRefCount(holder, client, 0, 1);
+                    }
+                    mProviderRefCountMap.put(jBinder, prc);
                 }
+                retHolder = prc.holder;
             }
         }
-        return provider;
+
+        return retHolder;
     }
 
     private void attach(boolean system) {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 437362b..3e726e0 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -575,6 +575,15 @@
             reply.writeNoException();
             return true;
         }
+
+        case UNSTABLE_PROVIDER_DIED_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            IBinder provider = data.readStrongBinder();
+            unstableProviderDied(provider);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -1163,4 +1172,12 @@
         mRemote.transact(DUMP_DB_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
+
+    public void unstableProviderDied(IBinder provider) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(provider);
+        mRemote.transact(UNSTABLE_PROVIDER_DIED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 299e408..4c35a8c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1679,27 +1679,32 @@
 
         @Override
         protected IContentProvider acquireProvider(Context context, String name) {
-            return mMainThread.acquireProvider(context, name);
+            return mMainThread.acquireProvider(context, name, true);
         }
 
         @Override
         protected IContentProvider acquireExistingProvider(Context context, String name) {
-            return mMainThread.acquireExistingProvider(context, name);
+            return mMainThread.acquireExistingProvider(context, name, true);
         }
 
         @Override
         public boolean releaseProvider(IContentProvider provider) {
-            return mMainThread.releaseProvider(provider);
+            return mMainThread.releaseProvider(provider, true);
         }
 
         @Override
         protected IContentProvider acquireUnstableProvider(Context c, String name) {
-            return mMainThread.acquireUnstableProvider(c, name);
+            return mMainThread.acquireProvider(c, name, false);
         }
 
         @Override
         public boolean releaseUnstableProvider(IContentProvider icp) {
-            return mMainThread.releaseUnstableProvider(icp);
+            return mMainThread.releaseProvider(icp, false);
+        }
+
+        @Override
+        public void unstableProviderDied(IContentProvider icp) {
+            mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
         }
 
         private final ActivityThread mMainThread;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cf304df..609a047 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -116,14 +116,16 @@
     public void reportThumbnail(IBinder token,
             Bitmap thumbnail, CharSequence description) throws RemoteException;
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
-            String name) throws RemoteException;
+            String name, boolean stable) throws RemoteException;
     public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
             throws RemoteException;
-    public void removeContentProvider(IApplicationThread caller,
-            String name) throws RemoteException;
+    public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException;
     public void removeContentProviderExternal(String name, IBinder token) throws RemoteException;
     public void publishContentProviders(IApplicationThread caller,
             List<ContentProviderHolder> providers) throws RemoteException;
+    public boolean refContentProvider(IBinder connection, int stableDelta, int unstableDelta)
+            throws RemoteException;
+    public void unstableProviderDied(IBinder connection) throws RemoteException;
     public PendingIntent getRunningServiceControlPanel(ComponentName service)
             throws RemoteException;
     public ComponentName startService(IApplicationThread caller, Intent service,
@@ -363,6 +365,7 @@
     public static class ContentProviderHolder implements Parcelable {
         public final ProviderInfo info;
         public IContentProvider provider;
+        public IBinder connection;
         public boolean noReleaseNeeded;
 
         public ContentProviderHolder(ProviderInfo _info) {
@@ -380,6 +383,7 @@
             } else {
                 dest.writeStrongBinder(null);
             }
+            dest.writeStrongBinder(connection);
             dest.writeInt(noReleaseNeeded ? 1:0);
         }
 
@@ -398,6 +402,7 @@
             info = ProviderInfo.CREATOR.createFromParcel(source);
             provider = ContentProviderNative.asInterface(
                 source.readStrongBinder());
+            connection = source.readStrongBinder();
             noReleaseNeeded = source.readInt() != 0;
         }
     }
@@ -476,7 +481,7 @@
     int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
     int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
     int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
-    
+    int REF_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
     int FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
     int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
     int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
@@ -597,4 +602,5 @@
     int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147;
     int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
     int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149;
+    int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 70029d2..f60cfd6 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -128,6 +128,7 @@
             String[] args) throws RemoteException;
     void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
     void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException;
+    void unstableProviderDied(IBinder provider) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -176,4 +177,5 @@
     int DUMP_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43;
     int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
     int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
+    int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 75c6e11..cad4b01 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -987,7 +987,12 @@
     /**
      * Perform calling of the application's {@link Application#onCreate}
      * method.  The default implementation simply calls through to that method.
-     * 
+     *
+     * <p>Note: This method will be called immediately after {@link #onCreate(Bundle)}.
+     * Often instrumentation tests start their test thread in onCreate(); you
+     * need to be careful of races between these.  (Well between it and
+     * everything else, but let's start here.)
+     *
      * @param app The application being created.
      */
     public void callApplicationOnCreate(Application app) {
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 6eebed2..be8108c 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -145,8 +145,10 @@
             try {
                 IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(),
                         null);
-                session = new RestoreSession(mContext, binder);
-                result = session.restorePackage(mContext.getPackageName(), observer);
+                if (binder != null) {
+                    session = new RestoreSession(mContext, binder);
+                    result = session.restorePackage(mContext.getPackageName(), observer);
+                }
             } catch (RemoteException e) {
                 Log.w(TAG, "restoreSelf() unable to contact service");
             } finally {
@@ -170,7 +172,9 @@
             try {
                 // All packages, current transport
                 IRestoreSession binder = sService.beginRestoreSession(null, null);
-                session = new RestoreSession(mContext, binder);
+                if (binder != null) {
+                    session = new RestoreSession(mContext, binder);
+                }
             } catch (RemoteException e) {
                 Log.w(TAG, "beginRestoreSession() couldn't connect");
             }
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 423f1f6..5c315ce 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -20,6 +20,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.DeadObjectException;
 import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.os.ParcelFileDescriptor;
@@ -33,11 +34,19 @@
  * calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
  * using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
  * no longer needed and can be killed to free up resources.
+ *
+ * <p>Note that you should generally create a new ContentProviderClient instance
+ * for each thread that will be performing operations.  Unlike
+ * {@link ContentResolver}, the methods here such as {@link #query} and
+ * {@link #openFile} are not thread safe -- you must not call
+ * {@link #release()} on the ContentProviderClient those calls are made from
+ * until you are finished with the data they have returned.
  */
 public class ContentProviderClient {
     private final IContentProvider mContentProvider;
     private final ContentResolver mContentResolver;
     private final boolean mStable;
+    private boolean mReleased;
 
     /**
      * @hide
@@ -52,7 +61,14 @@
     /** See {@link ContentProvider#query ContentProvider.query} */
     public Cursor query(Uri url, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) throws RemoteException {
-        return query(url, projection, selection,  selectionArgs, sortOrder, null);
+        try {
+            return query(url, projection, selection,  selectionArgs, sortOrder, null);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#query ContentProvider.query} */
@@ -64,41 +80,90 @@
             remoteCancellationSignal = mContentProvider.createCancellationSignal();
             cancellationSignal.setRemote(remoteCancellationSignal);
         }
-        return mContentProvider.query(url, projection, selection,  selectionArgs, sortOrder,
-                remoteCancellationSignal);
+        try {
+            return mContentProvider.query(url, projection, selection,  selectionArgs, sortOrder,
+                    remoteCancellationSignal);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#getType ContentProvider.getType} */
     public String getType(Uri url) throws RemoteException {
-        return mContentProvider.getType(url);
+        try {
+            return mContentProvider.getType(url);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
     public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
-        return mContentProvider.getStreamTypes(url, mimeTypeFilter);
+        try {
+            return mContentProvider.getStreamTypes(url, mimeTypeFilter);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#insert ContentProvider.insert} */
     public Uri insert(Uri url, ContentValues initialValues)
             throws RemoteException {
-        return mContentProvider.insert(url, initialValues);
+        try {
+            return mContentProvider.insert(url, initialValues);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
     public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
-        return mContentProvider.bulkInsert(url, initialValues);
+        try {
+            return mContentProvider.bulkInsert(url, initialValues);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#delete ContentProvider.delete} */
     public int delete(Uri url, String selection, String[] selectionArgs)
             throws RemoteException {
-        return mContentProvider.delete(url, selection, selectionArgs);
+        try {
+            return mContentProvider.delete(url, selection, selectionArgs);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#update ContentProvider.update} */
     public int update(Uri url, ContentValues values, String selection,
             String[] selectionArgs) throws RemoteException {
-        return mContentProvider.update(url, values, selection, selectionArgs);
+        try {
+            return mContentProvider.update(url, values, selection, selectionArgs);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /**
@@ -110,7 +175,14 @@
      */
     public ParcelFileDescriptor openFile(Uri url, String mode)
             throws RemoteException, FileNotFoundException {
-        return mContentProvider.openFile(url, mode);
+        try {
+            return mContentProvider.openFile(url, mode);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /**
@@ -122,20 +194,41 @@
      */
     public AssetFileDescriptor openAssetFile(Uri url, String mode)
             throws RemoteException, FileNotFoundException {
-        return mContentProvider.openAssetFile(url, mode);
+        try {
+            return mContentProvider.openAssetFile(url, mode);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
     public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
             String mimeType, Bundle opts)
             throws RemoteException, FileNotFoundException {
-        return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
+        try {
+            return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException {
-        return mContentProvider.applyBatch(operations);
+        try {
+            return mContentProvider.applyBatch(operations);
+        } catch (DeadObjectException e) {
+            if (!mStable) {
+                mContentResolver.unstableProviderDied(mContentProvider);
+            }
+            throw e;
+        }
     }
 
     /**
@@ -144,10 +237,16 @@
      * @return true if this was release, false if it was already released
      */
     public boolean release() {
-        if (mStable) {
-            return mContentResolver.releaseProvider(mContentProvider);
-        } else {
-            return mContentResolver.releaseUnstableProvider(mContentProvider);
+        synchronized (this) {
+            if (mReleased) {
+                throw new IllegalStateException("Already released");
+            }
+            mReleased = true;
+            if (mStable) {
+                return mContentResolver.releaseProvider(mContentProvider);
+            } else {
+                return mContentResolver.releaseUnstableProvider(mContentProvider);
+            }
         }
     }
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f509fd8..34b5a30 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -20,27 +20,24 @@
 
 import android.accounts.Account;
 import android.app.ActivityManagerNative;
-import android.app.ActivityThread;
 import android.app.AppGlobals;
-import android.content.ContentProvider.Transport;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.CrossProcessCursorWrapper;
 import android.database.Cursor;
-import android.database.CursorWrapper;
 import android.database.IContentObserver;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.OperationCanceledException;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.StrictMode;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.EventLog;
@@ -202,6 +199,8 @@
     protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
     /** @hide */
     public abstract boolean releaseUnstableProvider(IContentProvider icp);
+    /** @hide */
+    public abstract void unstableProviderDied(IContentProvider icp);
 
     /**
      * Return the MIME type of the given content URL.
@@ -211,6 +210,7 @@
      * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
      */
     public final String getType(Uri url) {
+        // XXX would like to have an acquireExistingUnstableProvider for this.
         IContentProvider provider = acquireExistingProvider(url);
         if (provider != null) {
             try {
@@ -351,23 +351,37 @@
     public final Cursor query(final Uri uri, String[] projection,
             String selection, String[] selectionArgs, String sortOrder,
             CancellationSignal cancellationSignal) {
-        IContentProvider provider = acquireProvider(uri);
-        if (provider == null) {
+        IContentProvider unstableProvider = acquireUnstableProvider(uri);
+        if (unstableProvider == null) {
             return null;
         }
+        IContentProvider stableProvider = null;
         try {
             long startTime = SystemClock.uptimeMillis();
 
             ICancellationSignal remoteCancellationSignal = null;
             if (cancellationSignal != null) {
                 cancellationSignal.throwIfCanceled();
-                remoteCancellationSignal = provider.createCancellationSignal();
+                remoteCancellationSignal = unstableProvider.createCancellationSignal();
                 cancellationSignal.setRemote(remoteCancellationSignal);
             }
-            Cursor qCursor = provider.query(uri, projection,
-                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
+            Cursor qCursor;
+            try {
+                qCursor = unstableProvider.query(uri, projection,
+                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
+            } catch (DeadObjectException e) {
+                // The remote process has died...  but we only hold an unstable
+                // reference though, so we might recover!!!  Let's try!!!!
+                // This is exciting!!1!!1!!!!1
+                unstableProviderDied(unstableProvider);
+                stableProvider = acquireProvider(uri);
+                if (stableProvider == null) {
+                    return null;
+                }
+                qCursor = stableProvider.query(uri, projection,
+                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
+            }
             if (qCursor == null) {
-                releaseProvider(provider);
                 return null;
             }
             // force query execution
@@ -375,16 +389,21 @@
             long durationMillis = SystemClock.uptimeMillis() - startTime;
             maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
             // Wrap the cursor object into CursorWrapperInner object
-            return new CursorWrapperInner(qCursor, provider);
+            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
+                    stableProvider != null ? stableProvider : acquireProvider(uri));
+            stableProvider = null;
+            return wrapper;
         } catch (RemoteException e) {
-            releaseProvider(provider);
-
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
             return null;
-        } catch (RuntimeException e) {
-            releaseProvider(provider);
-            throw e;
+        } finally {
+            if (unstableProvider != null) {
+                releaseUnstableProvider(unstableProvider);
+            }
+            if (stableProvider != null) {
+                releaseProvider(stableProvider);
+            }
         }
     }
 
@@ -592,49 +611,63 @@
             if ("r".equals(mode)) {
                 return openTypedAssetFileDescriptor(uri, "*/*", null);
             } else {
-                int n = 0;
-                while (true) {
-                    n++;
-                    IContentProvider provider = acquireUnstableProvider(uri);
-                    if (provider == null) {
-                        throw new FileNotFoundException("No content provider: " + uri);
-                    }
+                IContentProvider unstableProvider = acquireUnstableProvider(uri);
+                if (unstableProvider == null) {
+                    throw new FileNotFoundException("No content provider: " + uri);
+                }
+                IContentProvider stableProvider = null;
+                AssetFileDescriptor fd = null;
+
+                try {
                     try {
-                        AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
+                        fd = unstableProvider.openAssetFile(uri, mode);
                         if (fd == null) {
                             // The provider will be released by the finally{} clause
                             return null;
                         }
-                        ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
-                                fd.getParcelFileDescriptor(), provider);
-
-                        // Success!  Don't release the provider when exiting, let
-                        // ParcelFileDescriptorInner do that when it is closed.
-                        provider = null;
-
-                        return new AssetFileDescriptor(pfd, fd.getStartOffset(),
-                                fd.getDeclaredLength());
-                    } catch (RemoteException e) {
-                        // The provider died for some reason.  Since we are
-                        // acquiring it unstable, its process could have gotten
-                        // killed and need to be restarted.  We'll retry a couple
-                        // times and if still can't succeed then fail.
-                        if (n <= 2) {
-                            try {
-                                Thread.sleep(100);
-                            } catch (InterruptedException e1) {
-                            }
-                            continue;
+                    } catch (DeadObjectException e) {
+                        // The remote process has died...  but we only hold an unstable
+                        // reference though, so we might recover!!!  Let's try!!!!
+                        // This is exciting!!1!!1!!!!1
+                        unstableProviderDied(unstableProvider);
+                        stableProvider = acquireProvider(uri);
+                        if (stableProvider == null) {
+                            throw new FileNotFoundException("No content provider: " + uri);
                         }
-                        // Whatever, whatever, we'll go away.
-                        throw new FileNotFoundException("Dead content provider: " + uri);
-                    } catch (FileNotFoundException e) {
-                        throw e;
-                    } finally {
-                        if (provider != null) {
-                            releaseUnstableProvider(provider);
+                        fd = stableProvider.openAssetFile(uri, mode);
+                        if (fd == null) {
+                            // The provider will be released by the finally{} clause
+                            return null;
                         }
                     }
+
+                    if (stableProvider == null) {
+                        stableProvider = acquireProvider(uri);
+                    }
+                    releaseUnstableProvider(unstableProvider);
+                    ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+                            fd.getParcelFileDescriptor(), stableProvider);
+
+                    // Success!  Don't release the provider when exiting, let
+                    // ParcelFileDescriptorInner do that when it is closed.
+                    stableProvider = null;
+
+                    return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+                            fd.getDeclaredLength());
+
+                } catch (RemoteException e) {
+                    // Whatever, whatever, we'll go away.
+                    throw new FileNotFoundException(
+                            "Failed opening content provider: " + uri);
+                } catch (FileNotFoundException e) {
+                    throw e;
+                } finally {
+                    if (stableProvider != null) {
+                        releaseProvider(stableProvider);
+                    }
+                    if (unstableProvider != null) {
+                        releaseUnstableProvider(unstableProvider);
+                    }
                 }
             }
         }
@@ -670,49 +703,63 @@
      */
     public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
             String mimeType, Bundle opts) throws FileNotFoundException {
-        int n = 0;
-        while (true) {
-            n++;
-            IContentProvider provider = acquireUnstableProvider(uri);
-            if (provider == null) {
-                throw new FileNotFoundException("No content provider: " + uri);
-            }
+        IContentProvider unstableProvider = acquireUnstableProvider(uri);
+        if (unstableProvider == null) {
+            throw new FileNotFoundException("No content provider: " + uri);
+        }
+        IContentProvider stableProvider = null;
+        AssetFileDescriptor fd = null;
+
+        try {
             try {
-                AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
+                fd = unstableProvider.openTypedAssetFile(uri, mimeType, opts);
                 if (fd == null) {
                     // The provider will be released by the finally{} clause
                     return null;
                 }
-                ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
-                        fd.getParcelFileDescriptor(), provider);
-
-                // Success!  Don't release the provider when exiting, let
-                // ParcelFileDescriptorInner do that when it is closed.
-                provider = null;
-
-                return new AssetFileDescriptor(pfd, fd.getStartOffset(),
-                        fd.getDeclaredLength());
-            } catch (RemoteException e) {
-                // The provider died for some reason.  Since we are
-                // acquiring it unstable, its process could have gotten
-                // killed and need to be restarted.  We'll retry a couple
-                // times and if still can't succeed then fail.
-                if (n <= 2) {
-                    try {
-                        Thread.sleep(100);
-                    } catch (InterruptedException e1) {
-                    }
-                    continue;
+            } catch (DeadObjectException e) {
+                // The remote process has died...  but we only hold an unstable
+                // reference though, so we might recover!!!  Let's try!!!!
+                // This is exciting!!1!!1!!!!1
+                unstableProviderDied(unstableProvider);
+                stableProvider = acquireProvider(uri);
+                if (stableProvider == null) {
+                    throw new FileNotFoundException("No content provider: " + uri);
                 }
-                // Whatever, whatever, we'll go away.
-                throw new FileNotFoundException("Dead content provider: " + uri);
-            } catch (FileNotFoundException e) {
-                throw e;
-            } finally {
-                if (provider != null) {
-                    releaseUnstableProvider(provider);
+                fd = stableProvider.openTypedAssetFile(uri, mimeType, opts);
+                if (fd == null) {
+                    // The provider will be released by the finally{} clause
+                    return null;
                 }
             }
+
+            if (stableProvider == null) {
+                stableProvider = acquireProvider(uri);
+            }
+            releaseUnstableProvider(unstableProvider);
+            ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+                    fd.getParcelFileDescriptor(), stableProvider);
+
+            // Success!  Don't release the provider when exiting, let
+            // ParcelFileDescriptorInner do that when it is closed.
+            stableProvider = null;
+
+            return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+                    fd.getDeclaredLength());
+
+        } catch (RemoteException e) {
+            // Whatever, whatever, we'll go away.
+            throw new FileNotFoundException(
+                    "Failed opening content provider: " + uri);
+        } catch (FileNotFoundException e) {
+            throw e;
+        } finally {
+            if (stableProvider != null) {
+                releaseProvider(stableProvider);
+            }
+            if (unstableProvider != null) {
+                releaseUnstableProvider(unstableProvider);
+            }
         }
     }
 
@@ -1061,7 +1108,7 @@
         if (name == null) {
             return null;
         }
-        return acquireProvider(mContext, name);
+        return acquireUnstableProvider(mContext, name);
     }
 
     /**
@@ -1113,10 +1160,15 @@
      * use it as needed and it won't disappear, even if your process is in the
      * background.  If using this method, you need to take care to deal with any
      * failures when communicating with the provider, and be sure to close it
-     * so that it can be re-opened later.
+     * so that it can be re-opened later.  In particular, catching a
+     * {@link android.os.DeadObjectException} from the calls there will let you
+     * know that the content provider has gone away; at that point the current
+     * ContentProviderClient object is invalid, and you should release it.  You
+     * can acquire a new one if you would like to try to restart the provider
+     * and perform new operations on it.
      */
     public final ContentProviderClient acquireUnstableContentProviderClient(Uri uri) {
-        IContentProvider provider = acquireProvider(uri);
+        IContentProvider provider = acquireUnstableProvider(uri);
         if (provider != null) {
             return new ContentProviderClient(this, provider, false);
         }
@@ -1133,10 +1185,15 @@
      * use it as needed and it won't disappear, even if your process is in the
      * background.  If using this method, you need to take care to deal with any
      * failures when communicating with the provider, and be sure to close it
-     * so that it can be re-opened later.
+     * so that it can be re-opened later.  In particular, catching a
+     * {@link android.os.DeadObjectException} from the calls there will let you
+     * know that the content provider has gone away; at that point the current
+     * ContentProviderClient object is invalid, and you should release it.  You
+     * can acquire a new one if you would like to try to restart the provider
+     * and perform new operations on it.
      */
     public final ContentProviderClient acquireUnstableContentProviderClient(String name) {
-        IContentProvider provider = acquireProvider(name);
+        IContentProvider provider = acquireUnstableProvider(name);
         if (provider != null) {
             return new ContentProviderClient(this, provider, false);
         }
@@ -1780,7 +1837,6 @@
 
     private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
         private final IContentProvider mContentProvider;
-        public static final String TAG="ParcelFileDescriptorInner";
         private boolean mReleaseProviderFlag = false;
 
         ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
@@ -1792,7 +1848,7 @@
         public void close() throws IOException {
             if(!mReleaseProviderFlag) {
                 super.close();
-                ContentResolver.this.releaseUnstableProvider(mContentProvider);
+                ContentResolver.this.releaseProvider(mContentProvider);
                 mReleaseProviderFlag = true;
             }
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8cb5c85..6421068 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2086,7 +2086,8 @@
     // Accessiblity constants for mPrivateFlags2
 
     /**
-     * Shift for accessibility related bits in {@link #mPrivateFlags2}.
+     * Shift for the bits in {@link #mPrivateFlags2} related to the
+     * "importantForAccessibility" attribute.
      */
     static final int IMPORTANT_FOR_ACCESSIBILITY_SHIFT = 20;
 
@@ -2142,6 +2143,72 @@
      */
     static final int VIEW_QUICK_REJECTED = 0x20000000;
 
+    // Accessiblity constants for mPrivateFlags2
+
+    /**
+     * Shift for the bits in {@link #mPrivateFlags2} related to the
+     * "accessibilityFocusable" attribute.
+     */
+    static final int ACCESSIBILITY_FOCUSABLE_SHIFT = 30;
+
+    /**
+     * The system determines whether the view can take accessibility focus - default (recommended).
+     * <p>
+     * Such a view is consideted by the focus search if it is:
+     * <ul>
+     * <li>
+     * Important for accessibility and actionable (clickable, long clickable, focusable)
+     * </li>
+     * <li>
+     * Important for accessibility, not actionable (clickable, long clickable, focusable),
+     * and does not have an actionable predecessor.
+     * </li>
+     * </ul>
+     * An accessibility srvice can request putting accessibility focus on such a view.
+     * </p>
+     *
+     * @hide
+     */
+    public static final int ACCESSIBILITY_FOCUSABLE_AUTO = 0x00000000;
+
+    /**
+     * The view can take accessibility focus.
+     * <p>
+     * A view that can take accessibility focus is always considered during focus
+     * search and an accessibility service can request putting accessibility focus
+     * on it.
+     * </p>
+     *
+     * @hide
+     */
+    public static final int ACCESSIBILITY_FOCUSABLE_YES = 0x00000001;
+
+    /**
+     * The view can not take accessibility focus.
+     * <p>
+     * A view that can not take accessibility focus is never considered during focus
+     * search and an accessibility service can not request putting accessibility focus
+     * on it.
+     * </p>
+     *
+     * @hide
+     */
+    public static final int ACCESSIBILITY_FOCUSABLE_NO = 0x00000002;
+
+    /**
+     * The default whether the view is accessiblity focusable.
+     */
+    static final int ACCESSIBILITY_FOCUSABLE_DEFAULT = ACCESSIBILITY_FOCUSABLE_AUTO;
+
+    /**
+     * Mask for obtainig the bits which specifies how to determine
+     * whether a view is accessibility focusable.
+     */
+    static final int ACCESSIBILITY_FOCUSABLE_MASK = (ACCESSIBILITY_FOCUSABLE_AUTO
+        | ACCESSIBILITY_FOCUSABLE_YES | ACCESSIBILITY_FOCUSABLE_NO)
+        << ACCESSIBILITY_FOCUSABLE_SHIFT;
+
+
     /* End of masks for mPrivateFlags2 */
 
     static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
@@ -3132,7 +3199,8 @@
         mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
                 (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
                 (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT) |
-                (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << IMPORTANT_FOR_ACCESSIBILITY_SHIFT);
+                (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << IMPORTANT_FOR_ACCESSIBILITY_SHIFT) |
+                (ACCESSIBILITY_FOCUSABLE_DEFAULT << ACCESSIBILITY_FOCUSABLE_SHIFT);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
         mUserPaddingStart = -1;
@@ -4788,7 +4856,10 @@
         }
 
         if (!isAccessibilityFocused()) {
-            info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+            final int mode = getAccessibilityFocusable();
+            if (mode == ACCESSIBILITY_FOCUSABLE_YES || mode == ACCESSIBILITY_FOCUSABLE_AUTO) {
+                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+            }
         } else {
             info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
         }
@@ -6069,7 +6140,7 @@
             return;
         }
         if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
-            if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) {
+            if (isAccessibilityFocusable()) {
                 views.add(this);
                 return;
             }
@@ -6403,12 +6474,9 @@
      * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
      */
     @ViewDebug.ExportedProperty(category = "accessibility", mapping = {
-            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_AUTO,
-                    to = "IMPORTANT_FOR_ACCESSIBILITY_AUTO"),
-            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_YES,
-                    to = "IMPORTANT_FOR_ACCESSIBILITY_YES"),
-            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO,
-                    to = "IMPORTANT_FOR_ACCESSIBILITY_NO")
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_AUTO, to = "auto"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_YES, to = "yes"),
+            @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO, to = "no")
         })
     public int getImportantForAccessibility() {
         return (mPrivateFlags2 & IMPORTANT_FOR_ACCESSIBILITY_MASK)
@@ -6461,6 +6529,73 @@
     }
 
     /**
+     * Gets the mode for determining whether this View can take accessibility focus.
+     *
+     * @return The mode for determining whether a View can take accessibility focus.
+     *
+     * @attr ref android.R.styleable#View_accessibilityFocusable
+     *
+     * @see #ACCESSIBILITY_FOCUSABLE_YES
+     * @see #ACCESSIBILITY_FOCUSABLE_NO
+     * @see #ACCESSIBILITY_FOCUSABLE_AUTO
+     *
+     * @hide
+     */
+    @ViewDebug.ExportedProperty(category = "accessibility", mapping = {
+            @ViewDebug.IntToString(from = ACCESSIBILITY_FOCUSABLE_AUTO, to = "auto"),
+            @ViewDebug.IntToString(from = ACCESSIBILITY_FOCUSABLE_YES, to = "yes"),
+            @ViewDebug.IntToString(from = ACCESSIBILITY_FOCUSABLE_NO, to = "no")
+        })
+    public int getAccessibilityFocusable() {
+        return (mPrivateFlags2 & ACCESSIBILITY_FOCUSABLE_MASK) >>> ACCESSIBILITY_FOCUSABLE_SHIFT;
+    }
+
+    /**
+     * Sets how to determine whether this view can take accessibility focus.
+     *
+     * @param mode How to determine whether this view can take accessibility focus.
+     *
+     * @attr ref android.R.styleable#View_accessibilityFocusable
+     *
+     * @see #ACCESSIBILITY_FOCUSABLE_YES
+     * @see #ACCESSIBILITY_FOCUSABLE_NO
+     * @see #ACCESSIBILITY_FOCUSABLE_AUTO
+     *
+     * @hide
+     */
+    public void setAccessibilityFocusable(int mode) {
+        if (mode != getAccessibilityFocusable()) {
+            mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSABLE_MASK;
+            mPrivateFlags2 |= (mode << ACCESSIBILITY_FOCUSABLE_SHIFT)
+                    & ACCESSIBILITY_FOCUSABLE_MASK;
+            notifyAccessibilityStateChanged();
+        }
+    }
+
+    /**
+     * Gets whether this view can take accessibility focus.
+     *
+     * @return Whether the view can take accessibility focus.
+     *
+     * @hide
+     */
+    public boolean isAccessibilityFocusable() {
+        final int mode = (mPrivateFlags2 & ACCESSIBILITY_FOCUSABLE_MASK)
+                >>> ACCESSIBILITY_FOCUSABLE_SHIFT;
+        switch (mode) {
+            case ACCESSIBILITY_FOCUSABLE_YES:
+                return true;
+            case ACCESSIBILITY_FOCUSABLE_NO:
+                return false;
+            case ACCESSIBILITY_FOCUSABLE_AUTO:
+                return canTakeAccessibilityFocusFromHover()
+                        || getAccessibilityNodeProvider() != null;
+            default:
+                throw new IllegalArgumentException("Unknow accessibility focusable mode: " + mode);
+        }
+    }
+
+    /**
      * Gets the parent for accessibility purposes. Note that the parent for
      * accessibility is not necessary the immediate parent. It is the first
      * predecessor that is important for accessibility.
@@ -6641,7 +6776,10 @@
                 }
             } break;
             case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
-                if (!isAccessibilityFocused()) {
+                final int mode = getAccessibilityFocusable();
+                if (!isAccessibilityFocused()
+                        && (mode == ACCESSIBILITY_FOCUSABLE_YES
+                                || mode == ACCESSIBILITY_FOCUSABLE_AUTO)) {
                     return requestAccessibilityFocus();
                 }
             } break;
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 8cd63ef..f9ef3c5 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -27,6 +28,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.os.Vibrator;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -52,10 +54,11 @@
 
     // Wave state machine
     private static final int STATE_IDLE = 0;
-    private static final int STATE_FIRST_TOUCH = 1;
-    private static final int STATE_TRACKING = 2;
-    private static final int STATE_SNAP = 3;
-    private static final int STATE_FINISH = 4;
+    private static final int STATE_START = 1;
+    private static final int STATE_FIRST_TOUCH = 2;
+    private static final int STATE_TRACKING = 3;
+    private static final int STATE_SNAP = 4;
+    private static final int STATE_FINISH = 5;
 
     // Animation properties.
     private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
@@ -74,17 +77,18 @@
     private static final int CHEVRON_INCREMENTAL_DELAY = 160;
     private static final int CHEVRON_ANIMATION_DURATION = 850;
     private static final int RETURN_TO_HOME_DELAY = 1200;
-    private static final int RETURN_TO_HOME_DURATION = 300;
+    private static final int RETURN_TO_HOME_DURATION = 200;
     private static final int HIDE_ANIMATION_DELAY = 200;
     private static final int HIDE_ANIMATION_DURATION = 200;
     private static final int SHOW_ANIMATION_DURATION = 200;
     private static final int SHOW_ANIMATION_DELAY = 50;
+    private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
+
     private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
-    private static final float TARGET_SCALE_SELECTED = 0.8f;
-    private static final long INITIAL_SHOW_HANDLE_DURATION = 200;
-    private static final float TARGET_SCALE_UNSELECTED = 1.0f;
-    private static final float RING_SCALE_UNSELECTED = 0.5f;
-    private static final float RING_SCALE_SELECTED = 1.5f;
+    private static final float TARGET_SCALE_EXPANDED = 1.0f;
+    private static final float TARGET_SCALE_COLLAPSED = 0.8f;
+    private static final float RING_SCALE_EXPANDED = 1.0f;
+    private static final float RING_SCALE_COLLAPSED = 0.5f;
 
     private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;
 
@@ -182,7 +186,7 @@
             if (mNewTargetResources != 0) {
                 internalSetTargetResources(mNewTargetResources);
                 mNewTargetResources = 0;
-                hideTargets(false);
+                hideTargets(false, false);
             }
             mAnimatingTargets = false;
         }
@@ -195,6 +199,7 @@
     private int mVerticalInset;
     private int mGravity = Gravity.TOP;
     private boolean mInitialLayout = true;
+    private Tweener mBackgroundAnimator;
 
     public MultiWaveView(Context context) {
         this(context, null);
@@ -358,14 +363,21 @@
         switch (state) {
             case STATE_IDLE:
                 deactivateTargets();
+                hideTargets(true, false);
+                startBackgroundAnimation(0, 0.0f);
                 mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
                 break;
 
+            case STATE_START:
+                deactivateHandle(0, 0, 1.0f, null);
+                startBackgroundAnimation(0, 0.0f);
+                break;
+
             case STATE_FIRST_TOUCH:
-                stopHandleAnimation();
                 deactivateTargets();
                 showTargets(true);
-                activateHandle();
+                mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
+                startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
                 setGrabbedState(OnTriggerListener.CENTER_HANDLE);
                 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                     announceTargets();
@@ -384,17 +396,29 @@
         }
     }
 
-    private void activateHandle() {
-        mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE);
-        if (mAlwaysTrackFinger) {
-            mHandleAnimations.stop();
-            mHandleDrawable.setAlpha(0.0f);
-            mHandleAnimations.add(Tweener.to(mHandleDrawable, INITIAL_SHOW_HANDLE_DURATION,
-                    "ease", Ease.Cubic.easeIn,
-                    "alpha", 1.0f,
-                    "onUpdate", mUpdateListener));
-            mHandleAnimations.start();
-        }
+    private void activateHandle(int duration, int delay, float finalAlpha,
+            AnimatorListener finishListener) {
+        mHandleAnimations.cancel();
+        mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
+                "ease", Ease.Cubic.easeIn,
+                "delay", delay,
+                "alpha", finalAlpha,
+                "onUpdate", mUpdateListener,
+                "onComplete", finishListener));
+        mHandleAnimations.start();
+    }
+
+    private void deactivateHandle(int duration, int delay, float finalAlpha,
+            AnimatorListener finishListener) {
+        mHandleAnimations.cancel();
+        mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
+            "ease", Ease.Quart.easeOut,
+            "delay", delay,
+            "alpha", finalAlpha,
+            "x", 0,
+            "y", 0,
+            "onUpdate", mUpdateListener,
+            "onComplete", finishListener));
     }
 
     /**
@@ -441,14 +465,6 @@
         mChevronAnimations.start();
     }
 
-    private void stopChevronAnimation() {
-        mChevronAnimations.stop();
-    }
-
-    private void stopHandleAnimation() {
-        mHandleAnimations.stop();
-    }
-
     private void deactivateTargets() {
         final int count = mTargetDrawables.size();
         for (int i = 0; i < count; i++) {
@@ -493,39 +509,33 @@
 
     private void doFinish() {
         final int activeTarget = mActiveTarget;
-        boolean targetHit =  activeTarget != -1;
+        final boolean targetHit =  activeTarget != -1;
 
-        // Hide unselected targets
-        hideTargets(true);
-
-        // Highlight the selected one
-        mHandleAnimations.cancel();
         if (targetHit) {
-            mHandleDrawable.setAlpha(0.0f);
-            mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
-            hideUnselected(activeTarget);
+            if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
+
+            highlightSelected(activeTarget);
 
             // Inform listener of any active targets.  Typically only one will be active.
-            if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
+            deactivateHandle(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
             dispatchTriggerEvent(activeTarget);
+        } else {
+            // Animate handle back to the center based on current state.
+            deactivateHandle(HIDE_ANIMATION_DURATION, HIDE_ANIMATION_DELAY, 1.0f,
+                    mResetListenerWithPing);
+            hideTargets(true, false);
+            mHandleAnimations.start();
         }
 
-        // Animate handle back to the center based on current state.
-        int delay = targetHit ? RETURN_TO_HOME_DELAY : 0;
-        int duration = RETURN_TO_HOME_DURATION;
-        mHandleAnimations.add(Tweener.to(mHandleDrawable, duration,
-                "ease", Ease.Quart.easeOut,
-                "delay", delay,
-                "alpha", mAlwaysTrackFinger ? 0.0f : 1.0f,
-                "x", 0,
-                "y", 0,
-                "onUpdate", mUpdateListener,
-                "onComplete", (mDragging && !targetHit) ? mResetListenerWithPing : mResetListener));
-        mHandleAnimations.start();
-
         setGrabbedState(OnTriggerListener.NO_HANDLE);
     }
 
+    private void highlightSelected(int activeTarget) {
+        // Highlight the given target and fade others
+        mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
+        hideUnselected(activeTarget);
+    }
+
     private void hideUnselected(int active) {
         for (int i = 0; i < mTargetDrawables.size(); i++) {
             if (i != active) {
@@ -535,16 +545,15 @@
         mOuterRing.setAlpha(0.0f);
     }
 
-    private void hideTargets(boolean animate) {
+    private void hideTargets(boolean animate, boolean expanded) {
         mTargetAnimations.cancel();
         // Note: these animations should complete at the same time so that we can swap out
         // the target assets asynchronously from the setTargetResources() call.
         mAnimatingTargets = animate;
         final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
         final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
-        final boolean targetSelected = mActiveTarget != -1;
 
-        final float targetScale = targetSelected ? TARGET_SCALE_SELECTED : TARGET_SCALE_UNSELECTED;
+        final float targetScale = expanded ? TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
         final int length = mTargetDrawables.size();
         for (int i = 0; i < length; i++) {
             TargetDrawable target = mTargetDrawables.get(i);
@@ -558,7 +567,7 @@
                     "onUpdate", mUpdateListener));
         }
 
-        final float ringScaleTarget = targetSelected ? RING_SCALE_SELECTED : RING_SCALE_UNSELECTED;
+        final float ringScaleTarget = expanded ? RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
         mTargetAnimations.add(Tweener.to(mOuterRing, duration,
                 "ease", Ease.Cubic.easeOut,
                 "alpha", 0.0f,
@@ -580,8 +589,6 @@
         for (int i = 0; i < length; i++) {
             TargetDrawable target = mTargetDrawables.get(i);
             target.setState(TargetDrawable.STATE_INACTIVE);
-            target.setScaleX(TARGET_SCALE_SELECTED);
-            target.setScaleY(TARGET_SCALE_SELECTED);
             mTargetAnimations.add(Tweener.to(target, duration,
                     "ease", Ease.Cubic.easeOut,
                     "alpha", 1.0f,
@@ -732,17 +739,30 @@
      * @param animate
      */
     public void reset(boolean animate) {
-        stopChevronAnimation();
-        stopHandleAnimation();
+        mChevronAnimations.stop();
+        mHandleAnimations.stop();
         mTargetAnimations.stop();
+        startBackgroundAnimation(0, 0.0f);
         hideChevrons();
-        hideTargets(animate);
-        mHandleDrawable.setX(0);
-        mHandleDrawable.setY(0);
-        mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
+        hideTargets(animate, false);
+        deactivateHandle(0, 0, 1.0f, null);
         Tweener.reset();
     }
 
+    private void startBackgroundAnimation(int duration, float alpha) {
+        Drawable background = getBackground();
+        if (mAlwaysTrackFinger && background != null) {
+            if (mBackgroundAnimator != null) {
+                mBackgroundAnimator.animator.end();
+            }
+            mBackgroundAnimator = Tweener.to(background, duration,
+                    "ease", Ease.Cubic.easeIn,
+                    "alpha", new int[] {0, (int)(255.0f * alpha)},
+                    "delay", SHOW_ANIMATION_DELAY);
+            mBackgroundAnimator.animator.start();
+        }
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getAction();
@@ -784,7 +804,10 @@
     }
 
     private void handleDown(MotionEvent event) {
-        if (!trySwitchToFirstTouchState(event.getX(), event.getY())) {
+        float eventX = event.getX();
+        float eventY = event.getY();
+        switchToState(STATE_START, eventX, eventY);
+        if (!trySwitchToFirstTouchState(eventX, eventY)) {
             mDragging = false;
             mTargetAnimations.cancel();
             ping();
@@ -830,7 +853,9 @@
 
             if (!mDragging) {
                 trySwitchToFirstTouchState(eventX, eventY);
-            } else {
+            }
+
+            if (mDragging) {
                 if (singleTarget) {
                     // Snap to outer ring if there's only one target
                     float snapRadius = mOuterRadius - mSnapMargin;
@@ -865,17 +890,11 @@
         if (activeTarget != -1) {
             switchToState(STATE_SNAP, x,y);
             TargetDrawable target = targets.get(activeTarget);
-            float newX = singleTarget ? x : target.getX();
-            float newY = singleTarget ? y : target.getY();
+            final float newX = singleTarget ? x : target.getX();
+            final float newY = singleTarget ? y : target.getY();
             moveHandleTo(newX, newY, false);
-            mHandleAnimations.cancel();
-            mHandleDrawable.setAlpha(0.0f);
         } else {
             switchToState(STATE_TRACKING, x, y);
-            if (mActiveTarget != -1) {
-                mHandleAnimations.cancel();
-                mHandleDrawable.setAlpha(1.0f);
-            }
             moveHandleTo(x, y, false);
         }
 
@@ -900,6 +919,9 @@
                     String targetContentDescription = getTargetDescription(activeTarget);
                     announceText(targetContentDescription);
                 }
+                activateHandle(0, 0, 0.0f, null);
+            } else {
+                activateHandle(0, 0, 1.0f, null);
             }
         }
         mActiveTarget = activeTarget;
@@ -1021,7 +1043,7 @@
 
         if (mInitialLayout) {
             hideChevrons();
-            hideTargets(false);
+            hideTargets(false, false);
             moveHandleTo(0, 0, false);
             mInitialLayout = false;
         }
diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
index 1d502ba..d559d9d 100644
--- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java
+++ b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
@@ -83,6 +83,9 @@
             } else if (value instanceof float[]) {
                 props.add(PropertyValuesHolder.ofFloat(key,
                         ((float[])value)[0], ((float[])value)[1]));
+            } else if (value instanceof int[]) {
+                props.add(PropertyValuesHolder.ofInt(key,
+                        ((int[])value)[0], ((int[])value)[1]));
             } else if (value instanceof Number) {
                 float floatValue = ((Number)value).floatValue();
                 props.add(PropertyValuesHolder.ofFloat(key, floatValue));
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index c8bb77e..fb11af3 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"ጥያቄ አጥራ"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"ጥያቄ አስረክብ"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"የድምፅ ፍለጋ"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"በመንካት አስስ ይንቃ?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g>  ማሰስን በንኪ ማንቃት ይፈልጋል። አስስ በንኪ በሚበራበት ጊዜ፣ ከጡባዊ ተኮው ጋር ለመግባባት ምን በጣትህ ስር ወይም ምልክቶችን ማከናወን እንዳለብህ ማብራሪያ ልታይ ወይም ልትሰማ ትችላለህ።"</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g>  ማሰስን በንኪ ማንቃት ይፈልጋል። አስስ በንኪ በሚበራበት ጊዜ፣ ከስልኩ ጋር ለመግባባት ምን በጣትህ ስር ወይም ምልክቶችን ማከናወን እንዳለብህ ማብራሪያ ልታይ ወይም ልትሰማ ትችላለህ።"</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"ከ1 ወር በፊት"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"ከ1 ወር በፊት"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"የግቤት ስልቶችን አዘጋጅ"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"የሚዳሰስ የቁልፍ ሰሌዳ"</string>
     <string name="hardware" msgid="7517821086888990278">"ሃርድዌር"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"የቁልፍ ሰሌዳ አቀማመጥ ምረጥ"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"የቁልፍ ሰሌዳ አቀማመጥ ለመምረጥ ንካ።"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"ዕጩዎች"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"ማሰሺያን አስነሳ?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"ጥሪ ተቀበል?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ዘወትር"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"አንዴ ብቻ"</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 97876f6..4190bdb 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"Neteja la consulta"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Envia la consulta"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Cerca per veu"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Vols activar l\'Exploració per tacte?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'Exploració per tacte. Quan l\'Exploració per tacte està activada, pots escoltar o veure les descripcions del que hi ha sota el dit o fer gestos per interactuar amb la tauleta."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'Exploració per tacte. Quan l\'Exploració per tacte està activada, pots escoltar o veure les descripcions del que hi ha sota el dit o fer gestos per interactuar amb el telèfon."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"Fa 1 mes"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Fa menys d\'1 mes"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"Configura els mètodes d\'entrada"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"Teclat físic"</string>
     <string name="hardware" msgid="7517821086888990278">"Maquinari"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecciona una disposició de teclat"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toca per seleccionar una disposició de teclat."</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Vols iniciar el navegador?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vols acceptar la trucada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"Només una"</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index acd4bba..ad25a3e 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -145,7 +145,7 @@
     <string name="shutdown_progress" msgid="2281079257329981203">"Vypínání..."</string>
     <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Tablet se vypne."</string>
     <string name="shutdown_confirm" product="default" msgid="649792175242821353">"Váš telefon bude vypnut."</string>
-    <string name="shutdown_confirm_question" msgid="2906544768881136183">"Chcete vypnout telefon?"</string>
+    <string name="shutdown_confirm_question" msgid="2906544768881136183">"Chcete zařízení vypnout?"</string>
     <string name="reboot_safemode_title" msgid="7054509914500140361">"Restart v nouzovém režimu"</string>
     <string name="reboot_safemode_confirm" msgid="55293944502784668">"Chcete zařízení restartovat v nouzovém režimu? Deaktivujete tak veškeré nainstalované aplikace třetích stran. Po dalším restartu budou obnoveny."</string>
     <string name="recent_tasks_title" msgid="3691764623638127888">"Nejnovější"</string>
@@ -1205,7 +1205,7 @@
     <string name="throttled_notification_title" msgid="6269541897729781332">"Byl překročen limit mobilních dat"</string>
     <string name="throttled_notification_message" msgid="5443457321354907181">"Dotykem zobrazíte další informace o využití mobilních dat."</string>
     <string name="no_matches" msgid="8129421908915840737">"Žádné shody"</string>
-    <string name="find_on_page" msgid="1946799233822820384">"Vyhledat na stránce"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Hledat na stránce"</string>
   <plurals name="matches_found">
     <item quantity="one" msgid="8167147081136579439">"1 shoda"</item>
     <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> z <xliff:g id="TOTAL">%d</xliff:g>"</item>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c44f789..8cde21f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"Απαλοιφή ερωτήματος"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Υποβολή ερωτήματος"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Φωνητική αναζήτηση"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Ενεργοποίηση Αναζήτησης μέσω αφής;"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"Η υπηρεσία <xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> απαιτεί ενεργοποίηση της Εξερεύνησης μέσω αφής. Όταν είναι ενεργοποιημένη η Εξερεύνηση μέσω αφής, μπορείτε να δείτε ή να ακούσετε περιγραφές για τις επιλογές που βρίσκονται κάτω από το δάχτυλό σας ή να κάνετε κινήσεις αλληλεπίδρασης με το tablet σας."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"Η υπηρεσία <xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> απαιτεί ενεργοποίηση της Εξερεύνησης μέσω αφής. Όταν είναι ενεργοποιημένη η Εξερεύνηση μέσω αφής, μπορείτε να δείτε ή να ακούσετε περιγραφές για τις επιλογές που βρίσκονται κάτω από το δάχτυλό σας ή να κάνετε κινήσεις αλληλεπίδρασης με το τηλέφωνό σας."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"πριν από 1 μήνα"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Παλαιότερα από 1 μήνα"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"Ρύθμιση μεθόδων εισαγωγής"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"Φυσικό πληκτρολόγιο"</string>
     <string name="hardware" msgid="7517821086888990278">"Υλικό"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Επιλογή διάταξης πληκτρολογίου"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Αγγίξτε για να επιλέξετε διάταξη πληκτρολογίου."</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"υποψήφιοι"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση προγράμματος περιήγησης;"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Αποδοχή κλήσης;"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Πάντα"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"Μόνο μία φορά"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a54c479..4ad7daf 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"Clear query"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Submit query"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Voice search"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Enable Explore by Touch?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> wants to enable Explore by Touch. When Explore by Touch is turned on, you can hear or see descriptions of what\'s under your finger or perform gestures to interact with the tablet."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> wants to enable Explore by Touch. When Explore by Touch is turned on, you can hear or see descriptions of what\'s under your finger or perform gestures to interact with the phone."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"1 month ago"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Before 1 month ago"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"Set up input methods"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"Physical keyboard"</string>
     <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Select keyboard layout"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Touch to select a keyboard layout."</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidates"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Launch Browser?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Accept call?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Always"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"Just once"</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index f5ffece..f5d8795 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"Borrar consulta"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Enviar consulta"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Búsqueda por voz"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"¿Habilitar exploración táctil?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> quiere habilitar la exploración táctil. Cuando esta función esté activada, podrás escuchar o ver descripciones del contenido seleccionado o usar gestos para interactuar con el tablet."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> quiere habilitar la exploración táctil. Cuando esta función esté activada, podrás escuchar o ver descripciones del contenido seleccionado o usar gestos para interactuar con el teléfono."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"Hace un mes"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Hace más de un mes"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"Configurar métodos de introducción"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"Teclado físico"</string>
     <string name="hardware" msgid="7517821086888990278">"Hardware"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecciona un diseño de teclado"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toca para seleccionar un diseño de teclado."</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"¿Iniciar el navegador?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"¿Aceptar la llamada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Siempre"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"Solo una vez"</string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index ea57cf5..eaa7c94 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -318,7 +318,7 @@
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"seo sisestusmeetodiga"</string>
     <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lubab omanikul siduda sisestusmeetodi ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
     <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"sidumine juurdepääsuteenusega"</string>
-    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lubab omanikul luua sideme juurdepääsuteenuseteenuse ülataseme liidesega. Tavarakenduste puhul ei tohiks seda kunagi vaja minna."</string>
+    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lubab omanikul luua sideme juurdepääsuteenuse ülataseme liidesega. Tavarakenduste puhul ei tohiks seda kunagi vaja minna."</string>
     <string name="permlab_bindTextService" msgid="7358378401915287938">"tekstiteenusega sidumine"</string>
     <string name="permdesc_bindTextService" msgid="8151968910973998670">"Võimaldab omanikul siduda tekstiteenuse (nt SpellCheckerService) ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
     <string name="permlab_bindVpnService" msgid="4708596021161473255">"seo VPN-teenusega"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f249c2c..f0187ecd 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -606,30 +606,30 @@
     <item msgid="1735177144948329370">"Faks Rumah"</item>
     <item msgid="603878674477207394">"Pager"</item>
     <item msgid="1650824275177931637">"Lainnya"</item>
-    <item msgid="9192514806975898961">"Ubahsuaian"</item>
+    <item msgid="9192514806975898961">"Khusus"</item>
   </string-array>
   <string-array name="emailAddressTypes">
     <item msgid="8073994352956129127">"Rumah"</item>
     <item msgid="7084237356602625604">"Kantor"</item>
     <item msgid="1112044410659011023">"Lainnya"</item>
-    <item msgid="2374913952870110618">"Ubahsuaian"</item>
+    <item msgid="2374913952870110618">"Khusus"</item>
   </string-array>
   <string-array name="postalAddressTypes">
     <item msgid="6880257626740047286">"Rumah"</item>
     <item msgid="5629153956045109251">"Kantor"</item>
     <item msgid="4966604264500343469">"Lainnya"</item>
-    <item msgid="4932682847595299369">"Ubahsuaian"</item>
+    <item msgid="4932682847595299369">"Khusus"</item>
   </string-array>
   <string-array name="imAddressTypes">
     <item msgid="1738585194601476694">"Rumah"</item>
     <item msgid="1359644565647383708">"Kantor"</item>
     <item msgid="7868549401053615677">"Lainnya"</item>
-    <item msgid="3145118944639869809">"Ubahsuaian"</item>
+    <item msgid="3145118944639869809">"Khusus"</item>
   </string-array>
   <string-array name="organizationTypes">
     <item msgid="7546335612189115615">"Kantor"</item>
     <item msgid="4378074129049520373">"Lainnya"</item>
-    <item msgid="3455047468583965104">"Ubahsuaian"</item>
+    <item msgid="3455047468583965104">"Khusus"</item>
   </string-array>
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
@@ -641,7 +641,7 @@
     <item msgid="2506857312718630823">"ICQ"</item>
     <item msgid="1648797903785279353">"Jabber"</item>
   </string-array>
-    <string name="phoneTypeCustom" msgid="1644738059053355820">"Ubahsuaian"</string>
+    <string name="phoneTypeCustom" msgid="1644738059053355820">"Khusus"</string>
     <string name="phoneTypeHome" msgid="2570923463033985887">"Rumah"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"Seluler"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"Kantor"</string>
@@ -662,24 +662,24 @@
     <string name="phoneTypeWorkPager" msgid="649938731231157056">"Pager Kantor"</string>
     <string name="phoneTypeAssistant" msgid="5596772636128562884">"Asisten"</string>
     <string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
-    <string name="eventTypeCustom" msgid="7837586198458073404">"Ubahsuaian"</string>
+    <string name="eventTypeCustom" msgid="7837586198458073404">"Khusus"</string>
     <string name="eventTypeBirthday" msgid="2813379844211390740">"Hari Ulang Tahun"</string>
     <string name="eventTypeAnniversary" msgid="3876779744518284000">"Hari Peringatan"</string>
     <string name="eventTypeOther" msgid="7388178939010143077">"Lainnya"</string>
-    <string name="emailTypeCustom" msgid="8525960257804213846">"Ubahsuaian"</string>
+    <string name="emailTypeCustom" msgid="8525960257804213846">"Khusus"</string>
     <string name="emailTypeHome" msgid="449227236140433919">"Rumah"</string>
     <string name="emailTypeWork" msgid="3548058059601149973">"Kantor"</string>
     <string name="emailTypeOther" msgid="2923008695272639549">"Lainnya"</string>
     <string name="emailTypeMobile" msgid="119919005321166205">"Seluler"</string>
-    <string name="postalTypeCustom" msgid="8903206903060479902">"Ubahsuaian"</string>
+    <string name="postalTypeCustom" msgid="8903206903060479902">"Khusus"</string>
     <string name="postalTypeHome" msgid="8165756977184483097">"Rumah"</string>
     <string name="postalTypeWork" msgid="5268172772387694495">"Kantor"</string>
     <string name="postalTypeOther" msgid="2726111966623584341">"Lainnya"</string>
-    <string name="imTypeCustom" msgid="2074028755527826046">"Ubahsuaian"</string>
+    <string name="imTypeCustom" msgid="2074028755527826046">"Khusus"</string>
     <string name="imTypeHome" msgid="6241181032954263892">"Rumah"</string>
     <string name="imTypeWork" msgid="1371489290242433090">"Kantor"</string>
     <string name="imTypeOther" msgid="5377007495735915478">"Lainnya"</string>
-    <string name="imProtocolCustom" msgid="6919453836618749992">"Ubahsuaian"</string>
+    <string name="imProtocolCustom" msgid="6919453836618749992">"Khusus"</string>
     <string name="imProtocolAim" msgid="7050360612368383417">"AIM"</string>
     <string name="imProtocolMsn" msgid="144556545420769442">"Windows Live"</string>
     <string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
@@ -691,8 +691,8 @@
     <string name="imProtocolNetMeeting" msgid="8287625655986827971">"NetMeeting"</string>
     <string name="orgTypeWork" msgid="29268870505363872">"Kantor"</string>
     <string name="orgTypeOther" msgid="3951781131570124082">"Lainnya"</string>
-    <string name="orgTypeCustom" msgid="225523415372088322">"Ubahsuaian"</string>
-    <string name="relationTypeCustom" msgid="3542403679827297300">"Ubahsuaian"</string>
+    <string name="orgTypeCustom" msgid="225523415372088322">"Khusus"</string>
+    <string name="relationTypeCustom" msgid="3542403679827297300">"Khusus"</string>
     <string name="relationTypeAssistant" msgid="6274334825195379076">"Asisten"</string>
     <string name="relationTypeBrother" msgid="8757913506784067713">"Saudara laki-laki"</string>
     <string name="relationTypeChild" msgid="1890746277276881626">"Anak"</string>
@@ -707,7 +707,7 @@
     <string name="relationTypeRelative" msgid="1799819930085610271">"Sanak saudara"</string>
     <string name="relationTypeSister" msgid="1735983554479076481">"Saudara perempuan"</string>
     <string name="relationTypeSpouse" msgid="394136939428698117">"Pasangan"</string>
-    <string name="sipAddressTypeCustom" msgid="2473580593111590945">"Ubahsuaian"</string>
+    <string name="sipAddressTypeCustom" msgid="2473580593111590945">"Khusus"</string>
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Beranda"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Kerjaan"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Lainnya"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 65cb688..265da95 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"נקה שאילתה"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"שלח שאילתה"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"חיפוש קולי"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"האם להפעיל את התכונה \'חקור על ידי מגע\'?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> רוצה להפעיל את התכונה \'חקור על ידי מגע\'. כאשר התכונה \'חקור על ידי מגע\' מופעלת, אתה יכול לשמוע או לראות תיאורים של הפריטים שעליהם אצבעך מונחת או לקיים אינטראקציה עם הטאבלט באמצעות מחוות."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> רוצה להפעיל את התכונה \'חקור על ידי מגע\'. כאשר התכונה \'חקור על ידי מגע\' מופעלת, אתה יכול לשמוע או לראות תיאורים של הפריטים שעליהם אצבעך מונחת או לקיים אינטראקציה עם הטלפון באמצעות מחוות."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"לפני חודש אחד"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"לפני חודש אחד"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"הגדר שיטות קלט"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"מקלדת פיזית"</string>
     <string name="hardware" msgid="7517821086888990278">"חומרה"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"בחירת פריסת מקלדת"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"גע כדי לבחור פריסת מקלדת."</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"מועמדים"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"להפעיל את הדפדפן?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"האם לקבל את השיחה?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"תמיד"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"רק פעם אחת"</string>
 </resources>
diff --git a/core/res/res/values-large/config.xml b/core/res/res/values-large/config.xml
index 9327200..d1ec4ef 100644
--- a/core/res/res/values-large/config.xml
+++ b/core/res/res/values-large/config.xml
@@ -24,9 +24,6 @@
     <dimen name="config_prefDialogWidth">440dp</dimen>
 
     <!-- see comment in values/config.xml -->
-    <integer name="config_longPressOnPowerBehavior">2</integer>
-
-    <!-- see comment in values/config.xml -->
     <integer name="config_longPressOnHomeBehavior">0</integer>
 
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 90398a3..2e019d4 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -317,8 +317,8 @@
     <string name="permdesc_readInputState" msgid="8387754901688728043">"Programu omogoča spremljanje tipk, ki jih pritisnete med interakcijo z drugim programom (na primer vnos gesla). Navadni programi tega nikoli ne potrebujejo."</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"povezovanje z načinom vnosa"</string>
     <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"Lastniku omogoča, da se poveže z vmesnikom načina vnosa najvišje ravni. Tega nikoli ni treba uporabiti za navadne programe."</string>
-    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"vezano na storitev za ljudi s posebnimi potrebami"</string>
-    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lastniku omogoča povezovanje z vmesnikom storitve za ljudi s posebnimi potrebami najvišje ravni. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
+    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"povezovanje s storitvijo za ljudi s posebnimi potrebami"</string>
+    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"Lastniku omogoča povezovanje z vmesnikom najvišje ravni storitve za ljudi s posebnimi potrebami. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
     <string name="permlab_bindTextService" msgid="7358378401915287938">"poveži z besedilno storitvijo"</string>
     <string name="permdesc_bindTextService" msgid="8151968910973998670">"Dovoljuje, da se lastnik poveže z vmesnikom besedilne storitve najvišje ravni (npr. SpellCheckerService). Tega nikoli ni treba uporabiti za navadne programe."</string>
     <string name="permlab_bindVpnService" msgid="4708596021161473255">"povezava s storitvijo navideznega zasebnega omrežja"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 40345d1..35079c5 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"Futa swali"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"Wasilisha hoja"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Utafutaji wa sauti"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Wezesha Kuchunguza kwa Kugusa?"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> inataka kuwezesha Kuchunguza kwa Kugusa. Wakati Kuchunguza kwa Kugusa kumewezeshwa, unaweza kusikia au kuona maelezo ya ni nini kilichochini ya kidole chako au kufanya ishara za kuingiliana na kumpyuta ndogo."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> inataka kuwezesha Kuchunguza kwa Kugusa. Wakati Kuchunguza kwa Kugusa kumewezeshwa, unaweza kusikia au kuona maelezo ya ni nini kilichochini ya kidole chako au kufanya ishara za kuingiliana na simu."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"Mwezi 1 uliopita"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Kabla ya mwezi 1 uliopita"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"Weka mbinu za ingizo"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"Kibodi halisi"</string>
     <string name="hardware" msgid="7517821086888990278">"Maunzi"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Teua mpangilio wa kibodi"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Gusa kuchagua mpangilio wa kibodi."</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"wagombeaji"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Zindua Kivinjari?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Kubali simu?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Kila mara"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"Mara moja tu"</string>
 </resources>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index b54e9d1..1486d9c 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -20,9 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- see comment in values/config.xml -->
-    <integer name="config_longPressOnPowerBehavior">2</integer>
-
     <!-- Enable lockscreen rotation -->
     <bool name="config_enableLockScreenRotation">true</bool>
 
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 8de8e5e..34e4433 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -855,12 +855,9 @@
     <string name="searchview_description_clear" msgid="1330281990951833033">"ล้างข้อความค้นหา"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"ส่งข้อความค้นหา"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"ค้นหาด้วยเสียง"</string>
-    <!-- no translation found for enable_explore_by_touch_warning_title (7460694070309730149) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (8655887539089910577) -->
-    <skip />
-    <!-- no translation found for enable_explore_by_touch_warning_message (2708199672852373195) -->
-    <skip />
+    <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"เปิดใช้งาน \"สำรวจโดยการแตะ\" หรือไม่"</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ต้องการเปิดใช้งาน \"สำรวจโดยการแตะ\" เมื่อเปิดใช้งานแล้ว คุณสามารถฟังหรือดูคำอธิบายของสิ่งที่อยู่ใต้นิ้วข​​องคุณ หรือใช้ท่าทางสัมผัสต่างๆ เพื่อโต้ตอบกับแท็บเล็ตได้"</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ต้องการเปิดใช้งาน \"สำรวจโดยการแตะ\" เมื่อเปิดใช้งานแล้ว คุณสามารถฟังหรือดูคำอธิบายของสิ่งที่อยู่ใต้นิ้วข​​องคุณ หรือใช้ท่าทางสัมผัสต่างๆ เพื่อโต้ตอบกับโทรศัพท์ได้"</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"1 เดือนที่ผ่านมา"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"ก่อน 1 เดือนที่แล้ว"</string>
   <plurals name="num_seconds_ago">
@@ -1127,10 +1124,8 @@
     <string name="configure_input_methods" msgid="9091652157722495116">"ตั้งค่าวิธีการป้อนข้อมูล"</string>
     <string name="use_physical_keyboard" msgid="6203112478095117625">"แป้นพิมพ์บนเครื่อง"</string>
     <string name="hardware" msgid="7517821086888990278">"ฮาร์ดแวร์"</string>
-    <!-- no translation found for select_keyboard_layout_notification_title (1407367017263030773) -->
-    <skip />
-    <!-- no translation found for select_keyboard_layout_notification_message (4465907700449257063) -->
-    <skip />
+    <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"เลือกรูปแบบแป้นพิมพ์"</string>
+    <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"แตะเพื่อเลือกรูปแบบแป้นพิมพ์"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string>
     <string name="candidates_style" msgid="4333913089637062257"><u>"ตัวเลือก"</u></string>
@@ -1324,6 +1319,5 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"เปิดเบราว์เซอร์หรือไม่"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"รับสายหรือไม่"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"ทุกครั้ง"</string>
-    <!-- no translation found for activity_resolver_use_once (2404644797149173758) -->
-    <skip />
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"เฉพาะครั้งนี้"</string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 7cd2d9d..865761b 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -185,10 +185,10 @@
     <string name="permgroupdesc_affectsBattery" msgid="6441275320638916947">"使用耗电量较大的功能。"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"日历"</string>
     <string name="permgroupdesc_calendar" msgid="5777534316982184416">"直接访问日历和活动。"</string>
-    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"读取用户字典"</string>
-    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"读取用户字典中的字词。"</string>
-    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"写入用户字典"</string>
-    <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"将字词添加到用户字典。"</string>
+    <string name="permgrouplab_dictionary" msgid="4148597128843641379">"读取用户词典"</string>
+    <string name="permgroupdesc_dictionary" msgid="7921166355964764490">"读取用户词典中的字词。"</string>
+    <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"写入用户词典"</string>
+    <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"将字词添加到用户词典。"</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"书签和历史记录"</string>
     <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"直接访问书签和浏览器历史记录。"</string>
     <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"闹钟"</string>
@@ -211,7 +211,7 @@
     <string name="permgroupdesc_syncSettings" msgid="7603195265129031797">"访问同步设置。"</string>
     <string name="permgrouplab_accounts" msgid="3359646291125325519">"您的帐户"</string>
     <string name="permgroupdesc_accounts" msgid="4948732641827091312">"访问可用的帐户。"</string>
-    <string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"硬件控件"</string>
+    <string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"硬件控制"</string>
     <string name="permgroupdesc_hardwareControls" msgid="4357057861225462702">"直接访问手机上的硬件。"</string>
     <string name="permgrouplab_phoneCalls" msgid="9067173988325865923">"手机通话"</string>
     <string name="permgroupdesc_phoneCalls" msgid="7489701620446183770">"监管、记录和处理电话呼叫。"</string>
@@ -554,7 +554,7 @@
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"允许应用获取有关当前同步的 Feed 的详情。"</string>
     <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"写入订阅的供稿"</string>
     <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"允许应用修改您当前同步的 Feed。恶意应用可能会更改您的同步 Feed。"</string>
-    <string name="permlab_readDictionary" msgid="4107101525746035718">"读取您添加到字典的字词"</string>
+    <string name="permlab_readDictionary" msgid="4107101525746035718">"读取您添加到词典的字词"</string>
     <string name="permdesc_readDictionary" msgid="8977815988329283705">"允许应用读取用户可能在用户词典中已存储的任意私有字词、名称和短语。"</string>
     <string name="permlab_writeDictionary" msgid="2296383164914812772">"写入用户定义的词典"</string>
     <string name="permdesc_writeDictionary" msgid="8185385716255065291">"允许应用向用户词典中写入新词。"</string>
@@ -1111,7 +1111,7 @@
     <string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"如果您打开 USB 存储设备,您正在使用的某些应用将会停止,并且在您关闭 USB 存储设备前都将无法使用。"</string>
     <string name="dlg_error_title" msgid="7323658469626514207">"USB 操作失败"</string>
     <string name="dlg_ok" msgid="7376953167039865701">"确定"</string>
-    <string name="usb_mtp_notification_title" msgid="3699913097391550394">"作为媒体设备连接"</string>
+    <string name="usb_mtp_notification_title" msgid="3699913097391550394">"已作为媒体设备连接"</string>
     <string name="usb_ptp_notification_title" msgid="1960817192216064833">"作为相机连接"</string>
     <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"作为安装程序连接"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"已连接到 USB 配件"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2f540a5..e9a3385 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2136,7 +2136,8 @@
              query the screen. Note: While not recommended, an accessibility service may
              decide to ignore this attribute and operate on all views in the view tree. -->
         <attr name="importantForAccessibility" format="integer">
-            <!-- The system determines whether the view is important for accessibility (recommended). -->
+            <!-- The system determines whether the view is important for accessibility - default
+                 (recommended). -->
             <enum name="auto" value="0" />
             <!-- The view is important for accessibility. -->
             <enum name="yes" value="1" />
@@ -2144,6 +2145,40 @@
             <enum name="no" value="2" />
         </attr>
 
+        <!-- @hide Controls whether this view can take accessibility focus. -->
+        <attr name="accessibilityFocusable" format="integer">
+            <!-- The system determines whether the view can take accessibility focus - default
+                 (recommended).
+                 <p>
+                 Such a view is consideted by the focus search if it is:
+                 <ul>
+                 <li>
+                 Important for accessibility and actionable (clickable, long clickable, focusable)
+                 </li>
+                 <li>
+                 Important for accessibility, not actionable (clickable, long clickable, focusable),
+                 and does not have an actionable predecessor.
+                 </li>
+                 </ul>
+                 An accessibility srvice can request putting accessibility focus on such a view.
+                 </p> -->
+            <enum name="auto" value="0" />
+            <!-- The view can take accessibility focus.
+                 <p>
+                 A view that can take accessibility focus is always considered during focus
+                 search and an accessibility service can request putting accessibility focus
+                 on it.
+                 </p> -->
+            <enum name="yes" value="1" />
+            <!-- The view can not take accessibility focus.
+                 <p>
+                 A view that can not take accessibility focus is never considered during focus
+                 search and an accessibility service can not request putting accessibility focus
+                 on it.
+                 </p> -->
+            <enum name="no" value="2" />
+        </attr>
+
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b677513..f24733c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -551,13 +551,13 @@
         <enum name="sensorLandscape" value="6" />
         <!-- Would like to have the screen in portrait orientation, but can
              use the sensor to change which direction the screen is facing. -->
-        <enum name="sensorPortait" value="7" />
+        <enum name="sensorPortrait" value="7" />
         <!-- Would like to have the screen in landscape orientation, turned in
              the opposite direction from normal landscape. -->
         <enum name="reverseLandscape" value="8" />
         <!-- Would like to have the screen in portrait orientation, turned in
              the opposite direction from normal portrait. -->
-        <enum name="reversePortait" value="9" />
+        <enum name="reversePortrait" value="9" />
         <!-- Orientation is determined by a physical orientation sensor:
              the display will rotate based on how the user moves the device.
              This allows any of the 4 possible rotations, regardless of what
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 98e7769..09e3fbb 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -577,11 +577,11 @@
     <!-- True if WallpaperService is enabled -->
     <bool name="config_enableWallpaperService">true</bool>
 
-    <!-- Component name of the service providing network location support. -->
-    <string name="config_networkLocationProvider" translatable="false">@null</string>
+    <!-- Package name providing network location support. -->
+    <string name="config_networkLocationProviderPackageName" translatable="false">@null</string>
 
-    <!-- Component name of the service providing geocoder API support. -->
-    <string name="config_geocodeProvider" translatable="false">@null</string>
+    <!-- Package name providing geocoder API support. -->
+    <string name="config_geocodeProviderPackageName" translatable="false">@null</string>
 
     <!-- Boolean indicating if current platform supports bluetooth SCO for off call
     use cases -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a143feb..4cfbff5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1303,6 +1303,9 @@
   <java-symbol type="string" name="global_actions_airplane_mode_off_status" />
   <java-symbol type="string" name="global_actions_airplane_mode_on_status" />
   <java-symbol type="string" name="global_actions_toggle_airplane_mode" />
+  <java-symbol type="string" name="global_action_silent_mode_off_status" />
+  <java-symbol type="string" name="global_action_silent_mode_on_status" />
+  <java-symbol type="string" name="global_action_toggle_silent_mode" />
   <java-symbol type="string" name="invalidPuk" />
   <java-symbol type="string" name="keyguard_password_enter_pin_code" />
   <java-symbol type="string" name="keyguard_password_enter_puk_code" />
@@ -1457,8 +1460,8 @@
   <java-symbol type="string" name="car_mode_disable_notification_title" />
   <java-symbol type="string" name="chooser_wallpaper" />
   <java-symbol type="string" name="config_datause_iface" />
-  <java-symbol type="string" name="config_geocodeProvider" />
-  <java-symbol type="string" name="config_networkLocationProvider" />
+  <java-symbol type="string" name="config_geocodeProviderPackageName" />
+  <java-symbol type="string" name="config_networkLocationProviderPackageName" />
   <java-symbol type="string" name="config_wimaxManagerClassname" />
   <java-symbol type="string" name="config_wimaxNativeLibLocation" />
   <java-symbol type="string" name="config_wimaxServiceClassname" />
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
index 4da05d9..74a15f2 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
@@ -22,53 +22,26 @@
     xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/search_panel_container"
-    android:layout_height="wrap_content"
-    android:layout_width="match_parent"
-    android:paddingBottom="0dip">
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
 
-    <RelativeLayout
-        android:id="@+id/search_bg_protect"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
+    <com.android.internal.widget.multiwaveview.MultiWaveView
+        android:id="@+id/multi_wave_view"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/navbar_search_panel_height"
+        android:layout_gravity="center_horizontal|bottom"
+        android:gravity="center_horizontal|top"
+        android:background="@drawable/navbar_search_bg_scrim"
 
-        <RelativeLayout
-            android:id="@+id/search_panel_container"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            android:layout_centerHorizontal="true">
-
-            <View
-                android:layout_width="0dip"
-                android:layout_height="0dip"
-                android:layout_alignTop="@id/multi_wave_view"
-                android:layout_alignLeft="@id/multi_wave_view"
-                android:layout_alignRight="@id/multi_wave_view"
-                android:layout_alignBottom="@id/multi_wave_view"
-                android:layout_marginBottom="@dimen/navigation_bar_size"
-                android:background="@drawable/navbar_search_bg_scrim"/>
-
-            <com.android.internal.widget.multiwaveview.MultiWaveView
-                android:id="@+id/multi_wave_view"
-                android:orientation="horizontal"
-                android:layout_width="wrap_content"
-                android:layout_height="@dimen/navbar_search_panel_height"
-                android:layout_alignParentBottom="true"
-                android:gravity="top"
-
-                prvandroid:targetDrawables="@array/navbar_search_targets"
-                prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
-                prvandroid:directionDescriptions="@array/navbar_search_direction_descriptions"
-                prvandroid:handleDrawable="@drawable/navbar_search_handle"
-                prvandroid:waveDrawable="@drawable/navbar_search_outerring"
-                prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
-                prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
-                prvandroid:feedbackCount="0"
-                prvandroid:vibrationDuration="@integer/config_vibration_duration"
-                prvandroid:alwaysTrackFinger="true"/>
-
-        </RelativeLayout>
-
-    </RelativeLayout>
+        prvandroid:targetDrawables="@array/navbar_search_targets"
+        prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
+        prvandroid:directionDescriptions="@array/navbar_search_direction_descriptions"
+        prvandroid:handleDrawable="@drawable/navbar_search_handle"
+        prvandroid:waveDrawable="@drawable/navbar_search_outerring"
+        prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
+        prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
+        prvandroid:feedbackCount="0"
+        prvandroid:vibrationDuration="@integer/config_vibration_duration"
+        prvandroid:alwaysTrackFinger="true"/>
 
 </com.android.systemui.SearchPanelView>
diff --git a/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
index 1ae8a69..2a97307 100644
--- a/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
@@ -22,53 +22,27 @@
     xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/search_panel_container"
-    android:layout_height="wrap_content"
-    android:layout_width="match_parent"
-    android:paddingBottom="0dip">
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
 
-    <RelativeLayout
-        android:id="@+id/search_bg_protect"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
+    <com.android.internal.widget.multiwaveview.MultiWaveView
+        android:id="@+id/multi_wave_view"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/navbar_search_panel_height"
+        android:layout_gravity="left|bottom"
+        android:gravity="top|right"
+        android:layout_marginLeft="-150dip"
+        android:background="@drawable/navbar_search_bg_scrim"
 
-        <RelativeLayout
-            android:id="@+id/search_panel_container"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/navbar_search_panel_height"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentLeft="true"
-            android:layout_marginLeft="-120dip">
-
-            <View
-                android:layout_width="0dip"
-                android:layout_height="0dip"
-                android:layout_alignTop="@id/multi_wave_view"
-                android:layout_alignLeft="@id/multi_wave_view"
-                android:layout_alignRight="@id/multi_wave_view"
-                android:layout_alignBottom="@id/multi_wave_view"
-                android:layout_marginBottom="@dimen/navigation_bar_size"
-                android:background="@drawable/navbar_search_bg_scrim"/>
-
-            <com.android.internal.widget.multiwaveview.MultiWaveView
-                android:id="@+id/multi_wave_view"
-                android:orientation="horizontal"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_alignParentBottom="true"
-
-                prvandroid:targetDrawables="@array/navbar_search_targets"
-                prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
-                prvandroid:directionDescriptions="@array/navbar_search_direction_descriptions"
-                prvandroid:handleDrawable="@drawable/navbar_search_handle"
-                prvandroid:waveDrawable="@drawable/navbar_search_outerring"
-                prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
-                prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
-                prvandroid:feedbackCount="0"
-                prvandroid:vibrationDuration="@integer/config_vibration_duration"
-                prvandroid:alwaysTrackFinger="true"/>
-
-        </RelativeLayout>
-
-    </RelativeLayout>
+        prvandroid:targetDrawables="@array/navbar_search_targets"
+        prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
+        prvandroid:directionDescriptions="@array/navbar_search_direction_descriptions"
+        prvandroid:handleDrawable="@drawable/navbar_search_handle"
+        prvandroid:waveDrawable="@drawable/navbar_search_outerring"
+        prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
+        prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
+        prvandroid:feedbackCount="0"
+        prvandroid:vibrationDuration="@integer/config_vibration_duration"
+        prvandroid:alwaysTrackFinger="true"/>
 
 </com.android.systemui.SearchPanelView>
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index a6e4487..060d08e 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.View;
@@ -38,7 +37,6 @@
 
 import com.android.internal.widget.multiwaveview.MultiWaveView;
 import com.android.internal.widget.multiwaveview.MultiWaveView.OnTriggerListener;
-import com.android.server.am.ActivityManagerService;
 import com.android.systemui.R;
 import com.android.systemui.recent.StatusBarTouchProxy;
 import com.android.systemui.statusbar.BaseStatusBar;
@@ -47,7 +45,8 @@
 import com.android.systemui.statusbar.tablet.TabletStatusBar;
 
 public class SearchPanelView extends FrameLayout implements
-        StatusBarPanel, Animator.AnimatorListener {
+        StatusBarPanel {
+    private static final int SEARCH_PANEL_HOLD_DURATION = 500;
     static final String TAG = "SearchPanelView";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
     private Context mContext;
@@ -123,7 +122,7 @@
     final MultiWaveView.OnTriggerListener mMultiWaveViewListener
             = new MultiWaveView.OnTriggerListener() {
 
-        private int mTarget = -1;
+        private boolean mWaitingForLaunch;
 
         public void onGrabbed(View v, int handle) {
         }
@@ -132,26 +131,28 @@
         }
 
         public void onGrabbedStateChange(View v, int handle) {
-            if (mTarget == -1 && OnTriggerListener.NO_HANDLE == handle) {
+            if (!mWaitingForLaunch && OnTriggerListener.NO_HANDLE == handle) {
                 mBar.hideSearchPanel();
             }
         }
 
-        public void onTrigger(View v, int target) {
-            mTarget = target;
+        public void onTrigger(View v, final int target) {
+            final int resId = mMultiWaveView.getResourceIdForTarget(target);
+            switch (resId) {
+                case com.android.internal.R.drawable.ic_lockscreen_search:
+                    mWaitingForLaunch = true;
+                    startAssistActivity();
+                    postDelayed(new Runnable() {
+                        public void run() {
+                            mWaitingForLaunch = false;
+                            mBar.hideSearchPanel();
+                        }
+                    }, SEARCH_PANEL_HOLD_DURATION);
+                break;
+            }
         }
 
         public void onFinishFinalAnimation() {
-            if (mTarget != -1) {
-                final int resId = mMultiWaveView.getResourceIdForTarget(mTarget);
-                mTarget = -1; // a safety to make sure we never launch w/o prior call to onTrigger
-                switch (resId) {
-                    case com.android.internal.R.drawable.ic_lockscreen_search:
-                        startAssistActivity();
-                    break;
-                }
-                mBar.hideSearchPanel();
-            }
         }
     };
 
@@ -194,15 +195,11 @@
     };
 
     public void show(final boolean show, boolean animate) {
-        if (animate) {
-            if (mShowing != show) {
-                mShowing = show;
-                // TODO: start animating ring
-            }
-        } else {
-            mShowing = show;
-            onAnimationEnd(null);
+        if (!show) {
+            final LayoutTransition transitioner = animate ? createLayoutTransitioner() : null;
+            ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(transitioner);
         }
+        mShowing = show;
         if (show) {
             if (getVisibility() != View.VISIBLE) {
                 setVisibility(View.VISIBLE);
@@ -228,25 +225,6 @@
         }
     }
 
-    public void onAnimationCancel(Animator animation) {
-    }
-
-    public void onAnimationEnd(Animator animation) {
-        if (mShowing) {
-            final LayoutTransition transitioner = new LayoutTransition();
-            ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(transitioner);
-            createCustomAnimations(transitioner);
-        } else {
-            ((ViewGroup)mSearchTargetsContainer).setLayoutTransition(null);
-        }
-    }
-
-    public void onAnimationRepeat(Animator animation) {
-    }
-
-    public void onAnimationStart(Animator animation) {
-    }
-
     /**
      * We need to be aligned at the bottom.  LinearLayout can't do this, so instead,
      * let LinearLayout do all the hard work, and then shift everything down to the bottom.
@@ -293,9 +271,11 @@
         }
     }
 
-    private void createCustomAnimations(LayoutTransition transitioner) {
+    private LayoutTransition createLayoutTransitioner() {
+        LayoutTransition transitioner = new LayoutTransition();
         transitioner.setDuration(200);
         transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
         transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
+        return transitioner;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 9f801b0..1302c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -83,6 +83,12 @@
         setLayoutTransition(null);
 
         mLinearLayout.removeAllViews();
+        for (int i = 0; i < mRecycledViews.size(); i++) {
+            View child = mRecycledViews.get(i);
+            if (child.getParent() != null) {
+                throw new RuntimeException("Recycled child has a parent");
+            }
+        }
         for (int i = 0; i < mAdapter.getCount(); i++) {
             View old = null;
             if (mRecycledViews.size() != 0) {
@@ -183,6 +189,9 @@
     public void onChildDismissed(View v) {
         mRecycledViews.add(v);
         mLinearLayout.removeView(v);
+        if (v.getParent() != null) {
+            throw new RuntimeException("Recycled child has parent");
+        }
         mCallback.handleSwipe(v);
         // Restore the alpha/translation parameters to what they were before swiping
         // (for when these items are recycled)
@@ -354,9 +363,15 @@
         mNumItemsInOneScreenful =
                 (int) FloatMath.ceil(dm.widthPixels / (float) child.getMeasuredWidth());
         mRecycledViews.add(child);
+        if (child.getParent() != null) {
+            throw new RuntimeException("First recycled child has parent");
+        }
 
         for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
             mRecycledViews.add(mAdapter.createView(mLinearLayout));
+            if (mRecycledViews.get(mRecycledViews.size() - 1).getParent() != null) {
+                throw new RuntimeException("Recycled child has parent");
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index c1597e0..3c71784 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -84,6 +84,12 @@
         setLayoutTransition(null);
 
         mLinearLayout.removeAllViews();
+        for (int i = 0; i < mRecycledViews.size(); i++) {
+            View child = mRecycledViews.get(i);
+            if (child.getParent() != null) {
+                throw new RuntimeException("Recycled child has parent");
+            }
+        }
         // Once we can clear the data associated with individual item views,
         // we can get rid of the removeAllViews() and the code below will
         // recycle them.
@@ -190,6 +196,9 @@
     public void onChildDismissed(View v) {
         mRecycledViews.add(v);
         mLinearLayout.removeView(v);
+        if (v.getParent() != null) {
+            throw new RuntimeException("Recycled child has parent");
+        }
         mCallback.handleSwipe(v);
         // Restore the alpha/translation parameters to what they were before swiping
         // (for when these items are recycled)
@@ -363,9 +372,15 @@
         mNumItemsInOneScreenful =
                 (int) FloatMath.ceil(dm.heightPixels / (float) child.getMeasuredHeight());
         mRecycledViews.add(child);
+        if (child.getParent() != null) {
+            throw new RuntimeException("First recycled child has parent");
+        }
 
         for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
             mRecycledViews.add(mAdapter.createView(mLinearLayout));
+            if (mRecycledViews.get(mRecycledViews.size() - 1).getParent() != null) {
+                throw new RuntimeException("Recycled child has parent");
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b9dd32f..344411b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -483,7 +483,14 @@
 
     @Override
     public void showSearchPanel() {
-        super.showSearchPanel();
+        // XXX This is a bit of a hack.  Since navbar is no longer slippery, we use the
+        // gesture to dismiss the expanded statusbar.
+        if (mExpanded) {
+            animateCollapse();
+            return;
+        } else {
+            super.showSearchPanel();
+        }
         WindowManager.LayoutParams lp =
             (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
         lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -506,7 +513,7 @@
     public int getStatusBarHeight() {
         if (mNaturalBarHeight < 0) {
             final Resources res = mContext.getResources();
-            mNaturalBarHeight = 
+            mNaturalBarHeight =
                     res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
         }
         return mNaturalBarHeight;
@@ -526,7 +533,9 @@
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
                 case MotionEvent.ACTION_DOWN:
-                    showSearchPanel();
+                    if (!shouldDisableNavbarGestures()) {
+                        showSearchPanel();
+                    }
                 break;
             }
             return false;
@@ -674,9 +683,9 @@
             if (INTRUDER_ALERT_DECAY_MS > 0) {
                 mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
             }
-        } else 
+        } else
          */
-        
+
         if (notification.notification.fullScreenIntent != null) {
             // not immersive & a full-screen alert should be shown
             Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
@@ -906,7 +915,7 @@
         flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
         flagdbg.append(">");
         Slog.d(TAG, flagdbg.toString());
-        
+
         if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
             mIcons.animate().cancel();
             if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
@@ -1016,7 +1025,7 @@
         if (mExpandedVisible) {
             return;
         }
-        
+
         mExpandedVisible = true;
         mNotificationPanel.setVisibility(View.VISIBLE);
 
@@ -1213,7 +1222,7 @@
         //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
         //        + " mAnimAccel=" + mAnimAccel);
     }
-    
+
     void doRevealAnimation(long frameTimeNanos) {
         if (SPEW) {
             Slog.d(TAG, "doRevealAnimation: dt=" + (frameTimeNanos - mAnimLastTimeNanos));
@@ -1465,11 +1474,11 @@
                         mTicker.halt();
                     }
                 }
-                
+
                 if (mNavigationBarView != null) {
                     mNavigationBarView.setLowProfile(lightsOut);
                 }
-                
+
                 setStatusBarLowProfile(lightsOut);
             }
 
@@ -1494,7 +1503,7 @@
                     ObjectAnimator.ofFloat(clock, View.ALPHA, 0.5f)
                 );
             mLightsOutAnimation.setDuration(750);
-            
+
             mLightsOnAnimation = new AnimatorSet();
             mLightsOnAnimation.playTogether(
                     ObjectAnimator.ofFloat(notifications, View.ALPHA, 1),
@@ -1505,7 +1514,7 @@
                 );
             mLightsOnAnimation.setDuration(250);
         }
-        
+
         mLightsOutAnimation.cancel();
         mLightsOnAnimation.cancel();
 
@@ -1518,7 +1527,7 @@
     private boolean areLightsOn() {
         return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
     }
-    
+
     public void setLightsOn(boolean on) {
         Log.v(TAG, "setLightsOn(" + on + ")");
         if (on) {
@@ -1622,7 +1631,7 @@
     protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
         // no ticking in lights-out mode
         if (!areLightsOn()) return;
-        
+
         // Show the ticker if one is requested. Also don't do this
         // until status bar window is attached to the window manager,
         // because...  well, what's the point otherwise?  And trying to
@@ -1822,7 +1831,6 @@
         }
 
         int panelh = 0;
-        
         final int disph = getExpandedViewMaxHeight();
 
         // If the expanded view is not visible, make sure they're still off screen.
@@ -2039,7 +2047,7 @@
         try {
             mBarService.onNotificationClear(
                     mCurrentlyIntrudingNotification.pkg,
-                    mCurrentlyIntrudingNotification.tag, 
+                    mCurrentlyIntrudingNotification.tag,
                     mCurrentlyIntrudingNotification.id);
         } catch (android.os.RemoteException ex) {
             // oh well
@@ -2095,14 +2103,14 @@
         mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
 
         mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
-        
+
         mNotificationPanelMarginBottomPx
             = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
         mNotificationPanelMarginLeftPx
             = (int) res.getDimension(R.dimen.notification_panel_margin_left);
         mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
         if (mNotificationPanelGravity <= 0) {
-            mNotificationPanelGravity = Gravity.CENTER_VERTICAL | Gravity.TOP; 
+            mNotificationPanelGravity = Gravity.CENTER_VERTICAL | Gravity.TOP;
         }
 
         if (false) Slog.v(TAG, "updateResources");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index c65f581..584a69e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -1000,7 +1000,7 @@
             mContentDescriptionPhoneSignal = mContext.getString(
                     R.string.accessibility_airplane_mode);
             mAirplaneIconId = R.drawable.stat_sys_signal_flightmode;
-            mDataTypeIconId = 0;
+            mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = 0;
 
             // combined values from connected wifi take precedence over airplane mode
             if (mWifiConnected) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index c627d47..c0ac50e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -192,7 +192,9 @@
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
                 case MotionEvent.ACTION_DOWN:
-                    showSearchPanel();
+                    if (!shouldDisableNavbarGestures()) {
+                        showSearchPanel();
+                    }
                 break;
             }
             return false;
@@ -254,12 +256,12 @@
 
         // network icons: either a combo icon that switches between mobile and data, or distinct
         // mobile and data icons
-        final ImageView mobileRSSI = 
+        final ImageView mobileRSSI =
                 (ImageView)mNotificationPanel.findViewById(R.id.mobile_signal);
         if (mobileRSSI != null) {
             mNetworkController.addPhoneSignalIconView(mobileRSSI);
         }
-        final ImageView wifiRSSI = 
+        final ImageView wifiRSSI =
                 (ImageView)mNotificationPanel.findViewById(R.id.wifi_signal);
         if (wifiRSSI != null) {
             mNetworkController.addWifiIconView(wifiRSSI);
@@ -493,7 +495,7 @@
         mBluetoothController.addIconView((ImageView)sb.findViewById(R.id.bluetooth));
 
         mNetworkController = new NetworkController(mContext);
-        final SignalClusterView signalCluster = 
+        final SignalClusterView signalCluster =
                 (SignalClusterView)sb.findViewById(R.id.signal_cluster);
         mNetworkController.addSignalCluster(signalCluster);
 
@@ -1061,7 +1063,7 @@
             if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
                 mHandler.removeMessages(MSG_HIDE_CHROME);
                 mHandler.removeMessages(MSG_SHOW_CHROME);
-                mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) 
+                mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)
                         ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
             }
 
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index aa73de4..fc187ce 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -28,7 +28,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
+import android.database.ContentObserver;
 import android.media.AudioManager;
+import android.net.ConnectivityManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -73,7 +75,7 @@
     private ArrayList<Action> mItems;
     private AlertDialog mDialog;
 
-    private SilentModeAction mSilentModeAction;
+    private Action mSilentModeAction;
     private ToggleAction mAirplaneModeOn;
 
     private MyAdapter mAdapter;
@@ -82,6 +84,8 @@
     private boolean mDeviceProvisioned = false;
     private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
     private boolean mIsWaitingForEcmExit = false;
+    private boolean mHasTelephony;
+    private boolean mHasVibrator;
 
     private IWindowManager mIWindowManager;
 
@@ -104,6 +108,14 @@
         TelephonyManager telephonyManager =
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+        ConnectivityManager cm = (ConnectivityManager)
+                context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.AIRPLANE_MODE_ON), true,
+                mAirplaneModeObserver);
+        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+        mHasVibrator = vibrator != null && vibrator.hasVibrator();
     }
 
     /**
@@ -130,13 +142,18 @@
         mDialog.show();
         mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
     }
+
     /**
      * Create the global actions dialog.
      * @return A new dialog.
      */
     private AlertDialog createDialog() {
-        mSilentModeAction = new SilentModeAction(mContext, mAudioManager, mHandler);
-
+        // Simple toggle style if there's no vibrator, otherwise use a tri-state
+        if (!mHasVibrator) {
+            mSilentModeAction = new SilentModeToggleAction();
+        } else {
+            mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
+        }
         mAirplaneModeOn = new ToggleAction(
                 R.drawable.ic_lock_airplane_mode,
                 R.drawable.ic_lock_airplane_mode_off,
@@ -145,7 +162,7 @@
                 R.string.global_actions_airplane_mode_off_status) {
 
             void onToggle(boolean on) {
-                if (Boolean.parseBoolean(
+                if (mHasTelephony && Boolean.parseBoolean(
                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
                     mIsWaitingForEcmExit = true;
                     // Launch ECM exit dialog
@@ -160,6 +177,8 @@
 
             @Override
             protected void changeStateFromPress(boolean buttonOn) {
+                if (!mHasTelephony) return;
+
                 // In ECM mode airplane state cannot be changed
                 if (!(Boolean.parseBoolean(
                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
@@ -176,6 +195,7 @@
                 return false;
             }
         };
+        onAirplaneModeChanged();
 
         mItems = new ArrayList<Action>();
 
@@ -247,6 +267,7 @@
                 mItems.add(switchToUser);
             }
         }
+
         mAdapter = new MyAdapter();
 
         final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
@@ -273,8 +294,7 @@
     }
 
     private void prepareDialog() {
-        final boolean silentModeOn =
-                mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+        refreshSilentMode();
         mAirplaneModeOn.updateState(mAirplaneState);
         mAdapter.notifyDataSetChanged();
         if (mKeyguardShowing) {
@@ -288,6 +308,15 @@
         }
     }
 
+    private void refreshSilentMode() {
+        if (!mHasVibrator) {
+            final boolean silentModeOn =
+                    mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+            ((ToggleAction)mSilentModeAction).updateState(
+                    silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
+        }
+    }
+
     /** {@inheritDoc} */
     public void onDismiss(DialogInterface dialog) {
         if (SHOW_SILENT_TOGGLE) {
@@ -297,7 +326,7 @@
 
     /** {@inheritDoc} */
     public void onClick(DialogInterface dialog, int which) {
-        if (!(mAdapter.getItem(which) instanceof SilentModeAction)) {
+        if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
             dialog.dismiss();
         }
         mAdapter.getItem(which).onPress();
@@ -495,12 +524,12 @@
          */
         public ToggleAction(int enabledIconResId,
                 int disabledIconResid,
-                int essage,
+                int message,
                 int enabledStatusMessageResId,
                 int disabledStatusMessageResId) {
             mEnabledIconResId = enabledIconResId;
             mDisabledIconResid = disabledIconResid;
-            mMessageResId = essage;
+            mMessageResId = message;
             mEnabledStatusMessageResId = enabledStatusMessageResId;
             mDisabledStatusMessageResId = disabledStatusMessageResId;
         }
@@ -583,21 +612,44 @@
         }
     }
 
-    private static class SilentModeAction implements Action, View.OnClickListener {
+    private class SilentModeToggleAction extends ToggleAction {
+        public SilentModeToggleAction() {
+            super(R.drawable.ic_audio_vol_mute,
+                    R.drawable.ic_audio_vol,
+                    R.string.global_action_toggle_silent_mode,
+                    R.string.global_action_silent_mode_on_status,
+                    R.string.global_action_silent_mode_off_status);
+        }
+
+        void onToggle(boolean on) {
+            if (on) {
+                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
+            } else {
+                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+            }
+        }
+
+        public boolean showDuringKeyguard() {
+            return true;
+        }
+
+        public boolean showBeforeProvisioning() {
+            return false;
+        }
+    }
+
+    private static class SilentModeTriStateAction implements Action, View.OnClickListener {
 
         private final int[] ITEM_IDS = { R.id.option1, R.id.option2, R.id.option3 };
 
         private final AudioManager mAudioManager;
         private final Handler mHandler;
-        private final boolean mHasVibrator;
         private final Context mContext;
 
-        SilentModeAction(Context context, AudioManager audioManager, Handler handler) {
+        SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
             mAudioManager = audioManager;
             mHandler = handler;
             mContext = context;
-            Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
-            mHasVibrator = vibrator != null && vibrator.hasVibrator();
         }
 
         private int ringerModeToIndex(int ringerMode) {
@@ -621,9 +673,6 @@
                 // Set up click handler
                 itemView.setTag(i);
                 itemView.setOnClickListener(this);
-                if (itemView.getId() == R.id.option2 && !mHasVibrator) {
-                    itemView.setVisibility(View.GONE);
-                }
             }
             return v;
         }
@@ -683,6 +732,7 @@
     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
         public void onServiceStateChanged(ServiceState serviceState) {
+            if (!mHasTelephony) return;
             final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
             mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
             mAirplaneModeOn.updateState(mAirplaneState);
@@ -699,6 +749,13 @@
         }
     };
 
+    private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            onAirplaneModeChanged();
+        }
+    };
+
     private static final int MESSAGE_DISMISS = 0;
     private static final int MESSAGE_REFRESH = 1;
     private static final int MESSAGE_SHOW = 2;
@@ -713,6 +770,7 @@
                 }
                 break;
             case MESSAGE_REFRESH:
+                refreshSilentMode();
                 mAdapter.notifyDataSetChanged();
                 break;
             case MESSAGE_SHOW:
@@ -722,6 +780,18 @@
         }
     };
 
+    private void onAirplaneModeChanged() {
+        // Let the service state callbacks handle the state.
+        if (mHasTelephony) return;
+
+        boolean airplaneModeOn = Settings.System.getInt(
+                mContext.getContentResolver(),
+                Settings.System.AIRPLANE_MODE_ON,
+                0) == 1;
+        mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
+        mAirplaneModeOn.updateState(mAirplaneState);
+    }
+
     /**
      * Change the airplane mode system setting
      */
@@ -734,6 +804,9 @@
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra("state", on);
         mContext.sendBroadcast(intent);
+        if (!mHasTelephony) {
+            mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
+        }
     }
 
     private IWindowManager getWindowManager() {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a99ae26..cce55d5 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1798,7 +1798,7 @@
                 }
             }
             if (down) {
-                if (!mHomePressed) {
+                if (!mHomePressed && mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
                     try {
                         mStatusBarService.preloadRecentApps();
                     } catch (RemoteException e) {
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 1e707b2..985249d 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -26,7 +26,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.location.Address;
@@ -123,8 +127,9 @@
     private static boolean sProvidersLoaded = false;
 
     private final Context mContext;
-    private final String mNetworkLocationProviderPackageName;
-    private final String mGeocodeProviderPackageName;
+    private PackageManager mPackageManager;  // final after initialize()
+    private String mNetworkLocationProviderPackageName;  // only used on handler thread
+    private String mGeocodeProviderPackageName;  // only used on handler thread
     private GeocoderProxy mGeocodeProvider;
     private IGpsStatusProvider mGpsStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
@@ -490,36 +495,91 @@
         addProvider(passiveProvider);
         mEnabledProviders.add(passiveProvider.getName());
 
-        // initialize external network location and geocoder services
-        PackageManager pm = mContext.getPackageManager();
-        if (mNetworkLocationProviderPackageName != null &&
-                pm.resolveService(new Intent(mNetworkLocationProviderPackageName), 0) != null) {
-            mNetworkLocationProvider =
-                new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
-                        mNetworkLocationProviderPackageName, mLocationHandler);
-
-            addProvider(mNetworkLocationProvider);
+        // initialize external network location and geocoder services.
+        // The initial value of mNetworkLocationProviderPackageName and
+        // mGeocodeProviderPackageName is just used to determine what
+        // signatures future mNetworkLocationProviderPackageName and
+        // mGeocodeProviderPackageName packages must have. So alternate
+        // providers can be installed under a different package name
+        // so long as they have the same signature as the original
+        // provider packages.
+        if (mNetworkLocationProviderPackageName != null) {
+            String packageName = findBestPackage(LocationProviderProxy.SERVICE_ACTION,
+                    mNetworkLocationProviderPackageName);
+            if (packageName != null) {
+                mNetworkLocationProvider = new LocationProviderProxy(mContext,
+                        LocationManager.NETWORK_PROVIDER,
+                        packageName, mLocationHandler);
+                mNetworkLocationProviderPackageName = packageName;
+                addProvider(mNetworkLocationProvider);
+            }
         }
-
-        if (mGeocodeProviderPackageName != null &&
-                pm.resolveService(new Intent(mGeocodeProviderPackageName), 0) != null) {
-            mGeocodeProvider = new GeocoderProxy(mContext, mGeocodeProviderPackageName);
+        if (mGeocodeProviderPackageName != null) {
+            String packageName = findBestPackage(GeocoderProxy.SERVICE_ACTION,
+                    mGeocodeProviderPackageName);
+            if (packageName != null) {
+                mGeocodeProvider = new GeocoderProxy(mContext, packageName);
+                mGeocodeProviderPackageName = packageName;
+            }
         }
 
         updateProvidersLocked();
     }
 
     /**
+     * Pick the best (network location provider or geocode provider) package.
+     * The best package:
+     * - implements serviceIntentName
+     * - has signatures that match that of sigPackageName
+     * - has the highest version value in a meta-data field in the service component
+     */
+    String findBestPackage(String serviceIntentName, String sigPackageName) {
+        Intent intent = new Intent(serviceIntentName);
+        List<ResolveInfo> infos = mPackageManager.queryIntentServices(intent,
+                PackageManager.GET_META_DATA);
+        if (infos == null) return null;
+
+        int bestVersion = Integer.MIN_VALUE;
+        String bestPackage = null;
+        for (ResolveInfo info : infos) {
+            String packageName = info.serviceInfo.packageName;
+            // check signature
+            if (mPackageManager.checkSignatures(packageName, sigPackageName) !=
+                    PackageManager.SIGNATURE_MATCH) {
+                Slog.w(TAG, packageName + " implements " + serviceIntentName +
+                       " but its signatures don't match those in " + sigPackageName +
+                       ", ignoring");
+                continue;
+            }
+            // read version
+            int version = 0;
+            if (info.serviceInfo.metaData != null) {
+                version = info.serviceInfo.metaData.getInt("version", 0);
+            }
+            if (LOCAL_LOGV) Slog.v(TAG, packageName + " implements " + serviceIntentName +
+                    " with version " + version);
+            if (version > bestVersion) {
+                bestVersion = version;
+                bestPackage = packageName;
+            }
+        }
+
+        return bestPackage;
+    }
+
+    /**
      * @param context the context that the LocationManagerService runs in
      */
     public LocationManagerService(Context context) {
         super();
         mContext = context;
         Resources resources = context.getResources();
+
         mNetworkLocationProviderPackageName = resources.getString(
-                com.android.internal.R.string.config_networkLocationProvider);
+                com.android.internal.R.string.config_networkLocationProviderPackageName);
         mGeocodeProviderPackageName = resources.getString(
-                com.android.internal.R.string.config_geocodeProvider);
+                com.android.internal.R.string.config_geocodeProviderPackageName);
+
         mPackageMonitor.register(context, null, true);
 
         if (LOCAL_LOGV) {
@@ -537,6 +597,7 @@
         // Create a wake lock, needs to be done before calling loadProviders() below
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+        mPackageManager = mContext.getPackageManager();
 
         // Load providers
         loadProviders();
@@ -1886,16 +1947,33 @@
                     }
                 } else if (msg.what == MESSAGE_PACKAGE_UPDATED) {
                     String packageName = (String) msg.obj;
-                    String packageDot = packageName + ".";
 
-                    // reconnect to external providers after their packages have been updated
-                    if (mNetworkLocationProvider != null &&
-                        mNetworkLocationProviderPackageName.startsWith(packageDot)) {
-                        mNetworkLocationProvider.reconnect();
+                    // reconnect to external providers if there is a better package
+                    if (mNetworkLocationProviderPackageName != null &&
+                            mPackageManager.resolveService(
+                            new Intent(LocationProviderProxy.SERVICE_ACTION)
+                            .setPackage(packageName), 0) != null) {
+                        // package implements service, perform full check
+                        String bestPackage = findBestPackage(
+                                LocationProviderProxy.SERVICE_ACTION,
+                                mNetworkLocationProviderPackageName);
+                        if (packageName.equals(bestPackage)) {
+                            mNetworkLocationProvider.reconnect(bestPackage);
+                            mNetworkLocationProviderPackageName = packageName;
+                        }
                     }
-                    if (mGeocodeProvider != null &&
-                        mGeocodeProviderPackageName.startsWith(packageDot)) {
-                        mGeocodeProvider.reconnect();
+                    if (mGeocodeProviderPackageName != null &&
+                            mPackageManager.resolveService(
+                            new Intent(GeocoderProxy.SERVICE_ACTION)
+                            .setPackage(packageName), 0) != null) {
+                        // package implements service, perform full check
+                        String bestPackage = findBestPackage(
+                                GeocoderProxy.SERVICE_ACTION,
+                                mGeocodeProviderPackageName);
+                        if (packageName.equals(bestPackage)) {
+                            mGeocodeProvider.reconnect(bestPackage);
+                            mGeocodeProviderPackageName = packageName;
+                        }
                     }
                 }
             } catch (Exception e) {
@@ -2004,6 +2082,11 @@
             // Called by main thread; divert work to LocationWorker.
             Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
         }
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+            // Called by main thread; divert work to LocationWorker.
+            Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
+        }
     };
 
     // Wake locks
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6464d7f..9d9b5b8 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -62,6 +62,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.IContentProvider;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
 import android.content.Intent;
@@ -1810,12 +1811,11 @@
                 }
             }
         }
-        if (app.conProviders.size() > 0) {
-            for (ContentProviderRecord cpr : app.conProviders.keySet()) {
-                if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
-                    updateLruProcessInternalLocked(cpr.proc, oomAdj,
-                            updateActivityTime, i+1);
-                }
+        for (int j=app.conProviders.size()-1; j>=0; j--) {
+            ContentProviderRecord cpr = app.conProviders.get(j).provider;
+            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
+                updateLruProcessInternalLocked(cpr.proc, oomAdj,
+                        updateActivityTime, i+1);
             }
         }
         
@@ -3742,7 +3742,7 @@
 
         N = providers.size();
         for (i=0; i<N; i++) {
-            removeDyingProviderLocked(null, providers.get(i));
+            removeDyingProviderLocked(null, providers.get(i), true);
         }
 
         if (doit) {
@@ -6058,53 +6058,72 @@
         return msg;
     }
 
-    boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
-            IBinder externalProcessToken) {
+    ContentProviderConnection incProviderCountLocked(ProcessRecord r,
+            final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
         if (r != null) {
-            Integer cnt = r.conProviders.get(cpr);
-            if (DEBUG_PROVIDER) Slog.v(TAG,
-                    "Adding provider requested by "
-                    + r.processName + " from process "
-                    + cpr.info.processName + ": " + cpr.name.flattenToShortString()
-                    + " cnt=" + (cnt == null ? 1 : cnt));
-            if (cnt == null) {
-                cpr.clients.add(r);
-                r.conProviders.put(cpr, new Integer(1));
-                return true;
-            } else {
-                r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
+            for (int i=0; i<r.conProviders.size(); i++) {
+                ContentProviderConnection conn = r.conProviders.get(i);
+                if (conn.provider == cpr) {
+                    if (DEBUG_PROVIDER) Slog.v(TAG,
+                            "Adding provider requested by "
+                            + r.processName + " from process "
+                            + cpr.info.processName + ": " + cpr.name.flattenToShortString()
+                            + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
+                    if (stable) {
+                        conn.stableCount++;
+                        conn.numStableIncs++;
+                    } else {
+                        conn.unstableCount++;
+                        conn.numUnstableIncs++;
+                    }
+                    return conn;
+                }
             }
-        } else {
-            cpr.addExternalProcessHandleLocked(externalProcessToken);
+            ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
+            if (stable) {
+                conn.stableCount = 1;
+                conn.numStableIncs = 1;
+            } else {
+                conn.unstableCount = 1;
+                conn.numUnstableIncs = 1;
+            }
+            cpr.connections.add(conn);
+            r.conProviders.add(conn);
+            return conn;
         }
-        return false;
+        cpr.addExternalProcessHandleLocked(externalProcessToken);
+        return null;
     }
 
-    boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
-            IBinder externalProcessToken) {
-        if (r != null) {
-            Integer cnt = r.conProviders.get(cpr);
+    boolean decProviderCountLocked(ContentProviderConnection conn,
+            ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+        if (conn != null) {
+            cpr = conn.provider;
             if (DEBUG_PROVIDER) Slog.v(TAG,
                     "Removing provider requested by "
-                    + r.processName + " from process "
+                    + conn.client.processName + " from process "
                     + cpr.info.processName + ": " + cpr.name.flattenToShortString()
-                    + " cnt=" + cnt);
-            if (cnt == null || cnt.intValue() <= 1) {
-                cpr.clients.remove(r);
-                r.conProviders.remove(cpr);
-                return true;
+                    + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
+            if (stable) {
+                conn.stableCount--;
             } else {
-                r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
+                conn.unstableCount--;
             }
-        } else {
-            cpr.removeExternalProcessHandleLocked(externalProcessToken);
+            if (conn.stableCount == 0 && conn.unstableCount == 0) {
+                cpr.connections.remove(conn);
+                conn.client.conProviders.remove(conn);
+                return true;
+            }
+            return false;
         }
+        cpr.removeExternalProcessHandleLocked(externalProcessToken);
         return false;
     }
 
     private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
-            String name, IBinder token) {
+            String name, IBinder token, boolean stable) {
         ContentProviderRecord cpr;
+        ContentProviderConnection conn = null;
         ProviderInfo cpi = null;
 
         synchronized(this) {
@@ -6135,20 +6154,19 @@
                     // of being published...  but it is also allowed to run
                     // in the caller's process, so don't make a connection
                     // and just let the caller instantiate its own instance.
-                    if (cpr.provider != null) {
-                        // don't give caller the provider object, it needs
-                        // to make its own.
-                        cpr = new ContentProviderRecord(cpr);
-                    }
-                    return cpr;
+                    ContentProviderHolder holder = cpr.newHolder(null);
+                    // don't give caller the provider object, it needs
+                    // to make its own.
+                    holder.provider = null;
+                    return holder;
                 }
 
                 final long origId = Binder.clearCallingIdentity();
 
                 // In this case the provider instance already exists, so we can
                 // return it right away.
-                final boolean countChanged = incProviderCount(r, cpr, token);
-                if (countChanged) {
+                conn = incProviderCountLocked(r, cpr, token, stable);
+                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                     if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                         // If this is a perceptible app accessing the provider,
                         // make sure to count it as being accessed and thus
@@ -6181,7 +6199,7 @@
                         Slog.i(TAG,
                                 "Existing provider " + cpr.name.flattenToShortString()
                                 + " is crashing; detaching " + r);
-                        boolean lastRef = decProviderCount(r, cpr, token);
+                        boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                         appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);
                         if (!lastRef) {
                             // This wasn't the last ref our process had on
@@ -6189,6 +6207,7 @@
                             return null;
                         }
                         providerRunning = false;
+                        conn = null;
                     }
                 }
 
@@ -6251,7 +6270,7 @@
                     // info and allow the caller to instantiate it.  Only do
                     // this if the provider is the same user as the caller's
                     // process, or can run as root (so can be in any process).
-                    return cpr;
+                    return cpr.newHolder(null);
                 }
 
                 if (DEBUG_PROVIDER) {
@@ -6312,7 +6331,10 @@
                 }
 
                 mProviderMap.putProviderByName(name, cpr);
-                incProviderCount(r, cpr, token);
+                conn = incProviderCountLocked(r, cpr, token, stable);
+                if (conn != null) {
+                    conn.waiting = true;
+                }
             }
         }
 
@@ -6334,16 +6356,23 @@
                         Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
                                 + cpr.launchingApp);
                     }
+                    if (conn != null) {
+                        conn.waiting = true;
+                    }
                     cpr.wait();
                 } catch (InterruptedException ex) {
+                } finally {
+                    if (conn != null) {
+                        conn.waiting = false;
+                    }
                 }
             }
         }
-        return cpr;
+        return cpr != null ? cpr.newHolder(conn) : null;
     }
 
     public final ContentProviderHolder getContentProvider(
-            IApplicationThread caller, String name) {
+            IApplicationThread caller, String name, boolean stable) {
         enforceNotIsolatedCaller("getContentProvider");
         if (caller == null) {
             String msg = "null IApplicationThread when getting content provider "
@@ -6352,7 +6381,7 @@
             throw new SecurityException(msg);
         }
 
-        return getContentProviderImpl(caller, name, null);
+        return getContentProviderImpl(caller, name, null, stable);
     }
 
     public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
@@ -6362,45 +6391,30 @@
     }
 
     private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
-        return getContentProviderImpl(null, name, token);
+        return getContentProviderImpl(null, name, token, true);
     }
 
     /**
      * Drop a content provider from a ProcessRecord's bookkeeping
      * @param cpr
      */
-    public void removeContentProvider(IApplicationThread caller, String name) {
+    public void removeContentProvider(IBinder connection, boolean stable) {
         enforceNotIsolatedCaller("removeContentProvider");
         synchronized (this) {
-            int userId = UserId.getUserId(Binder.getCallingUid());
-            ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
-            if(cpr == null) {
-                // remove from mProvidersByClass
-                if (DEBUG_PROVIDER) Slog.v(TAG, name +
-                        " provider not found in providers list");
-                return;
+            ContentProviderConnection conn;
+            try {
+                conn = (ContentProviderConnection)connection;
+            } catch (ClassCastException e) {
+                String msg ="removeContentProvider: " + connection
+                        + " not a ContentProviderConnection";
+                Slog.w(TAG, msg);
+                throw new IllegalArgumentException(msg);
             }
-            final ProcessRecord r = getRecordForAppLocked(caller);
-            if (r == null) {
-                throw new SecurityException(
-                        "Unable to find app for caller " + caller +
-                        " when removing content provider " + name);
+            if (conn == null) {
+                throw new NullPointerException("connection is null");
             }
-            //update content provider record entry info
-            ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
-            ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
-            if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
-                    + r.info.processName + " from process "
-                    + localCpr.appInfo.processName);
-            if (localCpr.launchingApp == r) {
-                //should not happen. taken care of as a local provider
-                Slog.w(TAG, "removeContentProvider called on local provider: "
-                        + cpr.info.name + " in process " + r.processName);
-                return;
-            } else {
-                if (decProviderCount(r, localCpr, null)) {
-                    updateOomAdjLocked();
-                }
+            if (decProviderCountLocked(conn, null, null, stable)) {
+                updateOomAdjLocked();
             }
         }
     }
@@ -6447,7 +6461,7 @@
         }
 
         enforceNotIsolatedCaller("publishContentProviders");
-        synchronized(this) {
+        synchronized (this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
             if (DEBUG_MU)
                 Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
@@ -6499,6 +6513,103 @@
         }
     }
 
+    public boolean refContentProvider(IBinder connection, int stable, int unstable) {
+        ContentProviderConnection conn;
+        try {
+            conn = (ContentProviderConnection)connection;
+        } catch (ClassCastException e) {
+            String msg ="refContentProvider: " + connection
+                    + " not a ContentProviderConnection";
+            Slog.w(TAG, msg);
+            throw new IllegalArgumentException(msg);
+        }
+        if (conn == null) {
+            throw new NullPointerException("connection is null");
+        }
+
+        synchronized (this) {
+            if (stable > 0) {
+                conn.numStableIncs += stable;
+            }
+            stable = conn.stableCount + stable;
+            if (stable < 0) {
+                throw new IllegalStateException("stableCount < 0: " + stable);
+            }
+
+            if (unstable > 0) {
+                conn.numUnstableIncs += unstable;
+            }
+            unstable = conn.unstableCount + unstable;
+            if (unstable < 0) {
+                throw new IllegalStateException("unstableCount < 0: " + unstable);
+            }
+
+            if ((stable+unstable) <= 0) {
+                throw new IllegalStateException("ref counts can't go to zero here: stable="
+                        + stable + " unstable=" + unstable);
+            }
+            conn.stableCount = stable;
+            conn.unstableCount = unstable;
+            return !conn.dead;
+        }
+    }
+
+    public void unstableProviderDied(IBinder connection) {
+        ContentProviderConnection conn;
+        try {
+            conn = (ContentProviderConnection)connection;
+        } catch (ClassCastException e) {
+            String msg ="refContentProvider: " + connection
+                    + " not a ContentProviderConnection";
+            Slog.w(TAG, msg);
+            throw new IllegalArgumentException(msg);
+        }
+        if (conn == null) {
+            throw new NullPointerException("connection is null");
+        }
+
+        // Safely retrieve the content provider associated with the connection.
+        IContentProvider provider;
+        synchronized (this) {
+            provider = conn.provider.provider;
+        }
+
+        if (provider == null) {
+            // Um, yeah, we're way ahead of you.
+            return;
+        }
+
+        // Make sure the caller is being honest with us.
+        if (provider.asBinder().pingBinder()) {
+            // Er, no, still looks good to us.
+            synchronized (this) {
+                Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
+                        + " says " + conn + " died, but we don't agree");
+                return;
+            }
+        }
+
+        // Well look at that!  It's dead!
+        synchronized (this) {
+            if (conn.provider.provider != provider) {
+                // But something changed...  good enough.
+                return;
+            }
+
+            ProcessRecord proc = conn.provider.proc;
+            if (proc == null || proc.thread == null) {
+                // Seems like the process is already cleaned up.
+                return;
+            }
+
+            // As far as we're concerned, this is just like receiving a
+            // death notification...  just a bit prematurely.
+            Slog.i(TAG, "Process " + proc.processName + " (pid " + proc.pid
+                    + ") early provider death");
+            appDiedLocked(proc, proc.pid, proc.thread);
+        }
+    }
+
     public static final void installSystemProviders() {
         List<ProviderInfo> providers;
         synchronized (mSelf) {
@@ -10515,36 +10626,62 @@
         app.executingServices.clear();
     }
 
-    private final void removeDyingProviderLocked(ProcessRecord proc,
-            ContentProviderRecord cpr) {
-        synchronized (cpr) {
-            cpr.launchingApp = null;
-            cpr.notifyAll();
-        }
-        
-        mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
-        String names[] = cpr.info.authority.split(";");
-        for (int j = 0; j < names.length; j++) {
-            mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
-        }
-        
-        Iterator<ProcessRecord> cit = cpr.clients.iterator();
-        while (cit.hasNext()) {
-            ProcessRecord capp = cit.next();
-            if (!capp.persistent && capp.thread != null
-                    && capp.pid != 0
-                    && capp.pid != MY_PID) {
-                Slog.i(TAG, "Kill " + capp.processName
-                        + " (pid " + capp.pid + "): provider " + cpr.info.name
-                        + " in dying process " + (proc != null ? proc.processName : "??"));
-                EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
-                        capp.processName, capp.setAdj, "dying provider "
-                                + cpr.name.toShortString());
-                Process.killProcessQuiet(capp.pid);
+    private final boolean removeDyingProviderLocked(ProcessRecord proc,
+            ContentProviderRecord cpr, boolean always) {
+        final boolean inLaunching = mLaunchingProviders.contains(cpr);
+
+        if (!inLaunching || always) {
+            synchronized (cpr) {
+                cpr.launchingApp = null;
+                cpr.notifyAll();
+            }
+            mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
+            String names[] = cpr.info.authority.split(";");
+            for (int j = 0; j < names.length; j++) {
+                mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
             }
         }
-        
-        mLaunchingProviders.remove(cpr);
+
+        for (int i=0; i<cpr.connections.size(); i++) {
+            ContentProviderConnection conn = cpr.connections.get(i);
+            if (conn.waiting) {
+                // If this connection is waiting for the provider, then we don't
+                // need to mess with its process unless we are always removing
+                // or for some reason the provider is not currently launching.
+                if (inLaunching && !always) {
+                    continue;
+                }
+            }
+            ProcessRecord capp = conn.client;
+            conn.dead = true;
+            if (conn.stableCount > 0) {
+                if (!capp.persistent && capp.thread != null
+                        && capp.pid != 0
+                        && capp.pid != MY_PID) {
+                    Slog.i(TAG, "Kill " + capp.processName
+                            + " (pid " + capp.pid + "): provider " + cpr.info.name
+                            + " in dying process " + (proc != null ? proc.processName : "??"));
+                    EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
+                            capp.processName, capp.setAdj, "dying provider "
+                                    + cpr.name.toShortString());
+                    Process.killProcessQuiet(capp.pid);
+                }
+            } else if (capp.thread != null && conn.provider.provider != null) {
+                try {
+                    capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
+                } catch (RemoteException e) {
+                }
+                // In the protocol here, we don't expect the client to correctly
+                // clean up this connection, we'll just remove it.
+                cpr.connections.remove(i);
+                conn.client.conProviders.remove(conn);
+            }
+        }
+
+        if (inLaunching && always) {
+            mLaunchingProviders.remove(cpr);
+        }
+        return inLaunching;
     }
     
     /**
@@ -10590,34 +10727,21 @@
 
         boolean restart = false;
 
-        int NL = mLaunchingProviders.size();
-        
         // Remove published content providers.
         if (!app.pubProviders.isEmpty()) {
             Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
             while (it.hasNext()) {
                 ContentProviderRecord cpr = it.next();
+
+                final boolean always = app.bad || !allowRestart;
+                if (removeDyingProviderLocked(app, cpr, always) || always) {
+                    // We left the provider in the launching list, need to
+                    // restart it.
+                    restart = true;
+                }
+
                 cpr.provider = null;
                 cpr.proc = null;
-
-                // See if someone is waiting for this provider...  in which
-                // case we don't remove it, but just let it restart.
-                int i = 0;
-                if (!app.bad && allowRestart) {
-                    for (; i<NL; i++) {
-                        if (mLaunchingProviders.get(i) == cpr) {
-                            restart = true;
-                            break;
-                        }
-                    }
-                } else {
-                    i = NL;
-                }
-
-                if (i >= NL) {
-                    removeDyingProviderLocked(app, cpr);
-                    NL = mLaunchingProviders.size();
-                }
             }
             app.pubProviders.clear();
         }
@@ -10629,10 +10753,9 @@
         
         // Unregister from connected content providers.
         if (!app.conProviders.isEmpty()) {
-            Iterator it = app.conProviders.keySet().iterator();
-            while (it.hasNext()) {
-                ContentProviderRecord cpr = (ContentProviderRecord)it.next();
-                cpr.clients.remove(app);
+            for (int i=0; i<app.conProviders.size(); i++) {
+                ContentProviderConnection conn = app.conProviders.get(i);
+                conn.provider.connections.remove(conn);
             }
             app.conProviders.clear();
         }
@@ -10643,10 +10766,10 @@
         // XXX Commented out for now.  Trying to figure out a way to reproduce
         // the actual situation to identify what is actually going on.
         if (false) {
-            for (int i=0; i<NL; i++) {
+            for (int i=0; i<mLaunchingProviders.size(); i++) {
                 ContentProviderRecord cpr = (ContentProviderRecord)
                         mLaunchingProviders.get(i);
-                if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) {
+                if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
                     synchronized (cpr) {
                         cpr.launchingApp = null;
                         cpr.notifyAll();
@@ -10743,7 +10866,7 @@
                 if (!alwaysBad && !app.bad) {
                     restart = true;
                 } else {
-                    removeDyingProviderLocked(app, cpr);
+                    removeDyingProviderLocked(app, cpr, true);
                     NL = mLaunchingProviders.size();
                 }
             }
@@ -13948,48 +14071,49 @@
             while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ
                     || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
                 ContentProviderRecord cpr = jt.next();
-                if (cpr.clients.size() != 0) {
-                    Iterator<ProcessRecord> kt = cpr.clients.iterator();
-                    while (kt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
-                        ProcessRecord client = kt.next();
-                        if (client == app) {
-                            // Being our own client is not interesting.
-                            continue;
+                for (int i = cpr.connections.size()-1;
+                        i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                                || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                        i--) {
+                    ContentProviderConnection conn = cpr.connections.get(i);
+                    ProcessRecord client = conn.client;
+                    if (client == app) {
+                        // Being our own client is not interesting.
+                        continue;
+                    }
+                    int myHiddenAdj = hiddenAdj;
+                    if (myHiddenAdj > client.hiddenAdj) {
+                        if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
+                            myHiddenAdj = client.hiddenAdj;
+                        } else {
+                            myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
                         }
-                        int myHiddenAdj = hiddenAdj;
-                        if (myHiddenAdj > client.hiddenAdj) {
-                            if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
-                                myHiddenAdj = client.hiddenAdj;
-                            } else {
-                                myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
-                            }
+                    }
+                    int clientAdj = computeOomAdjLocked(
+                        client, myHiddenAdj, TOP_APP, true, doingAll);
+                    if (adj > clientAdj) {
+                        if (app.hasShownUi && app != mHomeProcess
+                                && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                            app.adjType = "bg-ui-provider";
+                        } else {
+                            adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
+                                    ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+                            app.adjType = "provider";
                         }
-                        int clientAdj = computeOomAdjLocked(
-                            client, myHiddenAdj, TOP_APP, true, doingAll);
-                        if (adj > clientAdj) {
-                            if (app.hasShownUi && app != mHomeProcess
-                                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                app.adjType = "bg-ui-provider";
-                            } else {
-                                adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
-                                        ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
-                                app.adjType = "provider";
-                            }
-                            if (!client.hidden) {
-                                app.hidden = false;
-                            }
-                            if (client.keeping) {
-                                app.keeping = true;
-                            }
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_PROVIDER_IN_USE;
-                            app.adjSource = client;
-                            app.adjSourceOom = clientAdj;
-                            app.adjTarget = cpr.name;
+                        if (!client.hidden) {
+                            app.hidden = false;
                         }
-                        if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                            schedGroup = Process.THREAD_GROUP_DEFAULT;
+                        if (client.keeping) {
+                            app.keeping = true;
                         }
+                        app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                .REASON_PROVIDER_IN_USE;
+                        app.adjSource = client;
+                        app.adjSourceOom = clientAdj;
+                        app.adjTarget = cpr.name;
+                    }
+                    if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+                        schedGroup = Process.THREAD_GROUP_DEFAULT;
                     }
                 }
                 // If the provider has external (non-framework) process
diff --git a/services/java/com/android/server/am/ContentProviderConnection.java b/services/java/com/android/server/am/ContentProviderConnection.java
new file mode 100644
index 0000000..84f8f02
--- /dev/null
+++ b/services/java/com/android/server/am/ContentProviderConnection.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.os.Binder;
+
+/**
+ * Represents a link between a content provider and client.
+ */
+public class ContentProviderConnection extends Binder {
+    public final ContentProviderRecord provider;
+    public final ProcessRecord client;
+    public int stableCount;
+    public int unstableCount;
+    // The client of this connection is currently waiting for the provider to appear.
+    // Protected by the provider lock.
+    public boolean waiting;
+    // The provider of this connection is now dead.
+    public boolean dead;
+
+    // For debugging.
+    public int numStableIncs;
+    public int numUnstableIncs;
+
+    public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) {
+        provider = _provider;
+        client = _client;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ContentProviderConnection{");
+        toShortString(sb);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public String toShortString() {
+        StringBuilder sb = new StringBuilder(128);
+        toShortString(sb);
+        return sb.toString();
+    }
+
+    public String toClientString() {
+        StringBuilder sb = new StringBuilder(128);
+        toClientString(sb);
+        return sb.toString();
+    }
+
+    public void toShortString(StringBuilder sb) {
+        sb.append(provider.toShortString());
+        sb.append("->");
+        toClientString(sb);
+    }
+
+    public void toClientString(StringBuilder sb) {
+        sb.append(client.toShortString());
+        sb.append(" s");
+        sb.append(stableCount);
+        sb.append("/");
+        sb.append(numStableIncs);
+        sb.append(" u");
+        sb.append(unstableCount);
+        sb.append("/");
+        sb.append(numUnstableIncs);
+        if (waiting) {
+            sb.append(" WAITING");
+        }
+        if (dead) {
+            sb.append(" DEAD");
+        }
+    }
+}
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 608b09a..fb21b06 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -18,6 +18,7 @@
 
 import android.app.IActivityManager.ContentProviderHolder;
 import android.content.ComponentName;
+import android.content.IContentProvider;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.os.IBinder;
@@ -27,28 +28,35 @@
 import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 
-class ContentProviderRecord extends ContentProviderHolder {
+class ContentProviderRecord {
+    final ActivityManagerService service;
+    public final ProviderInfo info;
+    final int uid;
+    final ApplicationInfo appInfo;
+    final ComponentName name;
+    public IContentProvider provider;
+    public boolean noReleaseNeeded;
     // All attached clients
-    final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
+    final ArrayList<ContentProviderConnection> connections
+            = new ArrayList<ContentProviderConnection>();
+    //final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
     // Handles for non-framework processes supported by this provider
     HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
     // Count for external process for which we have no handles.
     int externalProcessNoHandleCount;
-    final ActivityManagerService service;
-    final int uid;
-    final ApplicationInfo appInfo;
-    final ComponentName name;
     ProcessRecord proc; // if non-null, hosting process.
     ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
     String stringName;
+    String shortStringName;
 
     public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
             ApplicationInfo ai, ComponentName _name) {
-        super(_info);
         service = _service;
+        info = _info;
         uid = ai.uid;
         appInfo = ai;
         name = _name;
@@ -56,12 +64,20 @@
     }
 
     public ContentProviderRecord(ContentProviderRecord cpr) {
-        super(cpr.info);
+        service = cpr.service;
+        info = cpr.info;
         uid = cpr.uid;
         appInfo = cpr.appInfo;
         name = cpr.name;
         noReleaseNeeded = cpr.noReleaseNeeded;
-        service = cpr.service;
+    }
+
+    public ContentProviderHolder newHolder(ContentProviderConnection conn) {
+        ContentProviderHolder holder = new ContentProviderHolder(info);
+        holder.provider = provider;
+        holder.noReleaseNeeded = noReleaseNeeded;
+        holder.connection = conn;
+        return holder;
     }
 
     public boolean canRunHere(ProcessRecord app) {
@@ -120,30 +136,51 @@
         return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
     }
 
-    void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("package=");
-                pw.print(info.applicationInfo.packageName);
-                pw.print(" process="); pw.println(info.processName);
+    void dump(PrintWriter pw, String prefix, boolean full) {
+        if (full) {
+            pw.print(prefix); pw.print("package=");
+                    pw.print(info.applicationInfo.packageName);
+                    pw.print(" process="); pw.println(info.processName);
+        }
         pw.print(prefix); pw.print("proc="); pw.println(proc);
         if (launchingApp != null) {
             pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
         }
-        pw.print(prefix); pw.print("uid="); pw.print(uid);
-                pw.print(" provider="); pw.println(provider);
-        pw.print(prefix); pw.print("name="); pw.println(info.authority);
-        if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
-            pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
-                    pw.print("multiprocess="); pw.print(info.multiprocess);
-                    pw.print(" initOrder="); pw.println(info.initOrder);
+        if (full) {
+            pw.print(prefix); pw.print("uid="); pw.print(uid);
+                    pw.print(" provider="); pw.println(provider);
         }
-        if (hasExternalProcessHandles()) {
-            pw.print(prefix); pw.print("externals=");
-            pw.println(externalProcessTokenToHandle.size());
+        pw.print(prefix); pw.print("authority="); pw.println(info.authority);
+        if (full) {
+            if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
+                pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
+                        pw.print(" multiprocess="); pw.print(info.multiprocess);
+                        pw.print(" initOrder="); pw.println(info.initOrder);
+            }
         }
-        if (clients.size() > 0) {
-            pw.print(prefix); pw.println("Clients:");
-            for (ProcessRecord cproc : clients) {
-                pw.print(prefix); pw.print("  - "); pw.println(cproc.toShortString());
+        if (full) {
+            if (hasExternalProcessHandles()) {
+                pw.print(prefix); pw.print("externals=");
+                        pw.println(externalProcessTokenToHandle.size());
+            }
+        } else {
+            if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
+                pw.print(prefix); pw.print(connections.size());
+                        pw.print(" connections, "); pw.print(externalProcessNoHandleCount);
+                        pw.println(" external handles");
+            }
+        }
+        if (connections.size() > 0) {
+            if (full) {
+                pw.print(prefix); pw.println("Connections:");
+            }
+            for (int i=0; i<connections.size(); i++) {
+                ContentProviderConnection conn = connections.get(i);
+                pw.print(prefix); pw.print("  -> "); pw.println(conn.toClientString());
+                if (conn.provider != this) {
+                    pw.print(prefix); pw.print("    *** WRONG PROVIDER: ");
+                            pw.println(conn.provider);
+                }
             }
         }
     }
@@ -162,6 +199,17 @@
         return stringName = sb.toString();
     }
 
+    public String toShortString() {
+        if (shortStringName != null) {
+            return shortStringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append('/');
+        sb.append(name.flattenToShortString());
+        return shortStringName = sb.toString();
+    }
+
     // This class represents a handle from an external process to a provider.
     private class ExternalProcessHandle implements DeathRecipient {
         private static final String LOG_TAG = "ExternalProcessHanldle";
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 4529ecc..8a3ba7f 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -125,8 +125,8 @@
     final HashMap<String, ContentProviderRecord> pubProviders
             = new HashMap<String, ContentProviderRecord>(); 
     // All ContentProviderRecord process is using
-    final HashMap<ContentProviderRecord, Integer> conProviders
-            = new HashMap<ContentProviderRecord, Integer>(); 
+    final ArrayList<ContentProviderConnection> conProviders
+            = new ArrayList<ContentProviderConnection>();
     
     boolean persistent;         // always keep this application running?
     boolean crashing;           // are we in the process of crashing?
@@ -257,25 +257,47 @@
                     pw.println();
         }
         if (activities.size() > 0) {
-            pw.print(prefix); pw.print("activities="); pw.println(activities);
+            pw.print(prefix); pw.println("Activities:");
+            for (int i=0; i<activities.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(activities.get(i));
+            }
         }
         if (services.size() > 0) {
-            pw.print(prefix); pw.print("services="); pw.println(services);
+            pw.print(prefix); pw.println("Services:");
+            for (ServiceRecord sr : services) {
+                pw.print(prefix); pw.print("  - "); pw.println(sr);
+            }
         }
         if (executingServices.size() > 0) {
-            pw.print(prefix); pw.print("executingServices="); pw.println(executingServices);
+            pw.print(prefix); pw.println("Executing Services:");
+            for (ServiceRecord sr : executingServices) {
+                pw.print(prefix); pw.print("  - "); pw.println(sr);
+            }
         }
         if (connections.size() > 0) {
-            pw.print(prefix); pw.print("connections="); pw.println(connections);
+            pw.print(prefix); pw.println("Connections:");
+            for (ConnectionRecord cr : connections) {
+                pw.print(prefix); pw.print("  - "); pw.println(cr);
+            }
         }
         if (pubProviders.size() > 0) {
-            pw.print(prefix); pw.print("pubProviders="); pw.println(pubProviders);
+            pw.print(prefix); pw.println("Published Providers:");
+            for (HashMap.Entry<String, ContentProviderRecord> ent : pubProviders.entrySet()) {
+                pw.print(prefix); pw.print("  - "); pw.println(ent.getKey());
+                pw.print(prefix); pw.print("    -> "); pw.println(ent.getValue());
+            }
         }
         if (conProviders.size() > 0) {
-            pw.print(prefix); pw.print("conProviders="); pw.println(conProviders);
+            pw.print(prefix); pw.println("Connected Providers:");
+            for (int i=0; i<conProviders.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(conProviders.get(i).toShortString());
+            }
         }
         if (receivers.size() > 0) {
-            pw.print(prefix); pw.print("receivers="); pw.println(receivers);
+            pw.print(prefix); pw.println("Receivers:");
+            for (ReceiverList rl : receivers) {
+                pw.print(prefix); pw.print("  - "); pw.println(rl);
+            }
         }
     }
     
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index ccc928f..d148ec3 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -177,27 +177,9 @@
         while (it.hasNext()) {
             Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
             ContentProviderRecord r = e.getValue();
-            if (dumpAll) {
-                pw.print("  * ");
-                pw.println(r);
-                r.dump(pw, "    ");
-            } else {
-                pw.print("  * ");
-                pw.println(r);
-                if (r.proc != null) {
-                    pw.print("    proc=");
-                    pw.println(r.proc);
-                }
-                if (r.launchingApp != null) {
-                    pw.print("    launchingApp=");
-                    pw.println(r.launchingApp);
-                }
-                if (r.clients.size() > 0 || r.externalProcessNoHandleCount > 0) {
-                    pw.print("    "); pw.print(r.clients.size());
-                            pw.print(" clients, "); pw.print(r.externalProcessNoHandleCount);
-                            pw.println(" external handles");
-                }
-            }
+            pw.print("  * ");
+            pw.println(r);
+            r.dump(pw, "    ", dumpAll);
         }
     }
 
@@ -210,7 +192,7 @@
             pw.print("  ");
             pw.print(e.getKey());
             pw.print(": ");
-            pw.println(r);
+            pw.println(r.toShortString());
         }
     }
 
@@ -221,10 +203,10 @@
                 pw.println(" ");
             pw.println("  Published content providers (by class):");
             dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
-            pw.println("");
         }
 
         if (mProvidersByClassPerUser.size() > 1) {
+            pw.println("");
             for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
                 HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
                 pw.println("  User " + mProvidersByClassPerUser.keyAt(i) + ":");
@@ -321,7 +303,7 @@
                     if (r.proc != null) pw.println(r.proc.pid);
                     else pw.println("(not running)");
             if (dumpAll) {
-                r.dump(pw, innerPrefix);
+                r.dump(pw, innerPrefix, true);
             }
         }
         if (r.proc != null && r.proc.thread != null) {
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index b38ea13..07f3125 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -39,27 +39,28 @@
 
     private static final String TAG = "GeocoderProxy";
 
+    public static final String SERVICE_ACTION =
+        "com.android.location.service.GeocodeProvider";
+
     private final Context mContext;
     private final Intent mIntent;
     private final Object mMutex = new Object();  // synchronizes access to mServiceConnection
-    private Connection mServiceConnection = new Connection();  // never null
+    private Connection mServiceConnection;  // never null after ctor
 
-    public GeocoderProxy(Context context, String serviceName) {
+    public GeocoderProxy(Context context, String packageName) {
         mContext = context;
-        mIntent = new Intent(serviceName);
-        mContext.bindService(mIntent, mServiceConnection,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                | Context.BIND_ALLOW_OOM_MANAGEMENT);
+        mIntent = new Intent(SERVICE_ACTION);
+        reconnect(packageName);
     }
 
-    /**
-     * When unbundled NetworkLocationService package is updated, we
-     * need to unbind from the old version and re-bind to the new one.
-     */
-    public void reconnect() {
+    /** Bind to service. Will reconnect if already connected */
+    public void reconnect(String packageName) {
         synchronized (mMutex) {
-            mContext.unbindService(mServiceConnection);
+            if (mServiceConnection != null) {
+                mContext.unbindService(mServiceConnection);
+            }
             mServiceConnection = new Connection();
+            mIntent.setPackage(packageName);
             mContext.bindService(mIntent, mServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
                     | Context.BIND_ALLOW_OOM_MANAGEMENT);
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 0bc1664..a227ab6 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -42,12 +42,15 @@
 
     private static final String TAG = "LocationProviderProxy";
 
+    public static final String SERVICE_ACTION =
+        "com.android.location.service.NetworkLocationProvider";
+
     private final Context mContext;
     private final String mName;
     private final Intent mIntent;
     private final Handler mHandler;
     private final Object mMutex = new Object();  // synchronizes access to non-final members
-    private Connection mServiceConnection = new Connection();  // never null
+    private Connection mServiceConnection;  // never null after ctor
 
     // cached values set by the location manager
     private boolean mLocationTracking = false;
@@ -58,28 +61,26 @@
     private NetworkInfo mNetworkInfo;
 
     // constructor for proxying location providers implemented in a separate service
-    public LocationProviderProxy(Context context, String name, String serviceName,
+    public LocationProviderProxy(Context context, String name, String packageName,
             Handler handler) {
         mContext = context;
         mName = name;
-        mIntent = new Intent(serviceName);
+        mIntent = new Intent(SERVICE_ACTION);
         mHandler = handler;
-        mContext.bindService(mIntent, mServiceConnection,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                | Context.BIND_ALLOW_OOM_MANAGEMENT);
+        reconnect(packageName);
     }
 
-    /**
-     * When unbundled NetworkLocationService package is updated, we
-     * need to unbind from the old version and re-bind to the new one.
-     */
-    public void reconnect() {
+    /** Bind to service. Will reconnect if already connected */
+    public void reconnect(String packageName) {
         synchronized (mMutex) {
-            mContext.unbindService(mServiceConnection);
+            if (mServiceConnection != null) {
+                mContext.unbindService(mServiceConnection);
+            }
             mServiceConnection = new Connection();
+            mIntent.setPackage(packageName);
             mContext.bindService(mIntent, mServiceConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                    | Context.BIND_ALLOW_OOM_MANAGEMENT);
+                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND |
+                    Context.BIND_ALLOW_OOM_MANAGEMENT);
         }
     }
 
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index de756b1..1953ad7 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -79,12 +79,9 @@
     }
 
     public void setDummyAnimation() {
-        if (animation == null) {
-            if (WindowManagerService.localLOGV) Slog.v(
-                TAG, "Setting dummy animation in " + mAppToken);
-            animation = sDummyAnimation;
-            animInitialized = false;
-        }
+        if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken);
+        animation = sDummyAnimation;
+        animInitialized = false;
         hasTransformation = true;
         transformation.clear();
         transformation.setAlpha(mAppToken.reportedVisible ? 1 : 0);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b1612a1..4ce8c97 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -1638,7 +1638,7 @@
                 // it is of no interest to us.
                 if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
-                            "Skipping not hidden or animating token: " + w);
+                            "Skipping hidden and not animating token: " + w);
                     continue;
                 }
             }
@@ -3544,7 +3544,8 @@
             wtoken.groupId = groupId;
             wtoken.appFullscreen = fullscreen;
             wtoken.requestedOrientation = requestedOrientation;
-            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + wtoken);
+            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + wtoken
+                    + " at " + addPos);
             mAppTokens.add(addPos, wtoken);
             addAppTokenToAnimating(addPos, wtoken);
             mTokenMap.put(token.asBinder(), wtoken);
@@ -3864,6 +3865,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(
                     TAG, "Prepare app transition: transit=" + transit
                     + " mNextAppTransition=" + mNextAppTransition
+                    + " alwaysKeepCurrent=" + alwaysKeepCurrent
                     + " Callers=" + Debug.getCallers(3));
             if (okToDisplay()) {
                 if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
@@ -3933,6 +3935,15 @@
         }
     }
 
+    private void cancelWindowAnimations(final AppWindowToken wtoken) {
+        for (int i = wtoken.windows.size() - 1; i >= 0; i--) {
+            final WindowStateAnimator winAnimator = wtoken.windows.get(i).mWinAnimator;
+            if (winAnimator.isAnimating()) {
+                winAnimator.clearAnimation();
+            }
+        }
+    }
+
     public void executeAppTransition() {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "executeAppTransition()")) {
@@ -3948,6 +3959,12 @@
             }
             if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
                 mAppTransitionReady = true;
+                for (int i = mOpeningApps.size() - 1; i >= 0; i--) {
+                    cancelWindowAnimations(mOpeningApps.get(i));
+                }
+                for (int i = mClosingApps.size() - 1; i >= 0; i--) {
+                    cancelWindowAnimations(mClosingApps.get(i));
+                }
                 final long origId = Binder.clearCallingIdentity();
                 performLayoutAndPlaceSurfacesLocked();
                 Binder.restoreCallingIdentity(origId);
@@ -4295,6 +4312,7 @@
 
                 if (DEBUG_APP_TRANSITIONS) Slog.v(
                         TAG, "Setting dummy animation on: " + wtoken);
+                cancelWindowAnimations(wtoken);
                 wtoken.mAppAnimator.setDummyAnimation();
                 mOpeningApps.remove(wtoken);
                 mClosingApps.remove(wtoken);
@@ -4816,8 +4834,7 @@
                 }
             }
 
-            if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
-                    && !mAppTransitionRunning) {
+            if (!mAppTransitionRunning) {
                 mAnimatingAppTokens.clear();
                 mAnimatingAppTokens.addAll(mAppTokens);
                 moveAppWindowsLocked(tokens, mAppTokens.size());
@@ -4836,8 +4853,7 @@
         final long origId = Binder.clearCallingIdentity();
         synchronized(mWindowMap) {
             final int N = tokens.size();
-            if (N > 0 && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET
-                    && !mAppTransitionRunning) {
+            if (N > 0 && !mAppTransitionRunning) {
                 // animating towards back, hang onto old list for duration of animation.
                 mAnimatingAppTokens.clear();
                 mAnimatingAppTokens.addAll(mAppTokens);
@@ -4857,8 +4873,7 @@
                 }
             }
 
-            if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
-                    && !mAppTransitionRunning) {
+            if (!mAppTransitionRunning) {
                 mAnimatingAppTokens.clear();
                 mAnimatingAppTokens.addAll(mAppTokens);
                 moveAppWindowsLocked(tokens, 0);
@@ -5321,7 +5336,8 @@
             // the background..)
             if (on) {
                 boolean isVisible = false;
-                for (WindowState ws : mWindows) {
+                for (int i = mWindows.size() - 1; i >= 0; i--) {
+                    final WindowState ws = mWindows.get(i);
                     if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
                         isVisible = true;
                         break;
@@ -6439,7 +6455,10 @@
 
             int keyboardPresence = 0;
             int navigationPresence = 0;
-            for (InputDevice device : mInputManager.getInputDevices()) {
+            final InputDevice[] devices = mInputManager.getInputDevices();
+            final int len = devices.length;
+            for (int i = 0; i < len; i++) {
+                InputDevice device = devices[i];
                 if (!device.isVirtual()) {
                     final int sources = device.getSources();
                     final int presenceFlag = device.isExternal() ?
@@ -7948,7 +7967,7 @@
             for (i=0; i<NN && goodToGo; i++) {
                 AppWindowToken wtoken = mOpeningApps.get(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                        "Check opening app" + wtoken + ": allDrawn="
+                        "Check opening app=" + wtoken + ": allDrawn="
                         + wtoken.allDrawn + " startingDisplayed="
                         + wtoken.startingDisplayed + " startingMoved="
                         + wtoken.startingMoved);
@@ -8058,7 +8077,7 @@
                 }
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                         "New transit: " + transit);
-            } else if (oldWallpaper != null) {
+            } else if ((oldWallpaper != null) && (oldWallpaper != mWallpaperTarget)) {
                 // We are transitioning from an activity with
                 // a wallpaper to one without.
                 transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE;
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 28b729d..41125dd 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -78,6 +78,10 @@
     public static final String RECEIVE_EMERGENCY_BROADCAST_PERMISSION =
             "android.permission.RECEIVE_EMERGENCY_BROADCAST";
 
+    /** Permission required to send SMS to short codes without user confirmation. */
+    private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION =
+            "android.permission.SEND_SMS_NO_CONFIRMATION";
+
     /** Query projection for checking for duplicate message segments. */
     private static final String[] PDU_PROJECTION = new String[] {
             "pdu"
@@ -944,7 +948,8 @@
      * @return true if the destination is approved; false if user confirmation event was sent
      */
     boolean checkDestination(SmsTracker tracker) {
-        if (mUsageMonitor.isApprovedShortCodeSender(tracker.mAppPackage)) {
+        if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION)
+                == PackageManager.PERMISSION_GRANTED) {
             return true;            // app is pre-approved to send to short codes
         } else {
             String countryIso = mTelephonyManager.getSimCountryIso();
diff --git a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
index f40958d..1804d97 100644
--- a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -81,12 +81,6 @@
     private final HashMap<String, ArrayList<Long>> mSmsStamp =
             new HashMap<String, ArrayList<Long>>();
 
-    /**
-     * Hash of package names that are allowed to send to short codes.
-     * TODO: persist this across reboots.
-     */
-    private final HashSet<String> mApprovedShortCodeSenders = new HashSet<String>();
-
     /** Context for retrieving regexes from XML resource. */
     private final Context mContext;
 
@@ -248,9 +242,6 @@
                 DEFAULT_SMS_CHECK_PERIOD);
 
         mSettingsObserverHandler = new SettingsObserverHandler();
-
-        // system MMS app is always allowed to send to short codes
-        mApprovedShortCodeSenders.add("com.android.mms");
     }
 
     /**
@@ -358,28 +349,6 @@
     }
 
     /**
-     * Return whether the app is approved to send to any short code.
-     * @param appName the package name of the app requesting to send an SMS
-     * @return true if the app is approved; false if we need to confirm short code destinations
-     */
-    public boolean isApprovedShortCodeSender(String appName) {
-        synchronized (mApprovedShortCodeSenders) {
-            return mApprovedShortCodeSenders.contains(appName);
-        }
-    }
-
-    /**
-     * Add app package name to the list of approved short code senders.
-     * @param appName the package name of the app to add
-     */
-    public void addApprovedShortCodeSender(String appName) {
-        if (DBG) log("Adding " + appName + " to list of approved short code senders.");
-        synchronized (mApprovedShortCodeSenders) {
-            mApprovedShortCodeSenders.add(appName);
-        }
-    }
-
-    /**
      * Check if the destination is a possible premium short code.
      * NOTE: the caller is expected to strip non-digits from the destination number with
      * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
diff --git a/test-runner/src/android/test/mock/MockContentResolver.java b/test-runner/src/android/test/mock/MockContentResolver.java
index 65eb21b..715da0f 100644
--- a/test-runner/src/android/test/mock/MockContentResolver.java
+++ b/test-runner/src/android/test/mock/MockContentResolver.java
@@ -118,6 +118,11 @@
         return releaseProvider(icp);
     }
 
+    /** @hide */
+    @Override
+    public void unstableProviderDied(IContentProvider icp) {
+    }
+
     /**
      * Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)
      * ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
index fec2c3f..8d259d7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java
@@ -72,6 +72,11 @@
         return releaseProvider(icp);
     }
 
+    /** @hide */
+    @Override
+    public void unstableProviderDied(IContentProvider icp) {
+    }
+
     /**
      * Stub for the layoutlib bridge content resolver.
      */