am b4e2cea5: simplify gl table

* commit 'b4e2cea57dd7ca74baede6f4fed48ce67a661abe':
  simplify gl table
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 193724d..d6db8c2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4857,7 +4857,11 @@
 
         mFragments.dump(prefix, fd, writer, args);
 
-        getWindow().getDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
+        if (getWindow() != null &&
+                getWindow().peekDecorView() != null &&
+                getWindow().peekDecorView().getViewRootImpl() != null) {
+            getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
+        }
 
         mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
     }
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 22a21cd..aab6ed8 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -205,7 +205,9 @@
         try {
             mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
                 public void onKeyguardExitResult(boolean success) throws RemoteException {
-                    callback.onKeyguardExitResult(success);
+                    if (callback != null) {
+                        callback.onKeyguardExitResult(success);
+                    }
                 }
             });
         } catch (RemoteException e) {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 7bcf43e..2045ed8 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -58,10 +58,7 @@
             | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
             | DISABLE_SEARCH;
 
-    public static final int NAVIGATION_HINT_BACK_NOP      = 1 << 0;
-    public static final int NAVIGATION_HINT_HOME_NOP      = 1 << 1;
-    public static final int NAVIGATION_HINT_RECENT_NOP    = 1 << 2;
-    public static final int NAVIGATION_HINT_BACK_ALT      = 1 << 3;
+    public static final int NAVIGATION_HINT_BACK_ALT      = 1 << 0;
 
     public static final int WINDOW_STATUS_BAR = 1;
     public static final int WINDOW_NAVIGATION_BAR = 2;
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 607930c..91b0d7c 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -146,7 +146,9 @@
     @Override
     public void shutdown() {
         synchronized (mLock) {
-            throwIfCalledByNotTrustedUidLocked();
+            if (isConnectedLocked()) {
+                throwIfCalledByNotTrustedUidLocked();
+            }
             throwIfShutdownLocked();
             mIsShutdown = true;
             if (isConnectedLocked()) {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a9d0559..ddde3fb 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -398,67 +398,6 @@
             return AppOpsManager.MODE_ALLOWED;
         }
 
-        private void enforceReadPermissionInner(Uri uri) throws SecurityException {
-            final Context context = getContext();
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            String missingPerm = null;
-
-            if (UserHandle.isSameApp(uid, mMyUid)) {
-                return;
-            }
-
-            if (mExported) {
-                final String componentPerm = getReadPermission();
-                if (componentPerm != null) {
-                    if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
-                        return;
-                    } else {
-                        missingPerm = componentPerm;
-                    }
-                }
-
-                // track if unprotected read is allowed; any denied
-                // <path-permission> below removes this ability
-                boolean allowDefaultRead = (componentPerm == null);
-
-                final PathPermission[] pps = getPathPermissions();
-                if (pps != null) {
-                    final String path = uri.getPath();
-                    for (PathPermission pp : pps) {
-                        final String pathPerm = pp.getReadPermission();
-                        if (pathPerm != null && pp.match(path)) {
-                            if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
-                                return;
-                            } else {
-                                // any denied <path-permission> means we lose
-                                // default <provider> access.
-                                allowDefaultRead = false;
-                                missingPerm = pathPerm;
-                            }
-                        }
-                    }
-                }
-
-                // if we passed <path-permission> checks above, and no default
-                // <provider> permission, then allow access.
-                if (allowDefaultRead) return;
-            }
-
-            // last chance, check against any uri grants
-            if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                    == PERMISSION_GRANTED) {
-                return;
-            }
-
-            final String failReason = mExported
-                    ? " requires " + missingPerm + ", or grantUriPermission()"
-                    : " requires the provider be exported, or grantUriPermission()";
-            throw new SecurityException("Permission Denial: reading "
-                    + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid
-                    + ", uid=" + uid + failReason);
-        }
-
         private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException {
             enforceWritePermissionInner(uri);
             if (mWriteOp != AppOpsManager.OP_NONE) {
@@ -466,67 +405,130 @@
             }
             return AppOpsManager.MODE_ALLOWED;
         }
+    }
 
-        private void enforceWritePermissionInner(Uri uri) throws SecurityException {
-            final Context context = getContext();
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            String missingPerm = null;
+    /** {@hide} */
+    protected void enforceReadPermissionInner(Uri uri) throws SecurityException {
+        final Context context = getContext();
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        String missingPerm = null;
 
-            if (UserHandle.isSameApp(uid, mMyUid)) {
-                return;
+        if (UserHandle.isSameApp(uid, mMyUid)) {
+            return;
+        }
+
+        if (mExported) {
+            final String componentPerm = getReadPermission();
+            if (componentPerm != null) {
+                if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+                    return;
+                } else {
+                    missingPerm = componentPerm;
+                }
             }
 
-            if (mExported) {
-                final String componentPerm = getWritePermission();
-                if (componentPerm != null) {
-                    if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
-                        return;
-                    } else {
-                        missingPerm = componentPerm;
-                    }
-                }
+            // track if unprotected read is allowed; any denied
+            // <path-permission> below removes this ability
+            boolean allowDefaultRead = (componentPerm == null);
 
-                // track if unprotected write is allowed; any denied
-                // <path-permission> below removes this ability
-                boolean allowDefaultWrite = (componentPerm == null);
-
-                final PathPermission[] pps = getPathPermissions();
-                if (pps != null) {
-                    final String path = uri.getPath();
-                    for (PathPermission pp : pps) {
-                        final String pathPerm = pp.getWritePermission();
-                        if (pathPerm != null && pp.match(path)) {
-                            if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
-                                return;
-                            } else {
-                                // any denied <path-permission> means we lose
-                                // default <provider> access.
-                                allowDefaultWrite = false;
-                                missingPerm = pathPerm;
-                            }
+            final PathPermission[] pps = getPathPermissions();
+            if (pps != null) {
+                final String path = uri.getPath();
+                for (PathPermission pp : pps) {
+                    final String pathPerm = pp.getReadPermission();
+                    if (pathPerm != null && pp.match(path)) {
+                        if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+                            return;
+                        } else {
+                            // any denied <path-permission> means we lose
+                            // default <provider> access.
+                            allowDefaultRead = false;
+                            missingPerm = pathPerm;
                         }
                     }
                 }
-
-                // if we passed <path-permission> checks above, and no default
-                // <provider> permission, then allow access.
-                if (allowDefaultWrite) return;
             }
 
-            // last chance, check against any uri grants
-            if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-                    == PERMISSION_GRANTED) {
-                return;
-            }
-
-            final String failReason = mExported
-                    ? " requires " + missingPerm + ", or grantUriPermission()"
-                    : " requires the provider be exported, or grantUriPermission()";
-            throw new SecurityException("Permission Denial: writing "
-                    + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid
-                    + ", uid=" + uid + failReason);
+            // if we passed <path-permission> checks above, and no default
+            // <provider> permission, then allow access.
+            if (allowDefaultRead) return;
         }
+
+        // last chance, check against any uri grants
+        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                == PERMISSION_GRANTED) {
+            return;
+        }
+
+        final String failReason = mExported
+                ? " requires " + missingPerm + ", or grantUriPermission()"
+                : " requires the provider be exported, or grantUriPermission()";
+        throw new SecurityException("Permission Denial: reading "
+                + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid
+                + ", uid=" + uid + failReason);
+    }
+
+    /** {@hide} */
+    protected void enforceWritePermissionInner(Uri uri) throws SecurityException {
+        final Context context = getContext();
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        String missingPerm = null;
+
+        if (UserHandle.isSameApp(uid, mMyUid)) {
+            return;
+        }
+
+        if (mExported) {
+            final String componentPerm = getWritePermission();
+            if (componentPerm != null) {
+                if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+                    return;
+                } else {
+                    missingPerm = componentPerm;
+                }
+            }
+
+            // track if unprotected write is allowed; any denied
+            // <path-permission> below removes this ability
+            boolean allowDefaultWrite = (componentPerm == null);
+
+            final PathPermission[] pps = getPathPermissions();
+            if (pps != null) {
+                final String path = uri.getPath();
+                for (PathPermission pp : pps) {
+                    final String pathPerm = pp.getWritePermission();
+                    if (pathPerm != null && pp.match(path)) {
+                        if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+                            return;
+                        } else {
+                            // any denied <path-permission> means we lose
+                            // default <provider> access.
+                            allowDefaultWrite = false;
+                            missingPerm = pathPerm;
+                        }
+                    }
+                }
+            }
+
+            // if we passed <path-permission> checks above, and no default
+            // <provider> permission, then allow access.
+            if (allowDefaultWrite) return;
+        }
+
+        // last chance, check against any uri grants
+        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+                == PERMISSION_GRANTED) {
+            return;
+        }
+
+        final String failReason = mExported
+                ? " requires " + missingPerm + ", or grantUriPermission()"
+                : " requires the provider be exported, or grantUriPermission()";
+        throw new SecurityException("Permission Denial: writing "
+                + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid
+                + ", uid=" + uid + failReason);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index c5d0999..c428a17 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -19,27 +19,24 @@
 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
 
 import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
-import android.os.IBinder;
-import android.os.RemoteException;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Surface;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Stack;
 
 /**
  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -49,6 +46,8 @@
     private final String TAG;
     private final boolean DEBUG;
 
+    private static final int REQUEST_ID_NONE = -1;
+
     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
     private ICameraDeviceUser mRemoteDevice;
 
@@ -63,7 +62,8 @@
     private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
             new SparseArray<CaptureListenerHolder>();
 
-    private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
+    private int mRepeatingRequestId = REQUEST_ID_NONE;
+    private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
     // Map stream IDs to Surfaces
     private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
 
@@ -186,7 +186,7 @@
             stopRepeating();
 
             try {
-                mRemoteDevice.waitUntilIdle();
+                waitUntilIdle();
 
                 // TODO: mRemoteDevice.beginConfigure
                 // Delete all streams first (to free up HW resources)
@@ -293,7 +293,11 @@
             }
 
             if (repeating) {
-                mRepeatingRequestIdStack.add(requestId);
+                // Queue for deletion after in-flight requests finish
+                if (mRepeatingRequestId != REQUEST_ID_NONE) {
+                    mRepeatingRequestIdDeletedList.add(mRepeatingRequestId);
+                }
+                mRepeatingRequestId = requestId;
             }
 
             if (mIdle) {
@@ -327,8 +331,13 @@
 
         synchronized (mLock) {
             checkIfCameraClosed();
-            while (!mRepeatingRequestIdStack.isEmpty()) {
-                int requestId = mRepeatingRequestIdStack.pop();
+            if (mRepeatingRequestId != REQUEST_ID_NONE) {
+
+                int requestId = mRepeatingRequestId;
+                mRepeatingRequestId = REQUEST_ID_NONE;
+
+                // Queue for deletion after in-flight requests finish
+                mRepeatingRequestIdDeletedList.add(requestId);
 
                 try {
                     mRemoteDevice.cancelRequest(requestId);
@@ -347,7 +356,7 @@
 
         synchronized (mLock) {
             checkIfCameraClosed();
-            if (!mRepeatingRequestIdStack.isEmpty()) {
+            if (mRepeatingRequestId != REQUEST_ID_NONE) {
                 throw new IllegalStateException("Active repeating request ongoing");
             }
 
@@ -359,6 +368,10 @@
                 // impossible
                 return;
             }
+
+            mRepeatingRequestId = REQUEST_ID_NONE;
+            mRepeatingRequestIdDeletedList.clear();
+            mCaptureListenerMap.clear();
         }
     }
 
@@ -572,13 +585,28 @@
                 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
 
                 // Clean up listener once we no longer expect to see it.
-
-                // TODO: how to handle repeating listeners?
-                // we probably want cancelRequest to return # of times it already enqueued and
-                // keep a counter.
                 if (holder != null && !holder.isRepeating()) {
                     CameraDevice.this.mCaptureListenerMap.remove(requestId);
                 }
+
+                // TODO: add 'capture sequence completed' callback to the
+                // service, and clean up repeating requests there instead.
+
+                // If we received a result for a repeating request and have
+                // prior repeating requests queued for deletion, remove those
+                // requests from mCaptureListenerMap.
+                if (holder != null && holder.isRepeating()
+                        && mRepeatingRequestIdDeletedList.size() > 0) {
+                    Iterator<Integer> iter = mRepeatingRequestIdDeletedList.iterator();
+                    while (iter.hasNext()) {
+                        int deletedRequestId = iter.next();
+                        if (deletedRequestId < requestId) {
+                            CameraDevice.this.mCaptureListenerMap.remove(deletedRequestId);
+                            iter.remove();
+                        }
+                    }
+                }
+
             }
 
             // Check if we have a listener for this
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index b674324..8a2c2b6 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -97,7 +97,7 @@
                 } catch (Exception e) {
                     port = 8080;
                 }
-                ret.add(new Proxy(Type.HTTP, new InetSocketAddress(host, port)));
+                ret.add(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(host, port)));
             }
         }
         if (ret.size() == 0) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dbaa325..7ae8ca8 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -844,12 +844,13 @@
     public static final int DATA_CONNECTION_EVDO_B = 12;
     public static final int DATA_CONNECTION_LTE = 13;
     public static final int DATA_CONNECTION_EHRPD = 14;
-    public static final int DATA_CONNECTION_OTHER = 15;
+    public static final int DATA_CONNECTION_HSPAP = 15;
+    public static final int DATA_CONNECTION_OTHER = 16;
 
     static final String[] DATA_CONNECTION_NAMES = {
         "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
         "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
-        "ehrpd", "other"
+        "ehrpd", "hspap", "other"
     };
     
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 2ab5a91..ed9264a 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -33,7 +33,6 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.LayoutInflater;
@@ -125,8 +124,6 @@
         PreferenceManager.OnPreferenceTreeClickListener,
         PreferenceFragment.OnPreferenceStartFragmentCallback {
 
-    private static final String TAG = "PreferenceActivity";
-
     // Constants for state save/restore
     private static final String HEADERS_TAG = ":android:headers";
     private static final String CUR_HEADER_TAG = ":android:cur_header";
@@ -576,12 +573,14 @@
             // Single pane, showing just a prefs fragment.
             findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
             mPrefsContainer.setVisibility(View.VISIBLE);
+            CharSequence initialTitleStr = null;
+            CharSequence initialShortTitleStr = null;
             if (initialTitle != 0) {
-                CharSequence initialTitleStr = getText(initialTitle);
-                CharSequence initialShortTitleStr = initialShortTitle != 0
+                initialTitleStr = getText(initialTitle);
+                initialShortTitleStr = initialShortTitle != 0
                         ? getText(initialShortTitle) : null;
-                showBreadCrumbs(initialTitleStr, initialShortTitleStr);
             }
+            showBreadCrumbs(initialTitleStr, initialShortTitleStr);
         } else if (mHeaders.size() > 0) {
             setListAdapter(new HeaderAdapter(this, mHeaders));
             if (!mSinglePane) {
diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl
index 9d384fb..2b95c12 100644
--- a/core/java/android/print/IPrintDocumentAdapter.aidl
+++ b/core/java/android/print/IPrintDocumentAdapter.aidl
@@ -37,4 +37,5 @@
     void write(in PageRange[] pages, in ParcelFileDescriptor fd,
             IWriteResultCallback callback, int sequence);
     void finish();
+    void cancel();
 }
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index bbfc307..d6d56bb 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -616,6 +616,18 @@
         }
 
         @Override
+        public void cancel() {
+            // Start not called or finish called or destroyed - nothing to do.
+            if (!mStartReqeusted || mFinishRequested || mDestroyed) {
+                return;
+            }
+            // Request cancellation of pending work if needed.
+            synchronized (mLock) {
+                cancelPreviousCancellableOperationLocked();
+            }
+        }
+
+        @Override
         public void onActivityPaused(Activity activity) {
             /* do nothing */
         }
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index e35b8eb..cc81be5 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -216,6 +216,8 @@
      * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be
      * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and
      * limited to only return the 64 most recently modified documents.
+     * <p>
+     * Recent documents do not support change notifications.
      *
      * @param projection list of {@link Document} columns to put into the
      *            cursor. If {@code null} all supported columns should be
@@ -512,10 +514,7 @@
         final boolean callerHasManage =
                 context.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DOCUMENTS)
                 == PackageManager.PERMISSION_GRANTED;
