Merge "Fix 6538388: Home buttons do not respect the no "vibrate on touch" option." 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/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 89068e7..035a7c6 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1059,6 +1059,7 @@
         }
 
         native_takePicture(msgType);
+        mFaceDetectionRunning = false;
     }
 
     /**
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 33dea6c..46153e7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1892,6 +1892,13 @@
      * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
      * that they don't impact the current touch mode of the UI.
      *
+     * <p>Note that it's discouraged to send such key events in normal operation;
+     * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
+     * text fields, or for non-rich input methods. A reasonably capable software
+     * input method should use the
+     * {@link android.view.inputmethod.InputConnection#commitText} family of methods
+     * to send text to an application, rather than sending key events.</p>
+     *
      * @param keyEventCode The raw key code to send, as defined by
      * {@link KeyEvent}.
      */
@@ -1949,7 +1956,11 @@
      * {@link InputConnection#commitText InputConnection.commitText()} with
      * the character; some, however, may be handled different.  In particular,
      * the enter character ('\n') will either be delivered as an action code
-     * or a raw key event, as appropriate.
+     * or a raw key event, as appropriate.  Consider this as a convenience
+     * method for IMEs that do not have a full implementation of actions; a
+     * fully complying IME will decide of the right action for each event and
+     * will likely never call this method except maybe to handle events coming
+     * from an actual hardware keyboard.
      * 
      * @param charCode The UTF-16 character code to send.
      */
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 7e29dc7..4fede32 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -28,6 +28,10 @@
  * Provides a basic foundation for entering and editing text.
  * Subclasses should override {@link #onKeyDown} and {@link #onKeyUp} to insert
  * characters as keys are pressed.
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public abstract class BaseKeyListener extends MetaKeyKeyListener
         implements KeyListener {
diff --git a/core/java/android/text/method/DateKeyListener.java b/core/java/android/text/method/DateKeyListener.java
index 7c11434..e6f63d1 100644
--- a/core/java/android/text/method/DateKeyListener.java
+++ b/core/java/android/text/method/DateKeyListener.java
@@ -21,6 +21,10 @@
 
 /**
  * For entering dates in a text field.
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class DateKeyListener extends NumberKeyListener
 {
diff --git a/core/java/android/text/method/DateTimeKeyListener.java b/core/java/android/text/method/DateTimeKeyListener.java
index f8ebc40..523e986 100644
--- a/core/java/android/text/method/DateTimeKeyListener.java
+++ b/core/java/android/text/method/DateTimeKeyListener.java
@@ -21,6 +21,10 @@
 
 /**
  * For entering dates and times in the same text field.
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class DateTimeKeyListener extends NumberKeyListener
 {
diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java
index 07127b7..ce51fae 100644
--- a/core/java/android/text/method/DialerKeyListener.java
+++ b/core/java/android/text/method/DialerKeyListener.java
@@ -23,6 +23,10 @@
 
 /**
  * For dialing-only text entry
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class DialerKeyListener extends NumberKeyListener
 {
diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java
index f0f072c..3d9daed 100644
--- a/core/java/android/text/method/DigitsKeyListener.java
+++ b/core/java/android/text/method/DigitsKeyListener.java
@@ -24,6 +24,10 @@
 
 /**
  * For digits-only text entry
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class DigitsKeyListener extends NumberKeyListener
 {
diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java
index 318149a..bb79ecd 100644
--- a/core/java/android/text/method/KeyListener.java
+++ b/core/java/android/text/method/KeyListener.java
@@ -27,6 +27,12 @@
  * {@link android.view.inputmethod.InputMethod}; it should only be used
  * for cases where an application has its own on-screen keypad and also wants
  * to process hard keyboard events to match it.
+ * <p></p>
+ * Key presses on soft input methods are not required to trigger the methods
+ * in this listener, and are in fact discouraged to do so.  The default
+ * android keyboard will not trigger these for any key to any application
+ * targetting Jelly Bean or later, and will only deliver it for some
+ * key presses to applications targetting Ice Cream Sandwich or earlier.
  */
 public interface KeyListener {
     /**
diff --git a/core/java/android/text/method/MultiTapKeyListener.java b/core/java/android/text/method/MultiTapKeyListener.java
index 2a739fa..95ac0a1 100644
--- a/core/java/android/text/method/MultiTapKeyListener.java
+++ b/core/java/android/text/method/MultiTapKeyListener.java
@@ -28,6 +28,10 @@
  * This is the standard key listener for alphabetic input on 12-key
  * keyboards.  You should generally not need to instantiate this yourself;
  * TextKeyListener will do it for you.
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class MultiTapKeyListener extends BaseKeyListener
         implements SpanWatcher {
diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java
index 1e72309..5d4c732 100644
--- a/core/java/android/text/method/NumberKeyListener.java
+++ b/core/java/android/text/method/NumberKeyListener.java
@@ -27,6 +27,10 @@
 
 /**
  * For numeric text entry
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public abstract class NumberKeyListener extends BaseKeyListener
     implements InputFilter
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index 4c82b81..c5261f3 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -27,6 +27,10 @@
  * This is the standard key listener for alphabetic input on qwerty
  * keyboards.  You should generally not need to instantiate this yourself;
  * TextKeyListener will do it for you.
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class QwertyKeyListener extends BaseKeyListener {
     private static QwertyKeyListener[] sInstance =
diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java
index 8312fe1..994f3d7 100644
--- a/core/java/android/text/method/TextKeyListener.java
+++ b/core/java/android/text/method/TextKeyListener.java
@@ -33,6 +33,10 @@
 /**
  * This is the key listener for typing normal text.  It delegates to
  * other key listeners appropriate to the current keyboard and language.
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class TextKeyListener extends BaseKeyListener implements SpanWatcher {
     private static TextKeyListener[] sInstance =
diff --git a/core/java/android/text/method/TimeKeyListener.java b/core/java/android/text/method/TimeKeyListener.java
index 3fbfd8c..c5bfd5c 100644
--- a/core/java/android/text/method/TimeKeyListener.java
+++ b/core/java/android/text/method/TimeKeyListener.java
@@ -21,6 +21,10 @@
 
 /**
  * For entering times in a text field.
+ * <p></p>
+ * As for all implementations of {@link KeyListener}, this class is only concerned
+ * with hardware keyboards.  Software input methods have no obligation to trigger
+ * the methods in this class.
  */
 public class TimeKeyListener extends NumberKeyListener
 {
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index cd54f24..2a7dc18 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -47,7 +47,6 @@
      * @hide
      */
     public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
-        protected static final int DONE = -1;
 
         protected String mText;
 
@@ -104,20 +103,20 @@
             if (offset >= textLegth) {
                 return null;
             }
-            int start = -1;
-            if (offset < 0) {
-                offset = 0;
-                if (mImpl.isBoundary(offset)) {
-                    start = offset;
+            int start = offset;
+            if (start < 0) {
+                start = 0;
+            }
+            while (!mImpl.isBoundary(start)) {
+                start = mImpl.following(start);
+                if (start == BreakIterator.DONE) {
+                    return null;
                 }
             }
-            if (start < 0) {
-                start = mImpl.following(offset);
-            }
-            if (start < 0) {
+            final int end = mImpl.following(start);
+            if (end == BreakIterator.DONE) {
                 return null;
             }
-            final int end = mImpl.following(start);
             return getRange(start, end);
         }
 
@@ -130,20 +129,20 @@
             if (offset <= 0) {
                 return null;
             }
-            int end = -1;
-            if (offset > mText.length()) {
-                offset = mText.length();
-                if (mImpl.isBoundary(offset)) {
-                    end = offset;
+            int end = offset;
+            if (end > textLegth) {
+                end = textLegth;
+            }
+            while (!mImpl.isBoundary(end)) {
+                end = mImpl.preceding(end);
+                if (end == BreakIterator.DONE) {
+                    return null;
                 }
             }
-            if (end < 0) {
-                end = mImpl.preceding(offset);
-            }
-            if (end < 0) {
+            final int start = mImpl.preceding(end);
+            if (start == BreakIterator.DONE) {
                 return null;
             }
-            final int start = mImpl.preceding(end);
             return getRange(start, end);
         }
 
@@ -195,25 +194,20 @@
             if (offset >= mText.length()) {
                 return null;
             }
-            int start = -1;
-            if (offset < 0) {
-                offset = 0;
-                if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) {
-                    start = offset;
-                }
-            }
+            int start = offset;
             if (start < 0) {
-                while ((offset = mImpl.following(offset)) != DONE) {
-                    if (isLetterOrDigit(offset)) {
-                        start = offset;
-                        break;
-                    }
-                }
+                start = 0;
             }
-            if (start < 0) {
-                return null;
+            while (!isLetterOrDigit(start) && !isStartBoundary(start)) {
+                start = mImpl.following(start);
+                if (start == BreakIterator.DONE) {
+                    return null;
+                }
             }
             final int end = mImpl.following(start);
+            if (end == BreakIterator.DONE || !isEndBoundary(end)) {
+                return null;
+            }
             return getRange(start, end);
         }
 
@@ -226,28 +220,33 @@
             if (offset <= 0) {
                 return null;
             }
-            int end = -1;
-            if (offset > mText.length()) {
-                offset = mText.length();
-                if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) {
-                    end = offset;
-                }
+            int end = offset;
+            if (end > textLegth) {
+                end = textLegth;
             }
-            if (end < 0) {
-                while ((offset = mImpl.preceding(offset)) != DONE) {
-                    if (offset > 0 && isLetterOrDigit(offset - 1)) {
-                        end = offset;
-                        break;
-                    }
+            while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) {
+                end = mImpl.preceding(end);
+                if (end == BreakIterator.DONE) {
+                    return null;
                 }
             }
-            if (end < 0) {
-                return null;
-            }
             final int start = mImpl.preceding(end);
+            if (start == BreakIterator.DONE || !isStartBoundary(start)) {
+                return null;
+            }
             return getRange(start, end);
         }
 
+        private boolean isStartBoundary(int index) {
+            return isLetterOrDigit(index)
+                && (index == 0 || !isLetterOrDigit(index - 1));
+        }
+
+        private boolean isEndBoundary(int index) {
+            return (index > 0 && isLetterOrDigit(index - 1))
+                && (index == mText.length() || !isLetterOrDigit(index));
+        }
+
         private boolean isLetterOrDigit(int index) {
             if (index >= 0 && index < mText.length()) {
                 final int codePoint = mText.codePointAt(index);
@@ -276,31 +275,19 @@
             if (offset >= textLength) {
                 return null;
             }
-            int start = -1;
-            if (offset < 0) {
-                start = 0;
-            } else {
-                for (int i = offset + 1; i < textLength; i++) {
-                    if (mText.charAt(i) == '\n') {
-                        start = i;
-                        break;
-                    }
-                }
-            }
+            int start = offset;
             if (start < 0) {
-                return null;
+                start = 0;
             }
-            while (start < textLength && mText.charAt(start) == '\n') {
+            while (start < textLength && mText.charAt(start) == '\n'
+                    && !isStartBoundary(start)) {
                 start++;
             }
-            int end = start;
-            for (int i = end + 1; i < textLength; i++) {
-                end = i;
-                if (mText.charAt(i) == '\n') {
-                    break;
-                }
+            if (start >= textLength) {
+                return null;
             }
-            while (end < textLength && mText.charAt(end) == '\n') {
+            int end = start + 1;
+            while (end < textLength && !isEndBoundary(end)) {
                 end++;
             }
             return getRange(start, end);
@@ -315,38 +302,31 @@
             if (offset <= 0) {
                 return null;
             }
-            int end = -1;
-            if (offset > mText.length()) {
-                end = mText.length();
-            } else {
-                if (offset > 0 && mText.charAt(offset - 1) == '\n') {
-                    offset--;
-                }
-                for (int i = offset - 1; i >= 0; i--) {
-                    if (i > 0 && mText.charAt(i - 1) == '\n') {
-                        end = i;
-                        break;
-                    }
-                }
+            int end = offset;
+            if (end > textLength) {
+                end = textLength;
+            }
+            while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) {
+                end--;
             }
             if (end <= 0) {
                 return null;
             }
-            int start = end;
-            while (start > 0 && mText.charAt(start - 1) == '\n') {
+            int start = end - 1;
+            while (start > 0 && !isStartBoundary(start)) {
                 start--;
             }
-            if (start == 0 && mText.charAt(start) == '\n') {
-                return null;
-            }
-            for (int i = start - 1; i >= 0; i--) {
-                start = i;
-                if (start > 0 && mText.charAt(i - 1) == '\n') {
-                    break;
-                }
-            }
-            start = Math.max(0, start);
             return getRange(start, end);
         }
+
+        private boolean isStartBoundary(int index) {
+            return (mText.charAt(index) != '\n'
+                && (index == 0 || mText.charAt(index - 1) == '\n'));
+        }
+
+        private boolean isEndBoundary(int index) {
+            return (index > 0 && mText.charAt(index - 1) != '\n'
+                && (index == mText.length() || mText.charAt(index) == '\n'));
+        }
     }
 }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index ace7aa8..1080229 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -52,6 +52,19 @@
  * to characters.  Be aware that there may be multiple key input devices active
  * at the same time and each will have its own key character map.
  * </p><p>
+ * As soft input methods can use multiple and inventive ways of inputting text,
+ * there is no guarantee that any key press on a soft keyboard will generate a key
+ * event: this is left to the IME's discretion, and in fact sending such events is
+ * discouraged.  You should never rely on receiving KeyEvents for any key on a soft
+ * input method.  In particular, the default software keyboard will never send any
+ * key event to any application targetting Jelly Bean or later, and will only send
+ * events for some presses of the delete and return keys to applications targetting
+ * Ice Cream Sandwich or earlier.  Be aware that other software input methods may
+ * never send key events regardless of the version.  Consider using editor actions
+ * like {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} if you need
+ * specific interaction with the software keyboard, as it gives more visibility to
+ * the user as to how your application will react to key presses.
+ * </p><p>
  * When interacting with an IME, the framework may deliver key events
  * with the special action {@link #ACTION_MULTIPLE} that either specifies
  * that single repeated key code or a sequence of characters to insert.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8cb5c85..9613149 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -204,12 +204,12 @@
  *     <tr>
  *         <td rowspan="4">Event processing</td>
  *         <td><code>{@link #onKeyDown(int, KeyEvent)}</code></td>
- *         <td>Called when a new key event occurs.
+ *         <td>Called when a new hardware key event occurs.
  *         </td>
  *     </tr>
  *     <tr>
  *         <td><code>{@link #onKeyUp(int, KeyEvent)}</code></td>
- *         <td>Called when a key up event occurs.
+ *         <td>Called when a hardware key up event occurs.
  *         </td>
  *     </tr>
  *     <tr>
@@ -1596,7 +1596,7 @@
     /**
      * @hide
      */
-    private int mAccessibilityCursorPosition = -1;
+    private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
 
     /**
      * The view's tag.
@@ -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;
@@ -2468,6 +2535,11 @@
     public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
 
     /**
+     * The undefined cursor position.
+     */
+    private static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1;
+
+    /**
      * Indicates that the screen has changed state and is now off.
      *
      * @see #onScreenStateChanged(int)
@@ -3132,7 +3204,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;
@@ -4082,7 +4155,9 @@
     }
 
     /**
-     * Register a callback to be invoked when a key is pressed in this view.
+     * Register a callback to be invoked when a hardware key is pressed in this view.
+     * Key presses in software input methods will generally not trigger the methods of
+     * this listener.
      * @param l the key listener to attach to this view
      */
     public void setOnKeyListener(OnKeyListener l) {
@@ -4788,7 +4863,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 +6147,7 @@
             return;
         }
         if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
-            if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) {
+            if (isAccessibilityFocusable()) {
                 views.add(this);
                 return;
             }
@@ -6202,7 +6280,7 @@
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
             notifyAccessibilityStateChanged();
             // Clear the text navigation state.
-            setAccessibilityCursorPosition(-1);
+            setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
         }
         // Clear the global reference of accessibility focus if this
         // view or any of its descendants had accessibility focus.
@@ -6252,6 +6330,7 @@
     void clearAccessibilityFocusNoCallbacks() {
         if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
+            setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
             invalidate();
         }
     }
@@ -6403,12 +6482,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 +6537,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 +6784,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;
@@ -6681,12 +6827,11 @@
         final int current = getAccessibilityCursorPosition();
         final int[] range = iterator.following(current);
         if (range == null) {
-            setAccessibilityCursorPosition(-1);
             return false;
         }
         final int start = range[0];
         final int end = range[1];
-        setAccessibilityCursorPosition(start);
+        setAccessibilityCursorPosition(end);
         sendViewTextTraversedAtGranularityEvent(
                 AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
                 granularity, start, end);
@@ -6702,16 +6847,26 @@
         if (iterator == null) {
             return false;
         }
-        final int selectionStart = getAccessibilityCursorPosition();
-        final int current = selectionStart >= 0 ? selectionStart : text.length() + 1;
+        int current = getAccessibilityCursorPosition();
+        if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
+            current = text.length();
+        } else if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
+            // When traversing by character we always put the cursor after the character
+            // to ease edit and have to compensate before asking the for previous segment.
+            current--;
+        }
         final int[] range = iterator.preceding(current);
         if (range == null) {
-            setAccessibilityCursorPosition(-1);
             return false;
         }
         final int start = range[0];
         final int end = range[1];
-        setAccessibilityCursorPosition(end);
+        // Always put the cursor after the character to ease edit.
+        if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
+            setAccessibilityCursorPosition(end);
+        } else {
+            setAccessibilityCursorPosition(start);
+        }
         sendViewTextTraversedAtGranularityEvent(
                 AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
                 granularity, start, end);
@@ -7389,6 +7544,10 @@
      * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER}
      * is released, if the view is enabled and clickable.
      *
+     * <p>Key presses in software keyboards will generally NOT trigger this listener,
+     * although some may elect to do so in some situations. Do not rely on this to
+     * catch software key presses.
+     *
      * @param keyCode A key code that represents the button pressed, from
      *                {@link android.view.KeyEvent}.
      * @param event   The KeyEvent object that defines the button action.
@@ -7420,6 +7579,9 @@
      * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
      * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
      * the event).
+     * <p>Key presses in software keyboards will generally NOT trigger this listener,
+     * although some may elect to do so in some situations. Do not rely on this to
+     * catch software key presses.
      */
     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
         return false;
@@ -7430,6 +7592,9 @@
      * KeyEvent.Callback.onKeyUp()}: perform clicking of the view
      * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or
      * {@link KeyEvent#KEYCODE_ENTER} is released.
+     * <p>Key presses in software keyboards will generally NOT trigger this listener,
+     * although some may elect to do so in some situations. Do not rely on this to
+     * catch software key presses.
      *
      * @param keyCode A key code that represents the button pressed, from
      *                {@link android.view.KeyEvent}.
@@ -7464,6 +7629,9 @@
      * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
      * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
      * the event).
+     * <p>Key presses in software keyboards will generally NOT trigger this listener,
+     * although some may elect to do so in some situations. Do not rely on this to
+     * catch software key presses.
      *
      * @param keyCode     A key code that represents the button pressed, from
      *                    {@link android.view.KeyEvent}.
@@ -8023,6 +8191,7 @@
     private void removeSendViewScrolledAccessibilityEventCallback() {
         if (mSendViewScrolledAccessibilityEvent != null) {
             removeCallbacks(mSendViewScrolledAccessibilityEvent);
+            mSendViewScrolledAccessibilityEvent.mIsPending = false;
         }
     }
 
@@ -16760,14 +16929,20 @@
     }
 
     /**
-     * Interface definition for a callback to be invoked when a key event is
-     * dispatched to this view. The callback will be invoked before the key
-     * event is given to the view.
+     * Interface definition for a callback to be invoked when a hardware key event is
+     * dispatched to this view. The callback will be invoked before the key event is
+     * given to the view. This is only useful for hardware keyboards; a software input
+     * method has no obligation to trigger this listener.
      */
     public interface OnKeyListener {
         /**
-         * Called when a key is dispatched to a view. This allows listeners to
+         * Called when a hardware key is dispatched to a view. This allows listeners to
          * get a chance to respond before the target view.
+         * <p>Key presses in software keyboards will generally NOT trigger this method,
+         * although some may elect to do so in some situations. Do not assume a
+         * software input method has to be key-based; even if it is, it may use key presses
+         * in a different way than you expect, so there is no way to reliably catch soft
+         * input key presses.
          *
          * @param v The view the key has been dispatched to.
          * @param keyCode The code for the physical key that was pressed
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 3563d4d..76c6d19 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -283,7 +283,7 @@
     
     /**
      * Tell the editor that you are done with a batch edit previously
-     * initiated with {@link #endBatchEdit}.
+     * initiated with {@link #beginBatchEdit}.
      */
     public boolean endBatchEdit();
     
@@ -307,7 +307,11 @@
      * {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD} on all
      * key event objects you give to this API; the flag will not be set
      * for you.
-     * 
+     *
+     * <p>Note that it's discouraged to send such key events in normal operation;
+     * this is mainly for use with {@link android.text.InputType#TYPE_NULL} type
+     * text fields. Use the {@link #commitText} family of methods to send text
+     * to the application instead.
      * @param event The key event.
      *        
      * @return Returns true on success, false if the input connection is no longer
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index fc59e6e..161e8fb 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -217,7 +217,7 @@
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
             boolean allowImplicitlySelectedSubtype) {
         try {
-            // Passing null as a locale for ICS
+            // Passing null as a locale until we support multiple enabled spell checker subtypes.
             return sService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
         } catch (RemoteException e) {
             Log.e(TAG, "Error in getCurrentSpellCheckerSubtype: " + e);
diff --git a/core/java/android/webkit/AutoCompletePopup.java b/core/java/android/webkit/AutoCompletePopup.java
index 87e878b..c624ce4 100644
--- a/core/java/android/webkit/AutoCompletePopup.java
+++ b/core/java/android/webkit/AutoCompletePopup.java
@@ -181,8 +181,11 @@
                 // There is no autofill profile setup yet and the user has
                 // elected to try and set one up. Call through to the
                 // embedder to action that.
-                mWebView.getWebChromeClient().setupAutoFill(
+                WebChromeClient webChromeClient = mWebView.getWebChromeClient();
+                if (webChromeClient != null) {
+                    webChromeClient.setupAutoFill(
                         mHandler.obtainMessage(AUTOFILL_FORM));
+                }
             }
         } else {
             Object selectedItem;
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
index d8065e9..f64547f 100644
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ b/core/java/android/webkit/WebViewInputDispatcher.java
@@ -661,6 +661,7 @@
                         // Web kit has decided to consume the event!
                         if (d.mEventType == EVENT_TYPE_TOUCH) {
                             enqueueUiCancelTouchEventIfNeededLocked();
+                            unscheduleLongPressLocked();
                         }
                     } else {
                         // Web kit is being friendly.  Pass the event to the UI.
diff --git a/core/java/android/widget/AccessibilityIterators.java b/core/java/android/widget/AccessibilityIterators.java
index e800e8d..a3d58a4 100644
--- a/core/java/android/widget/AccessibilityIterators.java
+++ b/core/java/android/widget/AccessibilityIterators.java
@@ -56,16 +56,18 @@
             if (offset >= mText.length()) {
                 return null;
             }
-            int nextLine = -1;
+            int nextLine;
             if (offset < 0) {
                 nextLine = mLayout.getLineForOffset(0);
             } else {
                 final int currentLine = mLayout.getLineForOffset(offset);
-                if (currentLine < mLayout.getLineCount() - 1) {
+                if (getLineEdgeIndex(currentLine, DIRECTION_START) == offset) {
+                    nextLine = currentLine;
+                } else {
                     nextLine = currentLine + 1;
                 }
             }
-            if (nextLine < 0) {
+            if (nextLine >= mLayout.getLineCount()) {
                 return null;
             }
             final int start = getLineEdgeIndex(nextLine, DIRECTION_START);
@@ -82,12 +84,14 @@
             if (offset <= 0) {
                 return null;
             }
-            int previousLine = -1;
+            int previousLine;
             if (offset > mText.length()) {
                 previousLine = mLayout.getLineForOffset(mText.length());
             } else {
-                final int currentLine = mLayout.getLineForOffset(offset - 1);
-                if (currentLine > 0) {
+                final int currentLine = mLayout.getLineForOffset(offset);
+                if (getLineEdgeIndex(currentLine, DIRECTION_END) + 1 == offset) {
+                    previousLine = currentLine;
+                } else {
                     previousLine = currentLine - 1;
                 }
             }
@@ -141,29 +145,18 @@
                 return null;
             }
 
-            final int currentLine = mLayout.getLineForOffset(offset);
+            final int start = Math.max(0, offset);
+
+            final int currentLine = mLayout.getLineForOffset(start);
             final int currentLineTop = mLayout.getLineTop(currentLine);
             final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
                     - mView.getTotalPaddingBottom();
+            final int nextPageStartY = currentLineTop + pageHeight;
+            final int lastLineTop = mLayout.getLineTop(mLayout.getLineCount() - 1);
+            final int currentPageEndLine = (nextPageStartY < lastLineTop)
+                    ? mLayout.getLineForVertical(nextPageStartY) - 1 : mLayout.getLineCount() - 1;
 
-            final int nextPageStartLine;
-            final int nextPageEndLine;
-            if (offset < 0) {
-                nextPageStartLine = currentLine;
-                final int nextPageEndY = currentLineTop + pageHeight;
-                nextPageEndLine = mLayout.getLineForVertical(nextPageEndY);
-            } else {
-                final int nextPageStartY = currentLineTop + pageHeight;
-                nextPageStartLine = mLayout.getLineForVertical(nextPageStartY) + 1;
-                if (mLayout.getLineTop(nextPageStartLine) <= nextPageStartY) {
-                    return null;
-                }
-                final int nextPageEndY = nextPageStartY + pageHeight;
-                nextPageEndLine = mLayout.getLineForVertical(nextPageEndY);
-            }
-
-            final int start = getLineEdgeIndex(nextPageStartLine, DIRECTION_START);
-            final int end = getLineEdgeIndex(nextPageEndLine, DIRECTION_END) + 1;
+            final int end = getLineEdgeIndex(currentPageEndLine, DIRECTION_END) + 1;
 
             return getRange(start, end);
         }
@@ -181,37 +174,17 @@
                 return null;
             }
 
-            final int currentLine = mLayout.getLineForOffset(offset);
+            final int end = Math.min(mText.length(), offset);
+
+            final int currentLine = mLayout.getLineForOffset(end);
             final int currentLineTop = mLayout.getLineTop(currentLine);
             final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
                     - mView.getTotalPaddingBottom();
+            final int previousPageEndY = currentLineTop - pageHeight;
+            final int currentPageStartLine = (previousPageEndY > 0) ?
+                     mLayout.getLineForVertical(previousPageEndY) + 1 : 0;
 
-            final int previousPageStartLine;
-            final int previousPageEndLine;
-            if (offset > mText.length()) {
-                final int prevousPageStartY = mLayout.getHeight() - pageHeight;
-                if (prevousPageStartY < 0) {
-                    return null;
-                }
-                previousPageStartLine = mLayout.getLineForVertical(prevousPageStartY);
-                previousPageEndLine = mLayout.getLineCount() - 1;
-            } else {
-                final int prevousPageStartY;
-                if (offset == mText.length()) {
-                    prevousPageStartY = mLayout.getHeight() - 2 * pageHeight;
-                } else {
-                    prevousPageStartY = currentLineTop - 2 * pageHeight;
-                }
-                if (prevousPageStartY < 0) {
-                    return null;
-                }
-                previousPageStartLine = mLayout.getLineForVertical(prevousPageStartY);
-                final int previousPageEndY = prevousPageStartY + pageHeight;
-                previousPageEndLine = mLayout.getLineForVertical(previousPageEndY) - 1;
-            }
-
-            final int start = getLineEdgeIndex(previousPageStartLine, DIRECTION_START);
-            final int end = getLineEdgeIndex(previousPageEndLine, DIRECTION_END) + 1;
+            final int start = getLineEdgeIndex(currentPageStartLine, DIRECTION_START);
 
             return getRange(start, end);
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index bd19f00..3e2d43a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8376,10 +8376,12 @@
     @Override
     public int getAccessibilityCursorPosition() {
         if (TextUtils.isEmpty(getContentDescription())) {
-            return getSelectionEnd();
-        } else {
-            return super.getAccessibilityCursorPosition();
+            final int selectionEnd = getSelectionEnd();
+            if (selectionEnd >= 0) {
+                return selectionEnd;
+            }
         }
+        return super.getAccessibilityCursorPosition();
     }
 
     /**
@@ -8391,7 +8393,7 @@
             return;
         }
         if (TextUtils.isEmpty(getContentDescription())) {
-            if (index >= 0) {
+            if (index >= 0 && index <= mText.length()) {
                 Selection.setSelection((Spannable) mText, index);
             } else {
                 Selection.removeSelection((Spannable) mText);
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index f9ef3c5..a22395b 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -519,6 +519,10 @@
             // Inform listener of any active targets.  Typically only one will be active.
             deactivateHandle(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
             dispatchTriggerEvent(activeTarget);
+            if (!mAlwaysTrackFinger) {
+                // Force ring and targets to finish animation to final expanded state
+                mTargetAnimations.stop();
+            }
         } else {
             // Animate handle back to the center based on current state.
             deactivateHandle(HIDE_ANIMATION_DURATION, HIDE_ANIMATION_DELAY, 1.0f,
@@ -542,7 +546,6 @@
                 mTargetDrawables.get(i).setAlpha(0.0f);
             }
         }
-        mOuterRing.setAlpha(0.0f);
     }
 
     private void hideTargets(boolean animate, boolean expanded) {
@@ -809,7 +812,6 @@
         switchToState(STATE_START, eventX, eventY);
         if (!trySwitchToFirstTouchState(eventX, eventY)) {
             mDragging = false;
-            mTargetAnimations.cancel();
             ping();
         }
     }
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-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-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/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 5d8ddc9a..4cfbff5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1460,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/docs/html/guide/topics/ui/index.jd b/docs/html/guide/topics/ui/index.jd
index 45c9ac9..be07249 100644
--- a/docs/html/guide/topics/ui/index.jd
+++ b/docs/html/guide/topics/ui/index.jd
@@ -148,7 +148,7 @@
 On<em>&lt;something></em>Listener, each with a callback method called <code>On<em>&lt;something></em>()</code>.
 For example, {@link android.view.View.OnClickListener} (for handling "clicks" on a View),
 {@link android.view.View.OnTouchListener} (for handling touch screen events in a View), and
-{@link android.view.View.OnKeyListener} (for handling device key presses within a View). So if you want your View
+{@link android.view.View.OnKeyListener} if you want to handle hardware key presses within a View. So if you want your View
 to be notified when it is "clicked" (such as when a button is selected), implement OnClickListener and define
 its <code>onClick()</code> callback method (where you perform the action upon click), and register it
 to the View with <code>{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()}</code>.
@@ -158,7 +158,7 @@
 that occur within it. Example events you can handle include when the
 screen is touched (<code>{@link android.view.View#onTouchEvent(MotionEvent) onTouchEvent()}</code>), when
 the trackball is moved (<code>{@link android.view.View#onTrackballEvent(MotionEvent) onTrackballEvent()}</code>), 
-or when a key on the device is pressed (<code>{@link android.view.View#onKeyDown(int, KeyEvent)
+or when a <em>hardware</em> key on the device is pressed (<code>{@link android.view.View#onKeyDown(int, KeyEvent)
 onKeyDown()}</code>). This allows you to define the default behavior for each event inside your custom View and determine
 whether the event should be passed on to some other child View. Again, these are callbacks to the View class,
 so your only chance to define them is when you 
diff --git a/docs/html/guide/topics/ui/ui-events.jd b/docs/html/guide/topics/ui/ui-events.jd
index 93bad43..707d4b1 100644
--- a/docs/html/guide/topics/ui/ui-events.jd
+++ b/docs/html/guide/topics/ui/ui-events.jd
@@ -64,7 +64,7 @@
     This is called when the user navigates onto or away from the item, using the navigation-keys or trackball.</dd>
   <dt><code>onKey()</code></dt>
     <dd>From {@link android.view.View.OnKeyListener}. 
-    This is called when the user is focused on the item and presses or releases a key on the device.</dd>
+    This is called when the user is focused on the item and presses or releases a hardware key on the device.</dd>
   <dt><code>onTouch()</code></dt>
     <dd>From {@link android.view.View.OnTouchListener}. 
     This is called when the user performs an action qualified as a touch event, including a press, a release,
@@ -143,13 +143,23 @@
     within the event, such as a finger gesture, or the eventual up action event.</li>
 </ul>
 
-<p>Remember that key events are always delivered to the View currently in focus. They are dispatched starting from the top
+<p>Remember that hardware key events are always delivered to the View currently in focus. They are dispatched starting from the top
 of the View hierarchy, and then down, until they reach the appropriate destination. If your View (or a child of your View)
 currently has focus, then you can see the event travel through the <code>{@link android.view.View#dispatchKeyEvent(KeyEvent)
 dispatchKeyEvent()}</code> method. As an alternative to capturing key events through your View, you can also receive 
 all of the events inside your Activity with <code>{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}</code>
 and <code>{@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()}</code>.</p>
 
+<p>Also, when thinking about text input for your application, remember that many devices only have software input
+methods. Such methods are not required to be key-based; some may use voice input, handwriting, and so on. Even if
+an input method presents a keyboard-like interface, it will generally <strong>not</strong> trigger the
+<code>{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}</code> family of events. You should never
+build a UI that requires specific key presses to be controlled unless you want to limit your application to devices
+with a hardware keyboard. In particular, do not rely on these methods to validate input when the user presses the
+return key; instead, use actions like {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} to signal the
+input method how your application expects to react, so it may change its UI in a meaningful way. Avoid assumptions
+about how a software input method should work and just trust it to supply already formatted text to your application.</p>
+
 <p class="note"><strong>Note:</strong> Android will call event handlers first and then the appropriate default
 handlers from the class definition second. As such, returning <em>true</em> from these event listeners will stop
 the propagation of the event to other event listeners and will also block the callback to the
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index b0c1c35..41ec120 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -2623,16 +2623,21 @@
             M4OSA_Char* tmpString =
                 (M4OSA_Char *)videoEditJava_getString(&initialized, pEnv, tempPath,
                 NULL, M4OSA_NULL);
+            M4OSA_UInt32 length = strlen((const char *)tmpString);
+            // Malloc additional 2 bytes for beginning and tail separator.
+            M4OSA_UInt32 pathLength = length + 2;
+
             pContext->initParams.pTempPath = (M4OSA_Char *)
-                 M4OSA_32bitAlignedMalloc(strlen((const char *)tmpString) + 1, 0x0,
-                                                 (M4OSA_Char *)"tempPath");
+                 M4OSA_32bitAlignedMalloc(pathLength, 0x0, (M4OSA_Char *)"tempPath");
+
             //initialize the first char. so that strcat works.
             M4OSA_Char *ptmpChar = (M4OSA_Char*)pContext->initParams.pTempPath;
             ptmpChar[0] = 0x00;
             strncat((char *)pContext->initParams.pTempPath, (const char *)tmpString,
-                (size_t)strlen((const char *)tmpString));
+                length);
             strncat((char *)pContext->initParams.pTempPath, (const char *)"/", (size_t)1);
             free(tmpString);
+            tmpString = NULL;
             pContext->mIsUpdateOverlay = false;
             pContext->mOverlayFileName = NULL;
             pContext->decoders = NULL;
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f5fa237..26dba67 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -57,6 +57,9 @@
     <!-- Show rotation lock button in phone-style notification panel. -->
     <bool name="config_showRotationLock">true</bool>
 
+    <!-- Amount of time to hold off before showing the search panel when the user presses home -->
+    <integer name="config_show_search_delay">200</integer>
+
     <!-- Vibration duration for MultiWaveView used in SearchPanelView -->
     <integer translatable="false" name="config_vibration_duration">0</integer>
 
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index 29daf2c..08d9842 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -40,6 +40,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recent.StatusBarTouchProxy;
 import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.tablet.StatusBarPanel;
 import com.android.systemui.statusbar.tablet.TabletStatusBar;
@@ -49,7 +50,7 @@
     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;
+    private final Context mContext;
     private BaseStatusBar mBar;
     private StatusBarTouchProxy mStatusBarTouchProxy;
 
@@ -106,7 +107,7 @@
 
     private void startAssistActivity() {
         // Close Recent Apps if needed
-        mBar.animateCollapse();
+        mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
         // Launch Assist
         Intent intent = getAssistIntent();
         try {
@@ -161,7 +162,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mSearchTargetsContainer = (ViewGroup) findViewById(R.id.search_panel_container);
+        mSearchTargetsContainer = findViewById(R.id.search_panel_container);
         mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy);
         // TODO: fetch views
         mMultiWaveView = (MultiWaveView) findViewById(R.id.multi_wave_view);
@@ -187,7 +188,7 @@
         }
     }
 
-    private OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
+    private final OnPreDrawListener mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
         public boolean onPreDraw() {
             getViewTreeObserver().removeOnPreDrawListener(this);
             mMultiWaveView.resumeAnimations();
@@ -231,7 +232,7 @@
     public void hide(boolean animate) {
         if (mBar != null) {
             // This will indirectly cause show(false, ...) to get called
-            mBar.animateCollapse();
+            mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
         } else {
             setVisibility(View.INVISIBLE);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 39d686f..89bf3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -58,6 +58,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.tablet.StatusBarPanel;
 import com.android.systemui.statusbar.tablet.TabletStatusBar;
@@ -368,7 +369,7 @@
         }
         if (mBar != null) {
             // This will indirectly cause show(false, ...) to get called
-            mBar.animateCollapse();
+            mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
         }
     }
 
@@ -822,7 +823,7 @@
                     if (viewHolder != null) {
                         final TaskDescription ad = viewHolder.taskDescription;
                         startApplicationDetailsActivity(ad.packageName);
-                        mBar.animateCollapse();
+                        mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
                     } else {
                         throw new IllegalStateException("Oops, no tag on view " + selectedView);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 384e47f..f088e0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -268,7 +268,7 @@
                     public boolean onMenuItemClick(MenuItem item) {
                         if (item.getItemId() == R.id.notification_inspect_item) {
                             startApplicationDetailsActivity(packageNameF);
-                            animateCollapse();
+                            animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
                         } else {
                             return false;
                         }
@@ -618,7 +618,7 @@
             }
 
             // close the shade if it was open
-            animateCollapse();
+            animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
             visibilityChanged(false);
 
             // If this click was on the intruder alert, hide that instead
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 4209354..a00d95a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -66,6 +66,13 @@
 
     private static final int MSG_SET_NAVIGATION_ICON_HINTS = 14 << MSG_SHIFT;
 
+    public static final int FLAG_EXCLUDE_NONE = 0;
+    public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
+    public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
+    public static final int FLAG_EXCLUDE_NOTIFICATION_PANEL = 1 << 2;
+    public static final int FLAG_EXCLUDE_INPUT_METHODS_PANEL = 1 << 3;
+    public static final int FLAG_EXCLUDE_COMPAT_MODE_PANEL = 1 << 4;
+
     private StatusBarIconList mList;
     private Callbacks mCallbacks;
     private Handler mHandler = new H();
@@ -88,7 +95,7 @@
         public void removeNotification(IBinder key);
         public void disable(int state);
         public void animateExpand();
-        public void animateCollapse();
+        public void animateCollapse(int flags);
         public void setSystemUiVisibility(int vis, int mask);
         public void topAppWindowChanged(boolean visible);
         public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
@@ -161,9 +168,13 @@
     }
 
     public void animateCollapse() {
+        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
+    }
+
+    public void animateCollapse(int flags) {
         synchronized (mList) {
             mHandler.removeMessages(MSG_SET_VISIBILITY);
-            mHandler.obtainMessage(MSG_SET_VISIBILITY, OP_COLLAPSE, 0, null).sendToTarget();
+            mHandler.obtainMessage(MSG_SET_VISIBILITY, OP_COLLAPSE, flags, null).sendToTarget();
         }
     }
 
@@ -277,7 +288,7 @@
                     if (msg.arg1 == OP_EXPAND) {
                         mCallbacks.animateExpand();
                     } else {
-                        mCallbacks.animateCollapse();
+                        mCallbacks.animateCollapse(msg.arg2);
                     }
                     break;
                 case MSG_SET_SYSTEMUI_VISIBILITY:
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 344411b..3a50560 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -76,6 +76,7 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.RotationToggle;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -529,16 +530,29 @@
         }
     };
 
+    private int mShowSearchHoldoff = 0;
+    private Runnable mShowSearchPanel = new Runnable() {
+        public void run() {
+            showSearchPanel();
+        }
+    };
+
     View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    if (!shouldDisableNavbarGestures()) {
-                        showSearchPanel();
-                    }
-                break;
-            }
-            return false;
+            case MotionEvent.ACTION_DOWN:
+                if (!shouldDisableNavbarGestures()) {
+                    mHandler.removeCallbacks(mShowSearchPanel);
+                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
+                }
+            break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mHandler.removeCallbacks(mShowSearchPanel);
+            break;
+        }
+        return false;
         }
     };
 
@@ -733,6 +747,8 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         updateRecentsPanel();
+        mShowSearchHoldoff  = mContext.getResources().getInteger(
+                R.integer.config_show_search_delay);
     }
 
     private void loadNotificationShade() {
@@ -1057,29 +1073,33 @@
     }
 
     public void animateCollapse() {
-        animateCollapse(false);
+        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
     }
 
-    public void animateCollapse(boolean excludeRecents) {
-        animateCollapse(excludeRecents, 1.0f);
+    public void animateCollapse(int flags) {
+        animateCollapse(flags, 1.0f);
     }
 
-    public void animateCollapse(boolean excludeRecents, float velocityMultiplier) {
+    public void animateCollapse(int flags, float velocityMultiplier) {
         if (SPEW) {
             Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
                     + " mExpandedVisible=" + mExpandedVisible
                     + " mExpanded=" + mExpanded
                     + " mAnimating=" + mAnimating
                     + " mAnimY=" + mAnimY
-                    + " mAnimVel=" + mAnimVel);
+                    + " mAnimVel=" + mAnimVel
+                    + " flags=" + flags);
         }
 
-        if (!excludeRecents) {
+        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
             mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
             mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
         }
-        mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
-        mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
+
+        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
+            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
+            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
+        }
 
         if (!mExpandedVisible) {
             return;
@@ -1941,7 +1961,7 @@
                     }
                 }
                 if (snapshot.isEmpty()) {
-                    animateCollapse(false);
+                    animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
                     return;
                 }
                 new Thread(new Runnable() {
@@ -1989,7 +2009,7 @@
                         mHandler.postDelayed(new Runnable() {
                             @Override
                             public void run() {
-                                animateCollapse(false);
+                                animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
                             }
                         }, totalDelay + 225);
                     }
@@ -2016,14 +2036,14 @@
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
-                boolean excludeRecents = false;
+                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
                     String reason = intent.getStringExtra("reason");
-                    if (reason != null) {
-                        excludeRecents = reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS);
+                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
+                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
                     }
                 }
-                animateCollapse(excludeRecents);
+                animateCollapse(flags);
             }
             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 updateResources();
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 c0ac50e..8df9b85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -67,6 +67,7 @@
 import com.android.systemui.recent.RecentTasksLoader;
 import com.android.systemui.recent.RecentsPanelView;
 import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DoNotDisturb;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.SignalClusterView;
@@ -186,16 +187,30 @@
 
     private int mNavigationIconHints = 0;
 
+    private int mShowSearchHoldoff = 0;
+
     public Context getContext() { return mContext; }
 
+    private Runnable mShowSearchPanel = new Runnable() {
+        public void run() {
+            showSearchPanel();
+        }
+    };
+
     private View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
                 case MotionEvent.ACTION_DOWN:
                     if (!shouldDisableNavbarGestures()) {
-                        showSearchPanel();
+                        mHandler.removeCallbacks(mShowSearchPanel);
+                        mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
                     }
                 break;
+
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    mHandler.removeCallbacks(mShowSearchPanel);
+                break;
             }
             return false;
         }
@@ -387,6 +402,8 @@
         WindowManagerImpl.getDefault().updateViewLayout(mNotificationPanel,
                 mNotificationPanelParams);
         mRecentsPanel.updateValuesFromResources();
+        mShowSearchHoldoff = mContext.getResources().getInteger(
+                R.integer.config_show_search_delay);
     }
 
     protected void loadDimens() {
@@ -1001,22 +1018,31 @@
     }
 
     public void animateCollapse() {
-        animateCollapse(false);
+        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
     }
 
-    private void animateCollapse(boolean excludeRecents) {
-        mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL);
-        mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL);
-        if (!excludeRecents) {
+    public void animateCollapse(int flags) {
+        if ((flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
+            mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL);
+            mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL);
+        }
+        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
             mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
             mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
         }
-        mHandler.removeMessages(MSG_CLOSE_INPUT_METHODS_PANEL);
-        mHandler.sendEmptyMessage(MSG_CLOSE_INPUT_METHODS_PANEL);
-        mHandler.removeMessages(MSG_CLOSE_COMPAT_MODE_PANEL);
-        mHandler.sendEmptyMessage(MSG_CLOSE_COMPAT_MODE_PANEL);
-        mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
-        mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
+        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
+            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
+            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
+        }
+        if ((flags & CommandQueue.FLAG_EXCLUDE_INPUT_METHODS_PANEL) == 0) {
+            mHandler.removeMessages(MSG_CLOSE_INPUT_METHODS_PANEL);
+            mHandler.sendEmptyMessage(MSG_CLOSE_INPUT_METHODS_PANEL);
+        }
+        if ((flags & CommandQueue.FLAG_EXCLUDE_COMPAT_MODE_PANEL) == 0) {
+            mHandler.removeMessages(MSG_CLOSE_COMPAT_MODE_PANEL);
+            mHandler.sendEmptyMessage(MSG_CLOSE_COMPAT_MODE_PANEL);
+        }
+
     }
 
     @Override // CommandQueue
@@ -1594,11 +1620,11 @@
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
                 || Intent.ACTION_SCREEN_OFF.equals(action)) {
-                boolean excludeRecents = false;
+                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
                     String reason = intent.getStringExtra("reason");
-                    if (reason != null) {
-                        excludeRecents = reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS);
+                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
+                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
                     }
                 }
                 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
@@ -1607,9 +1633,9 @@
                     // TODO: hide other things, like the notification tray,
                     // with no animation as well
                     mRecentsPanel.show(false, false);
-                    excludeRecents = true;
+                    flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
                 }
-                animateCollapse(excludeRecents);
+                animateCollapse(flags);
             }
         }
     };
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/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index c7b336f..c74dd00 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -198,7 +198,7 @@
     }
 
     // TODO: Respect allowImplicitlySelectedSubtype
-    // TODO: Save SpellCheckerSubtype by supported languages.
+    // TODO: Save SpellCheckerSubtype by supported languages by looking at "locale".
     @Override
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
             String locale, boolean allowImplicitlySelectedSubtype) {
@@ -250,10 +250,10 @@
             for (int i = 0; i < sci.getSubtypeCount(); ++i) {
                 final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
                 if (hashCode == 0) {
-                    if (candidateLocale.equals(locale)) {
+                    final String scsLocale = scs.getLocale();
+                    if (candidateLocale.equals(scsLocale)) {
                         return scs;
                     } else if (candidate == null) {
-                        final String scsLocale = scs.getLocale();
                         if (candidateLocale.length() >= 2 && scsLocale.length() >= 2
                                 && candidateLocale.startsWith(scsLocale)) {
                             // Fall back to the applicable language
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 af6d504..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;
                 }
             }
@@ -3865,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
@@ -3934,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()")) {
@@ -3949,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);
@@ -4296,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);
@@ -5319,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;
@@ -6437,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() ?
@@ -7946,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);
@@ -8056,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/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index d3b667f..04ec820 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -225,9 +225,10 @@
 {
     ALOGD("nuSensorService thread starting...");
 
-    const size_t numEventMax = 16 * (1 + mVirtualSensorList.size());
-    sensors_event_t buffer[numEventMax];
-    sensors_event_t scratch[numEventMax];
+    const size_t numEventMax = 16;
+    const size_t minBufferSize = numEventMax * mVirtualSensorList.size();
+    sensors_event_t buffer[minBufferSize];
+    sensors_event_t scratch[minBufferSize];
     SensorDevice& device(SensorDevice::getInstance());
     const size_t vcount = mVirtualSensorList.size();
 
@@ -255,10 +256,17 @@
                         fusion.process(event[i]);
                     }
                 }
-                for (size_t i=0 ; i<size_t(count) ; i++) {
+                for (size_t i=0 ; i<size_t(count) && k<minBufferSize ; i++) {
                     for (size_t j=0 ; j<activeVirtualSensorCount ; j++) {
+                        if (count + k >= minBufferSize) {
+                            ALOGE("buffer too small to hold all events: "
+                                    "count=%u, k=%u, size=%u",
+                                    count, k, minBufferSize);
+                            break;
+                        }
                         sensors_event_t out;
-                        if (virtualSensors.valueAt(j)->process(&out, event[i])) {
+                        SensorInterface* si = virtualSensors.valueAt(j);
+                        if (si->process(&out, event[i])) {
                             buffer[count + k] = out;
                             k++;
                         }
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.
      */