-        if (!callerHasManage) {
-            getContext().enforceCallingOrSelfUriPermission(
-                    documentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, method);
-        }
+        enforceWritePermissionInner(documentUri);
 
         final Bundle out = new Bundle();
         try {
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index b808363..2752085 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -993,8 +993,16 @@
         return runAction(new Action<Set<String>>() {
             @Override
             public Set<String> run(ITextToSpeechService service) throws RemoteException {
-                String[] features = service.getFeaturesForLanguage(
+                String[] features = null;
+                try {
+                    features = service.getFeaturesForLanguage(
                         locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
+                } catch(MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " +
+                            "country code for locale: " + locale, e);
+                    return null;
+                }
+
                 if (features != null) {
                     final Set<String> featureSet = new HashSet<String>();
                     Collections.addAll(featureSet, features);
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 5fbd22e..4f996cd 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -44,6 +44,7 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Locale;
+import java.util.MissingResourceException;
 
 /**
  * Support class for querying the list of available engines
@@ -369,28 +370,34 @@
     public String getDefaultLocale() {
         final Locale locale = Locale.getDefault();
 
-        // Note that the default locale might have an empty variant
-        // or language, and we take care that the construction is
-        // the same as {@link #getV1Locale} i.e no trailing delimiters
-        // or spaces.
-        String defaultLocale = locale.getISO3Language();
-        if (TextUtils.isEmpty(defaultLocale)) {
-            Log.w(TAG, "Default locale is empty.");
-            return "";
-        }
+        try {
+            // Note that the default locale might have an empty variant
+            // or language, and we take care that the construction is
+            // the same as {@link #getV1Locale} i.e no trailing delimiters
+            // or spaces.
+            String defaultLocale = locale.getISO3Language();
+            if (TextUtils.isEmpty(defaultLocale)) {
+                Log.w(TAG, "Default locale is empty.");
+                return "";
+            }
 
-        if (!TextUtils.isEmpty(locale.getISO3Country())) {
-            defaultLocale += LOCALE_DELIMITER + locale.getISO3Country();
-        } else {
-            // Do not allow locales of the form lang--variant with
-            // an empty country.
+            if (!TextUtils.isEmpty(locale.getISO3Country())) {
+                defaultLocale += LOCALE_DELIMITER + locale.getISO3Country();
+            } else {
+                // Do not allow locales of the form lang--variant with
+                // an empty country.
+                return defaultLocale;
+            }
+            if (!TextUtils.isEmpty(locale.getVariant())) {
+                defaultLocale += LOCALE_DELIMITER + locale.getVariant();
+            }
+
             return defaultLocale;
+        } catch (MissingResourceException e) {
+            // Default locale does not have a ISO 3166 and/or ISO 639-2/T codes. Return the
+            // default "eng-usa" (that would be the result of Locale.getDefault() == Locale.US).
+            return "eng-usa";
         }
-        if (!TextUtils.isEmpty(locale.getVariant())) {
-            defaultLocale += LOCALE_DELIMITER + locale.getVariant();
-        }
-
-        return defaultLocale;
     }
 
     /**
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 160c630..f839d52 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -391,6 +391,15 @@
                 out.append("&gt;");
             } else if (c == '&') {
                 out.append("&amp;");
+            } else if (c >= 0xD800 && c <= 0xDFFF) {
+                if (c < 0xDC00 && i + 1 < end) {
+                    char d = text.charAt(i + 1);
+                    if (d >= 0xDC00 && d <= 0xDFFF) {
+                        i++;
+                        int codepoint = 0x010000 | (int) c - 0xD800 << 10 | (int) d - 0xDC00;
+                        out.append("&#").append(codepoint).append(";");
+                    }
+                }
             } else if (c > 0x7E || c < ' ') {
                 out.append("&#").append((int) c).append(";");
             } else if (c == ' ') {
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index f76e190..da9ba5a 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1255,7 +1255,8 @@
             Animator anim = runningAnimators.keyAt(i);
             if (anim != null) {
                 AnimationInfo oldInfo = runningAnimators.get(anim);
-                if (oldInfo != null) {
+                if (oldInfo != null && oldInfo.view != null &&
+                        oldInfo.view.getContext() == sceneRoot.getContext()) {
                     boolean cancel = false;
                     TransitionValues oldValues = oldInfo.values;
                     View oldView = oldInfo.view;
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index 4af0f51..9f77d5e 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -20,9 +20,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.util.ArrayMap;
 import android.util.AttributeSet;
-import android.util.SparseArray;
 import android.util.Xml;
 import android.view.InflateException;
 import android.view.ViewGroup;
@@ -43,15 +41,7 @@
  */
 public class TransitionInflater {
 
-    // We only need one inflater for any given context. Also, this allows us to associate
-    // ids with unique instances per-Context, used to avoid re-inflating
-    // already-inflated resources into new/different instances
-    private static final ArrayMap<Context, TransitionInflater> sInflaterMap =
-            new ArrayMap<Context, TransitionInflater>();
-
     private Context mContext;
-    // TODO: do we need id maps for transitions and transitionMgrs as well?
-    SparseArray<Scene> mScenes = new SparseArray<Scene>();
 
     private TransitionInflater(Context context) {
         mContext = context;
@@ -61,13 +51,7 @@
      * Obtains the TransitionInflater from the given context.
      */
     public static TransitionInflater from(Context context) {
-        TransitionInflater inflater = sInflaterMap.get(context);
-        if (inflater != null) {
-            return inflater;
-        }
-        inflater = new TransitionInflater(context);
-        sInflaterMap.put(context, inflater);
-        return inflater;
+        return new TransitionInflater(context);
     }
 
     /**
diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java
index f4a9b0b..28b788b 100644
--- a/core/java/android/util/MapCollections.java
+++ b/core/java/android/util/MapCollections.java
@@ -97,10 +97,10 @@
             if (!mEntryValid) {
                 throw new IllegalStateException();
             }
+            colRemoveAt(mIndex);
             mIndex--;
             mEnd--;
             mEntryValid = false;
-            colRemoveAt(mIndex);
         }
 
         @Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6c04c0b..b74202d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3102,6 +3102,8 @@
      */
     private static final int UNDEFINED_PADDING = Integer.MIN_VALUE;
 
+    private boolean mUseBackgroundPadding = false;
+
     /**
      * @hide
      */
@@ -5900,6 +5902,8 @@
                 sThreadLocal.set(localInsets);
             }
             boolean res = computeFitSystemWindows(insets, localInsets);
+            mUserPaddingLeftInitial = localInsets.left;
+            mUserPaddingRightInitial = localInsets.right;
             internalSetPadding(localInsets.left, localInsets.top,
                     localInsets.right, localInsets.bottom);
             return res;
@@ -12133,12 +12137,14 @@
         if (!isTextAlignmentResolved()) {
             resolveTextAlignment();
         }
-        if (!isPaddingResolved()) {
-            resolvePadding();
-        }
+        // Should resolve Drawables before Padding because we need the layout direction of the
+        // Drawable to correctly resolve Padding.
         if (!isDrawablesResolved()) {
             resolveDrawables();
         }
+        if (!isPaddingResolved()) {
+            resolvePadding();
+        }
         onRtlPropertiesChanged(getLayoutDirection());
         return true;
     }
@@ -12341,6 +12347,16 @@
             // If start / end padding are defined, they will be resolved (hence overriding) to
             // left / right or right / left depending on the resolved layout direction.
             // If start / end padding are not defined, use the left / right ones.
+            if (mBackground != null && mUseBackgroundPadding) {
+                Rect padding = sThreadLocal.get();
+                if (padding == null) {
+                    padding = new Rect();
+                    sThreadLocal.set(padding);
+                }
+                mBackground.getPadding(padding);
+                mUserPaddingLeftInitial = padding.left;
+                mUserPaddingRightInitial = padding.right;
+            }
             switch (resolvedLayoutDirection) {
                 case LAYOUT_DIRECTION_RTL:
                     if (mUserPaddingStart != UNDEFINED_PADDING) {
@@ -15336,6 +15352,9 @@
                         mUserPaddingRightInitial = padding.right;
                         internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
                 }
+                mUseBackgroundPadding = true;
+            } else {
+                mUseBackgroundPadding = false;
             }
 
             // Compare the minimum sizes of the old Drawable and the new.  If there isn't an old or
@@ -15361,6 +15380,8 @@
             /* Remove the background */
             mBackground = null;
 
+            mUseBackgroundPadding = false;
+
             if ((mPrivateFlags & PFLAG_ONLY_DRAWS_BACKGROUND) != 0) {
                 /*
                  * This view ONLY drew the background before and we're removing
@@ -15432,6 +15453,8 @@
         mUserPaddingLeftInitial = left;
         mUserPaddingRightInitial = right;
 
+        mUseBackgroundPadding = false;
+
         internalSetPadding(left, top, right, bottom);
     }
 
@@ -15518,6 +15541,8 @@
         mUserPaddingStart = start;
         mUserPaddingEnd = end;
 
+        mUseBackgroundPadding = false;
+
         switch(getLayoutDirection()) {
             case LAYOUT_DIRECTION_RTL:
                 mUserPaddingLeftInitial = end;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 637af6f..bc0d7e3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3393,16 +3393,7 @@
         public final void deliver(QueuedInputEvent q) {
             if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                 forward(q);
-            } else if (mView == null || !mAdded) {
-                Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
-                finish(q, false);
-            } else if (!mAttachInfo.mHasWindowFocus &&
-                  !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
-                  !isTerminalInputEvent(q.mEvent)) {
-                // If this is a focused event and the window doesn't currently have input focus,
-                // then drop this event.  This could be an event that came back from the previous
-                // stage but the window has lost focus in the meantime.
-                Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+            } else if (shouldDropInputEvent(q)) {
                 finish(q, false);
             } else {
                 apply(q, onProcess(q));
@@ -3461,6 +3452,22 @@
             }
         }
 
+        protected boolean shouldDropInputEvent(QueuedInputEvent q) {
+            if (mView == null || !mAdded) {
+                Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
+                return true;
+            } else if (!mAttachInfo.mHasWindowFocus &&
+                  !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
+                  !isTerminalInputEvent(q.mEvent)) {
+                // If this is a focused event and the window doesn't currently have input focus,
+                // then drop this event.  This could be an event that came back from the previous
+                // stage but the window has lost focus in the meantime.
+                Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+                return true;
+            }
+            return false;
+        }
+
         void dump(String prefix, PrintWriter writer) {
             if (mNext != null) {
                 mNext.dump(prefix, writer);
@@ -3846,6 +3853,10 @@
                 return FINISH_HANDLED;
             }
 
+            if (shouldDropInputEvent(q)) {
+                return FINISH_NOT_HANDLED;
+            }
+
             // If the Control modifier is held, try to interpret the key as a shortcut.
             if (event.getAction() == KeyEvent.ACTION_DOWN
                     && event.isCtrlPressed()
@@ -3854,12 +3865,18 @@
                 if (mView.dispatchKeyShortcutEvent(event)) {
                     return FINISH_HANDLED;
                 }
+                if (shouldDropInputEvent(q)) {
+                    return FINISH_NOT_HANDLED;
+                }
             }
 
             // Apply the fallback event policy.
             if (mFallbackEventHandler.dispatchKeyEvent(event)) {
                 return FINISH_HANDLED;
             }
+            if (shouldDropInputEvent(q)) {
+                return FINISH_NOT_HANDLED;
+            }
 
             // Handle automatic focus changes.
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3eb0052..092f474 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6686,6 +6686,13 @@
 
             scrap.dispatchStartTemporaryDetach();
 
+            // The the accessibility state of the view may change while temporary
+            // detached and we do not allow detached views to fire accessibility
+            // events. So we are announcing that the subtree changed giving a chance
+            // to clients holding on to a view in this subtree to refresh it.
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+
             // Don't scrap views that have transient state.
             final boolean scrapHasTransientState = scrap.hasTransientState();
             if (scrapHasTransientState) {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 65a2d4d..5392a96 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -959,9 +959,11 @@
         if (!mInDrawing) {
             if (verifyDrawable(dr)) {
                 final Rect dirty = dr.getBounds();
+                final int scrollX = mScrollX + mPaddingLeft;
+                final int scrollY = mScrollY + mPaddingTop;
 
-                invalidate(dirty.left + mScrollX, dirty.top + mScrollY,
-                        dirty.right + mScrollX, dirty.bottom + mScrollY);
+                invalidate(dirty.left + scrollX, dirty.top + scrollY,
+                        dirty.right + scrollX, dirty.bottom + scrollY);
             } else {
                 super.invalidateDrawable(dr);
             }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cb930d6..7a9809f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -56,6 +56,7 @@
 import android.text.SpanWatcher;
 import android.text.Spannable;
 import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.SpannedString;
 import android.text.StaticLayout;
@@ -3494,19 +3495,7 @@
             ss.selEnd = end;
 
             if (mText instanceof Spanned) {
-                /*
-                 * Calling setText() strips off any ChangeWatchers;
-                 * strip them now to avoid leaking references.
-                 * But do it to a copy so that if there are any
-                 * further changes to the text of this view, it
-                 * won't get into an inconsistent state.
-                 */
-
-                Spannable sp = new SpannableString(mText);
-
-                for (ChangeWatcher cw : sp.getSpans(0, sp.length(), ChangeWatcher.class)) {
-                    sp.removeSpan(cw);
-                }
+                Spannable sp = new SpannableStringBuilder(mText);
 
                 if (mEditor != null) {
                     removeMisspelledSpans(sp);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e0a154c..a99aa33 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2172,6 +2172,9 @@
                 case TelephonyManager.NETWORK_TYPE_EHRPD:
                     bin = DATA_CONNECTION_EHRPD;
                     break;
+                case TelephonyManager.NETWORK_TYPE_HSPAP:
+                    bin = DATA_CONNECTION_HSPAP;
+                    break;
                 default:
                     bin = DATA_CONNECTION_OTHER;
                     break;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 73d34c3..c44afae 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -301,6 +301,8 @@
                         count++;
                     } catch (ClassNotFoundException e) {
                         Log.w(TAG, "Class not found for preloading: " + line);
+                    } catch (UnsatisfiedLinkError e) {
+                        Log.w(TAG, "Problem preloading " + line + ": " + e);
                     } catch (Throwable t) {
                         Log.e(TAG, "Error preloading " + line + ".", t);
                         if (t instanceof Error) {
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 44e7ec1..4654178 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -721,7 +721,8 @@
             if (subMenu == null) return false;
 
             mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
-            return false;
+            final MenuPresenter.Callback cb = getCallback();
+            return cb != null ? cb.onOpenSubMenu(subMenu) : false;
         }
 
         @Override
@@ -729,6 +730,10 @@
             if (menu instanceof SubMenuBuilder) {
                 ((SubMenuBuilder) menu).getRootMenu().close(false);
             }
+            final MenuPresenter.Callback cb = getCallback();
+            if (cb != null) {
+                cb.onCloseMenu(menu, allMenusAreClosing);
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
index db0d6dd..92e9ea6 100644
--- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
@@ -144,6 +144,10 @@
         mCallback = cb;
     }
 
+    public Callback getCallback() {
+        return mCallback;
+    }
+
     /**
      * Create a new item view that can be re-bound to other item data later.
      *
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index b5d74e8..5f9d8f2 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -566,7 +566,11 @@
         mUpGoerFive.setEnabled(enable);
         mUpGoerFive.setFocusable(enable);
         // Make sure the home button has an accurate content description for accessibility.
-        if (!enable) {
+        updateHomeAccessibility(enable);
+    }
+
+    private void updateHomeAccessibility(boolean homeEnabled) {
+        if (!homeEnabled) {
             mUpGoerFive.setContentDescription(null);
             mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         } else {
@@ -677,19 +681,7 @@
         }
 
         // Make sure the home button has an accurate content description for accessibility.
-        if (!mHomeLayout.isEnabled()) {
-            mHomeLayout.setContentDescription(null);
-            mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-        } else {
-            mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-            if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
-                mHomeLayout.setContentDescription(mContext.getResources().getText(
-                        R.string.action_bar_up_description));
-            } else {
-                mHomeLayout.setContentDescription(mContext.getResources().getText(
-                        R.string.action_bar_home_description));
-            }
-        }
+        updateHomeAccessibility(!mUpGoerFive.isEnabled());
     }
 
     public void setIcon(Drawable icon) {
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index a7a0bb2..ccd75d5 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -34,6 +34,13 @@
     if (NULL != name) {
         AutoJavaStringToUTF8    str(env, name);
         face = SkTypeface::CreateFromName(str.c_str(), style);
+        // Try to find the closest matching font, using the standard heuristic
+        if (NULL == face) {
+            face = SkTypeface::CreateFromName(str.c_str(), (SkTypeface::Style)(style ^ SkTypeface::kItalic));
+        }
+        for (int i = 0; NULL == face && i < 4; i++) {
+            face = SkTypeface::CreateFromName(str.c_str(), (SkTypeface::Style)i);
+        }
     }
 
     // return the default font at the best style if no exact match exists
@@ -45,8 +52,13 @@
 
 static SkTypeface* Typeface_createFromTypeface(JNIEnv* env, jobject, SkTypeface* family, int style) {
     SkTypeface* face = SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)style);
-    // return the default font at the best style if the requested style does not
-    // exist in the provided family
+    // Try to find the closest matching font, using the standard heuristic
+    if (NULL == face) {
+        face = SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic));
+    }
+    for (int i = 0; NULL == face && i < 4; i++) {
+        face = SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)i);
+    }
     if (NULL == face) {
         face = SkTypeface::CreateFromName(NULL, (SkTypeface::Style)style);
     }
diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png
index ca65994..a804a8a 100644
--- a/core/res/res/drawable-hdpi/toast_frame.9.png
+++ b/core/res/res/drawable-hdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png
deleted file mode 100644
index a804a8a..0000000
--- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/toast_frame.9.png b/core/res/res/drawable-ldpi/toast_frame.9.png
index 3b344ff..e64dc75 100644
--- a/core/res/res/drawable-ldpi/toast_frame.9.png
+++ b/core/res/res/drawable-ldpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png
index 9e93fe7..778e4e6 100644
--- a/core/res/res/drawable-mdpi/toast_frame.9.png
+++ b/core/res/res/drawable-mdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png
deleted file mode 100644
index 778e4e6..0000000
--- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_frame.9.png b/core/res/res/drawable-xhdpi/toast_frame.9.png
index 1f63420..77e69c7 100644
--- a/core/res/res/drawable-xhdpi/toast_frame.9.png
+++ b/core/res/res/drawable-xhdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
deleted file mode 100644
index 77e69c7..0000000
--- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/toast_frame.9.png b/core/res/res/drawable-xxhdpi/toast_frame.9.png
index 882b9c6..edecb63 100644
--- a/core/res/res/drawable-xxhdpi/toast_frame.9.png
+++ b/core/res/res/drawable-xxhdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
deleted file mode 100644
index edecb63..0000000
--- a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/values-mcc311-mnc190/config.xml b/core/res/res/values-mcc311-mnc190/config.xml
new file mode 100644
index 0000000..a6c4d1b
--- /dev/null
+++ b/core/res/res/values-mcc311-mnc190/config.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
+    <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+    <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
+    <integer-array translatable="false" name="config_tether_upstream_types">
+      <item>1</item>
+      <item>4</item>
+      <item>7</item>
+      <item>9</item>
+    </integer-array>
+
+    <!-- String containing the apn value for tethering.  May be overriden by secure settings
+         TETHER_DUN_APN.  Value is a comma separated series of strings:
+         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
+    <string translatable="false" name="config_tether_apndata">Tether,broadband.cellular1.net,,,,,,,,,311,190,,DUN</string>
+
+</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index ef30b98..91af50a 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -22,7 +22,7 @@
     <!-- Do not translate. These are all of the drawable resources that should be preloaded by
          the zygote process before it starts forking application processes. -->
     <array name="preloaded_drawables">
-       <item>@drawable/toast_frame_holo</item>
+       <item>@drawable/toast_frame</item>
        <item>@drawable/btn_check_on_pressed_holo_light</item>
        <item>@drawable/btn_check_on_pressed_holo_dark</item>
        <item>@drawable/btn_check_on_holo_light</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 42ea384..18cf57d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -585,8 +585,8 @@
     <!-- Disable lockscreen rotation by default -->
     <bool name="config_enableLockScreenRotation">false</bool>
 
-    <!-- Disable lockscreen translucent decor by default -->
-    <bool name="config_enableLockScreenTranslucentDecor">false</bool>
+    <!-- Enable lockscreen translucent decor by default -->
+    <bool name="config_enableLockScreenTranslucentDecor">true</bool>
 
     <!-- Enable translucent decor by default -->
     <bool name="config_enableTranslucentDecor">true</bool>
@@ -1176,6 +1176,19 @@
          where if the preferred is used we don't try the others. -->
     <bool name="config_dontPreferApn">false</bool>
 
+    <!-- The list of ril radio technologies (see ServiceState.java) which only support
+         a single data connection at one time.  This may change by carrier via
+         overlays (some don't support multiple pdp on UMTS).  All unlisted radio
+         tech types support unlimited types (practically only 2-4 used). -->
+    <integer-array name="config_onlySingleDcAllowed">
+        <item>4</item>  <!-- IS95A -->
+        <item>5</item>  <!-- IS95B -->
+        <item>6</item>  <!-- 1xRTT -->
+        <item>7</item>  <!-- EVDO_0 -->
+        <item>8</item>  <!-- EVDO_A -->
+        <item>12</item> <!-- EVDO_B -->
+    </integer-array>
+
     <!-- Vibrator pattern to be used as the default for notifications
          that specify DEFAULT_VIBRATE.
      -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 22a9402..b85bff4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1435,6 +1435,7 @@
   <java-symbol type="array" name="config_locationProviderPackageNames" />
   <java-symbol type="array" name="config_defaultNotificationVibePattern" />
   <java-symbol type="array" name="config_notificationFallbackVibePattern" />
+  <java-symbol type="array" name="config_onlySingleDcAllowed" />
   <java-symbol type="bool" name="config_animateScreenLights" />
   <java-symbol type="bool" name="config_automatic_brightness_available" />
   <java-symbol type="bool" name="config_enableFusedLocationOverlay" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 4c80e7d..c8d9fc6 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -1048,7 +1048,7 @@
         <item name="presentationTheme">@android:style/Theme.Holo.Dialog.Presentation</item>
 
         <!-- Toast attributes -->
-        <item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
+        <item name="toastFrameBackground">@android:drawable/toast_frame</item>
 
         <!-- Panel attributes -->
         <item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_dark</item>
@@ -1363,7 +1363,7 @@
         <item name="presentationTheme">@android:style/Theme.Holo.Light.Dialog.Presentation</item>
 
         <!-- Toast attributes -->
-        <item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
+        <item name="toastFrameBackground">@android:drawable/toast_frame</item>
 
         <!-- Panel attributes -->
         <item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_light</item>
diff --git a/data/keyboards/AVRCP.kl b/data/keyboards/AVRCP.kl
index 736b43c..4c91ece 100644
--- a/data/keyboards/AVRCP.kl
+++ b/data/keyboards/AVRCP.kl
@@ -14,8 +14,8 @@
 
 # Key layout used for Bluetooth AVRCP support.
 
-key 200   MEDIA_PLAY          WAKE
-key 201   MEDIA_PAUSE         WAKE
+key 200   MEDIA_PLAY_PAUSE    WAKE
+key 201   MEDIA_PLAY_PAUSE    WAKE
 key 166   MEDIA_STOP          WAKE
 key 163   MEDIA_NEXT          WAKE
 key 165   MEDIA_PREVIOUS      WAKE
diff --git a/docs/html/about/versions/kitkat.jd b/docs/html/about/versions/kitkat.jd
index 6c6cb6c..92ebf65 100644
--- a/docs/html/about/versions/kitkat.jd
+++ b/docs/html/about/versions/kitkat.jd
@@ -55,7 +55,7 @@
 <div id="44-android-44" class="version-section">
 
   <div style="padding:0px 0px 0px 60px;margin-top:-3px;float:right;">
-    <img src="{@docRoot}images/kk-android-44.png" alt="Android 4.4 on phone and tablet" width="380"> 
+    <img src="{@docRoot}design/media/index_landing_page.png" alt="Android 4.4 on phone and tablet" width="380">
   </div>
 
   <div class="landing-docs" style="float:right;clear:both;margin:22px 0 2em 3em;">
@@ -143,7 +143,8 @@
   lets you tune your app's behavior to match the device's memory configuration.
   You can modify or disable large-memory features as needed, depending on the
   use-cases you want to support on entry-level devices. Learn more about
-  optimizing your apps for low-memory devices <a href="">here</a>.
+  optimizing your apps for low-memory devices <a
+  href="{@docRoot}training/articles/memory.html">here</a>.
 </p>
 
 <p>
@@ -612,18 +613,20 @@
   Now it's easy to create high-quality video of your app, directly from your
   Android device. <span style="white-space:nowrap;">Android 4.4</span> adds
   support for screen recording and provides a <strong>screen recording
-  utility</strong> that lets you capture video as you use the device and store
-  it as an MP4 file. It's a great new way to create walkthroughs and tutorials
-  for your app, testing materials, marketing videos, and much more.
+  utility</strong> that lets you start and stop recording on a device that's
+  connected to your Android SDK environment over USB. It's a great new way to
+  create walkthroughs and tutorials for your app, testing materials, marketing
+  videos, and more.
 </p>
 
 <p>
-  You can record at any device-supported resolution and bitrate you want, and
-  the output retains the aspect ratio of the display. By default, the utility
-  selects a resolution equal or close to the device's display resolution in the
-  current orientation. When you are done recording, you can share the video
-  directly from your device or pull the MP4 file to your host computer for
-  post-production.
+  With the screen recording utility, you can capture video of your device screen
+  contents and store the video as an MP4 file on the device. You can record at any
+  device-supported resolution and bitrate you want, and the output retains the
+  aspect ratio of the display. By default, the utility selects a resolution
+  equal or close to the device's display resolution in the current orientation.
+  When you are done recording, you can share the video directly from your
+  device or pull the MP4 file to your host computer for post-production.
 </p>
 
 <p>
@@ -827,9 +830,7 @@
 
 <p>
   <span style="white-space:nowrap;">Android 4.4</span> upgrades its
-  SurfaceFlinger from OpenGL ES 1.0 to OpenGL ES 2.0. This boosts performance
-  by using multi-texturing, and it improves color calibration and supports more
-  advanced special effects.
+  SurfaceFlinger from OpenGL ES 1.0 to OpenGL ES 2.0.
 </p>
 
 <h4 id="44-composer">New Hardware Composer support for virtual displays</h4>
diff --git a/docs/html/images/kk-saf2-n5.jpg b/docs/html/images/kk-saf2-n5.jpg
index c96d5d4..1c54a95 100644
--- a/docs/html/images/kk-saf2-n5.jpg
+++ b/docs/html/images/kk-saf2-n5.jpg
Binary files differ
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6d60dd2..3c24683 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1167,6 +1167,11 @@
      * @see #reconfigure(int, int, Config)
      */
     public final int getAllocationByteCount() {
+        if (mBuffer == null) {
+            // native backed bitmaps don't support reconfiguration,
+            // so alloc size is always content size
+            return getByteCount();
+        }
         return mBuffer.length;
     }
 
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 6fd1763..429be49 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -259,14 +259,26 @@
          * (e.g. the bitmap is drawn, getPixels() is called), they will be
          * automatically re-decoded.
          *
-         * For the re-decode to happen, the bitmap must have access to the
+         * <p>For the re-decode to happen, the bitmap must have access to the
          * encoded data, either by sharing a reference to the input
          * or by making a copy of it. This distinction is controlled by
          * inInputShareable. If this is true, then the bitmap may keep a shallow
          * reference to the input. If this is false, then the bitmap will
          * explicitly make a copy of the input data, and keep that. Even if
          * sharing is allowed, the implementation may still decide to make a
-         * deep copy of the input data.
+         * deep copy of the input data.</p>
+         *
+         * <p>While inPurgeable can help avoid big Dalvik heap allocations (from
+         * API level 11 onward), it sacrifices performance predictability since any
+         * image that the view system tries to draw may incur a decode delay which
+         * can lead to dropped frames. Therefore, most apps should avoid using
+         * inPurgeable to allow for a fast and fluid UI. To minimize Dalvik heap
+         * allocations use the {@link #inBitmap} flag instead.</p>
+         *
+         * <p class="note"><strong>Note:</strong> This flag is ignored when used
+         * with {@link #decodeResource(Resources, int,
+         * android.graphics.BitmapFactory.Options)} or {@link #decodeFile(String,
+         * android.graphics.BitmapFactory.Options)}.</p>
          */
         public boolean inPurgeable;
 
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index e350e8d..af8441a 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -23,6 +23,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.SystemClock;
+import android.util.LayoutDirection;
 import android.util.SparseArray;
 
 /**
@@ -59,6 +60,8 @@
     private long mExitAnimationEnd;
     private Drawable mLastDrawable;
 
+    private Insets mInsets = Insets.NONE;
+
     // overrides from Drawable
 
     @Override
@@ -78,18 +81,30 @@
                 | mDrawableContainerState.mChildrenChangingConfigurations;
     }
 
+    private boolean needsMirroring() {
+        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+    }
+
     @Override
     public boolean getPadding(Rect padding) {
         final Rect r = mDrawableContainerState.getConstantPadding();
+        boolean result = true;
         if (r != null) {
             padding.set(r);
-            return true;
-        }
-        if (mCurrDrawable != null) {
-            return mCurrDrawable.getPadding(padding);
         } else {
-            return super.getPadding(padding);
+            if (mCurrDrawable != null) {
+                result = mCurrDrawable.getPadding(padding);
+            } else {
+                result = super.getPadding(padding);
+            }
         }
+        if (needsMirroring()) {
+            final int left = padding.left;
+            final int right = padding.right;
+            padding.left = right;
+            padding.right = left;
+        }
+        return result;
     }
 
     /**
@@ -97,7 +112,7 @@
      */
     @Override
     public Insets getOpticalInsets() {
-        return (mCurrDrawable == null) ? Insets.NONE : mCurrDrawable.getOpticalInsets();
+        return mInsets;
     }
 
     @Override
@@ -334,6 +349,7 @@
             mCurrDrawable = d;
             mCurIndex = idx;
             if (d != null) {
+                mInsets = d.getOpticalInsets();
                 d.mutate();
                 if (mDrawableContainerState.mEnterFadeDuration > 0) {
                     mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
@@ -348,9 +364,12 @@
                 d.setBounds(getBounds());
                 d.setLayoutDirection(getLayoutDirection());
                 d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+            } else {
+                mInsets = Insets.NONE;
             }
         } else {
             mCurrDrawable = null;
+            mInsets = Insets.NONE;
             mCurIndex = -1;
         }
 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 1f5fefd..b836f50 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2913,12 +2913,10 @@
             int index;
             if (isMuted()) {
                 index = 0;
-            } else if (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC &&
-                       (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+            } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                        mAvrcpAbsVolSupported) {
                 index = (mIndexMax + 5)/10;
-            }
-            else {
+            } else {
                 index = (getIndex(device) + 5)/10;
             }
             AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
@@ -2943,6 +2941,9 @@
                 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                     if (isMuted()) {
                         index = 0;
+                    } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+                            mAvrcpAbsVolSupported) {
+                        index = (mIndexMax + 5)/10;
                     } else {
                         index = ((Integer)entry.getValue() + 5)/10;
                     }
@@ -3215,7 +3216,14 @@
             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                 if (streamType != streamState.mStreamType &&
                         mStreamVolumeAlias[streamType] == streamState.mStreamType) {
-                    mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
+                    // Make sure volume is also maxed out on A2DP device for aliased stream
+                    // that may have a different device selected
+                    int streamDevice = getDeviceForStream(streamType);
+                    if ((device != streamDevice) && mAvrcpAbsVolSupported &&
+                            ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
+                        mStreamStates[streamType].applyDeviceVolume(device);
+                    }
+                    mStreamStates[streamType].applyDeviceVolume(streamDevice);
                 }
             }
 
@@ -3843,9 +3851,12 @@
         // address is not used for now, but may be used when multiple a2dp devices are supported
         synchronized (mA2dpAvrcpLock) {
             mAvrcpAbsVolSupported = support;
-            VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
             sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
-                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
+                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
+                    mStreamStates[AudioSystem.STREAM_MUSIC], 0);
+            sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
+                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
+                    mStreamStates[AudioSystem.STREAM_RING], 0);
         }
     }
 
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
index 8be15cb..b4847f0 100644
--- a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -32,12 +32,10 @@
         android:id="@+id/carrier_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:singleLine="true"
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textSize="@dimen/kg_status_line_font_size"
-        android:textColor="?android:attr/textColorSecondary"
-        android:textAllCaps="@bool/kg_use_all_caps" />
+        android:textColor="?android:attr/textColorSecondary" />
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
index 7d1f24f..3dc4d5c 100644
--- a/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
+++ b/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java
@@ -243,11 +243,12 @@
         final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0;
         final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0;
 
-        mPreview.setPivotX(0);
+        final boolean isRtl = mPreview.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        mPreview.setPivotX(isRtl ? mPreview.width : 0);
         mPreview.setPivotY(0);
         mPreview.setScaleX(pvScale);
         mPreview.setScaleY(pvScale);
-        mPreview.setTranslationX(pvTransX);
+        mPreview.setTranslationX((isRtl ? -1 : 1) * pvTransX);
         mPreview.setTranslationY(pvTransY);
 
         mRenderedSize.set(width, height);
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index c33f174..88558cd 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -17,14 +17,18 @@
 package com.android.keyguard;
 
 import android.content.Context;
+import android.text.method.SingleLineTransformationMethod;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.TextView;
 
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.widget.LockPatternUtils;
 
+import java.util.Locale;
+
 public class CarrierText extends TextView {
     private static CharSequence mSeparator;
 
@@ -77,6 +81,8 @@
     public CarrierText(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLockPatternUtils = new LockPatternUtils(mContext);
+        boolean useAllCaps = mContext.getResources().getBoolean(R.bool.kg_use_all_caps);
+        setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
     }
 
     protected void updateCarrierText(State simState, CharSequence plmn, CharSequence spn) {
@@ -258,4 +264,25 @@
 
         return mContext.getText(carrierHelpTextId);
     }
+
+    private class CarrierTextTransformationMethod extends SingleLineTransformationMethod {
+        private final Locale mLocale;
+        private final boolean mAllCaps;
+
+        public CarrierTextTransformationMethod(Context context, boolean allCaps) {
+            mLocale = context.getResources().getConfiguration().locale;
+            mAllCaps = allCaps;
+        }
+
+        @Override
+        public CharSequence getTransformation(CharSequence source, View view) {
+            source = super.getTransformation(source, view);
+
+            if (mAllCaps && source != null) {
+                source = source.toString().toUpperCase(mLocale);
+            }
+
+            return source;
+        }
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
index 677f1f1..2ee21ac 100644
--- a/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
+++ b/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java
@@ -39,7 +39,7 @@
      *
      * @param b true to show, false to hide
      */
-    void showChallenge(boolean b);
+    void showChallenge(boolean show);
 
     /**
      * Show the bouncer challenge. This may block access to other child views.
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java b/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
index 9a1aa5b..0a915ea 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java
@@ -99,6 +99,11 @@
 
     public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
         LockPatternUtils lockPatternUtils = getLockPatternUtils();
+
+        // Workaround to avoid camera release/acquisition race when resuming face unlock
+        // after showing lockscreen camera (bug 11063890).
+        KeyguardUpdateMonitor.getInstance(getContext()).setAlternateUnlockEnabled(false);
+
         if (lockPatternUtils.isSecure()) {
             // Launch the secure version of the camera
             if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index fdc06a6..1bae9b8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -218,7 +218,7 @@
         mTransportState = (dcs.clearing ? TRANSPORT_GONE :
             (isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
 
-        if (DEBUG) Log.v(TAG, "Initial transport state: "
+        if (DEBUGXPORT) Log.v(TAG, "Initial transport state: "
                 + mTransportState + ", pbstate=" + dcs.playbackState);
     }
 
@@ -1369,7 +1369,7 @@
         }
     }
 
-    Runnable mSwitchPageRunnable = new Runnable() {
+    private final Runnable mSwitchPageRunnable = new Runnable() {
         @Override
         public void run() {
            showAppropriateWidgetPage();
@@ -1438,7 +1438,7 @@
         mAppWidgetToShow = ss.appWidgetToShow;
         setInsets(ss.insets);
         if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
-        post(mSwitchPageRunnable);
+        mSwitchPageRunnable.run();
     }
 
     @Override
@@ -1472,10 +1472,21 @@
     }
 
     private void showAppropriateWidgetPage() {
-        int state = mTransportState;
-        ensureTransportPresentOrRemoved(state);
-        int pageToShow = getAppropriateWidgetPage(state);
-        mAppWidgetContainer.setCurrentPage(pageToShow);
+        final int state = mTransportState;
+        final boolean transportAdded = ensureTransportPresentOrRemoved(state);
+        final int pageToShow = getAppropriateWidgetPage(state);
+        if (!transportAdded) {
+            mAppWidgetContainer.setCurrentPage(pageToShow);
+        } else if (state == TRANSPORT_VISIBLE) {
+            // If the transport was just added, we need to wait for layout to happen before
+            // we can set the current page.
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    mAppWidgetContainer.setCurrentPage(pageToShow);
+                }
+            });
+        }
     }
 
     /**
@@ -1499,12 +1510,11 @@
      *
      * @param state
      */
-    private void ensureTransportPresentOrRemoved(int state) {
+    private boolean ensureTransportPresentOrRemoved(int state) {
         final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
         final boolean visible = state == TRANSPORT_VISIBLE;
         final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
         if (!showing && (visible || shouldBeVisible)) {
-            if (DEBUGXPORT) Log.v(TAG, "add transport");
             // insert to left of camera if it exists, otherwise after right-most widget
             int lastWidget = mAppWidgetContainer.getChildCount() - 1;
             int position = 0; // handle no widget case
@@ -1512,13 +1522,16 @@
                 position = mAppWidgetContainer.isCameraPage(lastWidget) ?
                         lastWidget : lastWidget + 1;
             }
+            if (DEBUGXPORT) Log.v(TAG, "add transport at " + position);
             mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
+            return true;
         } else if (showing && state == TRANSPORT_GONE) {
             if (DEBUGXPORT) Log.v(TAG, "remove transport");
             mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
             mTransportControl = null;
             KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null);
         }
+        return false;
     }
 
     private CameraWidgetFrame findCameraPage() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index 69075ec..751572c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -177,6 +177,7 @@
 
     public KeyguardMessageArea(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
 
         mLockPatternUtils = new LockPatternUtils(context);
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
index 8ccd6fe..36b2446 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -68,8 +68,6 @@
     }
 
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
-        private boolean mSetHiddenCalled;
-        private boolean mIsHidden;
         public boolean isShowing() {
             return mKeyguardViewMediator.isShowing();
         }
@@ -91,10 +89,7 @@
         }
         public void setHidden(boolean isHidden) {
             checkPermission();
-            if (mSetHiddenCalled && mIsHidden == isHidden) return;
             mKeyguardViewMediator.setHidden(isHidden);
-            mSetHiddenCalled = true;
-            mIsHidden = isHidden;
         }
         public void dismiss() {
             mKeyguardViewMediator.dismiss();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index e39622a..9accbb4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -46,9 +46,10 @@
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
     private static final String LOG_TAG = "KeyguardSimPinView";
     private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
+    public static final String TAG = "KeyguardSimPinView";
 
     private ProgressDialog mSimUnlockProgressDialog = null;
-    private volatile boolean mSimCheckInProgress;
+    private CheckSimPin mCheckSimPinThread;
 
     private AlertDialog mRemainingAttemptsDialog;
 
@@ -169,14 +170,17 @@
         @Override
         public void run() {
             try {
+                Log.v(TAG, "call supplyPinReportResult()");
                 final int[] result = ITelephony.Stub.asInterface(ServiceManager
                         .checkService("phone")).supplyPinReportResult(mPin);
+                Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
                 post(new Runnable() {
                     public void run() {
                         onSimCheckResponse(result[0], result[1]);
                     }
                 });
             } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException for supplyPinReportResult:", e);
                 post(new Runnable() {
                     public void run() {
                         onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
@@ -229,9 +233,8 @@
 
         getSimUnlockProgressDialog().show();
 
-        if (!mSimCheckInProgress) {
-            mSimCheckInProgress = true; // there should be only one
-            new CheckSimPin(mPasswordEntry.getText().toString()) {
+        if (mCheckSimPinThread == null) {
+            mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText().toString()) {
                 void onSimCheckResponse(final int result, final int attemptsRemaining) {
                     post(new Runnable() {
                         public void run() {
@@ -263,11 +266,12 @@
                                 mPasswordEntry.setText("");
                             }
                             mCallback.userActivity(0);
-                            mSimCheckInProgress = false;
+                            mCheckSimPinThread = null;
                         }
                     });
                 }
-            }.start();
+            };
+            mCheckSimPinThread.start();
         }
     }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
index 31518a1..6e9e83e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -45,9 +45,10 @@
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
     private static final String LOG_TAG = "KeyguardSimPukView";
     private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
+    public static final String TAG = "KeyguardSimPukView";
 
     private ProgressDialog mSimUnlockProgressDialog = null;
-    private volatile boolean mCheckInProgress;
+    private CheckSimPuk mCheckSimPukThread;
     private String mPukText;
     private String mPinText;
     private StateMachine mStateMachine = new StateMachine();
@@ -220,15 +221,17 @@
         @Override
         public void run() {
             try {
+                Log.v(TAG, "call supplyPukReportResult()");
                 final int[] result = ITelephony.Stub.asInterface(ServiceManager
                         .checkService("phone")).supplyPukReportResult(mPuk, mPin);
-
+                Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]);
                 post(new Runnable() {
                     public void run() {
                         onSimLockChangedResponse(result[0], result[1]);
                     }
                 });
             } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException for supplyPukReportResult:", e);
                 post(new Runnable() {
                     public void run() {
                         onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
@@ -295,9 +298,8 @@
     private void updateSim() {
         getSimUnlockProgressDialog().show();
 
-        if (!mCheckInProgress) {
-            mCheckInProgress = true;
-            new CheckSimPuk(mPukText, mPinText) {
+        if (mCheckSimPukThread == null) {
+            mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText) {
                 void onSimLockChangedResponse(final int result, final int attemptsRemaining) {
                     post(new Runnable() {
                         public void run() {
@@ -326,11 +328,12 @@
                                         + " attemptsRemaining=" + attemptsRemaining);
                                 mStateMachine.reset();
                             }
-                            mCheckInProgress = false;
+                            mCheckSimPukThread = null;
                         }
                     });
                 }
-            }.start();
+            };
+            mCheckSimPukThread.start();
         }
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index d933275..0bfee38 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -98,26 +98,13 @@
     }
 
     protected void refresh() {
-        Resources res = mContext.getResources();
-        Locale locale = Locale.getDefault();
-        final String dateFormat = DateFormat.getBestDateTimePattern(locale,
-                res.getString(R.string.abbrev_wday_month_day_no_year));
+        Patterns.update(mContext);
 
-        mDateView.setFormat24Hour(dateFormat);
-        mDateView.setFormat12Hour(dateFormat);
+        mDateView.setFormat24Hour(Patterns.dateView);
+        mDateView.setFormat12Hour(Patterns.dateView);
 
-        // 12-hour clock.
-        // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
-        // format.  The following code removes the AM/PM indicator if we didn't want it.
-        final String clock12skel = res.getString(R.string.clock_12hr_format);
-        String clock12hr = DateFormat.getBestDateTimePattern(locale, clock12skel);
-        clock12hr = clock12skel.contains("a") ? clock12hr : clock12hr.replaceAll("a", "").trim();
-        mClockView.setFormat12Hour(clock12hr);
-
-        // 24-hour clock
-        final String clock24skel = res.getString(R.string.clock_24hr_format);
-        final String clock24hr = DateFormat.getBestDateTimePattern(locale, clock24skel);
-        mClockView.setFormat24Hour(clock24hr);
+        mClockView.setFormat12Hour(Patterns.clockView12);
+        mClockView.setFormat24Hour(Patterns.clockView24);
 
         refreshAlarmStatus();
     }
@@ -149,4 +136,35 @@
         return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
     }
 
+    // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+    // This is an optimization to ensure we only recompute the patterns when the inputs change.
+    private static final class Patterns {
+        static String dateView;
+        static String clockView12;
+        static String clockView24;
+        static String cacheKey;
+
+        static void update(Context context) {
+            final Locale locale = Locale.getDefault();
+            final Resources res = context.getResources();
+            final String dateViewSkel = res.getString(R.string.abbrev_wday_month_day_no_year);
+            final String clockView12Skel = res.getString(R.string.clock_12hr_format);
+            final String clockView24Skel = res.getString(R.string.clock_24hr_format);
+            final String key = locale.toString() + dateViewSkel + clockView12Skel + clockView24Skel;
+            if (key.equals(cacheKey)) return;
+
+            dateView = DateFormat.getBestDateTimePattern(locale, dateViewSkel);
+
+            clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
+            // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+            // format.  The following code removes the AM/PM indicator if we didn't want it.
+            if (!clockView12Skel.contains("a")) {
+                clockView12 = clockView12.replaceAll("a", "").trim();
+            }
+
+            clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
+
+            cacheKey = key;
+        }
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
index 29ba60d..2719c5c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
@@ -104,7 +104,9 @@
             new RemoteController.OnClientUpdateListener() {
         @Override
         public void onClientChange(boolean clearing) {
-            clearMetadata();
+            if (clearing) {
+                clearMetadata();
+            }
         }
 
         @Override
@@ -206,10 +208,10 @@
             = new KeyguardUpdateMonitorCallback() {
         public void onScreenTurnedOff(int why) {
             setEnableMarquee(false);
-        };
+        }
         public void onScreenTurnedOn() {
             setEnableMarquee(true);
-        };
+        }
     };
 
     public KeyguardTransportControlView(Context context, AttributeSet attrs) {
@@ -328,6 +330,33 @@
         removeCallbacks(mUpdateSeekBars);
     }
 
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        SavedState ss = new SavedState(super.onSaveInstanceState());
+        ss.artist = mMetadata.artist;
+        ss.trackTitle = mMetadata.trackTitle;
+        ss.albumTitle = mMetadata.albumTitle;
+        ss.duration = mMetadata.duration;
+        ss.bitmap = mMetadata.bitmap;
+        return ss;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (!(state instanceof SavedState)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        mMetadata.artist = ss.artist;
+        mMetadata.trackTitle = ss.trackTitle;
+        mMetadata.albumTitle = ss.albumTitle;
+        mMetadata.duration = ss.duration;
+        mMetadata.bitmap = ss.bitmap;
+        populateMetadata();
+    }
+
     void setBadgeIcon(Drawable bmp) {
         mBadge.setImageDrawable(bmp);
 
@@ -587,6 +616,11 @@
 
     static class SavedState extends BaseSavedState {
         boolean clientPresent;
+        String artist;
+        String trackTitle;
+        String albumTitle;
+        long duration;
+        Bitmap bitmap;
 
         SavedState(Parcelable superState) {
             super(superState);
@@ -594,13 +628,23 @@
 
         private SavedState(Parcel in) {
             super(in);
-            this.clientPresent = in.readInt() != 0;
+            clientPresent = in.readInt() != 0;
+            artist = in.readString();
+            trackTitle = in.readString();
+            albumTitle = in.readString();
+            duration = in.readLong();
+            bitmap = Bitmap.CREATOR.createFromParcel(in);
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             super.writeToParcel(out, flags);
-            out.writeInt(this.clientPresent ? 1 : 0);
+            out.writeInt(clientPresent ? 1 : 0);
+            out.writeString(artist);
+            out.writeString(trackTitle);
+            out.writeString(albumTitle);
+            out.writeLong(duration);
+            bitmap.writeToParcel(out, flags);
         }
 
         public static final Parcelable.Creator<SavedState> CREATOR
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 520cea3..a849316 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -815,7 +815,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onKeyguardVisibilityChanged(isShowing);
+                cb.onKeyguardVisibilityChangedRaw(isShowing);
             }
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 76f9637..c08880d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
+import android.os.SystemClock;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -27,6 +28,11 @@
  * Callback for general information relevant to lock screen.
  */
 class KeyguardUpdateMonitorCallback {
+
+    private static final long VISIBILITY_CHANGED_COLLAPSE_MS = 1000;
+    private long mVisibilityChangedCalled;
+    private boolean mShowing;
+
     /**
      * Called when the battery status changes, e.g. when plugged in or unplugged, charge
      * level, etc. changes.
@@ -70,6 +76,15 @@
      */
     void onKeyguardVisibilityChanged(boolean showing) { }
 
+    void onKeyguardVisibilityChangedRaw(boolean showing) {
+        final long now = SystemClock.elapsedRealtime();
+        if (showing == mShowing
+                && (now - mVisibilityChangedCalled) < VISIBILITY_CHANGED_COLLAPSE_MS) return;
+        onKeyguardVisibilityChanged(showing);
+        mVisibilityChangedCalled = now;
+        mShowing = showing;
+    }
+
     /**
      * Called when visibility of lockscreen clock changes, such as when
      * obscured by a widget.
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
index 8e39628..f0413d6 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
@@ -15,6 +15,9 @@
  */
 package com.android.keyguard;
 
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -46,6 +49,20 @@
 
     int mChallengeTop = 0;
 
+    private final AnimatorListener mPauseListener = new AnimatorListenerAdapter() {
+        public void onAnimationEnd(Animator animation) {
+            mKeyguardSecurityContainer.onPause();
+        }
+    };
+
+    private final AnimatorListener mResumeListener = new AnimatorListenerAdapter() {
+        public void onAnimationEnd(Animator animation) {
+            if (((View)mKeyguardSecurityContainer).isShown()) {
+                mKeyguardSecurityContainer.onResume(0);
+            }
+        }
+    };
+
     public KeyguardViewStateManager(KeyguardHostView hostView) {
         mKeyguardHostView = hostView;
     }
@@ -102,20 +119,20 @@
     }
 
     public void fadeOutSecurity(int duration) {
-        ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration).start();
+        ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration)
+                .setListener(mPauseListener);
     }
 
     public void fadeInSecurity(int duration) {
-        ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration).start();
+        ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration)
+                .setListener(mResumeListener);
     }
 
     public void onPageBeginMoving() {
         if (mChallengeLayout.isChallengeOverlapping() &&
                 mChallengeLayout instanceof SlidingChallengeLayout) {
             SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
-            if (!mKeyguardWidgetPager.isWarping()) {
-                scl.fadeOutChallenge();
-            }
+            scl.fadeOutChallenge();
             mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage();
         }
         // We use mAppWidgetToShow to show a particular widget after you add it--
@@ -139,9 +156,6 @@
             boolean isCameraPage = newPage instanceof CameraWidgetFrame;
             SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
             scl.setChallengeInteractive(!isCameraPage);
-            if (isCameraPage) {
-                scl.fadeOutChallenge();
-            }
             final int currentFlags = mKeyguardWidgetPager.getSystemUiVisibility();
             final int newFlags = isCameraPage ? (currentFlags | View.STATUS_BAR_DISABLE_SEARCH)
                     : (currentFlags & ~View.STATUS_BAR_DISABLE_SEARCH);
@@ -178,7 +192,7 @@
             boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
             if (challengeOverlapping && !newCurPage.isSmall()
                     && mPageListeningToSlider != newPageIndex) {
-                newCurPage.shrinkWidget();
+                newCurPage.shrinkWidget(true);
             }
         }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
index ab8a759..8ee9b61 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java
@@ -375,10 +375,6 @@
         return mSmallFrameHeight;
     }
 
-    public void shrinkWidget() {
-        shrinkWidget(true);
-    }
-
     public void setWidgetLockedSmall(boolean locked) {
         if (locked) {
             setWidgetHeight(mSmallWidgetHeight);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
index 704af6e..99f7757 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -194,7 +194,9 @@
 
     @Override
     public void onPageEndWarp() {
-        hideOutlinesAndSidePages();
+        // if we're moving to the warp page, then immediately hide the other widgets.
+        int duration = getPageWarpIndex() == getNextPage() ? 0 : -1;
+        animateOutlinesAndSidePages(false, duration);
         mViewStateManager.onPageEndWarp();
     }
 
@@ -669,7 +671,7 @@
                 // On the very first measure pass, if the challenge is showing, we need to make sure
                 // that the widget on the current page is small.
                 if (challengeShowing && i == mCurrentPage && !mHasMeasure) {
-                    frame.shrinkWidget();
+                    frame.shrinkWidget(true);
                 }
             }
         }
diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java
index 9d237dc..b9404d4 100644
--- a/packages/Keyguard/src/com/android/keyguard/PagedView.java
+++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java
@@ -267,6 +267,8 @@
 
     private boolean mIsCameraEvent;
     private float mWarpPeekAmount;
+    private boolean mOnPageEndWarpCalled;
+    private boolean mOnPageBeginWarpCalled;
 
     public interface PageSwitchListener {
         void onPageSwitching(View newPage, int newPageIndex);
@@ -491,7 +493,7 @@
         if (!mIsPageMoving) {
             mIsPageMoving = true;
             if (isWarping()) {
-                onPageBeginWarp();
+                dispatchOnPageBeginWarp();
                 if (mPageSwapIndex != -1) {
                     swapPages(mPageSwapIndex, mPageWarpIndex);
                 }
@@ -500,6 +502,22 @@
         }
     }
 
+    private void dispatchOnPageBeginWarp() {
+        if (!mOnPageBeginWarpCalled) {
+            onPageBeginWarp();
+            mOnPageBeginWarpCalled = true;
+        }
+        mOnPageEndWarpCalled = false;
+    }
+
+    private void dispatchOnPageEndWarp() {
+        if (!mOnPageEndWarpCalled) {
+            onPageEndWarp();
+            mOnPageEndWarpCalled = true;
+        }
+        mOnPageBeginWarpCalled = false;
+    }
+
     protected void pageEndMoving() {
         if (DEBUG_WARP) Log.v(TAG, "pageEndMoving(" + mIsPageMoving + ")");
         if (mIsPageMoving) {
@@ -508,7 +526,7 @@
                 if (mPageSwapIndex != -1) {
                     swapPages(mPageSwapIndex, mPageWarpIndex);
                 }
-                onPageEndWarp();
+                dispatchOnPageEndWarp();
                 resetPageWarp();
             }
             onPageEndMoving();
@@ -1919,7 +1937,7 @@
         }
 
         if (isWarping()) {
-            onPageEndWarp();
+            dispatchOnPageEndWarp();
             resetPageWarp();
         }
 
@@ -2260,11 +2278,11 @@
         mTempVisiblePagesRange[0] = 0;
         mTempVisiblePagesRange[1] = getPageCount() - 1;
         boundByReorderablePages(true, mTempVisiblePagesRange);
-        mReorderingStarted = true;
 
         // Check if we are within the reordering range
         if (mTempVisiblePagesRange[0] <= dragViewIndex &&
                 dragViewIndex <= mTempVisiblePagesRange[1]) {
+            mReorderingStarted = true;
             if (zoomOut()) {
                 // Find the drag view under the pointer
                 mDragView = getChildAt(dragViewIndex);
@@ -2702,12 +2720,12 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             mWarpAnimation = null;
-            mWarpPageExposed = true;
+            mWarpPageExposed = false;
         }
     };
 
     private void cancelWarpAnimation(String msg, boolean abortAnimation) {
-        if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ")");
+        if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ",abort=" + abortAnimation + ")");
         if (abortAnimation) {
             // We're done with the animation and moving to a new page.  Let the scroller
             // take over the animation.
@@ -2727,9 +2745,9 @@
 
     private void animateWarpPageOnScreen(String reason) {
         if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOnScreen(" + reason + ")");
-        if (isWarping()) {
+        if (isWarping() && !mWarpPageExposed) {
             mWarpPageExposed = true;
-            onPageBeginWarp();
+            dispatchOnPageBeginWarp();
             KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
             if (DEBUG_WARP) Log.v(TAG, "moving page on screen: Tx=" + v.getTranslationX());
             DecelerateInterpolator interp = new DecelerateInterpolator(1.5f);
@@ -2744,7 +2762,7 @@
     private void animateWarpPageOffScreen(String reason, boolean animate) {
         if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOffScreen(" + reason + " anim:" + animate + ")");
         if (isWarping()) {
-            onPageEndWarp();
+            dispatchOnPageEndWarp();
             KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
             if (DEBUG_WARP) Log.v(TAG, "moving page off screen: Tx=" + v.getTranslationX());
             AccelerateInterpolator interp = new AccelerateInterpolator(1.5f);
diff --git a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
index 7a9a1c8..3d515ce 100644
--- a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
+++ b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
@@ -1003,6 +1003,16 @@
         }
     }
 
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        // Focus security fileds before widgets.
+        if (mChallengeView != null &&
+                mChallengeView.requestFocus(direction, previouslyFocusedRect)) {
+            return true;
+        }
+        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+    }
+
     public void computeScroll() {
         super.computeScroll();
 
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index f2e768a..d2613d0 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -93,6 +93,12 @@
     <!-- Title of the action bar button to got to add a printer. [CHAR LIMIT=25] -->
     <string name="print_add_printer">Add printer</string>
 
+    <!-- Title of the menu item to select a printer. [CHAR LIMIT=25] -->
+    <string name="print_select_printer">Select printer</string>
+
+    <!-- Title of the menu item to forget a printer. [CHAR LIMIT=25] -->
+    <string name="print_forget_printer">Forget printer</string>
+
     <!-- Utterance to announce a change in the number of matches during a search. This is spoken to a blind user. [CHAR LIMIT=none] -->
     <plurals name="print_search_result_count_utterance">
         <item quantity="one"><xliff:g id="count" example="1">%1$s</xliff:g> printer found</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
index 0601467..9831839 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -79,6 +79,8 @@
 
     private PrinterId mTrackedPrinter;
 
+    private boolean mPrintersUpdatedBefore;
+
     public FusedPrintersProvider(Context context) {
         super(context);
         mPersistenceManager = new PersistenceManager(context);
@@ -88,13 +90,14 @@
         mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
     }
 
-    private void computeAndDeliverResult(Map<PrinterId, PrinterInfo> discoveredPrinters) {
+    private void computeAndDeliverResult(ArrayMap<PrinterId, PrinterInfo> discoveredPrinters,
+            ArrayMap<PrinterId, PrinterInfo> favoritePrinters) {
         List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
 
         // Add the updated favorite printers.
-        final int favoritePrinterCount = mFavoritePrinters.size();
+        final int favoritePrinterCount = favoritePrinters.size();
         for (int i = 0; i < favoritePrinterCount; i++) {
-            PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
+            PrinterInfo favoritePrinter = favoritePrinters.valueAt(i);
             PrinterInfo updatedPrinter = discoveredPrinters.remove(
                     favoritePrinter.getId());
             if (updatedPrinter != null) {
@@ -123,8 +126,11 @@
         mPrinters.addAll(printers);
 
         if (isStarted()) {
-            // Deliver the printers.
+            // If stated deliver the new printers.
             deliverResult(printers);
+        } else {
+            // Otherwise, take a note for the change.
+            onContentChanged();
         }
     }
 
@@ -165,6 +171,8 @@
                     .getSystemService(Context.PRINT_SERVICE);
             mDiscoverySession = printManager.createPrinterDiscoverySession();
             mPersistenceManager.readPrinterHistory();
+        } else if (mPersistenceManager.isHistoryChanged()) {
+            mPersistenceManager.readPrinterHistory();
         }
         if (mPersistenceManager.isReadHistoryCompleted()
                 && !mDiscoverySession.isPrinterDiscoveryStarted()) {
@@ -176,7 +184,7 @@
                                 + mDiscoverySession.getPrinters().size()
                                 + " " + FusedPrintersProvider.this.hashCode());
                     }
-                    updatePrinters(mDiscoverySession.getPrinters());
+                    updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
                 }
             });
             final int favoriteCount = mFavoritePrinters.size();
@@ -187,15 +195,19 @@
             mDiscoverySession.startPrinterDisovery(printerIds);
             List<PrinterInfo> printers = mDiscoverySession.getPrinters();
             if (!printers.isEmpty()) {
-                updatePrinters(printers);
+                updatePrinters(printers, mFavoritePrinters);
             }
         }
     }
 
-    private void updatePrinters(List<PrinterInfo> printers) {
-        if (mPrinters.equals(printers)) {
+    private void updatePrinters(List<PrinterInfo> printers, List<PrinterInfo> favoritePrinters) {
+        if (mPrintersUpdatedBefore && mPrinters.equals(printers)
+                && mFavoritePrinters.equals(favoritePrinters)) {
             return;
         }
+
+        mPrintersUpdatedBefore = true;
+
         ArrayMap<PrinterId, PrinterInfo> printersMap =
                 new ArrayMap<PrinterId, PrinterInfo>();
         final int printerCount = printers.size();
@@ -203,7 +215,16 @@
             PrinterInfo printer = printers.get(i);
             printersMap.put(printer.getId(), printer);
         }
-        computeAndDeliverResult(printersMap);
+
+        ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap =
+                new ArrayMap<PrinterId, PrinterInfo>();
+        final int favoritePrinterCount = favoritePrinters.size();
+        for (int i = 0; i < favoritePrinterCount; i++) {
+            PrinterInfo favoritePrinter = favoritePrinters.get(i);
+            favoritePrintersMap.put(favoritePrinter.getId(), favoritePrinter);
+        }
+
+        computeAndDeliverResult(printersMap, favoritePrintersMap);
     }
 
     @Override
@@ -264,6 +285,42 @@
         }
     }
 
+    public boolean isFavoritePrinter(PrinterId printerId) {
+        final int printerCount = mFavoritePrinters.size();
+        for (int i = 0; i < printerCount; i++) {
+            PrinterInfo favoritePritner = mFavoritePrinters.get(i);
+            if (favoritePritner.getId().equals(printerId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void forgetFavoritePrinter(PrinterId printerId) {
+        List<PrinterInfo> newFavoritePrinters = null;
+
+        // Remove the printer from the favorites.
+        final int favoritePrinterCount = mFavoritePrinters.size();
+        for (int i = 0; i < favoritePrinterCount; i++) {
+            PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
+            if (favoritePrinter.getId().equals(printerId)) {
+                newFavoritePrinters = new ArrayList<PrinterInfo>();
+                newFavoritePrinters.addAll(mPrinters);
+                newFavoritePrinters.remove(i);
+                break;
+            }
+        }
+
+        // If we removed a favorite printer, we have work to do.
+        if (newFavoritePrinters != null) {
+            // Remove the printer from history and persist the latter.
+            mPersistenceManager.removeHistoricalPrinterAndWritePrinterHistory(printerId);
+
+            // Recompute and deliver the printers.
+            updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters);
+        }
+    }
+
     private final class PersistenceManager {
         private static final String PERSIST_FILE_NAME = "printer_history.xml";
 
@@ -281,13 +338,15 @@
 
         private final AtomicFile mStatePersistFile;
 
-        private List<PrinterInfo> mHistoricalPrinters;
+        private List<PrinterInfo> mHistoricalPrinters = new ArrayList<PrinterInfo>();
 
         private boolean mReadHistoryCompleted;
         private boolean mReadHistoryInProgress;
 
         private ReadTask mReadTask;
 
+        private volatile long mLastReadHistoryTimestamp;
+
         private PersistenceManager(Context context) {
             mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
                     PERSIST_FILE_NAME));
@@ -327,6 +386,27 @@
                     new ArrayList<PrinterInfo>(mHistoricalPrinters));
         }
 
+        @SuppressWarnings("unchecked")
+        public void removeHistoricalPrinterAndWritePrinterHistory(PrinterId printerId) {
+            boolean writeHistory = false;
+            final int printerCount = mHistoricalPrinters.size();
+            for (int i = printerCount - 1; i >= 0; i--) {
+                PrinterInfo historicalPrinter = mHistoricalPrinters.get(i);
+                if (historicalPrinter.getId().equals(printerId)) {
+                    mHistoricalPrinters.remove(i);
+                    writeHistory = true;
+                }
+            }
+            if (writeHistory) {
+                new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+                        new ArrayList<PrinterInfo>(mHistoricalPrinters));
+            }
+        }
+
+        public boolean isHistoryChanged() {
+            return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
+        }
+
         private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
             Map<PrinterId, PrinterRecord> recordMap =
                     new ArrayMap<PrinterId, PrinterRecord>();
@@ -423,11 +503,10 @@
                 mReadHistoryInProgress = false;
                 mReadHistoryCompleted = true;
 
-                // Deliver the favorites.
-                Map<PrinterId, PrinterInfo> discoveredPrinters = Collections.emptyMap();
-                computeAndDeliverResult(discoveredPrinters);
+                // Deliver the printers.
+                updatePrinters(mDiscoverySession.getPrinters(), mHistoricalPrinters);
 
-                // Start loading the available printers.
+                // Loading the available printers if needed.
                 loadInternal();
 
                 // We are done.
@@ -450,6 +529,8 @@
                     XmlPullParser parser = Xml.newPullParser();
                     parser.setInput(in, null);
                     parseState(parser, printers);
+                    // Take a note which version of the history was read.
+                    mLastReadHistoryTimestamp = mStatePersistFile.getBaseFile().lastModified();
                     return printers;
                 } catch (IllegalStateException ise) {
                     Slog.w(LOG_TAG, "Failed parsing ", ise);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 8f26361..88403a3 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -357,6 +357,9 @@
         }
 
         public void cancel() {
+            if (isWorking()) {
+                mRemotePrintAdapter.cancel();
+            }
             mControllerState = CONTROLLER_STATE_CANCELLED;
         }
 
@@ -934,6 +937,7 @@
                             mPrintJobId, mCurrentPrinter);
 
                     if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE) {
+                        mCapabilitiesTimeout.post();
                         updateUi();
                         return;
                     }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 609ae64..778fb4d 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -544,7 +544,7 @@
         final int printJobCount = mPrintJobs.size();
         for (int i = 0; i < printJobCount; i++) {
             PrintJobInfo printJob = mPrintJobs.get(i);
-            if (isActiveState(printJob.getState())
+            if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
                     && printJob.getPrinterId().getServiceName().equals(service)) {
                 return true;
             }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
index fd14af9..d9ccb5d 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
@@ -137,4 +137,15 @@
             Log.e(LOG_TAG, "Error calling finish()", re);
         }
     }
+
+    public void cancel() {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "cancel()");
+        }
+        try {
+            mRemoteInterface.cancel();
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error calling cancel()", re);
+        }
+    }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
index 204c152..fe5920c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
@@ -46,6 +46,8 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -54,6 +56,7 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.ArrayAdapter;
 import android.widget.BaseAdapter;
 import android.widget.Filter;
@@ -81,6 +84,8 @@
     private static final String FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS =
             "FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS";
 
+    private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
+
     private final ArrayList<PrintServiceInfo> mAddPrinterServices =
             new ArrayList<PrintServiceInfo>();
 
@@ -127,6 +132,9 @@
         mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                if (!((DestinationAdapter) mListView.getAdapter()).isActionable(position)) {
+                    return;
+                }
                 PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
                 Activity activity = getActivity();
                 if (activity instanceof OnPrinterSelectedListener) {
@@ -138,6 +146,8 @@
             }
         });
 
+        registerForContextMenu(mListView);
+
         return content;
     }
 
@@ -185,6 +195,62 @@
     }
 
     @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        if (view == mListView) {
+            final int position = ((AdapterContextMenuInfo) menuInfo).position;
+            PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
+
+            menu.setHeaderTitle(printer.getName());
+
+            // Add the select menu item if applicable.
+            if (printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
+                MenuItem selectItem = menu.add(Menu.NONE, R.string.print_select_printer,
+                        Menu.NONE, R.string.print_select_printer);
+                Intent intent = new Intent();
+                intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
+                selectItem.setIntent(intent);
+            }
+
+            // Add the forget menu item if applicable.
+            FusedPrintersProvider provider = (FusedPrintersProvider) (Loader<?>)
+                    getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
+            if (provider.isFavoritePrinter(printer.getId())) {
+                MenuItem forgetItem = menu.add(Menu.NONE, R.string.print_forget_printer,
+                        Menu.NONE, R.string.print_forget_printer);
+                Intent intent = new Intent();
+                intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
+                forgetItem.setIntent(intent);
+            }
+        }
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.string.print_select_printer: {
+                PrinterId printerId = (PrinterId) item.getIntent().getParcelableExtra(
+                        EXTRA_PRINTER_ID);
+                Activity activity = getActivity();
+                if (activity instanceof OnPrinterSelectedListener) {
+                    ((OnPrinterSelectedListener) activity).onPrinterSelected(printerId);
+                } else {
+                    throw new IllegalStateException("the host activity must implement"
+                            + " OnPrinterSelectedListener");
+                }
+            } return true;
+
+            case R.string.print_forget_printer: {
+                PrinterId printerId = (PrinterId) item.getIntent().getParcelableExtra(
+                        EXTRA_PRINTER_ID);
+                FusedPrintersProvider provider = (FusedPrintersProvider) (Loader<?>)
+                        getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
+                provider.forgetFavoritePrinter(printerId);
+            } return true;
+        }
+        return false;
+    }
+
+    @Override
     public void onResume() {
         updateAddPrintersAdapter();
         getActivity().invalidateOptionsMenu();
@@ -464,7 +530,7 @@
                         R.layout.printer_list_item, parent, false);
             }
 
-            convertView.setEnabled(isEnabled(position));
+            convertView.setEnabled(isActionable(position));
 
             CharSequence title = null;
             CharSequence subtitle = null;
@@ -506,8 +572,7 @@
             return convertView;
         }
 
-        @Override
-        public boolean isEnabled(int position) {
+        public boolean isActionable(int position) {
             PrinterInfo printer =  (PrinterInfo) getItem(position);
             return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
         }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ffb4c20..29e8d1d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -5,7 +5,6 @@
         >
 
     <!-- Standard permissions granted to the shell. -->
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
@@ -64,6 +63,7 @@
     <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/packages/SystemUI/res/drawable-hdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-hdpi/bg_protect.9.png
new file mode 100644
index 0000000..5bbfa4f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
index 2d8d074..693abf5 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png
index 7220968..e3b3eeb 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png
index 8f4cb64..c6f03c4 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/search_light.png b/packages/SystemUI/res/drawable-hdpi/search_light.png
index 116b1f0..3c0dc4e 100644
--- a/packages/SystemUI/res/drawable-hdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-hdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-hdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-hdpi/bg_protect.9.png
new file mode 100644
index 0000000..1a58144
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-hdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-mdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-mdpi/bg_protect.9.png
new file mode 100644
index 0000000..a12519e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-mdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-xhdpi/bg_protect.9.png
new file mode 100644
index 0000000..ce41454
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-xxhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-land-xxhdpi/bg_protect.9.png
new file mode 100644
index 0000000..b0b4561
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-xxhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-mdpi/bg_protect.9.png
new file mode 100644
index 0000000..2856e09
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
index 399db00..15340d3 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png
index 8c2dc68..cc81794 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png
index 2142147..1c2d7aa 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_light.png b/packages/SystemUI/res/drawable-mdpi/search_light.png
index 7a70984..8010ce7 100644
--- a/packages/SystemUI/res/drawable-mdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-mdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-xhdpi/bg_protect.9.png
new file mode 100644
index 0000000..72269f2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
index c0032e2..e3cc9b0 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png
index bffbf55..65d15b5 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png
index b0ea8e0..fbd4d6b 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_light.png b/packages/SystemUI/res/drawable-xhdpi/search_light.png
index e2aed09..6d46fdd 100644
--- a/packages/SystemUI/res/drawable-xhdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-xhdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/bg_protect.9.png b/packages/SystemUI/res/drawable-xxhdpi/bg_protect.9.png
new file mode 100644
index 0000000..efc9b04
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/bg_protect.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
index a3cc08d..e15981a 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png
index ab841d2..1a5d26a 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png
index aac3428..86df881 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_camera.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_light.png b/packages/SystemUI/res/drawable-xxhdpi/search_light.png
index e5ef85d..7742207 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/search_light.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/search_light.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index b2ba25a..0c0be29 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -24,6 +24,7 @@
     android:id="@+id/recents_root"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
+    android:foreground="@drawable/bg_protect"
     systemui:recentItemLayout="@layout/status_bar_recent_item"
     >
     <FrameLayout
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index d7312df..eb66908 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -95,12 +95,12 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     />
-                <!-- battery must be padded below by 2px to match assets -->
+                <!-- battery must be padded below to match assets -->
                 <com.android.systemui.BatteryMeterView
                     android:id="@+id/battery"
                     android:layout_height="16dp"
                     android:layout_width="10.5dp"
-                    android:layout_marginBottom="2px"
+                    android:layout_marginBottom="0.33dp"
                     android:layout_marginStart="4dip"
                     />
             </LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_recent_panel.xml b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
index e41475b..2f3968d 100644
--- a/packages/SystemUI/res/layout/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
@@ -24,6 +24,7 @@
     android:id="@+id/recents_root"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
+    android:foreground="@drawable/bg_protect"
     systemui:recentItemLayout="@layout/status_bar_recent_item"
     >
     <FrameLayout
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index b6e03e1..13aafb2 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -66,7 +66,7 @@
     private final RectF mFrame = new RectF();
     private final RectF mButtonFrame = new RectF();
     private final RectF mClipFrame = new RectF();
-    private final Rect mBoltFrame = new Rect();
+    private final RectF mBoltFrame = new RectF();
 
     private class BatteryTracker extends BroadcastReceiver {
         public static final int UNKNOWN_LEVEL = -1;
@@ -319,10 +319,10 @@
 
         if (tracker.plugged) {
             // draw the bolt
-            final int bl = (int)(mFrame.left + mFrame.width() / 4.5f);
-            final int bt = (int)(mFrame.top + mFrame.height() / 6f);
-            final int br = (int)(mFrame.right - mFrame.width() / 7f);
-            final int bb = (int)(mFrame.bottom - mFrame.height() / 10f);
+            final float bl = mFrame.left + mFrame.width() / 4.5f;
+            final float bt = mFrame.top + mFrame.height() / 6f;
+            final float br = mFrame.right - mFrame.width() / 7f;
+            final float bb = mFrame.bottom - mFrame.height() / 10f;
             if (mBoltFrame.left != bl || mBoltFrame.top != bt
                     || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
                 mBoltFrame.set(bl, bt, br, bb);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 6a2bc5f..ed00398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
-import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
@@ -70,6 +69,7 @@
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SearchPanelView;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
 import com.android.systemui.statusbar.policy.NotificationRowLayout;
 
 import java.util.ArrayList;
@@ -128,7 +128,6 @@
     protected boolean mUseHeadsUp = false;
 
     protected IDreamManager mDreamManager;
-    KeyguardManager mKeyguardManager;
     PowerManager mPowerManager;
     protected int mRowHeight;
 
@@ -221,7 +220,6 @@
 
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
-        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
 
         mProvisioningObserver.onChange(false); // set up
@@ -749,9 +747,7 @@
                     Log.w(TAG, "Sending contentIntent failed: " + e);
                 }
 
-                KeyguardManager kgm =
-                    (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-                if (kgm != null) kgm.exitKeyguardSecurely(null);
+                KeyguardTouchDelegate.getInstance(mContext).dismiss();
             }
 
             try {
@@ -1056,10 +1052,12 @@
         boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
                 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
 
+        final KeyguardTouchDelegate keyguard = KeyguardTouchDelegate.getInstance(mContext);
         boolean interrupt = (isFullscreen || (isHighPriority && isNoisy))
                 && isAllowed
                 && mPowerManager.isScreenOn()
-                && !mKeyguardManager.isKeyguardLocked();
+                && !keyguard.isShowingAndNotHidden()
+                && !keyguard.isInputRestricted();
         try {
             interrupt = interrupt && !mDreamManager.isDreaming();
         } catch (RemoteException e) {
@@ -1087,8 +1085,7 @@
     }
 
     public boolean inKeyguardRestrictedInputMode() {
-        KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        return km.inKeyguardRestrictedInputMode();
+        return KeyguardTouchDelegate.getInstance(mContext).isInputRestricted();
     }
 
     public void setInteracting(int barWindow, boolean interacting) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e8173b7..39333d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -55,8 +55,7 @@
     private static final int MSG_TOGGLE_RECENT_APPS         = 13 << MSG_SHIFT;
     private static final int MSG_PRELOAD_RECENT_APPS        = 14 << MSG_SHIFT;
     private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 15 << MSG_SHIFT;
-    private static final int MSG_SET_NAVIGATION_ICON_HINTS  = 16 << MSG_SHIFT;
-    private static final int MSG_SET_WINDOW_STATE           = 17 << MSG_SHIFT;
+    private static final int MSG_SET_WINDOW_STATE           = 16 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -98,7 +97,6 @@
         public void showSearchPanel();
         public void hideSearchPanel();
         public void cancelPreloadRecentApps();
-        public void setNavigationIconHints(int hints);
         public void setWindowState(int window, int state);
     }
 
@@ -227,13 +225,6 @@
         }
     }
 
-    public void setNavigationIconHints(int hints) {
-        synchronized (mList) {
-            mHandler.removeMessages(MSG_SET_NAVIGATION_ICON_HINTS);
-            mHandler.obtainMessage(MSG_SET_NAVIGATION_ICON_HINTS, hints, 0, null).sendToTarget();
-        }
-    }
-
     public void setWindowState(int window, int state) {
         synchronized (mList) {
             // don't coalesce these
@@ -318,9 +309,6 @@
                 case MSG_CANCEL_PRELOAD_RECENT_APPS:
                     mCallbacks.cancelPreloadRecentApps();
                     break;
-                case MSG_SET_NAVIGATION_ICON_HINTS:
-                    mCallbacks.setNavigationIconHints(msg.arg1);
-                    break;
                 case MSG_SET_WINDOW_STATE:
                     mCallbacks.setWindowState(msg.arg1, msg.arg2);
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 8ad538b..cb17ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -99,6 +99,10 @@
         mBarBackground.finishAnimation();
     }
 
+    public void setContentVisible(boolean visible) {
+        // for subclasses
+    }
+
     private static class BarBackgroundDrawable extends Drawable {
         private final int mOpaque;
         private final int mSemiTransparent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
index 5c55f0d..c1646ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
@@ -77,10 +77,11 @@
     }
 
     public static KeyguardTouchDelegate getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new KeyguardTouchDelegate(context);
+        KeyguardTouchDelegate instance = sInstance;
+        if (instance == null) {
+            instance = sInstance = new KeyguardTouchDelegate(context);
         }
-        return sInstance;
+        return instance;
     }
 
     public boolean isSecure() {
@@ -165,7 +166,21 @@
                 Slog.e(TAG, "RemoteException launching camera!", e);
             }
         } else {
-            Slog.w(TAG, "dispatch(event): NO SERVICE!");
+            Slog.w(TAG, "launchCamera(): NO SERVICE!");
+        }
+    }
+
+    public void dismiss() {
+        final IKeyguardService service = mService;
+        if (service != null) {
+            try {
+                service.dismiss();
+            } catch (RemoteException e) {
+                // What to do?
+                Slog.e(TAG, "RemoteException dismissing keyguard!", e);
+            }
+        } else {
+            Slog.w(TAG, "dismiss(): NO SERVICE!");
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 5d4b995..a74230b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -30,6 +30,9 @@
 
 public final class NavigationBarTransitions extends BarTransitions {
 
+    private static final float KEYGUARD_QUIESCENT_ALPHA = 0.5f;
+    private static final int CONTENT_FADE_DURATION = 200;
+
     private final NavigationBarView mView;
     private final IStatusBarService mBarService;
 
@@ -73,18 +76,57 @@
 
     private void applyMode(int mode, boolean animate, boolean force) {
         // apply to key buttons
-        final boolean isOpaque = mode == MODE_OPAQUE || mode == MODE_LIGHTS_OUT;
-        final float alpha = isOpaque ? KeyButtonView.DEFAULT_QUIESCENT_ALPHA : 1f;
-        setKeyButtonViewQuiescentAlpha(mView.getBackButton(), alpha, animate);
+        final float alpha = alphaForMode(mode);
         setKeyButtonViewQuiescentAlpha(mView.getHomeButton(), alpha, animate);
         setKeyButtonViewQuiescentAlpha(mView.getRecentsButton(), alpha, animate);
         setKeyButtonViewQuiescentAlpha(mView.getMenuButton(), alpha, animate);
-        setKeyButtonViewQuiescentAlpha(mView.getCameraButton(), alpha, animate);
+
+        setKeyButtonViewQuiescentAlpha(mView.getSearchLight(), KEYGUARD_QUIESCENT_ALPHA, animate);
+        setKeyButtonViewQuiescentAlpha(mView.getCameraButton(), KEYGUARD_QUIESCENT_ALPHA, animate);
+
+        applyBackButtonQuiescentAlpha(mode, animate);
 
         // apply to lights out
         applyLightsOut(mode == MODE_LIGHTS_OUT, animate, force);
     }
 
+    private float alphaForMode(int mode) {
+        final boolean isOpaque = mode == MODE_OPAQUE || mode == MODE_LIGHTS_OUT;
+        return isOpaque ? KeyButtonView.DEFAULT_QUIESCENT_ALPHA : 1f;
+    }
+
+    public void applyBackButtonQuiescentAlpha(int mode, boolean animate) {
+        float backAlpha = 0;
+        backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getSearchLight());
+        backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getCameraButton());
+        backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getHomeButton());
+        backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getRecentsButton());
+        backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getMenuButton());
+        if (backAlpha > 0) {
+            setKeyButtonViewQuiescentAlpha(mView.getBackButton(), backAlpha, animate);
+        }
+    }
+
+    private static float maxVisibleQuiescentAlpha(float max, View v) {
+        if ((v instanceof KeyButtonView) && v.isShown()) {
+            return Math.max(max, ((KeyButtonView)v).getQuiescentAlpha());
+        }
+        return max;
+    }
+
+    @Override
+    public void setContentVisible(boolean visible) {
+        final float alpha = visible ? 1 : 0;
+        fadeContent(mView.getCameraButton(), alpha);
+        fadeContent(mView.getSearchLight(), alpha);
+    }
+
+    private void fadeContent(View v, float alpha) {
+        if (v != null) {
+            v.animate().alpha(alpha).setDuration(CONTENT_FADE_DURATION);
+        }
+    }
+
     private void setKeyButtonViewQuiescentAlpha(View button, float alpha, boolean animate) {
         if (button instanceof KeyButtonView) {
             ((KeyButtonView) button).setQuiescentAlpha(alpha, animate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d1c4109..839016d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,6 +17,10 @@
 package com.android.systemui.statusbar.phone;
 
 import android.animation.LayoutTransition;
+import android.animation.LayoutTransition.TransitionListener;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.app.ActivityManagerNative;
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
@@ -48,12 +52,12 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.DelegateViewHelper;
 import com.android.systemui.statusbar.policy.DeadZone;
+import com.android.systemui.statusbar.policy.KeyButtonView;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 public class NavigationBarView extends LinearLayout {
-    private static final int CAMERA_BUTTON_FADE_DURATION = 200;
     final static boolean DEBUG = false;
     final static String TAG = "PhoneStatusBar/NavigationBarView";
 
@@ -89,6 +93,54 @@
     // used to disable the camera icon in navbar when disabled by DPM
     private boolean mCameraDisabledByDpm;
 
+    // performs manual animation in sync with layout transitions
+    private final NavTransitionListener mTransitionListener = new NavTransitionListener();
+
+    private class NavTransitionListener implements TransitionListener {
+        private boolean mBackTransitioning;
+        private boolean mHomeAppearing;
+        private long mStartDelay;
+        private long mDuration;
+        private TimeInterpolator mInterpolator;
+
+        @Override
+        public void startTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType) {
+            if (view.getId() == R.id.back) {
+                mBackTransitioning = true;
+            } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
+                mHomeAppearing = true;
+                mStartDelay = transition.getStartDelay(transitionType);
+                mDuration = transition.getDuration(transitionType);
+                mInterpolator = transition.getInterpolator(transitionType);
+            }
+        }
+
+        @Override
+        public void endTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType) {
+            if (view.getId() == R.id.back) {
+                mBackTransitioning = false;
+            } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
+                mHomeAppearing = false;
+            }
+        }
+
+        public void onBackAltCleared() {
+            // When dismissing ime during unlock, force the back button to run the same appearance
+            // animation as home (if we catch this condition early enough).
+            if (!mBackTransitioning && getBackButton().getVisibility() == VISIBLE
+                    && mHomeAppearing && getHomeButton().getAlpha() == 0) {
+                getBackButton().setAlpha(0);
+                ValueAnimator a = ObjectAnimator.ofFloat(getBackButton(), "alpha", 0, 1);
+                a.setStartDelay(mStartDelay);
+                a.setDuration(mDuration);
+                a.setInterpolator(mInterpolator);
+                a.start();
+            }
+        }
+    }
+
     // simplified click handler to be used when device is in accessibility mode
     private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
         @Override
@@ -108,12 +160,12 @@
                 case MotionEvent.ACTION_DOWN:
                     // disable search gesture while interacting with camera
                     mDelegateHelper.setDisabled(true);
-                    transitionCameraAndSearchButtonAlpha(0.0f);
+                    mBarTransitions.setContentVisible(false);
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
                     mDelegateHelper.setDisabled(false);
-                    transitionCameraAndSearchButtonAlpha(1.0f);
+                    mBarTransitions.setContentVisible(true);
                     break;
             }
             return KeyguardTouchDelegate.getInstance(getContext()).dispatch(event);
@@ -163,17 +215,6 @@
         watchForDevicePolicyChanges();
     }
 
-    protected void transitionCameraAndSearchButtonAlpha(float alpha) {
-        View cameraButtonView = getCameraButton();
-        if (cameraButtonView != null) {
-            cameraButtonView.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
-        }
-        View searchLight = getSearchLight();
-        if (searchLight != null) {
-            searchLight.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
-        }
-    }
-
     private void watchForDevicePolicyChanges() {
         final IntentFilter filter = new IntentFilter();
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
@@ -277,7 +318,10 @@
 
     public void setNavigationIconHints(int hints, boolean force) {
         if (!force && hints == mNavigationIconHints) return;
-
+        final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+        if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) {
+            mTransitionListener.onBackAltCleared();
+        }
         if (DEBUG) {
             android.widget.Toast.makeText(mContext,
                 "Navigation icon hints = " + hints,
@@ -286,15 +330,7 @@
 
         mNavigationIconHints = hints;
 
-        getBackButton().setAlpha(
-            (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_NOP)) ? 0.5f : 1.0f);
-        getHomeButton().setAlpha(
-            (0 != (hints & StatusBarManager.NAVIGATION_HINT_HOME_NOP)) ? 0.5f : 1.0f);
-        getRecentsButton().setAlpha(
-            (0 != (hints & StatusBarManager.NAVIGATION_HINT_RECENT_NOP)) ? 0.5f : 1.0f);
-
-        ((ImageView)getBackButton()).setImageDrawable(
-            (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT))
+        ((ImageView)getBackButton()).setImageDrawable(backAlt
                 ? (mVertical ? mBackAltLandIcon : mBackAltIcon)
                 : (mVertical ? mBackLandIcon : mBackIcon));
 
@@ -322,13 +358,20 @@
             setSlippery(disableHome && disableRecent && disableBack && disableSearch);
         }
 
-        if (!mScreenOn && mCurrentView != null) {
-            ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
-            LayoutTransition lt = navButtons == null ? null : navButtons.getLayoutTransition();
+        ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+        if (navButtons != null) {
+            LayoutTransition lt = navButtons.getLayoutTransition();
             if (lt != null) {
-                lt.disableTransitionType(
-                        LayoutTransition.CHANGE_APPEARING | LayoutTransition.CHANGE_DISAPPEARING |
-                        LayoutTransition.APPEARING | LayoutTransition.DISAPPEARING);
+                if (!lt.getTransitionListeners().contains(mTransitionListener)) {
+                    lt.addTransitionListener(mTransitionListener);
+                }
+                if (!mScreenOn && mCurrentView != null) {
+                    lt.disableTransitionType(
+                            LayoutTransition.CHANGE_APPEARING |
+                            LayoutTransition.CHANGE_DISAPPEARING |
+                            LayoutTransition.APPEARING |
+                            LayoutTransition.DISAPPEARING);
+                }
             }
         }
 
@@ -336,12 +379,17 @@
         getHomeButton()   .setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
         getRecentsButton().setVisibility(disableRecent     ? View.INVISIBLE : View.VISIBLE);
 
-        final boolean shouldShowSearch = disableHome && !disableSearch;
-        getSearchLight().setVisibility(shouldShowSearch ? View.VISIBLE : View.GONE);
-        final View cameraButton = getCameraButton();
-        if (cameraButton != null) {
-            cameraButton.setVisibility(
-                    shouldShowSearch && !mCameraDisabledByDpm ? View.VISIBLE : View.GONE);
+        final boolean showSearch = disableHome && !disableSearch;
+        final boolean showCamera = showSearch && !mCameraDisabledByDpm;
+        setVisibleOrGone(getSearchLight(), showSearch);
+        setVisibleOrGone(getCameraButton(), showCamera);
+
+        mBarTransitions.applyBackButtonQuiescentAlpha(mBarTransitions.getMode(), true /*animate*/);
+    }
+
+    private void setVisibleOrGone(View view, boolean visible) {
+        if (view != null) {
+            view.setVisibility(visible ? VISIBLE : GONE);
         }
     }
 
@@ -574,28 +622,31 @@
                         mVertical ? "true" : "false",
                         mShowMenu ? "true" : "false"));
 
-        final View back = getBackButton();
-        final View home = getHomeButton();
-        final View recent = getRecentsButton();
-        final View menu = getMenuButton();
+        dumpButton(pw, "back", getBackButton());
+        dumpButton(pw, "home", getHomeButton());
+        dumpButton(pw, "rcnt", getRecentsButton());
+        dumpButton(pw, "menu", getMenuButton());
+        dumpButton(pw, "srch", getSearchLight());
+        dumpButton(pw, "cmra", getCameraButton());
 
-        pw.println("      back: "
-                + PhoneStatusBar.viewInfo(back)
-                + " " + visibilityToString(back.getVisibility())
-                );
-        pw.println("      home: "
-                + PhoneStatusBar.viewInfo(home)
-                + " " + visibilityToString(home.getVisibility())
-                );
-        pw.println("      rcnt: "
-                + PhoneStatusBar.viewInfo(recent)
-                + " " + visibilityToString(recent.getVisibility())
-                );
-        pw.println("      menu: "
-                + PhoneStatusBar.viewInfo(menu)
-                + " " + visibilityToString(menu.getVisibility())
-                );
         pw.println("    }");
     }
 
+    private static void dumpButton(PrintWriter pw, String caption, View button) {
+        pw.print("      " + caption + ": ");
+        if (button == null) {
+            pw.print("null");
+        } else {
+            pw.print(PhoneStatusBar.viewInfo(button)
+                    + " " + visibilityToString(button.getVisibility())
+                    + " alpha=" + button.getAlpha()
+                    );
+            if (button instanceof KeyButtonView) {
+                pw.print(" drawingAlpha=" + ((KeyButtonView)button).getDrawingAlpha());
+                pw.print(" quiescentAlpha=" + ((KeyButtonView)button).getQuiescentAlpha());
+            }
+        }
+        pw.println();
+    }
+
 }
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 39a9ba7..607ce41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -53,6 +53,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -632,6 +633,10 @@
             }
         }
 
+        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mBroadcastReceiver.onReceive(mContext,
+                new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
+
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -649,14 +654,14 @@
     @Override
     protected void onShowSearchPanel() {
         if (mNavigationBarView != null) {
-            mNavigationBarView.transitionCameraAndSearchButtonAlpha(0.0f);
+            mNavigationBarView.getBarTransitions().setContentVisible(false);
         }
     }
 
     @Override
     protected void onHideSearchPanel() {
         if (mNavigationBarView != null) {
-            mNavigationBarView.transitionCameraAndSearchButtonAlpha(1.0f);
+            mNavigationBarView.getBarTransitions().setContentVisible(true);
         }
     }
 
@@ -802,7 +807,7 @@
     }
 
     private void repositionNavigationBar() {
-        if (mNavigationBarView == null) return;
+        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
 
         prepareNavigationBarView();
 
@@ -1807,8 +1812,7 @@
         return mGestureRec;
     }
 
-    @Override // CommandQueue
-    public void setNavigationIconHints(int hints) {
+    private void setNavigationIconHints(int hints) {
         if (hints == mNavigationIconHints) return;
 
         mNavigationIconHints = hints;
@@ -2045,7 +2049,7 @@
         boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
             || ((vis & InputMethodService.IME_VISIBLE) != 0);
 
-        mCommandQueue.setNavigationIconHints(
+        setNavigationIconHints(
                 altBack ? (mNavigationIconHints | NAVIGATION_HINT_BACK_ALT)
                         : (mNavigationIconHints & ~NAVIGATION_HINT_BACK_ALT));
         if (mQS != null) mQS.setImeWindowStatus(vis > 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 37504fb..2eef2b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -314,11 +314,19 @@
                 collapsePanels();
                 final UserManager um = UserManager.get(mContext);
                 if (um.getUsers(true).size() > 1) {
-                    try {
-                        WindowManagerGlobal.getWindowManagerService().lockNow(null);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Couldn't show user switcher", e);
-                    }
+                    // Since keyguard and systemui were merged into the same process to save
+                    // memory, they share the same Looper and graphics context.  As a result,
+                    // there's no way to allow concurrent animation while keyguard inflates.
+                    // The workaround is to add a slight delay to allow the animation to finish.
+                    mHandler.postDelayed(new Runnable() {
+                        public void run() {
+                            try {
+                                WindowManagerGlobal.getWindowManagerService().lockNow(null);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Couldn't show user switcher", e);
+                            }
+                        }
+                    }, 400); // TODO: ideally this would be tied to the collapse of the panel
                 } else {
                     Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
                             mContext, v, ContactsContract.Profile.CONTENT_URI,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index e77b420..4901823 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -113,7 +113,7 @@
             handled = super.onTouchEvent(ev);
         }
         final int action = ev.getAction();
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+        if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) {
             mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
         }
         return handled;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 55fb95d..718acc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -36,6 +36,7 @@
 import android.view.SoundEffectConstants;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 
@@ -53,10 +54,13 @@
     int mTouchSlop;
     Drawable mGlowBG;
     int mGlowWidth, mGlowHeight;
-    float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
+    float mGlowAlpha = 0f, mGlowScale = 1f;
+    @ViewDebug.ExportedProperty(category = "drawing")
+    float mDrawingAlpha = 1f;
+    @ViewDebug.ExportedProperty(category = "drawing")
     float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
     boolean mSupportsLongpress = true;
-    RectF mRect = new RectF(0f,0f,0f,0f);
+    RectF mRect = new RectF();
     AnimatorSet mPressedAnim;
     Animator mAnimateToQuiescent = new ObjectAnimator();
 
@@ -90,8 +94,8 @@
         mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
 
         mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
+        setDrawingAlpha(mQuiescentAlpha);
         if (mGlowBG != null) {
-            setDrawingAlpha(mQuiescentAlpha);
             mGlowWidth = mGlowBG.getIntrinsicWidth();
             mGlowHeight = mGlowBG.getIntrinsicHeight();
         }
@@ -126,16 +130,14 @@
     public void setQuiescentAlpha(float alpha, boolean animate) {
         mAnimateToQuiescent.cancel();
         alpha = Math.min(Math.max(alpha, 0), 1);
-        if (alpha == mQuiescentAlpha) return;
+        if (alpha == mQuiescentAlpha && alpha == mDrawingAlpha) return;
         mQuiescentAlpha = alpha;
         if (DEBUG) Log.d(TAG, "New quiescent alpha = " + mQuiescentAlpha);
-        if (mGlowBG != null) {
-            if (animate) {
-                mAnimateToQuiescent = animateToQuiescent();
-                mAnimateToQuiescent.start();
-            } else {
-                setDrawingAlpha(mQuiescentAlpha);
-            }
+        if (mGlowBG != null && animate) {
+            mAnimateToQuiescent = animateToQuiescent();
+            mAnimateToQuiescent.start();
+        } else {
+            setDrawingAlpha(mQuiescentAlpha);
         }
     }
 
@@ -143,13 +145,15 @@
         return ObjectAnimator.ofFloat(this, "drawingAlpha", mQuiescentAlpha);
     }
 
+    public float getQuiescentAlpha() {
+        return mQuiescentAlpha;
+    }
+
     public float getDrawingAlpha() {
-        if (mGlowBG == null) return 0;
         return mDrawingAlpha;
     }
 
     public void setDrawingAlpha(float x) {
-        if (mGlowBG == null) return;
         // Calling setAlpha(int), which is an ImageView-specific
         // method that's different from setAlpha(float). This sets
         // the alpha on this ImageView's drawable directly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index a53b25a5..dd13e31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -89,10 +89,6 @@
     }
 
     @Override // CommandQueue
-    public void setNavigationIconHints(int hints) {
-    }
-
-    @Override // CommandQueue
     public void setWindowState(int window, int state) {
     }
 
diff --git a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
index e14f89a..8511de2 100644
--- a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
+++ b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
@@ -24,6 +24,9 @@
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Build;
@@ -53,7 +56,8 @@
     private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) {
         mDecoder = decoder;
     }
-    public static SimpleBitmapRegionDecoderWrapper newInstance(String pathName, boolean isShareable) {
+    public static SimpleBitmapRegionDecoderWrapper newInstance(
+            String pathName, boolean isShareable) {
         try {
             BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(pathName, isShareable);
             if (d != null) {
@@ -65,7 +69,8 @@
         }
         return null;
     }
-    public static SimpleBitmapRegionDecoderWrapper newInstance(InputStream is, boolean isShareable) {
+    public static SimpleBitmapRegionDecoderWrapper newInstance(
+            InputStream is, boolean isShareable) {
         try {
             BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable);
             if (d != null) {
@@ -89,8 +94,9 @@
 }
 
 class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder {
-    //byte[] streamCopy;
     Bitmap mBuffer;
+    Canvas mTempCanvas;
+    Paint mTempPaint;
     private DumbBitmapRegionDecoder(Bitmap b) {
         mBuffer = b;
     }
@@ -115,9 +121,23 @@
         return mBuffer.getHeight();
     }
     public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
-        System.out.println("DECODING WITH SAMPLE LEVEL OF " + options.inSampleSize);
-        return Bitmap.createBitmap(
-                mBuffer, wantRegion.left, wantRegion.top, wantRegion.width(), wantRegion.height());
+        if (mTempCanvas == null) {
+            mTempCanvas = new Canvas();
+            mTempPaint = new Paint();
+            mTempPaint.setFilterBitmap(true);
+        }
+        int sampleSize = Math.max(options.inSampleSize, 1);
+        Bitmap newBitmap = Bitmap.createBitmap(
+                wantRegion.width() / sampleSize,
+                wantRegion.height() / sampleSize,
+                Bitmap.Config.ARGB_8888);
+        mTempCanvas.setBitmap(newBitmap);
+        mTempCanvas.save();
+        mTempCanvas.scale(1f / sampleSize, 1f / sampleSize);
+        mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint);
+        mTempCanvas.restore();
+        mTempCanvas.setBitmap(null);
+        return newBitmap;
     }
 }
 
@@ -256,6 +276,7 @@
                 if (regionDecoder == null) {
                     is = regenerateInputStream();
                     regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+                    Utils.closeSilently(is);
                 }
                 return regionDecoder;
             } catch (FileNotFoundException e) {
@@ -280,8 +301,9 @@
         }
         @Override
         public boolean readExif(ExifInterface ei) {
+            InputStream is = null;
             try {
-                InputStream is = regenerateInputStream();
+                is = regenerateInputStream();
                 ei.readExif(is);
                 Utils.closeSilently(is);
                 return true;
@@ -291,6 +313,8 @@
             } catch (IOException e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return false;
+            } finally {
+                Utils.closeSilently(is);
             }
         }
     }
@@ -316,6 +340,7 @@
             if (regionDecoder == null) {
                 is = regenerateInputStream();
                 regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+                Utils.closeSilently(is);
             }
             return regionDecoder;
         }
diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
index d12140d..57c0581 100644
--- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
+++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
@@ -247,19 +247,19 @@
     private static int getRotationFromExifHelper(
             String path, Resources res, int resId, Context context, Uri uri) {
         ExifInterface ei = new ExifInterface();
+        InputStream is = null;
+        BufferedInputStream bis = null;
         try {
             if (path != null) {
                 ei.readExif(path);
             } else if (uri != null) {
-                InputStream is = context.getContentResolver().openInputStream(uri);
-                BufferedInputStream bis = new BufferedInputStream(is);
+                is = context.getContentResolver().openInputStream(uri);
+                bis = new BufferedInputStream(is);
                 ei.readExif(bis);
-                bis.close();
             } else {
-                InputStream is = res.openRawResource(resId);
-                BufferedInputStream bis = new BufferedInputStream(is);
+                is = res.openRawResource(resId);
+                bis = new BufferedInputStream(is);
                 ei.readExif(bis);
-                bis.close();
             }
             Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
             if (ori != null) {
@@ -267,6 +267,9 @@
             }
         } catch (IOException e) {
             Log.w(LOGTAG, "Getting exif data failed", e);
+        } finally {
+            Utils.closeSilently(bis);
+            Utils.closeSilently(is);
         }
         return 0;
     }
@@ -326,40 +329,15 @@
         // Get the crop
         boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
 
-        Point minDims = new Point();
-        Point maxDims = new Point();
+
         Display d = getWindowManager().getDefaultDisplay();
-        d.getCurrentSizeRange(minDims, maxDims);
 
         Point displaySize = new Point();
         d.getSize(displaySize);
-
-        int maxDim = Math.max(maxDims.x, maxDims.y);
-        final int minDim = Math.min(minDims.x, minDims.y);
-        int defaultWallpaperWidth;
-        if (isScreenLarge(getResources())) {
-            defaultWallpaperWidth = (int) (maxDim *
-                    wallpaperTravelToScreenWidthRatio(maxDim, minDim));
-        } else {
-            defaultWallpaperWidth = Math.max((int)
-                    (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
-        }
-
         boolean isPortrait = displaySize.x < displaySize.y;
-        int portraitHeight;
-        if (isPortrait) {
-            portraitHeight = mCropView.getHeight();
-        } else {
-            // TODO: how to actually get the proper portrait height?
-            // This is not quite right:
-            portraitHeight = Math.max(maxDims.x, maxDims.y);
-        }
-        if (android.os.Build.VERSION.SDK_INT >=
-                android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            Point realSize = new Point();
-            d.getRealSize(realSize);
-            portraitHeight = Math.max(realSize.x, realSize.y);
-        }
+
+        Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
+                getWindowManager());
         // Get the crop
         RectF cropRect = mCropView.getCrop();
         int cropRotation = mCropView.getImageRotation();
@@ -378,7 +356,7 @@
         // (or all the way to the left, in RTL)
         float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
         // Cap the amount of extra width
-        float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width();
+        float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
         extraSpace = Math.min(extraSpace, maxExtraSpace);
 
         if (ltr) {
@@ -389,10 +367,10 @@
 
         // ADJUST CROP HEIGHT
         if (isPortrait) {
-            cropRect.bottom = cropRect.top + portraitHeight / cropScale;
+            cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
         } else { // LANDSCAPE
             float extraPortraitHeight =
-                    portraitHeight / cropScale - cropRect.height();
+                    defaultWallpaperSize.y / cropScale - cropRect.height();
             float expandHeight =
                     Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
                             extraPortraitHeight / 2);
@@ -606,13 +584,13 @@
                 }
 
                 // See how much we're reducing the size of the image
-                int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth,
-                        roundedTrueCrop.height() / mOutHeight);
-
+                int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
+                        roundedTrueCrop.height() / mOutHeight));
                 // Attempt to open a region decoder
                 BitmapRegionDecoder decoder = null;
+                InputStream is = null;
                 try {
-                    InputStream is = regenerateInputStream();
+                    is = regenerateInputStream();
                     if (is == null) {
                         Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
                         failure = true;
@@ -622,6 +600,9 @@
                     Utils.closeSilently(is);
                 } catch (IOException e) {
                     Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
+                } finally {
+                   Utils.closeSilently(is);
+                   is = null;
                 }
 
                 Bitmap crop = null;
@@ -637,7 +618,7 @@
 
                 if (crop == null) {
                     // BitmapRegionDecoder has failed, try to crop in-memory
-                    InputStream is = regenerateInputStream();
+                    is = regenerateInputStream();
                     Bitmap fullSize = null;
                     if (is != null) {
                         BitmapFactory.Options options = new BitmapFactory.Options();
@@ -757,7 +738,7 @@
 
     protected void updateWallpaperDimensions(int width, int height) {
         String spKey = getSharedPreferencesKey();
-        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
+        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
         SharedPreferences.Editor editor = sp.edit();
         if (width != 0 && height != 0) {
             editor.putInt(WALLPAPER_WIDTH_KEY, width);
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index 596435a..10bcdad 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -117,8 +117,8 @@
                         if (!proxy.equals(Proxy.NO_PROXY)) {
                             // Only Inets created by PacProxySelector.
                             InetSocketAddress inetSocketAddress =
-                                    (InetSocketAddress)list.get(0).address();
-                            server = new Socket(inetSocketAddress.getAddress(),
+                                    (InetSocketAddress)proxy.address();
+                            server = new Socket(inetSocketAddress.getHostName(),
                                     inetSocketAddress.getPort());
                             sendLine(server, requestLine);
                         } else {
diff --git a/policy/src/com/android/internal/policy/impl/BarController.java b/policy/src/com/android/internal/policy/impl/BarController.java
index 8d97fc8..c38ad04 100644
--- a/policy/src/com/android/internal/policy/impl/BarController.java
+++ b/policy/src/com/android/internal/policy/impl/BarController.java
@@ -129,8 +129,8 @@
         final boolean wasAnim = mWin.isAnimatingLw();
         final boolean change = show ? mWin.showLw(true) : mWin.hideLw(true);
         final int state = computeStateLw(wasVis, wasAnim, mWin, change);
-        updateStateLw(state);
-        return change;
+        final boolean stateChanged = updateStateLw(state);
+        return change || stateChanged;
     }
 
     private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
@@ -139,6 +139,8 @@
             final boolean anim = win.isAnimatingLw();
             if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
                 return StatusBarManager.WINDOW_STATE_HIDDEN;
+            } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) {
+                return StatusBarManager.WINDOW_STATE_SHOWING;
             } else if (change) {
                 if (wasVis && vis && !wasAnim && anim) {
                     return StatusBarManager.WINDOW_STATE_HIDING;
@@ -150,7 +152,7 @@
         return mState;
     }
 
-    private void updateStateLw(final int state) {
+    private boolean updateStateLw(final int state) {
         if (state != mState) {
             mState = state;
             if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
@@ -169,7 +171,9 @@
                     }
                 }
             });
+            return true;
         }
+        return false;
     }
 
     public boolean checkHiddenLw() {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 7c61c44..594f683 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -77,6 +77,7 @@
 import android.net.wimax.WimaxManagerConstants;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Build;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -141,6 +142,7 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.URL;
+import java.net.URLConnection;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -154,6 +156,10 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+
 /**
  * @hide
  */
@@ -4066,8 +4072,28 @@
     static class CheckMp extends
             AsyncTask<CheckMp.Params, Void, Integer> {
         private static final String CHECKMP_TAG = "CheckMp";
+
+        // adb shell setprop persist.checkmp.testfailures 1 to enable testing failures
+        private static boolean mTestingFailures;
+
+        // Choosing 4 loops as half of them will use HTTPS and the other half HTTP
+        private static final int MAX_LOOPS = 4;
+
+        // Number of milli-seconds to complete all of the retires
         public static final int MAX_TIMEOUT_MS =  60000;
+
+        // The socket should retry only 5 seconds, the default is longer
         private static final int SOCKET_TIMEOUT_MS = 5000;
+
+        // Sleep time for network errors
+        private static final int NET_ERROR_SLEEP_SEC = 3;
+
+        // Sleep time for network route establishment
+        private static final int NET_ROUTE_ESTABLISHMENT_SLEEP_SEC = 3;
+
+        // Short sleep time for polling :(
+        private static final int POLLING_SLEEP_SEC = 1;
+
         private Context mContext;
         private ConnectivityService mCs;
         private TelephonyManager mTm;
@@ -4093,6 +4119,31 @@
             }
         }
 
+        // As explained to me by Brian Carlstrom and Kenny Root, Certificates can be
+        // issued by name or ip address, for Google its by name so when we construct
+        // this HostnameVerifier we'll pass the original Uri and use it to verify
+        // the host. If the host name in the original uril fails we'll test the
+        // hostname parameter just incase things change.
+        static class CheckMpHostnameVerifier implements HostnameVerifier {
+            Uri mOrgUri;
+
+            CheckMpHostnameVerifier(Uri orgUri) {
+                mOrgUri = orgUri;
+            }
+
+            @Override
+            public boolean verify(String hostname, SSLSession session) {
+                HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
+                String orgUriHost = mOrgUri.getHost();
+                boolean retVal = hv.verify(orgUriHost, session) || hv.verify(hostname, session);
+                if (DBG) {
+                    log("isMobileOk: hostnameVerify retVal=" + retVal + " hostname=" + hostname
+                        + " orgUriHost=" + orgUriHost);
+                }
+                return retVal;
+            }
+        }
+
         /**
          * The call back object passed in Params. onComplete will be called
          * on the main thread.
@@ -4103,6 +4154,13 @@
         }
 
         public CheckMp(Context context, ConnectivityService cs) {
+            if (Build.IS_DEBUGGABLE) {
+                mTestingFailures =
+                        SystemProperties.getInt("persist.checkmp.testfailures", 0) == 1;
+            } else {
+                mTestingFailures = false;
+            }
+
             mContext = context;
             mCs = cs;
 
@@ -4174,7 +4232,7 @@
                             mCs.setEnableFailFastMobileData(DctConstants.ENABLED);
                             break;
                         }
-                        sleep(1);
+                        sleep(POLLING_SLEEP_SEC);
                     }
                 }
 
@@ -4192,7 +4250,7 @@
                     }
                     if (VDBG) log("isMobileOk: hipri not started yet");
                     result = CMP_RESULT_CODE_NO_CONNECTION;
-                    sleep(1);
+                    sleep(POLLING_SLEEP_SEC);
                 }
 
                 // Continue trying to connect until time has run out
@@ -4208,7 +4266,7 @@
                                 log("isMobileOk: not connected ni=" +
                                     mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
                             }
-                            sleep(1);
+                            sleep(POLLING_SLEEP_SEC);
                             result = CMP_RESULT_CODE_NO_CONNECTION;
                             continue;
                         }
@@ -4226,7 +4284,7 @@
 
                         // Get of the addresses associated with the url host. We need to use the
                         // address otherwise HttpURLConnection object will use the name to get
-                        // the addresses and is will try every address but that will bypass the
+                        // the addresses and will try every address but that will bypass the
                         // route to host we setup and the connection could succeed as the default
                         // interface might be connected to the internet via wifi or other interface.
                         InetAddress[] addresses;
@@ -4263,14 +4321,14 @@
 
                         int addrTried = 0;
                         while (true) {
-                            // Loop through at most 3 valid addresses or until
+                            // Loop through at most MAX_LOOPS valid addresses or until
                             // we run out of time
-                            if (addrTried++ >= 3) {
-                                log("too many loops tried - giving up");
+                            if (addrTried++ >= MAX_LOOPS) {
+                                log("isMobileOk: too many loops tried - giving up");
                                 break;
                             }
                             if (SystemClock.elapsedRealtime() >= endTime) {
-                                log("spend too much time - giving up");
+                                log("isMobileOk: spend too much time - giving up");
                                 break;
                             }
 
@@ -4283,25 +4341,37 @@
                                 // Wait a short time to be sure the route is established ??
                                 log("isMobileOk:"
                                         + " wait to establish route to hostAddr=" + hostAddr);
-                                sleep(3);
+                                sleep(NET_ROUTE_ESTABLISHMENT_SLEEP_SEC);
                             } else {
                                 log("isMobileOk:"
                                         + " could not establish route to hostAddr=" + hostAddr);
+                                // Wait a short time before the next attempt
+                                sleep(NET_ERROR_SLEEP_SEC);
                                 continue;
                             }
 
-                            // Rewrite the url to have numeric address to use the specific route.
-                            // Add a pointless random query param to fool proxies into not caching.
-                            URL newUrl = new URL(orgUri.getScheme(),
-                                    hostAddr.getHostAddress(),
-                                    orgUri.getPath() + "?q=" + rand.nextInt(Integer.MAX_VALUE));
+                            // Rewrite the url to have numeric address to use the specific route
+                            // using http for half the attempts and https for the other half.
+                            // Doing https first and http second as on a redirected walled garden
+                            // such as t-mobile uses we get a SocketTimeoutException: "SSL
+                            // handshake timed out" which we declare as
+                            // CMP_RESULT_CODE_NO_TCP_CONNECTION. We could change this, but by
+                            // having http second we will be using logic used for some time.
+                            URL newUrl;
+                            String scheme = (addrTried <= (MAX_LOOPS/2)) ? "https" : "http";
+                            newUrl = new URL(scheme, hostAddr.getHostAddress(),
+                                        orgUri.getPath());
                             log("isMobileOk: newUrl=" + newUrl);
 
                             HttpURLConnection urlConn = null;
                             try {
-                                // Open the connection set the request header and get the response
-                                urlConn = (HttpURLConnection) newUrl.openConnection(
+                                // Open the connection set the request headers and get the response
+                                urlConn = (HttpURLConnection)newUrl.openConnection(
                                         java.net.Proxy.NO_PROXY);
+                                if (scheme.equals("https")) {
+                                    ((HttpsURLConnection)urlConn).setHostnameVerifier(
+                                            new CheckMpHostnameVerifier(orgUri));
+                                }
                                 urlConn.setInstanceFollowRedirects(false);
                                 urlConn.setConnectTimeout(SOCKET_TIMEOUT_MS);
                                 urlConn.setReadTimeout(SOCKET_TIMEOUT_MS);
@@ -4320,10 +4390,17 @@
                                 urlConn.disconnect();
                                 urlConn = null;
 
+                                if (mTestingFailures) {
+                                    // Pretend no connection, this tests using http and https
+                                    result = CMP_RESULT_CODE_NO_CONNECTION;
+                                    log("isMobileOk: TESTING_FAILURES, pretend no connction");
+                                    continue;
+                                }
+
                                 if (responseCode == 204) {
                                     // Return
                                     result = CMP_RESULT_CODE_CONNECTABLE;
-                                    log("isMobileOk: X expected responseCode=" + responseCode
+                                    log("isMobileOk: X got expected responseCode=" + responseCode
                                             + " result=" + result);
                                     return result;
                                 } else {
@@ -4337,12 +4414,14 @@
                                     result = CMP_RESULT_CODE_REDIRECTED;
                                 }
                             } catch (Exception e) {
-                                log("isMobileOk: HttpURLConnection Exception e=" + e);
+                                log("isMobileOk: HttpURLConnection Exception" + e);
                                 result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
                                 if (urlConn != null) {
                                     urlConn.disconnect();
                                     urlConn = null;
                                 }
+                                sleep(NET_ERROR_SLEEP_SEC);
+                                continue;
                             }
                         }
                         log("isMobileOk: X loops|timed out result=" + result);
@@ -4370,7 +4449,7 @@
                             log("isMobileOk: connected ni=" +
                                 mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
                         }
-                        sleep(1);
+                        sleep(POLLING_SLEEP_SEC);
                         continue;
                     }
                 }
@@ -4435,7 +4514,7 @@
             }
         }
 
-        private void log(String s) {
+        private static void log(String s) {
             Slog.d(ConnectivityService.TAG, "[" + CHECKMP_TAG + "] " + s);
         }
     }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 562a50f..a996dbd 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -309,6 +309,9 @@
             mShortcutInputMethodsAndSubtypes =
                 new HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>();
 
+    // Was the keyguard locked when this client became current?
+    private boolean mCurClientInKeyguard;
+
     /**
      * Set to true if our ServiceConnection is currently actively bound to
      * a service (whether or not we have gotten its IBinder back yet).
@@ -385,7 +388,6 @@
     private Locale mLastSystemLocale;
     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
     private final IPackageManager mIPackageManager;
-    private boolean mInputBoundToKeyguard;
 
     class SettingsObserver extends ContentObserver {
         String mLastEnabled = "";
@@ -874,12 +876,9 @@
         final boolean hardKeyShown = haveHardKeyboard
                 && conf.hardKeyboardHidden
                         != Configuration.HARDKEYBOARDHIDDEN_YES;
-        final boolean isScreenLocked =
-                mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
-        final boolean isScreenSecurelyLocked =
-                isScreenLocked && mKeyguardManager.isKeyguardSecure();
-        final boolean inputShown = mInputShown && (!isScreenLocked || mInputBoundToKeyguard);
-        final boolean inputActive = !isScreenSecurelyLocked && (inputShown || hardKeyShown);
+
+        final boolean isScreenLocked = isKeyguardLocked();
+        final boolean inputActive = !isScreenLocked && (mInputShown || hardKeyShown);
         // We assume the softkeyboard is shown when the input is active as long as the
         // hard keyboard is not shown.
         final boolean inputVisible = inputActive && !hardKeyShown;
@@ -1135,19 +1134,14 @@
             return mNoBinding;
         }
 
-        if (mCurClient == null) {
-            mInputBoundToKeyguard = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
-            if (DEBUG) {
-                Slog.v(TAG, "New bind. keyguard = " +  mInputBoundToKeyguard);
-            }
-        }
-
         if (mCurClient != cs) {
+            // Was the keyguard locked when switching over to the new client?
+            mCurClientInKeyguard = isKeyguardLocked();
             // If the client is changing, we need to switch over to the new
             // one.
             unbindCurrentClientLocked();
             if (DEBUG) Slog.v(TAG, "switching to client: client = "
-                    + cs.client.asBinder());
+                    + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
 
             // If the screen is on, inform the new client it is active
             if (mScreenOn) {
@@ -1499,6 +1493,10 @@
         }
     }
 
+    private boolean isKeyguardLocked() {
+        return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+    }
+
     // Caution! This method is called in this class. Handle multi-user carefully
     @SuppressWarnings("deprecation")
     @Override
@@ -1510,8 +1508,11 @@
                 Slog.w(TAG, "Ignoring setImeWindowStatus of uid " + uid + " token: " + token);
                 return;
             }
-
             synchronized (mMethodMap) {
+                // apply policy for binder calls
+                if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
+                    vis = 0;
+                }
                 mImeWindowVis = vis;
                 mBackDisposition = backDisposition;
                 if (mStatusBar != null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0e0f156..bb14259 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1104,6 +1104,19 @@
     private static native void nativeInit();
 
     public static void main(String[] args) {
+
+        /*
+         * In case the runtime switched since last boot (such as when
+         * the old runtime was removed in an OTA), set the system
+         * property so that it is in sync. We can't do this in
+         * libnativehelper's JniInvocation::Init code where we already
+         * had to fallback to a different runtime because it is
+         * running as root and we need to be the system user to set
+         * the property. http://b/11463182
+         */
+        SystemProperties.set("persist.sys.dalvik.vm.lib",
+                             VMRuntime.getRuntime().vmLibrary());
+
         if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
             // If a device's clock is before 1970 (before 0), a lot of
             // APIs crash dealing with negative numbers, notably
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 6957bac..c1a60ee 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -648,6 +648,10 @@
             if (width <= 0 || height <= 0) {
                 throw new IllegalArgumentException("width and height must be > 0");
             }
+            // Make sure it is at least as large as the display's maximum size.
+            int maxSizeDimension = getMaximumSizeDimension();
+            width = Math.max(width, maxSizeDimension);
+            height = Math.max(height, maxSizeDimension);
 
             if (width != wallpaper.width || height != wallpaper.height) {
                 wallpaper.width = width;
@@ -1146,9 +1150,7 @@
         }
 
         // We always want to have some reasonable width hint.
-        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-        Display d = wm.getDefaultDisplay();
-        int baseSize = d.getMaximumSizeDimension();
+        int baseSize = getMaximumSizeDimension();
         if (wallpaper.width < baseSize) {
             wallpaper.width = baseSize;
         }
@@ -1157,6 +1159,12 @@
         }
     }
 
+    private int getMaximumSizeDimension() {
+        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+        Display d = wm.getDefaultDisplay();
+        return d.getMaximumSizeDimension();
+    }
+
     // Called by SystemBackupAgent after files are restored to disk.
     void settingsRestored() {
         // TODO: If necessary, make it work for secondary users as well. This currently assumes
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index a99b58a..43f12eb 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -76,7 +76,7 @@
     private static final int STATE_DELEGATING = 0x00000004;
     private static final int STATE_GESTURE_DETECTING = 0x00000005;
 
-    // The minimum of the cosine between the vectors of two moving
+    // The maximum of the cosine between the vectors of two moving
     // pointers so they can be considered moving in the same direction.
     private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
 
@@ -436,13 +436,19 @@
                         final int pointerIdBits = (1 << pointerId);
                         mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits,
                                 policyFlags);
+                    } else {
+                        // Cache the event until we discern exploration from gesturing.
+                        mSendHoverEnterAndMoveDelayed.addEvent(event);
                     }
-                    // Cache the event until we discern exploration from gesturing.
-                    mSendHoverEnterAndMoveDelayed.addEvent(event);
                 }
             } break;
             case MotionEvent.ACTION_POINTER_DOWN: {
-                /* do nothing - let the code for ACTION_MOVE decide what to do */
+                // Another finger down means that if we have not started to deliver
+                // hover events, we will not have to. The code for ACTION_MOVE will
+                // decide what we will actually do next.
+                mSendHoverEnterAndMoveDelayed.cancel();
+                mSendHoverExitDelayed.cancel();
+                mPerformLongPressDelayed.cancel();
             } break;
             case MotionEvent.ACTION_MOVE: {
                 final int pointerId = receivedTracker.getPrimaryPointerId();
@@ -518,14 +524,11 @@
                                     mPerformLongPressDelayed.cancel();
                                 }
                             }
-                            // The user is either double tapping or performing a long
-                            // press, so do not send move events yet.
-                            if (mDoubleTapDetector.firstTapDetected()) {
-                                break;
+                            if (mTouchExplorationInProgress) {
+                                sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
+                                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
+                                        policyFlags);
                             }
-                            sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
-                            sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
-                                    policyFlags);
                         }
                     } break;
                     case 2: {
@@ -539,22 +542,24 @@
                             mPerformLongPressDelayed.cancel();
                         } else {
                             mPerformLongPressDelayed.cancel();
-                            // If the user is touch exploring the second pointer may be
-                            // performing a double tap to activate an item without need
-                            // for the user to lift his exploring finger.
-                            // It is *important* to use the distance traveled by the pointers
-                            // on the screen which may or may not be magnified.
-                            final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
-                                    - rawEvent.getX(pointerIndex);
-                            final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
-                                    - rawEvent.getY(pointerIndex);
-                            final double moveDelta = Math.hypot(deltaX, deltaY);
-                            if (moveDelta < mDoubleTapSlop) {
-                                break;
+                            if (mTouchExplorationInProgress) {
+                                // If the user is touch exploring the second pointer may be
+                                // performing a double tap to activate an item without need
+                                // for the user to lift his exploring finger.
+                                // It is *important* to use the distance traveled by the pointers
+                                // on the screen which may or may not be magnified.
+                                final float deltaX = receivedTracker.getReceivedPointerDownX(
+                                        pointerId) - rawEvent.getX(pointerIndex);
+                                final float deltaY = receivedTracker.getReceivedPointerDownY(
+                                        pointerId) - rawEvent.getY(pointerIndex);
+                                final double moveDelta = Math.hypot(deltaX, deltaY);
+                                if (moveDelta < mDoubleTapSlop) {
+                                    break;
+                                }
+                                // We are sending events so send exit and gesture
+                                // end since we transition to another state.
+                                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
                             }
-                            // We are sending events so send exit and gesture
-                            // end since we transition to another state.
-                            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
                         }
 
                         // We know that a new state transition is to happen and the
@@ -736,20 +741,34 @@
                         + "there is at least one pointer down!");
             }
             case MotionEvent.ACTION_UP: {
-                mAms.onTouchInteractionEnd();
+                // Offset the event if we are doing a long press as the
+                // target is not necessarily under the user's finger.
+                if (mLongPressingPointerId >= 0) {
+                    event = offsetEvent(event, - mLongPressingPointerDeltaX,
+                            - mLongPressingPointerDeltaY);
+                    // Clear the long press state.
+                    mLongPressingPointerId = -1;
+                    mLongPressingPointerDeltaX = 0;
+                    mLongPressingPointerDeltaY = 0;
+                }
+
+                // Deliver the event.
+                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+
                 // Announce the end of a the touch interaction.
+                mAms.onTouchInteractionEnd();
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
-                mLongPressingPointerId = -1;
-                mLongPressingPointerDeltaX = 0;
-                mLongPressingPointerDeltaY = 0;
+
                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_CANCEL: {
                 clear(event, policyFlags);
             } break;
+            default: {
+                // Deliver the event.
+                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+            }
         }
-        // Deliver the event.
-        sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
     }
 
     private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
@@ -959,27 +978,8 @@
         // long press it, or even worse to avoid the user long pressing
         // on the wrong item since click and long press behave differently.
         if (mLongPressingPointerId >= 0) {
-            final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
-            final int pointerCount = event.getPointerCount();
-            PointerProperties[] props = PointerProperties.createArray(pointerCount);
-            PointerCoords[] coords = PointerCoords.createArray(pointerCount);
-            for (int i = 0; i < pointerCount; i++) {
-                event.getPointerProperties(i, props[i]);
-                event.getPointerCoords(i, coords[i]);
-                if (i == remappedIndex) {
-                    coords[i].x -= mLongPressingPointerDeltaX;
-                    coords[i].y -= mLongPressingPointerDeltaY;
-                }
-            }
-            MotionEvent remapped = MotionEvent.obtain(event.getDownTime(),
-                    event.getEventTime(), event.getAction(), event.getPointerCount(),
-                    props, coords, event.getMetaState(), event.getButtonState(),
-                    1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
-                    event.getSource(), event.getFlags());
-            if (event != prototype) {
-                event.recycle();
-            }
-            event = remapped;
+            event = offsetEvent(event, - mLongPressingPointerDeltaX,
+                    - mLongPressingPointerDeltaY);
         }
 
         if (DEBUG) {
@@ -1004,6 +1004,39 @@
     }
 
     /**
+     * Offsets all pointers in the given event by adding the specified X and Y
+     * offsets.
+     *
+     * @param event The event to offset.
+     * @param offsetX The X offset.
+     * @param offsetY The Y offset.
+     * @return An event with the offset pointers or the original event if both
+     *         offsets are zero.
+     */
+    private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
+        if (offsetX == 0 && offsetY == 0) {
+            return event;
+        }
+        final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
+        final int pointerCount = event.getPointerCount();
+        PointerProperties[] props = PointerProperties.createArray(pointerCount);
+        PointerCoords[] coords = PointerCoords.createArray(pointerCount);
+        for (int i = 0; i < pointerCount; i++) {
+            event.getPointerProperties(i, props[i]);
+            event.getPointerCoords(i, coords[i]);
+            if (i == remappedIndex) {
+                coords[i].x += offsetX;
+                coords[i].y += offsetY;
+            }
+        }
+        return MotionEvent.obtain(event.getDownTime(),
+                event.getEventTime(), event.getAction(), event.getPointerCount(),
+                props, coords, event.getMetaState(), event.getButtonState(),
+                1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
+                event.getSource(), event.getFlags());
+    }
+
+    /**
      * Computes the action for an injected event based on a masked action
      * and a pointer index.
      *
@@ -1674,7 +1707,6 @@
 
         // Keep track of the last up pointer data.
         private long mLastReceivedUpPointerDownTime;
-        private int mLastReceivedUpPointerId;
         private float mLastReceivedUpPointerDownX;
         private float mLastReceivedUpPointerDownY;
 
@@ -1690,7 +1722,6 @@
             mReceivedPointersDown = 0;
             mPrimaryPointerId = 0;
             mLastReceivedUpPointerDownTime = 0;
-            mLastReceivedUpPointerId = 0;
             mLastReceivedUpPointerDownX = 0;
             mLastReceivedUpPointerDownY = 0;
         }
@@ -1823,7 +1854,6 @@
             final int pointerId = event.getPointerId(pointerIndex);
             final int pointerFlag = (1 << pointerId);
 
-            mLastReceivedUpPointerId = 0;
             mLastReceivedUpPointerDownTime = 0;
             mLastReceivedUpPointerDownX = 0;
             mLastReceivedUpPointerDownX = 0;
@@ -1848,7 +1878,6 @@
             final int pointerId = event.getPointerId(pointerIndex);
             final int pointerFlag = (1 << pointerId);
 
-            mLastReceivedUpPointerId = pointerId;
             mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
             mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
             mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index a64940c..ea0b978a 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -26,6 +26,7 @@
 
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import com.android.internal.app.ProcessStats;
 import com.android.internal.os.BatteryStatsImpl;
@@ -76,7 +77,7 @@
 
     // How long a service needs to be running until restarting its process
     // is no longer considered to be a relaunch of the service.
-    static final int SERVICE_RESTART_DURATION = 5*1000;
+    static final int SERVICE_RESTART_DURATION = 1*1000;
 
     // How long a service needs to be running until it will start back at
     // SERVICE_RESTART_DURATION after being killed.
@@ -239,7 +240,12 @@
 
     public ActiveServices(ActivityManagerService service) {
         mAm = service;
-        mMaxStartingBackground = ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
+        int maxBg = 0;
+        try {
+            maxBg = Integer.parseInt(SystemProperties.get("ro.config.max_starting_bg", "0"));
+        } catch(RuntimeException e) {
+        }
+        mMaxStartingBackground = maxBg > 0 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
     }
 
     ServiceRecord getServiceByName(ComponentName name, int callingUser) {
@@ -301,7 +307,7 @@
         ServiceRecord r = res.record;
         NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
                 callingUid, r.packageName, service, service.getFlags(), null);
-        if (unscheduleServiceRestartLocked(r)) {
+        if (unscheduleServiceRestartLocked(r, callingUid)) {
             if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
         }
         r.lastActivity = SystemClock.uptimeMillis();
@@ -564,7 +570,7 @@
                     if (r.isForeground) {
                         r.isForeground = false;
                         if (r.app != null) {
-                            mAm.updateLruProcessLocked(r.app, false, false);
+                            mAm.updateLruProcessLocked(r.app, false, null);
                             updateServiceForegroundLocked(r.app, true);
                         }
                     }
@@ -597,6 +603,42 @@
         }
     }
 
+    private boolean updateServiceClientActivitiesLocked(ProcessRecord proc,
+            ConnectionRecord modCr) {
+        if (modCr != null && modCr.binding.client != null) {
+            if (modCr.binding.client.activities.size() <= 0) {
+                // This connection is from a client without activities, so adding
+                // and removing is not interesting.
+                return false;
+            }
+        }
+
+        boolean anyClientActivities = false;
+        for (int i=proc.services.size()-1; i>=0 && !anyClientActivities; i--) {
+            ServiceRecord sr = proc.services.valueAt(i);
+            for (int conni=sr.connections.size()-1; conni>=0 && !anyClientActivities; conni--) {
+                ArrayList<ConnectionRecord> clist = sr.connections.valueAt(conni);
+                for (int cri=clist.size()-1; cri>=0; cri--) {
+                    ConnectionRecord cr = clist.get(cri);
+                    if (cr.binding.client == null || cr.binding.client == proc) {
+                        // Binding to ourself is not interesting.
+                        continue;
+                    }
+                    if (cr.binding.client.activities.size() > 0) {
+                        anyClientActivities = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if (anyClientActivities != proc.hasClientActivities) {
+            proc.hasClientActivities = anyClientActivities;
+            mAm.updateLruProcessLocked(proc, anyClientActivities, null);
+            return true;
+        }
+        return false;
+    }
+
     int bindServiceLocked(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
             IServiceConnection connection, int flags, int userId) {
@@ -659,7 +701,7 @@
         final long origId = Binder.clearCallingIdentity();
 
         try {
-            if (unscheduleServiceRestartLocked(s)) {
+            if (unscheduleServiceRestartLocked(s, callerApp.info.uid)) {
                 if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
                         + s);
             }
@@ -698,6 +740,9 @@
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                 b.client.hasAboveClient = true;
             }
+            if (s.app != null) {
+                updateServiceClientActivitiesLocked(s.app, c);
+            }
             clist = mServiceConnections.get(binder);
             if (clist == null) {
                 clist = new ArrayList<ConnectionRecord>();
@@ -714,6 +759,7 @@
 
             if (s.app != null) {
                 // This could have made the service more important.
+                mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities, b.client);
                 mAm.updateOomAdjLocked(s.app);
             }
 
@@ -1101,16 +1147,9 @@
                     r.restartCount = 1;
                     r.restartDelay = minDuration;
                 } else {
-                    if ((r.serviceInfo.applicationInfo.flags
-                            &ApplicationInfo.FLAG_PERSISTENT) != 0) {
-                        // Services in peristent processes will restart much more
-                        // quickly, since they are pretty important.  (Think SystemUI).
-                        r.restartDelay += minDuration/2;
-                    } else {
-                        r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
-                        if (r.restartDelay < minDuration) {
-                            r.restartDelay = minDuration;
-                        }
+                    r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
+                    if (r.restartDelay < minDuration) {
+                        r.restartDelay = minDuration;
                     }
                 }
             }
@@ -1137,7 +1176,7 @@
             } while (repeat);
 
         } else {
-            // Persistent processes are immediately restrted, so there is no
+            // Persistent processes are immediately restarted, so there is no
             // reason to hold of on restarting their services.
             r.totalRestartCount++;
             r.restartCount = 0;
@@ -1170,12 +1209,17 @@
         bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
     }
 
-    private final boolean unscheduleServiceRestartLocked(ServiceRecord r) {
+    private final boolean unscheduleServiceRestartLocked(ServiceRecord r, int callingUid) {
         if (r.restartDelay == 0) {
             return false;
         }
-        r.resetRestartCounter();
-        mRestartingServices.remove(r);
+        // Remove from the restarting list; if the service is currently on the
+        // restarting list, or the call is coming from another app, then this
+        // service has become of much more interest so we reset the restart interval.
+        boolean removed = mRestartingServices.remove(r);
+        if (removed || callingUid != r.appInfo.uid) {
+            r.resetRestartCounter();
+        }
         mAm.mHandler.removeCallbacks(r.restarter);
         return true;
     }
@@ -1316,7 +1360,8 @@
 
         app.services.add(r);
         bumpServiceExecutingLocked(r, execInFg, "create");
-        mAm.updateLruProcessLocked(app, true, false);
+        mAm.updateLruProcessLocked(app, false, null);
+        mAm.updateOomAdjLocked();
 
         boolean created = false;
         try {
@@ -1508,7 +1553,7 @@
         smap.mServicesByName.remove(r.name);
         smap.mServicesByIntent.remove(r.intent);
         r.totalRestartCount = 0;
-        unscheduleServiceRestartLocked(r);
+        unscheduleServiceRestartLocked(r, 0);
 
         // Also make sure it is not on the pending list.
         int N = mPendingServices.size();
@@ -1601,6 +1646,9 @@
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                 b.client.updateHasAboveClientLocked();
             }
+            if (s.app != null) {
+                updateServiceClientActivitiesLocked(s.app, c);
+            }
         }
         clist = mServiceConnections.get(binder);
         if (clist != null) {
@@ -1621,6 +1669,13 @@
                     && b.intent.hasBound) {
                 try {
                     bumpServiceExecutingLocked(s, false, "unbind");
+                    if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
+                            && s.app.setProcState <= ActivityManager.PROCESS_STATE_RECEIVER) {
+                        // If this service's process is not already in the cached list,
+                        // then update it in the LRU list here because this may be causing
+                        // it to go down there and we want it to start out near the top.
+                        mAm.updateLruProcessLocked(s.app, false, null);
+                    }
                     mAm.updateOomAdjLocked(s.app);
                     b.intent.hasBound = false;
                     // Assume the client doesn't want to know about a rebind;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d75fe5e..164e7b7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -34,7 +34,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.ProcessStats;
-import com.android.internal.app.ResolverActivity;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessCpuTracker;
@@ -218,6 +217,7 @@
     static final boolean DEBUG_IMMERSIVE = localLOGV || false;
     static final boolean DEBUG_MU = localLOGV || false;
     static final boolean DEBUG_OOM_ADJ = localLOGV || false;
+    static final boolean DEBUG_LRU = localLOGV || false;
     static final boolean DEBUG_PAUSE = localLOGV || false;
     static final boolean DEBUG_POWER = localLOGV || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
@@ -457,6 +457,23 @@
     final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
 
     /**
+     * Information about a process that is currently marked as bad.
+     */
+    static final class BadProcessInfo {
+        BadProcessInfo(long time, String shortMsg, String longMsg, String stack) {
+            this.time = time;
+            this.shortMsg = shortMsg;
+            this.longMsg = longMsg;
+            this.stack = stack;
+        }
+
+        final long time;
+        final String shortMsg;
+        final String longMsg;
+        final String stack;
+    }
+
+    /**
      * Set of applications that we consider to be bad, and will reject
      * incoming broadcasts from (which the user has no control over).
      * Processes are added to this set when they have crashed twice within
@@ -464,7 +481,7 @@
      * later restarted (hopefully due to some user action).  The value is the
      * time it was added to the list.
      */
-    final ProcessMap<Long> mBadProcesses = new ProcessMap<Long>();
+    final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<BadProcessInfo>();
 
     /**
      * All of the processes we currently have running organized by pid.
@@ -1743,7 +1760,8 @@
                 synchronized (mSelf.mPidsSelfLocked) {
                     mSelf.mPidsSelfLocked.put(app.pid, app);
                 }
-                mSelf.updateLruProcessLocked(app, true, false);
+                mSelf.updateLruProcessLocked(app, false, null);
+                mSelf.updateOomAdjLocked();
             }
         } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException(
@@ -2269,7 +2287,7 @@
 
         int lrui = mLruProcesses.lastIndexOf(app);
         if (lrui < 0) {
-            Log.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
+            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
                     + what + " " + obj + " from " + srcApp);
             return index;
         }
@@ -2289,6 +2307,8 @@
         if (index > 0) {
             index--;
         }
+        if (DEBUG_LRU) Slog.d(TAG, "Moving dep from " + lrui + " to " + index
+                + " in LRU list: " + app);
         mLruProcesses.add(index, app);
         return index;
     }
@@ -2306,8 +2326,9 @@
         }
     }
 
-    final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj, boolean activityChange) {
-        final boolean hasActivity = app.activities.size() > 0;
+    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+            ProcessRecord client) {
+        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities;
         final boolean hasService = false; // not impl yet. app.services.size() > 0;
         if (!activityChange && hasActivity) {
             // The process has activties, so we are only going to allow activity-based
@@ -2321,8 +2342,65 @@
         final long now = SystemClock.uptimeMillis();
         app.lastActivityTime = now;
 
+        // First a quick reject: if the app is already at the position we will
+        // put it, then there is nothing to do.
+        if (hasActivity) {
+            final int N = mLruProcesses.size();
+            if (N > 0 && mLruProcesses.get(N-1) == app) {
+                if (DEBUG_LRU) Slog.d(TAG, "Not moving, already top activity: " + app);
+                return;
+            }
+        } else {
+            if (mLruProcessServiceStart > 0
+                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
+                if (DEBUG_LRU) Slog.d(TAG, "Not moving, already top other: " + app);
+                return;
+            }
+        }
+
         int lrui = mLruProcesses.lastIndexOf(app);
 
+        if (app.persistent && lrui >= 0) {
+            // We don't care about the position of persistent processes, as long as
+            // they are in the list.
+            if (DEBUG_LRU) Slog.d(TAG, "Not moving, persistent: " + app);
+            return;
+        }
+
+        /* In progress: compute new position first, so we can avoid doing work
+           if the process is not actually going to move.  Not yet working.
+        int addIndex;
+        int nextIndex;
+        boolean inActivity = false, inService = false;
+        if (hasActivity) {
+            // Process has activities, put it at the very tipsy-top.
+            addIndex = mLruProcesses.size();
+            nextIndex = mLruProcessServiceStart;
+            inActivity = true;
+        } else if (hasService) {
+            // Process has services, put it at the top of the service list.
+            addIndex = mLruProcessActivityStart;
+            nextIndex = mLruProcessServiceStart;
+            inActivity = true;
+            inService = true;
+        } else  {
+            // Process not otherwise of interest, it goes to the top of the non-service area.
+            addIndex = mLruProcessServiceStart;
+            if (client != null) {
+                int clientIndex = mLruProcesses.lastIndexOf(client);
+                if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
+                        + app);
+                if (clientIndex >= 0 && addIndex > clientIndex) {
+                    addIndex = clientIndex;
+                }
+            }
+            nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
+        }
+
+        Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
+                + mLruProcessActivityStart + "): " + app);
+        */
+
         if (lrui >= 0) {
             if (lrui < mLruProcessActivityStart) {
                 mLruProcessActivityStart--;
@@ -2330,23 +2408,91 @@
             if (lrui < mLruProcessServiceStart) {
                 mLruProcessServiceStart--;
             }
+            /*
+            if (addIndex > lrui) {
+                addIndex--;
+            }
+            if (nextIndex > lrui) {
+                nextIndex--;
+            }
+            */
             mLruProcesses.remove(lrui);
         }
 
+        /*
+        mLruProcesses.add(addIndex, app);
+        if (inActivity) {
+            mLruProcessActivityStart++;
+        }
+        if (inService) {
+            mLruProcessActivityStart++;
+        }
+        */
+
         int nextIndex;
         if (hasActivity) {
-            // Process has activities, put it at the very tipsy-top.
-            mLruProcesses.add(app);
-            nextIndex = mLruProcessActivityStart;
+            final int N = mLruProcesses.size();
+            if (app.activities.size() == 0 && mLruProcessActivityStart < (N-1)) {
+                // Process doesn't have activities, but has clients with
+                // activities...  move it up, but one below the top (the top
+                // should always have a real activity).
+                if (DEBUG_LRU) Slog.d(TAG, "Adding to second-top of LRU activity list: " + app);
+                mLruProcesses.add(N-1, app);
+                // To keep it from spamming the LRU list (by making a bunch of clients),
+                // we will push down any other entries owned by the app.
+                final int uid = app.info.uid;
+                for (int i=N-2; i>mLruProcessActivityStart; i--) {
+                    ProcessRecord subProc = mLruProcesses.get(i);
+                    if (subProc.info.uid == uid) {
+                        // We want to push this one down the list.  If the process after
+                        // it is for the same uid, however, don't do so, because we don't
+                        // want them internally to be re-ordered.
+                        if (mLruProcesses.get(i-1).info.uid != uid) {
+                            if (DEBUG_LRU) Slog.d(TAG, "Pushing uid " + uid + " swapping at " + i
+                                    + ": " + mLruProcesses.get(i) + " : " + mLruProcesses.get(i-1));
+                            ProcessRecord tmp = mLruProcesses.get(i);
+                            mLruProcesses.set(i, mLruProcesses.get(i-1));
+                            mLruProcesses.set(i-1, tmp);
+                            i--;
+                        }
+                    } else {
+                        // A gap, we can stop here.
+                        break;
+                    }
+                }
+            } else {
+                // Process has activities, put it at the very tipsy-top.
+                if (DEBUG_LRU) Slog.d(TAG, "Adding to top of LRU activity list: " + app);
+                mLruProcesses.add(app);
+            }
+            nextIndex = mLruProcessServiceStart;
         } else if (hasService) {
             // Process has services, put it at the top of the service list.
+            if (DEBUG_LRU) Slog.d(TAG, "Adding to top of LRU service list: " + app);
             mLruProcesses.add(mLruProcessActivityStart, app);
             nextIndex = mLruProcessServiceStart;
             mLruProcessActivityStart++;
         } else  {
             // Process not otherwise of interest, it goes to the top of the non-service area.
-            mLruProcesses.add(mLruProcessServiceStart, app);
-            nextIndex = mLruProcessServiceStart-1;
+            int index = mLruProcessServiceStart;
+            if (client != null) {
+                // If there is a client, don't allow the process to be moved up higher
+                // in the list than that client.
+                int clientIndex = mLruProcesses.lastIndexOf(client);
+                if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG, "Unknown client " + client
+                        + " when updating " + app);
+                if (clientIndex <= lrui) {
+                    // Don't allow the client index restriction to push it down farther in the
+                    // list than it already is.
+                    clientIndex = lrui;
+                }
+                if (clientIndex >= 0 && index > clientIndex) {
+                    index = clientIndex;
+                }
+            }
+            if (DEBUG_LRU) Slog.d(TAG, "Adding at " + index + " of LRU list: " + app);
+            mLruProcesses.add(index, app);
+            nextIndex = index-1;
             mLruProcessActivityStart++;
             mLruProcessServiceStart++;
         }
@@ -2357,23 +2503,19 @@
             ConnectionRecord cr = app.connections.valueAt(j);
             if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                     && cr.binding.service.app != null
-                    && cr.binding.service.app.lruSeq != mLruSeq) {
+                    && cr.binding.service.app.lruSeq != mLruSeq
+                    && !cr.binding.service.app.persistent) {
                 nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
                         "service connection", cr, app);
             }
         }
         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) {
+            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
                 nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
                         "provider reference", cpr, app);
             }
         }
-
-        //Slog.i(TAG, "Putting proc to front: " + app.processName);
-        if (oomAdj) {
-            updateOomAdjLocked();
-        }
     }
 
     final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
@@ -4831,7 +4973,7 @@
                     isRestrictedBackupMode || !normalMode, app.persistent,
                     new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                     mCoreSettingsObserver.getCoreSettingsLocked());
-            updateLruProcessLocked(app, false, false);
+            updateLruProcessLocked(app, false, null);
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
             // todo: Yikes!  What should we do?  For now we will try to
@@ -7419,7 +7561,7 @@
                         // make sure to count it as being accessed and thus
                         // back up on the LRU list.  This is good because
                         // content providers are often expensive to start.
-                        updateLruProcessLocked(cpr.proc, false, false);
+                        updateLruProcessLocked(cpr.proc, false, null);
                     }
                 }
 
@@ -8028,7 +8170,8 @@
             if (isolated) {
                 mIsolatedProcesses.put(app.uid, app);
             }
-            updateLruProcessLocked(app, true, false);
+            updateLruProcessLocked(app, false, null);
+            updateOomAdjLocked();
         }
 
         // This package really, really can not be stopped.
@@ -9292,7 +9435,7 @@
                 ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
         startAppProblemLocked(app);
         app.stopFreezingAllLocked();
-        return handleAppCrashLocked(app);
+        return handleAppCrashLocked(app, shortMsg, longMsg, stackTrace);
     }
 
     private void makeAppNotRespondingLocked(ProcessRecord app,
@@ -9347,13 +9490,14 @@
                 app.waitDialog = null;
             }
             if (app.pid > 0 && app.pid != MY_PID) {
-                handleAppCrashLocked(app);
+                handleAppCrashLocked(app, null, null, null);
                 killUnneededProcessLocked(app, "user request after error");
             }
         }
     }
 
-    private boolean handleAppCrashLocked(ProcessRecord app) {
+    private boolean handleAppCrashLocked(ProcessRecord app, String shortMsg, String longMsg,
+            String stackTrace) {
         if (mHeadless) {
             Log.e(TAG, "handleAppCrashLocked: " + app.processName);
             return false;
@@ -9383,7 +9527,8 @@
                 if (!app.isolated) {
                     // XXX We don't have a way to mark isolated processes
                     // as bad, since they don't have a peristent identity.
-                    mBadProcesses.put(app.info.processName, app.uid, now);
+                    mBadProcesses.put(app.info.processName, app.uid,
+                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
                     mProcessCrashTimes.remove(app.info.processName, app.uid);
                 }
                 app.bad = true;
@@ -10101,7 +10246,7 @@
         if (app.persistent) {
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
         }
-        if (app.hasActivities) {
+        if (app.activities.size() > 0) {
             outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
         }
         outInfo.lastTrimLevel = app.trimMemoryLevel;
@@ -10645,11 +10790,11 @@
 
         if (mBadProcesses.getMap().size() > 0) {
             boolean printed = false;
-            final ArrayMap<String, SparseArray<Long>> pmap = mBadProcesses.getMap();
+            final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
             final int NP = pmap.size();
             for (int ip=0; ip<NP; ip++) {
                 String pname = pmap.keyAt(ip);
-                SparseArray<Long> uids = pmap.valueAt(ip);
+                SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
                 final int N = uids.size();
                 for (int i=0; i<N; i++) {
                     int puid = uids.keyAt(i);
@@ -10664,10 +10809,33 @@
                         pw.println("  Bad processes:");
                         printedAnything = true;
                     }
+                    BadProcessInfo info = uids.valueAt(i);
                     pw.print("    Bad process "); pw.print(pname);
                             pw.print(" uid "); pw.print(puid);
-                            pw.print(": crashed at time ");
-                            pw.println(uids.valueAt(i));
+                            pw.print(": crashed at time "); pw.println(info.time);
+                    if (info.shortMsg != null) {
+                        pw.print("      Short msg: "); pw.println(info.shortMsg);
+                    }
+                    if (info.longMsg != null) {
+                        pw.print("      Long msg: "); pw.println(info.longMsg);
+                    }
+                    if (info.stack != null) {
+                        pw.println("      Stack:");
+                        int lastPos = 0;
+                        for (int pos=0; pos<info.stack.length(); pos++) {
+                            if (info.stack.charAt(pos) == '\n') {
+                                pw.print("        ");
+                                pw.write(info.stack, lastPos, pos-lastPos);
+                                pw.println();
+                                lastPos = pos+1;
+                            }
+                        }
+                        if (lastPos < info.stack.length()) {
+                            pw.print("        ");
+                            pw.write(info.stack, lastPos, info.stack.length()-lastPos);
+                            pw.println();
+                        }
+                    }
                 }
             }
         }
@@ -11857,7 +12025,7 @@
                 thread = r.thread;
                 pid = r.pid;
                 oomAdj = r.getSetAdjWithServices();
-                hasActivities = r.hasActivities;
+                hasActivities = r.activities.size() > 0;
             }
             if (thread != null) {
                 if (!isCheckinRequest && dumpDetails) {
@@ -14079,7 +14247,6 @@
         app.adjTarget = null;
         app.empty = false;
         app.cached = false;
-        app.hasClientActivities = false;
 
         final int activitiesSize = app.activities.size();
 
@@ -14089,7 +14256,6 @@
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.maxAdj;
-            app.hasActivities = false;
             app.foregroundActivities = false;
             app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -14101,16 +14267,12 @@
             app.systemNoUi = true;
             if (app == TOP_APP) {
                 app.systemNoUi = false;
-                app.hasActivities = true;
             } else if (activitiesSize > 0) {
                 for (int j = 0; j < activitiesSize; j++) {
                     final ActivityRecord r = app.activities.get(j);
                     if (r.visible) {
                         app.systemNoUi = false;
                     }
-                    if (r.app == app) {
-                        app.hasActivities = true;
-                    }
                 }
             }
             if (!app.systemNoUi) {
@@ -14121,7 +14283,6 @@
 
         app.keeping = false;
         app.systemNoUi = false;
-        app.hasActivities = false;
 
         // Determine the importance of the process, starting with most
         // important to least, and assign an appropriate OOM adjustment.
@@ -14138,7 +14299,6 @@
             app.adjType = "top-activity";
             foregroundActivities = true;
             interesting = true;
-            app.hasActivities = true;
             procState = ActivityManager.PROCESS_STATE_TOP;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
@@ -14187,7 +14347,6 @@
                             + app + "?!?");
                     continue;
                 }
-                app.hasActivities = true;
                 if (r.visible) {
                     // App has a visible activity; only upgrade adjustment.
                     if (adj > ProcessList.VISIBLE_APP_ADJ) {
@@ -14436,27 +14595,6 @@
                                     clientAdj = adj;
                                 }
                             }
-                        } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                            if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) {
-                                // If this connection is keeping the service
-                                // created, then we want to try to better follow
-                                // its memory management semantics for activities.
-                                // That is, if it is sitting in the background
-                                // LRU list as a cached process (with activities),
-                                // we don't want the service it is connected to
-                                // to go into the empty LRU and quickly get killed,
-                                // because all we'll do is just end up restarting
-                                // the service.
-                                if (client.hasActivities) {
-                                    if (procState >
-                                            ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT) {
-                                        procState =
-                                                ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
-                                        app.adjType = "cch-client-act";
-                                    }
-                                    app.hasClientActivities = true;
-                                }
-                            }
                         }
                         if (adj > clientAdj) {
                             // If this process has recently shown UI, and
@@ -14674,6 +14812,12 @@
             }
         }
 
+        if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY && app.hasClientActivities) {
+            // This is a cached process, but with client activities.  Mark it so.
+            procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+            app.adjType = "cch-client-act";
+        }
+
         if (adj == ProcessList.SERVICE_ADJ) {
             if (doingAll) {
                 app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
@@ -15302,7 +15446,6 @@
         // application processes based on their current state.
         int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
         int nextCachedAdj = curCachedAdj+1;
-        int curClientCachedAdj = curCachedAdj+1;
         int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
         int nextEmptyAdj = curEmptyAdj+2;
         for (int i=N-1; i>=0; i--) {
@@ -15317,11 +15460,15 @@
                 if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
                     switch (app.curProcState) {
                         case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                             // This process is a cached process holding activities...
                             // assign it the next cached value for that type, and then
                             // step that cached level.
                             app.curRawAdj = curCachedAdj;
                             app.curAdj = app.modifyRawOomAdj(curCachedAdj);
+                            if (DEBUG_LRU && false) Slog.d(TAG, "Assigning activity LRU #" + i
+                                    + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+                                    + ")");
                             if (curCachedAdj != nextCachedAdj) {
                                 stepCached++;
                                 if (stepCached >= cachedFactor) {
@@ -15331,25 +15478,9 @@
                                     if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
                                         nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
                                     }
-                                    if (curClientCachedAdj <= curCachedAdj) {
-                                        curClientCachedAdj = curCachedAdj + 1;
-                                        if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                            curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                                        }
-                                    }
                                 }
                             }
                             break;
-                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-                            // Special case for cached client processes...  just step
-                            // down from after regular cached processes.
-                            app.curRawAdj = curClientCachedAdj;
-                            app.curAdj = app.modifyRawOomAdj(curClientCachedAdj);
-                            curClientCachedAdj++;
-                            if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                            }
-                            break;
                         default:
                             // For everything else, assign next empty cached process
                             // level and bump that up.  Note that this means that
@@ -15358,6 +15489,9 @@
                             // state is still as a service), which is what we want.
                             app.curRawAdj = curEmptyAdj;
                             app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+                            if (DEBUG_LRU && false) Slog.d(TAG, "Assigning empty LRU #" + i
+                                    + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+                                    + ")");
                             if (curEmptyAdj != nextEmptyAdj) {
                                 stepEmpty++;
                                 if (stepEmpty >= emptyFactor) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 44ff3bc..809452c 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -61,7 +61,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -566,7 +565,7 @@
 
         // Move userId's tasks to the top.
         int index = mTaskHistory.size();
-        for (int i = 0; i < index; ++i) {
+        for (int i = 0; i < index; ) {
             TaskRecord task = mTaskHistory.get(i);
             if (task.userId == userId) {
                 if (DEBUG_TASKS) Slog.d(TAG, "switchUserLocked: stack=" + getStackId() +
@@ -574,6 +573,9 @@
                 mTaskHistory.remove(i);
                 mTaskHistory.add(task);
                 --index;
+                // Use same value for i.
+            } else {
+                ++i;
             }
         }
         if (VALIDATE_TOKENS) {
@@ -997,8 +999,8 @@
                 if (r.isHomeActivity()) {
                     return true;
                 }
-                if (!r.finishing && r.visible && r.fullscreen) {
-                    // Passed activity is over a visible fullscreen activity.
+                if (!r.finishing && r.fullscreen) {
+                    // Passed activity is over a fullscreen activity.
                     return false;
                 }
             }
@@ -1398,7 +1400,7 @@
             if (next.app != null && next.app.thread != null) {
                 // No reason to do full oom adj update here; we'll let that
                 // happen whenever it needs to later.
-                mService.updateLruProcessLocked(next.app, false, true);
+                mService.updateLruProcessLocked(next.app, true, null);
             }
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return true;
@@ -1526,8 +1528,9 @@
             mResumedActivity = next;
             next.task.touchActiveTime();
             mService.addRecentTaskLocked(next.task);
-            mService.updateLruProcessLocked(next.app, true, true);
+            mService.updateLruProcessLocked(next.app, true, null);
             updateLRUListLocked(next);
+            mService.updateOomAdjLocked();
 
             // Have the window manager re-evaluate the orientation of
             // the screen based on the new activity order.
@@ -2781,7 +2784,7 @@
                 }
                 if (r.app.activities.isEmpty()) {
                     // No longer have activities, so update LRU list and oom adj.
-                    mService.updateLruProcessLocked(r.app, false, false);
+                    mService.updateLruProcessLocked(r.app, false, null);
                     mService.updateOomAdjLocked();
                 }
             }
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 7650a65..8251364 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -905,7 +905,8 @@
         if (idx < 0) {
             app.activities.add(r);
         }
-        mService.updateLruProcessLocked(app, true, true);
+        mService.updateLruProcessLocked(app, true, null);
+        mService.updateOomAdjLocked();
 
         final ActivityStack stack = r.task.stack;
         try {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 5e80135..dd3d8aa 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -220,7 +220,8 @@
         r.curApp = app;
         app.curReceiver = r;
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
-        mService.updateLruProcessLocked(app, true, false);
+        mService.updateLruProcessLocked(app, false, null);
+        mService.updateOomAdjLocked();
 
         // Tell the application to launch this receiver.
         r.intent.setComponent(r.curComponent);
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 486e916..187cd44 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -86,7 +86,6 @@
     boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
-    boolean hasActivities;      // Are there any activities running in this process?
     boolean hasClientActivities;  // Are there any client services with activities?
     boolean hasStartedServices; // Are there any started services running in this process?
     boolean foregroundServices; // Running any services that are foreground?
@@ -265,9 +264,8 @@
             pw.print(prefix); pw.print("persistent="); pw.print(persistent);
                     pw.print(" removed="); pw.println(removed);
         }
-        if (hasActivities || hasClientActivities || foregroundActivities) {
-            pw.print(prefix); pw.print("hasActivities="); pw.print(hasActivities);
-                    pw.print(" hasClientActivities="); pw.print(hasClientActivities);
+        if (hasClientActivities || foregroundActivities) {
+            pw.print(prefix); pw.print("hasClientActivities="); pw.print(hasClientActivities);
                     pw.print(" foregroundActivities="); pw.println(foregroundActivities);
         }
         if (hasStartedServices) {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 3d6b3c9..7291dd4 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -4959,6 +4959,18 @@
                         permissionMap.put(p.info.name, bp);
                     }
                     if (bp.perm == null) {
+                        if (bp.sourcePackage != null
+                                && !bp.sourcePackage.equals(p.info.packageName)) {
+                            // If this is a permission that was formerly defined by a non-system
+                            // app, but is now defined by a system app (following an upgrade),
+                            // discard the previous declaration and consider the system's to be
+                            // canonical.
+                            if (isSystemApp(p.owner)) {
+                                Slog.i(TAG, "New decl " + p.owner + " of permission  "
+                                        + p.info.name + " is system");
+                                bp.sourcePackage = null;
+                            }
+                        }
                         if (bp.sourcePackage == null
                                 || bp.sourcePackage.equals(p.info.packageName)) {
                             BasePermission tree = findPermissionTreeLP(p.info.name);
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index d3ccba6..0079b54 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1959,10 +1959,14 @@
         }
 
         boolean doNonData = true;
+        boolean hasSchemes = false;
 
         for (int ischeme=0; ischeme<tmpPa.countDataSchemes(); ischeme++) {
             boolean doScheme = true;
             String scheme = tmpPa.getDataScheme(ischeme);
+            if (scheme != null && !scheme.isEmpty()) {
+                hasSchemes = true;
+            }
             for (int issp=0; issp<tmpPa.countDataSchemeSpecificParts(); issp++) {
                 Uri.Builder builder = new Uri.Builder();
                 builder.scheme(scheme);
@@ -2016,11 +2020,25 @@
         }
 
         for (int idata=0; idata<tmpPa.countDataTypes(); idata++) {
-            Intent finalIntent = new Intent(intent);
             String mimeType = tmpPa.getDataType(idata);
-            finalIntent.setType(mimeType);
-            applyDefaultPreferredActivityLPw(service, finalIntent, flags, cn,
-                    null, null, null, null, mimeType, userId);
+            if (hasSchemes) {
+                Uri.Builder builder = new Uri.Builder();
+                for (int ischeme=0; ischeme<tmpPa.countDataSchemes(); ischeme++) {
+                    String scheme = tmpPa.getDataScheme(ischeme);
+                    if (scheme != null && !scheme.isEmpty()) {
+                        Intent finalIntent = new Intent(intent);
+                        builder.scheme(scheme);
+                        finalIntent.setDataAndType(builder.build(), mimeType);
+                        applyDefaultPreferredActivityLPw(service, finalIntent, flags, cn,
+                                scheme, null, null, null, mimeType, userId);
+                    }
+                }
+            } else {
+                Intent finalIntent = new Intent(intent);
+                finalIntent.setType(mimeType);
+                applyDefaultPreferredActivityLPw(service, finalIntent, flags, cn,
+                        null, null, null, null, mimeType, userId);
+            }
             doNonData = false;
         }
 
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 976a328..60d44c7 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -298,6 +298,10 @@
     // True if mAmbientLux holds a valid value.
     private boolean mAmbientLuxValid;
 
+    // The ambient light level threshold at which to brighten or darken the screen.
+    private float mBrighteningLuxThreshold;
+    private float mDarkeningLuxThreshold;
+
     // The most recent light sample.
     private float mLastObservedLux;
 
@@ -945,12 +949,24 @@
         mLastObservedLuxTime = time;
     }
 
+    private void setAmbientLux(float lux) {
+        mAmbientLux = lux;
+        mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
+        mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
+    }
+
     private void updateAmbientLux(long time) {
         // If the light sensor was just turned on then immediately update our initial
         // estimate of the current ambient light level.
-        if (!mAmbientLuxValid
-                || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) {
-            mAmbientLux = mRecentShortTermAverageLux;
+        if (!mAmbientLuxValid) {
+            final long timeWhenSensorWarmedUp =
+                mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
+            if (time < timeWhenSensorWarmedUp) {
+                mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED,
+                        timeWhenSensorWarmedUp);
+                return;
+            }
+            setAmbientLux(mRecentShortTermAverageLux);
             mAmbientLuxValid = true;
             mDebounceLuxDirection = 0;
             mDebounceLuxTime = time;
@@ -961,98 +977,90 @@
                         + ", mAmbientLux=" + mAmbientLux);
             }
             updateAutoBrightness(true);
-            return;
-        }
-
-        // Determine whether the ambient environment appears to be brightening.
-        float brighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
-        if (mRecentShortTermAverageLux > brighteningLuxThreshold
-                && mRecentLongTermAverageLux > brighteningLuxThreshold) {
+        } else if (mRecentShortTermAverageLux > mBrighteningLuxThreshold
+                && mRecentLongTermAverageLux > mBrighteningLuxThreshold) {
+            // The ambient environment appears to be brightening.
             if (mDebounceLuxDirection <= 0) {
                 mDebounceLuxDirection = 1;
                 mDebounceLuxTime = time;
                 if (DEBUG) {
                     Slog.d(TAG, "updateAmbientLux: Possibly brightened, waiting for "
                             + BRIGHTENING_LIGHT_DEBOUNCE + " ms: "
-                            + "brighteningLuxThreshold=" + brighteningLuxThreshold
+                            + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
                             + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
                             + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
                             + ", mAmbientLux=" + mAmbientLux);
                 }
             }
             long debounceTime = mDebounceLuxTime + BRIGHTENING_LIGHT_DEBOUNCE;
-            if (time >= debounceTime) {
-                mAmbientLux = mRecentShortTermAverageLux;
-                if (DEBUG) {
-                    Slog.d(TAG, "updateAmbientLux: Brightened: "
-                            + "brighteningLuxThreshold=" + brighteningLuxThreshold
-                            + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
-                            + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
-                            + ", mAmbientLux=" + mAmbientLux);
-                }
-                updateAutoBrightness(true);
-            } else {
+            if (time < debounceTime) {
                 mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
+                return;
             }
-            return;
-        }
-
-        // Determine whether the ambient environment appears to be darkening.
-        float darkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
-        if (mRecentShortTermAverageLux < darkeningLuxThreshold
-                && mRecentLongTermAverageLux < darkeningLuxThreshold) {
+            setAmbientLux(mRecentShortTermAverageLux);
+            if (DEBUG) {
+                Slog.d(TAG, "updateAmbientLux: Brightened: "
+                        + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
+                        + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
+                        + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
+                        + ", mAmbientLux=" + mAmbientLux);
+            }
+            updateAutoBrightness(true);
+        } else if (mRecentShortTermAverageLux < mDarkeningLuxThreshold
+                && mRecentLongTermAverageLux < mDarkeningLuxThreshold) {
+            // The ambient environment appears to be darkening.
             if (mDebounceLuxDirection >= 0) {
                 mDebounceLuxDirection = -1;
                 mDebounceLuxTime = time;
                 if (DEBUG) {
                     Slog.d(TAG, "updateAmbientLux: Possibly darkened, waiting for "
                             + DARKENING_LIGHT_DEBOUNCE + " ms: "
-                            + "darkeningLuxThreshold=" + darkeningLuxThreshold
+                            + "mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
                             + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
                             + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
                             + ", mAmbientLux=" + mAmbientLux);
                 }
             }
             long debounceTime = mDebounceLuxTime + DARKENING_LIGHT_DEBOUNCE;
-            if (time >= debounceTime) {
-                // Be conservative about reducing the brightness, only reduce it a little bit
-                // at a time to avoid having to bump it up again soon.
-                mAmbientLux = Math.max(mRecentShortTermAverageLux, mRecentLongTermAverageLux);
-                if (DEBUG) {
-                    Slog.d(TAG, "updateAmbientLux: Darkened: "
-                            + "darkeningLuxThreshold=" + darkeningLuxThreshold
-                            + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
-                            + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
-                            + ", mAmbientLux=" + mAmbientLux);
-                }
-                updateAutoBrightness(true);
-            } else {
+            if (time < debounceTime) {
                 mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
+                return;
             }
-            return;
-        }
-
-        // No change or change is within the hysteresis thresholds.
-        if (mDebounceLuxDirection != 0) {
+            // Be conservative about reducing the brightness, only reduce it a little bit
+            // at a time to avoid having to bump it up again soon.
+            setAmbientLux(Math.max(mRecentShortTermAverageLux, mRecentLongTermAverageLux));
+            if (DEBUG) {
+                Slog.d(TAG, "updateAmbientLux: Darkened: "
+                        + "mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
+                        + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
+                        + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
+                        + ", mAmbientLux=" + mAmbientLux);
+            }
+            updateAutoBrightness(true);
+        } else if (mDebounceLuxDirection != 0) {
+            // No change or change is within the hysteresis thresholds.
             mDebounceLuxDirection = 0;
             mDebounceLuxTime = time;
             if (DEBUG) {
                 Slog.d(TAG, "updateAmbientLux: Canceled debounce: "
-                        + "brighteningLuxThreshold=" + brighteningLuxThreshold
-                        + ", darkeningLuxThreshold=" + darkeningLuxThreshold
+                        + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
+                        + ", mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
                         + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
                         + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
                         + ", mAmbientLux=" + mAmbientLux);
             }
         }
 
-        // If the light level does not change, then the sensor may not report
-        // a new value.  This can cause problems for the auto-brightness algorithm
-        // because the filters might not be updated.  To work around it, we want to
-        // make sure to update the filters whenever the observed light level could
-        // possibly exceed one of the hysteresis thresholds.
-        if (mLastObservedLux > brighteningLuxThreshold
-                || mLastObservedLux < darkeningLuxThreshold) {
+        // Now that we've done all of that, we haven't yet posted a debounce
+        // message. So consider the case where current lux is beyond the
+        // threshold. It's possible that the light sensor may not report values
+        // if the light level does not change, so we need to occasionally
+        // synthesize sensor readings in order to make sure the brightness is
+        // adjusted accordingly. Note these thresholds may have changed since
+        // we entered the function because we called setAmbientLux and
+        // updateAutoBrightness along the way.
+        if (mLastObservedLux > mBrighteningLuxThreshold
+                || mLastObservedLux < mDarkeningLuxThreshold) {
             mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED,
                     time + SYNTHETIC_LIGHT_SENSOR_RATE_MILLIS);
         }
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index 8a3997a..98acc27 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -366,7 +366,7 @@
                 pw.println("PRINT MANAGER STATE (dumpsys print)");
                 final int userStateCount = mUserStates.size();
                 for (int i = 0; i < userStateCount; i++) {
-                    UserState userState = mUserStates.get(i);
+                    UserState userState = mUserStates.valueAt(i);
                     userState.dump(fd, pw, "");
                     pw.println();
                 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 63e09db..e1e9f5c 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -8269,7 +8269,8 @@
             // windows, since that means "perform layout as normal,
             // just don't display").
             if (!gone || !win.mHaveFrame || win.mLayoutNeeded
-                    || (win.mAttrs.type == TYPE_KEYGUARD && win.isConfigChanged())
+                    || win.mAttrs.type == TYPE_KEYGUARD && win.isConfigChanged()
+                    || mOpeningApps.contains(win.mAppToken)
                     || win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) {
                 if (!win.mLayoutAttached) {
                     if (initial) {
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index ab429fd..e3a1aa6 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1957,6 +1957,27 @@
     }
 
     /**
+     * Process phone number for CDMA, converting plus code using the home network number format.
+     * This is used for outgoing SMS messages.
+     *
+     * @param dialStr the original dial string
+     * @return the converted dial string
+     * @hide for internal use
+     */
+    public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
+        if (!TextUtils.isEmpty(dialStr)) {
+            if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
+                String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+                if (!TextUtils.isEmpty(defaultIso)) {
+                    int format = getFormatTypeFromCountryCode(defaultIso);
+                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
+                }
+            }
+        }
+        return dialStr;
+    }
+
+    /**
      * This function should be called from checkAndProcessPlusCode only
      * And it is used for test purpose also.
      *