Merge "Initial attempt at jank-tracking stat collection"
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index c8e0031..21a3543 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.Nullable;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -498,6 +499,7 @@
      * @return Return an IBinder through which clients can call on to the 
      *         service.
      */
+    @Nullable
     public abstract IBinder onBind(Intent intent);
 
     /**
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cd45cfb..4dadda2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -592,6 +592,86 @@
         }
     }
 
+    /**
+     * Optional detailed information that can go into a history step.  This is typically
+     * generated each time the battery level changes.
+     */
+    public final static class HistoryStepDetails {
+        // Time (in 1/100 second) spent in user space and the kernel since the last step.
+        public int userTime;
+        public int systemTime;
+
+        // Top three apps using CPU in the last step, with times in 1/100 second.
+        public int appCpuUid1;
+        public int appCpuUTime1;
+        public int appCpuSTime1;
+        public int appCpuUid2;
+        public int appCpuUTime2;
+        public int appCpuSTime2;
+        public int appCpuUid3;
+        public int appCpuUTime3;
+        public int appCpuSTime3;
+
+        // Information from /proc/stat
+        public int statUserTime;
+        public int statSystemTime;
+        public int statIOWaitTime;
+        public int statIrqTime;
+        public int statSoftIrqTime;
+        public int statIdlTime;
+
+        public HistoryStepDetails() {
+            clear();
+        }
+
+        public void clear() {
+            userTime = systemTime = 0;
+            appCpuUid1 = appCpuUid2 = appCpuUid3 = -1;
+            appCpuUTime1 = appCpuSTime1 = appCpuUTime2 = appCpuSTime2
+                    = appCpuUTime3 = appCpuSTime3 = 0;
+        }
+
+        public void writeToParcel(Parcel out) {
+            out.writeInt(userTime);
+            out.writeInt(systemTime);
+            out.writeInt(appCpuUid1);
+            out.writeInt(appCpuUTime1);
+            out.writeInt(appCpuSTime1);
+            out.writeInt(appCpuUid2);
+            out.writeInt(appCpuUTime2);
+            out.writeInt(appCpuSTime2);
+            out.writeInt(appCpuUid3);
+            out.writeInt(appCpuUTime3);
+            out.writeInt(appCpuSTime3);
+            out.writeInt(statUserTime);
+            out.writeInt(statSystemTime);
+            out.writeInt(statIOWaitTime);
+            out.writeInt(statIrqTime);
+            out.writeInt(statSoftIrqTime);
+            out.writeInt(statIdlTime);
+        }
+
+        public void readFromParcel(Parcel in) {
+            userTime = in.readInt();
+            systemTime = in.readInt();
+            appCpuUid1 = in.readInt();
+            appCpuUTime1 = in.readInt();
+            appCpuSTime1 = in.readInt();
+            appCpuUid2 = in.readInt();
+            appCpuUTime2 = in.readInt();
+            appCpuSTime2 = in.readInt();
+            appCpuUid3 = in.readInt();
+            appCpuUTime3 = in.readInt();
+            appCpuSTime3 = in.readInt();
+            statUserTime = in.readInt();
+            statSystemTime = in.readInt();
+            statIOWaitTime = in.readInt();
+            statIrqTime = in.readInt();
+            statSoftIrqTime = in.readInt();
+            statIdlTime = in.readInt();
+        }
+    }
+
     public final static class HistoryItem implements Parcelable {
         public HistoryItem next;
 
@@ -687,6 +767,9 @@
         // Kernel wakeup reason at this point.
         public HistoryTag wakeReasonTag;
 
+        // Non-null when there is more detailed information at this step.
+        public HistoryStepDetails stepDetails;
+
         public static final int EVENT_FLAG_START = 0x8000;
         public static final int EVENT_FLAG_FINISH = 0x4000;
 
@@ -3692,10 +3775,115 @@
                     }
                 }
                 pw.println();
+                if (rec.stepDetails != null) {
+                    if (!checkin) {
+                        pw.print("                 Details: cpu=");
+                        pw.print(rec.stepDetails.userTime);
+                        pw.print("u+");
+                        pw.print(rec.stepDetails.systemTime);
+                        pw.print("s");
+                        if (rec.stepDetails.appCpuUid1 >= 0) {
+                            pw.print(" (");
+                            printStepCpuUidDetails(pw, rec.stepDetails.appCpuUid1,
+                                    rec.stepDetails.appCpuUTime1, rec.stepDetails.appCpuSTime1);
+                            if (rec.stepDetails.appCpuUid2 >= 0) {
+                                pw.print(", ");
+                                printStepCpuUidDetails(pw, rec.stepDetails.appCpuUid2,
+                                        rec.stepDetails.appCpuUTime2, rec.stepDetails.appCpuSTime2);
+                            }
+                            if (rec.stepDetails.appCpuUid3 >= 0) {
+                                pw.print(", ");
+                                printStepCpuUidDetails(pw, rec.stepDetails.appCpuUid3,
+                                        rec.stepDetails.appCpuUTime3, rec.stepDetails.appCpuSTime3);
+                            }
+                            pw.print(')');
+                        }
+                        pw.println();
+                        pw.print("                          /proc/stat=");
+                        pw.print(rec.stepDetails.statUserTime);
+                        pw.print(" usr, ");
+                        pw.print(rec.stepDetails.statSystemTime);
+                        pw.print(" sys, ");
+                        pw.print(rec.stepDetails.statIOWaitTime);
+                        pw.print(" io, ");
+                        pw.print(rec.stepDetails.statIrqTime);
+                        pw.print(" irq, ");
+                        pw.print(rec.stepDetails.statSoftIrqTime);
+                        pw.print(" sirq, ");
+                        pw.print(rec.stepDetails.statIdlTime);
+                        pw.print(" idle");
+                        int totalRun = rec.stepDetails.statUserTime + rec.stepDetails.statSystemTime
+                                + rec.stepDetails.statIOWaitTime + rec.stepDetails.statIrqTime
+                                + rec.stepDetails.statSoftIrqTime;
+                        int total = totalRun + rec.stepDetails.statIdlTime;
+                        if (total > 0) {
+                            pw.print(" (");
+                            float perc = ((float)totalRun) / ((float)total) * 100;
+                            pw.print(String.format("%.1f%%", perc));
+                            pw.print(" of ");
+                            StringBuilder sb = new StringBuilder(64);
+                            formatTimeMsNoSpace(sb, total*10);
+                            pw.print(sb);
+                            pw.print(")");
+                        }
+                        pw.println();
+                    } else {
+                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                        pw.print(HISTORY_DATA); pw.print(",0,Dcpu=");
+                        pw.print(rec.stepDetails.userTime);
+                        pw.print(":");
+                        pw.print(rec.stepDetails.systemTime);
+                        if (rec.stepDetails.appCpuUid1 >= 0) {
+                            printStepCpuUidCheckinDetails(pw, rec.stepDetails.appCpuUid1,
+                                    rec.stepDetails.appCpuUTime1, rec.stepDetails.appCpuSTime1);
+                            if (rec.stepDetails.appCpuUid2 >= 0) {
+                                printStepCpuUidCheckinDetails(pw, rec.stepDetails.appCpuUid2,
+                                        rec.stepDetails.appCpuUTime2, rec.stepDetails.appCpuSTime2);
+                            }
+                            if (rec.stepDetails.appCpuUid3 >= 0) {
+                                printStepCpuUidCheckinDetails(pw, rec.stepDetails.appCpuUid3,
+                                        rec.stepDetails.appCpuUTime3, rec.stepDetails.appCpuSTime3);
+                            }
+                        }
+                        pw.println();
+                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                        pw.print(HISTORY_DATA); pw.print(",0,Dpst=");
+                        pw.print(rec.stepDetails.statUserTime);
+                        pw.print(',');
+                        pw.print(rec.stepDetails.statSystemTime);
+                        pw.print(',');
+                        pw.print(rec.stepDetails.statIOWaitTime);
+                        pw.print(',');
+                        pw.print(rec.stepDetails.statIrqTime);
+                        pw.print(',');
+                        pw.print(rec.stepDetails.statSoftIrqTime);
+                        pw.print(',');
+                        pw.print(rec.stepDetails.statIdlTime);
+                        pw.println();
+                    }
+                }
                 oldState = rec.states;
                 oldState2 = rec.states2;
             }
         }
+
+        private void printStepCpuUidDetails(PrintWriter pw, int uid, int utime, int stime) {
+            UserHandle.formatUid(pw, uid);
+            pw.print("=");
+            pw.print(utime);
+            pw.print("u+");
+            pw.print(stime);
+            pw.print("s");
+        }
+
+        private void printStepCpuUidCheckinDetails(PrintWriter pw, int uid, int utime, int stime) {
+            pw.print('/');
+            pw.print(uid);
+            pw.print(":");
+            pw.print(utime);
+            pw.print(":");
+            pw.print(stime);
+        }
     }
 
     private void printSizeValue(PrintWriter pw, long size) {
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index f023df7..266922d 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -33,7 +33,9 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.DataUsageFeedback;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
@@ -336,22 +338,33 @@
         // that was encoded into call log databases.
 
         /**
-         * The component name of the account in string form.
+         * The component name of the account used to place or receive the call; in string form.
          * <P>Type: TEXT</P>
          */
         public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
 
         /**
-         * The identifier of a account that is unique to a specified component.
+         * The identifier for the account used to place or receive the call.
          * <P>Type: TEXT</P>
          */
         public static final String PHONE_ACCOUNT_ID = "subscription_id";
 
         /**
-         * The identifier of a account that is unique to a specified component. Equivalent value
-         * to {@link #PHONE_ACCOUNT_ID}. For ContactsProvider internal use only.
+         * The address associated with the account used to place or receive the call; in string
+         * form. For SIM-based calls, this is the user's own phone number.
+         * <P>Type: TEXT</P>
+         *
+         * @hide
+         */
+        public static final String PHONE_ACCOUNT_ADDRESS = "phone_account_address";
+
+        /**
+         * The subscription ID used to place this call.  This is no longer used and has been
+         * replaced with PHONE_ACCOUNT_COMPONENT_NAME/PHONE_ACCOUNT_ID.
+         * For ContactsProvider internal use only.
          * <P>Type: INTEGER</P>
          *
+         * @Deprecated
          * @hide
          */
         public static final String SUB_ID = "sub_id";
@@ -422,6 +435,19 @@
             final ContentResolver resolver = context.getContentResolver();
             int numberPresentation = PRESENTATION_ALLOWED;
 
+            TelecomManager tm = null;
+            try {
+                tm = TelecomManager.from(context);
+            } catch (UnsupportedOperationException e) {}
+
+            String accountAddress = null;
+            if (tm != null && accountHandle != null) {
+                PhoneAccount account = tm.getPhoneAccount(accountHandle);
+                if (account != null) {
+                    accountAddress = account.getSubscriptionAddress().getSchemeSpecificPart();
+                }
+            }
+
             // Remap network specified number presentation types
             // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
             // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
@@ -463,6 +489,7 @@
             }
             values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
             values.put(PHONE_ACCOUNT_ID, accountId);
+            values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
             values.put(NEW, Integer.valueOf(1));
 
             if (callType == MISSED_TYPE) {
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index ca8a68a..c1498ec 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -25,7 +25,9 @@
 import android.app.ActivityManagerNative;
 import android.app.SearchManager;
 import android.os.UserHandle;
+
 import com.android.internal.R;
+import com.android.internal.view.ActionModeWrapper;
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.StandaloneActionMode;
 import com.android.internal.view.menu.ContextMenuBuilder;
@@ -2689,72 +2691,78 @@
             if (mode != null) {
                 mActionMode = mode;
             } else {
-                if (mActionModeView == null) {
-                    if (isFloating()) {
-                        // Use the action bar theme.
-                        final TypedValue outValue = new TypedValue();
-                        final Theme baseTheme = mContext.getTheme();
-                        baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
-
-                        final Context actionBarContext;
-                        if (outValue.resourceId != 0) {
-                            final Theme actionBarTheme = mContext.getResources().newTheme();
-                            actionBarTheme.setTo(baseTheme);
-                            actionBarTheme.applyStyle(outValue.resourceId, true);
-
-                            actionBarContext = new ContextThemeWrapper(mContext, 0);
-                            actionBarContext.getTheme().setTo(actionBarTheme);
-                        } else {
-                            actionBarContext = mContext;
-                        }
-
-                        mActionModeView = new ActionBarContextView(actionBarContext);
-                        mActionModePopup = new PopupWindow(actionBarContext, null,
-                                R.attr.actionModePopupWindowStyle);
-                        mActionModePopup.setWindowLayoutType(
-                                WindowManager.LayoutParams.TYPE_APPLICATION);
-                        mActionModePopup.setContentView(mActionModeView);
-                        mActionModePopup.setWidth(MATCH_PARENT);
-
-                        actionBarContext.getTheme().resolveAttribute(
-                                R.attr.actionBarSize, outValue, true);
-                        final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
-                                actionBarContext.getResources().getDisplayMetrics());
-                        mActionModeView.setContentHeight(height);
-                        mActionModePopup.setHeight(WRAP_CONTENT);
-                        mShowActionModePopup = new Runnable() {
-                            public void run() {
-                                mActionModePopup.showAtLocation(
-                                        mActionModeView.getApplicationWindowToken(),
-                                        Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
-                            }
-                        };
-                    } else {
-                        ViewStub stub = (ViewStub) findViewById(
-                                R.id.action_mode_bar_stub);
-                        if (stub != null) {
-                            mActionModeView = (ActionBarContextView) stub.inflate();
-                        }
-                    }
-                }
-
                 if (mActionModeView != null) {
                     mActionModeView.killMode();
-                    mode = new StandaloneActionMode(mActionModeView.getContext(), mActionModeView,
-                            wrappedCallback, mActionModePopup == null);
-                    if (callback.onCreateActionMode(mode, mode.getMenu())) {
-                        mode.invalidate();
-                        mActionModeView.initForMode(mode);
-                        mActionModeView.setVisibility(View.VISIBLE);
-                        mActionMode = mode;
-                        if (mActionModePopup != null) {
-                            post(mShowActionModePopup);
+                }
+                ActionModeWrapper wrapperMode =
+                        new ActionModeWrapper(mContext, wrappedCallback);
+                if (callback.onCreateActionMode(wrapperMode, wrapperMode.getMenu())) {
+                    if (wrapperMode.getType() == ActionMode.TYPE_PRIMARY) {
+                        if (mActionModeView == null) {
+                            if (isFloating()) {
+                                // Use the action bar theme.
+                                final TypedValue outValue = new TypedValue();
+                                final Theme baseTheme = mContext.getTheme();
+                                baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
+
+                                final Context actionBarContext;
+                                if (outValue.resourceId != 0) {
+                                    final Theme actionBarTheme = mContext.getResources().newTheme();
+                                    actionBarTheme.setTo(baseTheme);
+                                    actionBarTheme.applyStyle(outValue.resourceId, true);
+
+                                    actionBarContext = new ContextThemeWrapper(mContext, 0);
+                                    actionBarContext.getTheme().setTo(actionBarTheme);
+                                } else {
+                                    actionBarContext = mContext;
+                                }
+
+                                mActionModeView = new ActionBarContextView(actionBarContext);
+                                mActionModePopup = new PopupWindow(actionBarContext, null,
+                                        R.attr.actionModePopupWindowStyle);
+                                mActionModePopup.setWindowLayoutType(
+                                        WindowManager.LayoutParams.TYPE_APPLICATION);
+                                mActionModePopup.setContentView(mActionModeView);
+                                mActionModePopup.setWidth(MATCH_PARENT);
+
+                                actionBarContext.getTheme().resolveAttribute(
+                                        R.attr.actionBarSize, outValue, true);
+                                final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
+                                        actionBarContext.getResources().getDisplayMetrics());
+                                mActionModeView.setContentHeight(height);
+                                mActionModePopup.setHeight(WRAP_CONTENT);
+                                mShowActionModePopup = new Runnable() {
+                                    public void run() {
+                                        mActionModePopup.showAtLocation(
+                                                mActionModeView.getApplicationWindowToken(),
+                                                Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
+                                    }
+                                };
+                            } else {
+                                ViewStub stub = (ViewStub) findViewById(
+                                        R.id.action_mode_bar_stub);
+                                if (stub != null) {
+                                    mActionModeView = (ActionBarContextView) stub.inflate();
+                                }
+                            }
                         }
-                        mActionModeView.sendAccessibilityEvent(
-                                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-                    } else {
-                        mActionMode = null;
+                        if (mActionModeView != null) {
+                            wrapperMode.setActionModeView(mActionModeView);
+                            wrapperMode.setFocusable(mActionModePopup == null);
+                            wrapperMode.lockType();
+                            wrapperMode.invalidate();
+                            mActionModeView.initForMode(wrapperMode);
+                            mActionModeView.setVisibility(View.VISIBLE);
+                            mActionMode = wrapperMode;
+                            if (mActionModePopup != null) {
+                                post(mShowActionModePopup);
+                            }
+                            mActionModeView.sendAccessibilityEvent(
+                                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+                        }
                     }
+                } else {
+                    mActionMode = null;
                 }
             }
             if (mActionMode != null && getCallback() != null && !isDestroyed()) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 87fe216..f392682 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1343,7 +1343,7 @@
 
         boolean insetsChanged = false;
 
-        boolean layoutRequested = mLayoutRequested && !mStopped;
+        boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
         if (layoutRequested) {
 
             final Resources res = mView.getContext().getResources();
@@ -1776,7 +1776,7 @@
                 }
             }
 
-            if (!mStopped) {
+            if (!mStopped || mReportNextDraw) {
                 boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                         (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
@@ -1849,7 +1849,7 @@
             }
         }
 
-        final boolean didLayout = layoutRequested && !mStopped;
+        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
         boolean triggerGlobalLayoutListener = didLayout
                 || mAttachInfo.mRecomputeGlobalAttributes;
         if (didLayout) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 183527cb..9ae9fa7 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -35,6 +35,8 @@
 import android.view.textservice.SpellCheckerInfo;
 import android.view.textservice.TextServicesManager;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -518,7 +520,8 @@
         return NOT_A_SUBTYPE_ID;
     }
 
-    private static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
+    @VisibleForTesting
+    public static ArrayList<InputMethodSubtype> getImplicitlyApplicableSubtypesLocked(
             Resources res, InputMethodInfo imi) {
         final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi);
         final String systemLocale = res.getConfiguration().locale.toString();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 20bb95e..d0c7f8c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -94,7 +94,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 116 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 118 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -208,7 +208,7 @@
     final HistoryItem mHistoryLastLastWritten = new HistoryItem();
     final HistoryItem mHistoryReadTmp = new HistoryItem();
     final HistoryItem mHistoryAddTmp = new HistoryItem();
-    final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<HistoryTag, Integer>();
+    final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap();
     String[] mReadHistoryStrings;
     int[] mReadHistoryUids;
     int mReadHistoryChars;
@@ -227,6 +227,38 @@
     HistoryItem mHistoryLastEnd;
     HistoryItem mHistoryCache;
 
+    // Used by computeHistoryStepDetails
+    HistoryStepDetails mLastHistoryStepDetails = null;
+    byte mLastHistoryStepLevel = 0;
+    final HistoryStepDetails mCurHistoryStepDetails = new HistoryStepDetails();
+    final HistoryStepDetails mReadHistoryStepDetails = new HistoryStepDetails();
+    final HistoryStepDetails mTmpHistoryStepDetails = new HistoryStepDetails();
+    /**
+     * Total time (in 1/100 sec) spent executing in user code.
+     */
+    long mLastStepCpuUserTime;
+    long mCurStepCpuUserTime;
+    /**
+     * Total time (in 1/100 sec) spent executing in kernel code.
+     */
+    long mLastStepCpuSystemTime;
+    long mCurStepCpuSystemTime;
+    /**
+     * Times from /proc/stat
+     */
+    long mLastStepStatUserTime;
+    long mLastStepStatSystemTime;
+    long mLastStepStatIOWaitTime;
+    long mLastStepStatIrqTime;
+    long mLastStepStatSoftIrqTime;
+    long mLastStepStatIdleTime;
+    long mCurStepStatUserTime;
+    long mCurStepStatSystemTime;
+    long mCurStepStatIOWaitTime;
+    long mCurStepStatIrqTime;
+    long mCurStepStatSoftIrqTime;
+    long mCurStepStatIdleTime;
+
     private HistoryItem mHistoryIterator;
     private boolean mReadOverflow;
     private boolean mIteratingHistory;
@@ -1938,6 +1970,10 @@
     static final int STATE_BATTERY_PLUG_MASK    = 0x00000003;
     static final int STATE_BATTERY_PLUG_SHIFT   = 24;
 
+    // We use the low bit of the battery state int to indicate that we have full details
+    // from a battery level change.
+    static final int BATTERY_DELTA_LEVEL_FLAG   = 0x00000001;
+
     public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
         if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
             dest.writeInt(DELTA_TIME_ABS);
@@ -1958,7 +1994,11 @@
             deltaTimeToken = (int)deltaTime;
         }
         int firstToken = deltaTimeToken | (cur.states&DELTA_STATE_MASK);
-        final int batteryLevelInt = buildBatteryLevelInt(cur);
+        final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
+                ? BATTERY_DELTA_LEVEL_FLAG : 0;
+        final boolean computeStepDetails = includeStepDetails != 0
+                || mLastHistoryStepDetails == null;
+        final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
         final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
         if (batteryLevelIntChanged) {
             firstToken |= DELTA_BATTERY_LEVEL_FLAG;
@@ -2040,12 +2080,26 @@
                     + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
                     + cur.eventTag.string);
         }
+        if (computeStepDetails) {
+            computeHistoryStepDetails(mCurHistoryStepDetails, mLastHistoryStepDetails);
+            if (includeStepDetails != 0) {
+                mCurHistoryStepDetails.writeToParcel(dest);
+            }
+            cur.stepDetails = mCurHistoryStepDetails;
+            mLastHistoryStepDetails = mCurHistoryStepDetails;
+        } else {
+            cur.stepDetails = null;
+        }
+        if (mLastHistoryStepLevel < cur.batteryLevel) {
+            mLastHistoryStepDetails = null;
+        }
+        mLastHistoryStepLevel = cur.batteryLevel;
     }
 
     private int buildBatteryLevelInt(HistoryItem h) {
         return ((((int)h.batteryLevel)<<25)&0xfe000000)
-                | ((((int)h.batteryTemperature)<<14)&0x01ffc000)
-                | (((int)h.batteryVoltage)&0x00003fff);
+                | ((((int)h.batteryTemperature)<<14)&0x01ff8000)
+                | ((((int)h.batteryVoltage)<<1)&0x00007fff);
     }
 
     private int buildStateInt(HistoryItem h) {
@@ -2063,6 +2117,98 @@
                 | (h.states&(~DELTA_STATE_MASK));
     }
 
+    private void computeHistoryStepDetails(final HistoryStepDetails out,
+            final HistoryStepDetails last) {
+        final HistoryStepDetails tmp = last != null ? mTmpHistoryStepDetails : out;
+
+        // Perform a CPU update right after we do this collection, so we have started
+        // collecting good data for the next step.
+        requestImmediateCpuUpdate();
+
+        if (last == null) {
+            // We are not generating a delta, so all we need to do is reset the stats
+            // we will later be doing a delta from.
+            final int NU = mUidStats.size();
+            for (int i=0; i<NU; i++) {
+                final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+                uid.mLastStepUserTime = uid.mCurStepUserTime;
+                uid.mLastStepSystemTime = uid.mCurStepSystemTime;
+            }
+            mLastStepCpuUserTime = mCurStepCpuUserTime;
+            mLastStepCpuSystemTime = mCurStepCpuSystemTime;
+            mLastStepStatUserTime = mCurStepStatUserTime;
+            mLastStepStatSystemTime = mCurStepStatSystemTime;
+            mLastStepStatIOWaitTime = mCurStepStatIOWaitTime;
+            mLastStepStatIrqTime = mCurStepStatIrqTime;
+            mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime;
+            mLastStepStatIdleTime = mCurStepStatIdleTime;
+            tmp.clear();
+            return;
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTime + " sys="
+                    + mLastStepStatSystemTime + " io=" + mLastStepStatIOWaitTime
+                    + " irq=" + mLastStepStatIrqTime + " sirq="
+                    + mLastStepStatSoftIrqTime + " idle=" + mLastStepStatIdleTime);
+            Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTime + " sys="
+                    + mCurStepStatSystemTime + " io=" + mCurStepStatIOWaitTime
+                    + " irq=" + mCurStepStatIrqTime + " sirq="
+                    + mCurStepStatSoftIrqTime + " idle=" + mCurStepStatIdleTime);
+        }
+        out.userTime = (int)(mCurStepCpuUserTime - mLastStepCpuUserTime);
+        out.systemTime = (int)(mCurStepCpuSystemTime - mLastStepCpuSystemTime);
+        out.statUserTime = (int)(mCurStepStatUserTime - mLastStepStatUserTime);
+        out.statSystemTime = (int)(mCurStepStatSystemTime - mLastStepStatSystemTime);
+        out.statIOWaitTime = (int)(mCurStepStatIOWaitTime - mLastStepStatIOWaitTime);
+        out.statIrqTime = (int)(mCurStepStatIrqTime - mLastStepStatIrqTime);
+        out.statSoftIrqTime = (int)(mCurStepStatSoftIrqTime - mLastStepStatSoftIrqTime);
+        out.statIdlTime = (int)(mCurStepStatIdleTime - mLastStepStatIdleTime);
+        out.appCpuUid1 = out.appCpuUid2 = out.appCpuUid3 = -1;
+        out.appCpuUTime1 = out.appCpuUTime2 = out.appCpuUTime3 = 0;
+        out.appCpuSTime1 = out.appCpuSTime2 = out.appCpuSTime3 = 0;
+        final int NU = mUidStats.size();
+        for (int i=0; i<NU; i++) {
+            final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+            final int totalUTime = (int)(uid.mCurStepUserTime - uid.mLastStepUserTime);
+            final int totalSTime = (int)(uid.mCurStepSystemTime - uid.mLastStepSystemTime);
+            final int totalTime = totalUTime + totalSTime;
+            uid.mLastStepUserTime = uid.mCurStepUserTime;
+            uid.mLastStepSystemTime = uid.mCurStepSystemTime;
+            if (totalTime <= (out.appCpuUTime3+out.appCpuSTime3)) {
+                continue;
+            }
+            if (totalTime <= (out.appCpuUTime2+out.appCpuSTime2)) {
+                out.appCpuUid3 = uid.mUid;
+                out.appCpuUTime3 = totalUTime;
+                out.appCpuSTime3 = totalSTime;
+            } else {
+                out.appCpuUid3 = out.appCpuUid2;
+                out.appCpuUTime3 = out.appCpuUTime2;
+                out.appCpuSTime3 = out.appCpuSTime2;
+                if (totalTime <= (out.appCpuUTime1+out.appCpuSTime1)) {
+                    out.appCpuUid2 = uid.mUid;
+                    out.appCpuUTime2 = totalUTime;
+                    out.appCpuSTime2 = totalSTime;
+                } else {
+                    out.appCpuUid2 = out.appCpuUid1;
+                    out.appCpuUTime2 = out.appCpuUTime1;
+                    out.appCpuSTime2 = out.appCpuSTime1;
+                    out.appCpuUid1 = uid.mUid;
+                    out.appCpuUTime1 = totalUTime;
+                    out.appCpuSTime1 = totalSTime;
+                }
+            }
+        }
+        mLastStepCpuUserTime = mCurStepCpuUserTime;
+        mLastStepCpuSystemTime = mCurStepCpuSystemTime;
+        mLastStepStatUserTime = mCurStepStatUserTime;
+        mLastStepStatSystemTime = mCurStepStatSystemTime;
+        mLastStepStatIOWaitTime = mCurStepStatIOWaitTime;
+        mLastStepStatIrqTime = mCurStepStatIrqTime;
+        mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime;
+        mLastStepStatIdleTime = mCurStepStatIdleTime;
+    }
+
     public void readHistoryDelta(Parcel src, HistoryItem cur) {
         int firstToken = src.readInt();
         int deltaTimeToken = firstToken&DELTA_TIME_MASK;
@@ -2091,8 +2237,9 @@
             cur.numReadInts += 2;
         }
 
+        final int batteryLevelInt;
         if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
-            int batteryLevelInt = src.readInt();
+            batteryLevelInt = src.readInt();
             cur.batteryLevel = (byte)((batteryLevelInt>>25)&0x7f);
             cur.batteryTemperature = (short)((batteryLevelInt<<7)>>21);
             cur.batteryVoltage = (char)(batteryLevelInt&0x3fff);
@@ -2102,6 +2249,8 @@
                     + " batteryLevel=" + cur.batteryLevel
                     + " batteryTemp=" + cur.batteryTemperature
                     + " batteryVolt=" + (int)cur.batteryVoltage);
+        } else {
+            batteryLevelInt = 0;
         }
 
         if ((firstToken&DELTA_STATE_FLAG) != 0) {
@@ -2180,6 +2329,13 @@
         } else {
             cur.eventCode = HistoryItem.EVENT_NONE;
         }
+
+        if ((batteryLevelInt&BATTERY_DELTA_LEVEL_FLAG) != 0) {
+            cur.stepDetails = mReadHistoryStepDetails;
+            cur.stepDetails.readFromParcel(src);
+        } else {
+            cur.stepDetails = null;
+        }
     }
 
     @Override
@@ -2207,6 +2363,7 @@
                 && (diffStates2&lastDiffStates2) == 0
                 && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
                 && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
+                && mHistoryLastWritten.stepDetails == null
                 && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
                         || cur.eventCode == HistoryItem.EVENT_NONE)
                 && mHistoryLastWritten.batteryLevel == cur.batteryLevel
@@ -2632,6 +2789,11 @@
         }
     }
 
+    private void requestImmediateCpuUpdate() {
+        mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
+        mHandler.sendEmptyMessage(MSG_UPDATE_WAKELOCKS);
+    }
+
     public void setRecordAllHistoryLocked(boolean enabled) {
         mRecordAllHistory = enabled;
         if (!enabled) {
@@ -2823,6 +2985,10 @@
     public int startAddingCpuLocked() {
         mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
 
+        if (!mOnBatteryInternal) {
+            return -1;
+        }
+
         final int N = mPartialTimers.size();
         if (N == 0) {
             mLastPartialTimers.clear();
@@ -2853,7 +3019,23 @@
         return 0;
     }
 
-    public void finishAddingCpuLocked(int perc, int utime, int stime, long[] cpuSpeedTimes) {
+    public void finishAddingCpuLocked(int perc, int remainUTime, int remainSTtime,
+            int totalUTime, int totalSTime, int statUserTime, int statSystemTime,
+            int statIOWaitTime, int statIrqTime, int statSoftIrqTime, int statIdleTime,
+            long[] cpuSpeedTimes) {
+        if (DEBUG) Slog.d(TAG, "Adding cpu: tuser=" + totalUTime + " tsys=" + totalSTime
+                + " user=" + statUserTime + " sys=" + statSystemTime
+                + " io=" + statIOWaitTime + " irq=" + statIrqTime
+                + " sirq=" + statSoftIrqTime + " idle=" + statIdleTime);
+        mCurStepCpuUserTime += totalUTime;
+        mCurStepCpuSystemTime += totalSTime;
+        mCurStepStatUserTime += statUserTime;
+        mCurStepStatSystemTime += statSystemTime;
+        mCurStepStatIOWaitTime += statIOWaitTime;
+        mCurStepStatIrqTime += statIrqTime;
+        mCurStepStatSoftIrqTime += statSoftIrqTime;
+        mCurStepStatIdleTime += statIdleTime;
+
         final int N = mPartialTimers.size();
         if (perc != 0) {
             int num = 0;
@@ -2874,26 +3056,24 @@
                     if (st.mInList) {
                         Uid uid = st.mUid;
                         if (uid != null && uid.mUid != Process.SYSTEM_UID) {
-                            int myUTime = utime/num;
-                            int mySTime = stime/num;
-                            utime -= myUTime;
-                            stime -= mySTime;
+                            int myUTime = remainUTime/num;
+                            int mySTime = remainSTtime/num;
+                            remainUTime -= myUTime;
+                            remainSTtime -= mySTime;
                             num--;
                             Uid.Proc proc = uid.getProcessStatsLocked("*wakelock*");
-                            proc.addCpuTimeLocked(myUTime, mySTime);
-                            proc.addSpeedStepTimes(cpuSpeedTimes);
+                            proc.addCpuTimeLocked(myUTime, mySTime, cpuSpeedTimes);
                         }
                     }
                 }
             }
 
             // Just in case, collect any lost CPU time.
-            if (utime != 0 || stime != 0) {
+            if (remainUTime != 0 || remainSTtime != 0) {
                 Uid uid = getUidStatsLocked(Process.SYSTEM_UID);
                 if (uid != null) {
                     Uid.Proc proc = uid.getProcessStatsLocked("*lost*");
-                    proc.addCpuTimeLocked(utime, stime);
-                    proc.addSpeedStepTimes(cpuSpeedTimes);
+                    proc.addCpuTimeLocked(remainUTime, remainSTtime, cpuSpeedTimes);
                 }
             }
         }
@@ -4214,6 +4394,14 @@
         LongSamplingCounter mMobileRadioActiveCount;
 
         /**
+         * The CPU times we had at the last history details update.
+         */
+        long mLastStepUserTime;
+        long mLastStepSystemTime;
+        long mCurStepUserTime;
+        long mCurStepSystemTime;
+
+        /**
          * The statistics we have collected for this uid's wake locks.
          */
         final OverflowArrayMap<Wakelock> mWakelockStats = new OverflowArrayMap<Wakelock>() {
@@ -4876,6 +5064,9 @@
                 mPackageStats.clear();
             }
 
+            mLastStepUserTime = mLastStepSystemTime = 0;
+            mCurStepUserTime = mCurStepSystemTime = 0;
+
             if (!active) {
                 if (mWifiRunningTimer != null) {
                     mWifiRunningTimer.detach();
@@ -5678,9 +5869,22 @@
                 return BatteryStatsImpl.this;
             }
 
-            public void addCpuTimeLocked(int utime, int stime) {
+            public void addCpuTimeLocked(int utime, int stime, long[] speedStepBins) {
                 mUserTime += utime;
+                mCurStepUserTime += utime;
                 mSystemTime += stime;
+                mCurStepSystemTime += stime;
+
+                for (int i = 0; i < mSpeedBins.length && i < speedStepBins.length; i++) {
+                    long amt = speedStepBins[i];
+                    if (amt != 0) {
+                        SamplingCounter c = mSpeedBins[i];
+                        if (c == null) {
+                            mSpeedBins[i] = c = new SamplingCounter(mOnBatteryTimeBase);
+                        }
+                        c.addCountAtomic(speedStepBins[i]);
+                    }
+                }
             }
 
             public void addForegroundTimeLocked(long ttime) {
@@ -5770,20 +5974,6 @@
                 return val;
             }
 
-            /* Called by ActivityManagerService when CPU times are updated. */
-            public void addSpeedStepTimes(long[] values) {
-                for (int i = 0; i < mSpeedBins.length && i < values.length; i++) {
-                    long amt = values[i];
-                    if (amt != 0) {
-                        SamplingCounter c = mSpeedBins[i];
-                        if (c == null) {
-                            mSpeedBins[i] = c = new SamplingCounter(mOnBatteryTimeBase);
-                        }
-                        c.addCountAtomic(values[i]);
-                    }
-                }
-            }
-
             @Override
             public long getTimeAtCpuSpeedStep(int speedStep, int which) {
                 if (speedStep < mSpeedBins.length) {
@@ -6756,6 +6946,18 @@
             mWakeupReasonStats.clear();
         }
 
+        mLastHistoryStepDetails = null;
+        mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
+        mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
+        mLastStepCpuUserTime = mCurStepCpuUserTime = 0;
+        mLastStepCpuSystemTime = mCurStepCpuSystemTime = 0;
+        mLastStepStatUserTime = mCurStepStatUserTime = 0;
+        mLastStepStatSystemTime = mCurStepStatSystemTime = 0;
+        mLastStepStatIOWaitTime = mCurStepStatIOWaitTime = 0;
+        mLastStepStatIrqTime = mCurStepStatIrqTime = 0;
+        mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime = 0;
+        mLastStepStatIdleTime = mCurStepStatIdleTime = 0;
+
         initDischarge();
 
         clearHistoryLocked();
@@ -6872,7 +7074,7 @@
                 reset = true;
                 mNumDischargeStepDurations = 0;
             }
-            mOnBattery = mOnBatteryInternal = onBattery;
+            mOnBattery = mOnBatteryInternal = true;
             mLastDischargeStepLevel = level;
             mMinDischargeStepLevel = level;
             mLastDischargeStepTime = -1;
@@ -6900,7 +7102,7 @@
             mDischargeAmountScreenOff = 0;
             updateTimeBasesLocked(true, !screenOn, uptime, realtime);
         } else {
-            mOnBattery = mOnBatteryInternal = onBattery;
+            mOnBattery = mOnBatteryInternal = false;
             pullPendingStateUpdatesLocked();
             mHistoryCur.batteryLevel = (byte)level;
             mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index b5338df..501e0ec 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -152,6 +152,7 @@
     private int mRelIrqTime;
     private int mRelSoftIrqTime;
     private int mRelIdleTime;
+    private boolean mRelStatsAreGood;
 
     private int[] mCurPids;
     private int[] mCurThreadPids;
@@ -285,10 +286,9 @@
 
     public void update() {
         if (DEBUG) Slog.v(TAG, "Update: " + this);
-        mLastSampleTime = mCurrentSampleTime;
-        mCurrentSampleTime = SystemClock.uptimeMillis();
-        mLastSampleRealTime = mCurrentSampleRealTime;
-        mCurrentSampleRealTime = SystemClock.elapsedRealtime();
+
+        final long nowUptime = SystemClock.uptimeMillis();
+        final long nowRealtime = SystemClock.elapsedRealtime();
 
         final long[] sysCpu = mSystemCpuData;
         if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
@@ -304,30 +304,53 @@
             final long irqtime = sysCpu[5];
             final long softirqtime = sysCpu[6];
 
-            mRelUserTime = (int)(usertime - mBaseUserTime);
-            mRelSystemTime = (int)(systemtime - mBaseSystemTime);
-            mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
-            mRelIrqTime = (int)(irqtime - mBaseIrqTime);
-            mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
-            mRelIdleTime = (int)(idletime - mBaseIdleTime);
+            // This code is trying to avoid issues with idle time going backwards,
+            // but currently it gets into situations where it triggers most of the time. :(
+            if (true || (usertime >= mBaseUserTime && systemtime >= mBaseSystemTime
+                    && iowaittime >= mBaseIoWaitTime && irqtime >= mBaseIrqTime
+                    && softirqtime >= mBaseSoftIrqTime && idletime >= mBaseIdleTime)) {
+                mRelUserTime = (int)(usertime - mBaseUserTime);
+                mRelSystemTime = (int)(systemtime - mBaseSystemTime);
+                mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
+                mRelIrqTime = (int)(irqtime - mBaseIrqTime);
+                mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
+                mRelIdleTime = (int)(idletime - mBaseIdleTime);
+                mRelStatsAreGood = true;
 
-            if (DEBUG) {
-                Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
-                      + " S:" + sysCpu[2] + " I:" + sysCpu[3]
-                      + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
-                      + " O:" + sysCpu[6]);
-                Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
-                      + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
+                if (DEBUG) {
+                    Slog.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
+                          + " S:" + sysCpu[2] + " I:" + sysCpu[3]
+                          + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
+                          + " O:" + sysCpu[6]);
+                    Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
+                          + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
+                }
+
+                mBaseUserTime = usertime;
+                mBaseSystemTime = systemtime;
+                mBaseIoWaitTime = iowaittime;
+                mBaseIrqTime = irqtime;
+                mBaseSoftIrqTime = softirqtime;
+                mBaseIdleTime = idletime;
+
+            } else {
+                mRelUserTime = 0;
+                mRelSystemTime = 0;
+                mRelIoWaitTime = 0;
+                mRelIrqTime = 0;
+                mRelSoftIrqTime = 0;
+                mRelIdleTime = 0;
+                mRelStatsAreGood = false;
+                Slog.w(TAG, "/proc/stats has gone backwards; skipping CPU update");
+                return;
             }
-
-            mBaseUserTime = usertime;
-            mBaseSystemTime = systemtime;
-            mBaseIoWaitTime = iowaittime;
-            mBaseIrqTime = irqtime;
-            mBaseSoftIrqTime = softirqtime;
-            mBaseIdleTime = idletime;
         }
 
+        mLastSampleTime = mCurrentSampleTime;
+        mCurrentSampleTime = nowUptime;
+        mLastSampleRealTime = mCurrentSampleRealTime;
+        mCurrentSampleRealTime = nowRealtime;
+
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
         try {
             mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats);
@@ -647,6 +670,10 @@
         return mRelIdleTime;
     }
 
+    final public boolean hasGoodLastStats() {
+        return mRelStatsAreGood;
+    }
+
     final public float getTotalCpuPercent() {
         int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime;
         if (denom <= 0) {
diff --git a/core/java/com/android/internal/view/ActionModeWrapper.java b/core/java/com/android/internal/view/ActionModeWrapper.java
new file mode 100644
index 0000000..ef1981a
--- /dev/null
+++ b/core/java/com/android/internal/view/ActionModeWrapper.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import android.content.Context;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.ActionBarContextView;
+
+/**
+ * ActionMode implementation that wraps several actions modes and creates them on the fly depending
+ * on the ActionMode type chosen by the client.
+ */
+public class ActionModeWrapper extends ActionMode {
+
+    private ActionMode mActionMode;
+    private final Context mContext;
+    private MenuBuilder mMenu;
+    private final ActionMode.Callback mCallback;
+    private boolean mTypeLocked = false;
+
+    private CharSequence mTitle;
+    private CharSequence mSubtitle;
+    private View mCustomView;
+
+    // Fields for StandaloneActionMode
+    private ActionBarContextView mActionModeView;
+    private boolean mIsFocusable;
+
+    public ActionModeWrapper(Context context, ActionMode.Callback callback) {
+        mContext = context;
+        mMenu = new MenuBuilder(context).setDefaultShowAsAction(
+                MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        mCallback = callback;
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        if (mActionMode != null) {
+            mActionMode.setTitle(title);
+        } else {
+            mTitle = title;
+        }
+    }
+
+    @Override
+    public void setTitle(int resId) {
+        if (mActionMode != null) {
+            mActionMode.setTitle(resId);
+        } else {
+            mTitle = resId != 0 ? mContext.getString(resId) : null;
+        }
+    }
+
+    @Override
+    public void setSubtitle(CharSequence subtitle) {
+        if (mActionMode != null) {
+            mActionMode.setSubtitle(subtitle);
+        } else {
+            mSubtitle = subtitle;
+        }
+    }
+
+    @Override
+    public void setSubtitle(int resId) {
+        if (mActionMode != null) {
+            mActionMode.setSubtitle(resId);
+        } else {
+            mSubtitle = resId != 0 ? mContext.getString(resId) : null;
+        }
+    }
+
+    @Override
+    public void setCustomView(View view) {
+        if (mActionMode != null) {
+            mActionMode.setCustomView(view);
+        } else {
+            mCustomView = view;
+        }
+    }
+
+    /**
+     * Set the current type as final and create the necessary ActionMode. After this call, any
+     * changes to the ActionMode type will be ignored.
+     */
+    public void lockType() {
+        mTypeLocked = true;
+        switch (getType()) {
+            case ActionMode.TYPE_PRIMARY:
+            default:
+                mActionMode = new StandaloneActionMode(
+                        mActionModeView.getContext(),
+                        mActionModeView, mCallback, mIsFocusable, mMenu);
+                break;
+            case ActionMode.TYPE_FLOATING:
+                // Not implemented yet.
+                break;
+        }
+
+        if (mActionMode == null) {
+            return;
+        }
+
+        mActionMode.setTitle(mTitle);
+        mActionMode.setSubtitle(mSubtitle);
+        if (mCustomView != null) {
+            mActionMode.setCustomView(mCustomView);
+        }
+
+        mTitle = null;
+        mSubtitle = null;
+        mCustomView = null;
+    }
+
+    @Override
+    public void setType(int type) {
+        if (!mTypeLocked) {
+            super.setType(type);
+        } else {
+            throw new IllegalStateException(
+                    "You can't change the ActionMode's type after onCreateActionMode.");
+        }
+    }
+
+    @Override
+    public void invalidate() {
+        if (mActionMode != null) {
+            mActionMode.invalidate();
+        }
+    }
+
+    @Override
+    public void finish() {
+        if (mActionMode != null) {
+            mActionMode.finish();
+        }
+    }
+
+    @Override
+    public Menu getMenu() {
+        return mMenu;
+    }
+
+    @Override
+    public CharSequence getTitle() {
+        if (mActionMode != null) {
+            return mActionMode.getTitle();
+        }
+        return mTitle;
+    }
+
+    @Override
+    public CharSequence getSubtitle() {
+        if (mActionMode != null) {
+            return mActionMode.getSubtitle();
+        }
+        return mSubtitle;
+    }
+
+    @Override
+    public View getCustomView() {
+        if (mActionMode != null) {
+            return mActionMode.getCustomView();
+        }
+        return mCustomView;
+    }
+
+    @Override
+    public MenuInflater getMenuInflater() {
+        return new MenuInflater(mContext);
+    }
+
+    public void setActionModeView(ActionBarContextView actionModeView) {
+        mActionModeView = actionModeView;
+    }
+
+    public void setFocusable(boolean focusable) {
+        mIsFocusable = focusable;
+    }
+
+}
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index d5d3602..2812b77 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -20,6 +20,7 @@
 import com.android.internal.view.menu.SubMenuBuilder;
 import com.android.internal.widget.ActionBarContextView;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.view.ActionMode;
 import android.view.Menu;
@@ -41,13 +42,15 @@
     private MenuBuilder mMenu;
 
     public StandaloneActionMode(Context context, ActionBarContextView view,
-            ActionMode.Callback callback, boolean isFocusable) {
+            ActionMode.Callback callback, boolean isFocusable, @Nullable MenuBuilder menuBuilder) {
         mContext = context;
         mContextView = view;
         mCallback = callback;
 
-        mMenu = new MenuBuilder(view.getContext()).setDefaultShowAsAction(
-                MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        mMenu = (menuBuilder != null)
+                ? menuBuilder
+                : new MenuBuilder(view.getContext()).setDefaultShowAsAction(
+                        MenuItem.SHOW_AS_ACTION_IF_ROOM);
         mMenu.setCallback(this);
         mFocusable = isFocusable;
     }
@@ -64,12 +67,12 @@
 
     @Override
     public void setTitle(int resId) {
-        setTitle(mContext.getString(resId));
+        setTitle(resId != 0 ? mContext.getString(resId) : null);
     }
 
     @Override
     public void setSubtitle(int resId) {
-        setSubtitle(mContext.getString(resId));
+        setSubtitle(resId != 0 ? mContext.getString(resId) : null);
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 5d3f464..ae5999a 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -158,7 +158,7 @@
             removeView(mCustomView);
         }
         mCustomView = view;
-        if (mTitleLayout != null) {
+        if (view != null && mTitleLayout != null) {
             removeView(mTitleLayout);
             mTitleLayout = null;
         }
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
index 1557918..86021e5 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
@@ -29,8 +29,6 @@
 import com.android.internal.inputmethod.InputMethodUtils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -38,19 +36,33 @@
 public class InputMethodTest extends InstrumentationTestCase {
     private static final boolean IS_AUX = true;
     private static final boolean IS_DEFAULT = true;
-    private static final boolean IS_AUTO = true;
+    private static final boolean IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE = true;
     private static final boolean IS_ASCII_CAPABLE = true;
+    private static final boolean IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = true;
     private static final boolean IS_SYSTEM_READY = true;
-    private static final ArrayList<InputMethodSubtype> NO_SUBTYPE = null;
+    private static final Locale LOCALE_EN = new Locale("en");
     private static final Locale LOCALE_EN_US = new Locale("en", "US");
     private static final Locale LOCALE_EN_GB = new Locale("en", "GB");
     private static final Locale LOCALE_EN_IN = new Locale("en", "IN");
+    private static final Locale LOCALE_FI = new Locale("fi");
+    private static final Locale LOCALE_FI_FI = new Locale("fi", "FI");
+    private static final Locale LOCALE_FIL = new Locale("fil");
+    private static final Locale LOCALE_FIL_PH = new Locale("fil", "PH");
+    private static final Locale LOCALE_FR = new Locale("fr");
+    private static final Locale LOCALE_FR_CA = new Locale("fr", "CA");
     private static final Locale LOCALE_HI = new Locale("hi");
     private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
     private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN");
     private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW");
+    private static final Locale LOCALE_IN = new Locale("in");
+    private static final Locale LOCALE_ID = new Locale("id");
     private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
     private static final String SUBTYPE_MODE_VOICE = "voice";
+    private static final String SUBTYPE_MODE_ANY = null;
+    private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
+    private static final String EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable";
+    private static final String EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
+            "EnabledWhenDefaultIsNotAsciiCapable";
 
     @SmallTest
     public void testVoiceImes() throws Exception {
@@ -159,10 +171,417 @@
         }
     }
 
+    @SmallTest
+    public void testGetImplicitlyApplicableSubtypesLocked() throws Exception {
+        final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoFrCA = createDummyInputMethodSubtype("fr_CA",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoFr = createDummyInputMethodSubtype("fr_CA",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype autoSubtype = createDummyInputMethodSubtype("auto",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoJa = createDummyInputMethodSubtype("ja",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype =
+                createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
+                        !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                        IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2 =
+                createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
+                        !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                        IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+
+        // Make sure that an automatic subtype (overridesImplicitlyEnabledSubtype:true) is
+        // selected no matter what locale is specified.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoEnUS);
+            subtypes.add(nonAutoEnGB);
+            subtypes.add(nonAutoJa);
+            subtypes.add(nonAutoFil);
+            subtypes.add(autoSubtype);  // overridesImplicitlyEnabledSubtype == true
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
+            assertEquals(1, result.size());
+            verifyEquality(autoSubtype, result.get(0));
+        }
+
+        // Make sure that a subtype whose locale is exactly equal to the specified locale is
+        // selected as long as there is no no automatic subtype
+        // (overridesImplicitlyEnabledSubtype:true) in the given list.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoEnUS);  // locale == "en_US"
+            subtypes.add(nonAutoEnGB);
+            subtypes.add(nonAutoJa);
+            subtypes.add(nonAutoFil);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoEnUS, result.get(0));
+        }
+
+        // Make sure that a subtype whose locale is exactly equal to the specified locale is
+        // selected as long as there is no automatic subtype
+        // (overridesImplicitlyEnabledSubtype:true) in the given list.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoEnUS);
+            subtypes.add(nonAutoEnGB); // locale == "en_GB"
+            subtypes.add(nonAutoJa);
+            subtypes.add(nonAutoFil);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_GB, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoEnGB, result.get(0));
+        }
+
+        // If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and
+        // any subtype whose locale is exactly equal to the specified locale in the given list,
+        // try to find a subtype whose language is equal to the language part of the given locale.
+        // Here make sure that a subtype (locale: "fr_CA") can be found with locale: "fr".
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoFrCA);  // locale == "fr_CA"
+            subtypes.add(nonAutoJa);
+            subtypes.add(nonAutoFil);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoFrCA, result.get(0));
+        }
+        // Then make sure that a subtype (locale: "fr") can be found with locale: "fr_CA".
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoFr);  // locale == "fr"
+            subtypes.add(nonAutoJa);
+            subtypes.add(nonAutoFil);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR_CA, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoFrCA, result.get(0));
+        }
+
+        // Make sure that subtypes which have "EnabledWhenDefaultIsNotAsciiCapable" in its
+        // extra value is selected if and only if all other selected IMEs are not AsciiCapable.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoEnUS);
+            subtypes.add(nonAutoJa);    // not ASCII capable
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_JA_JP, imi);
+            assertEquals(3, result.size());
+            verifyEquality(nonAutoJa, result.get(0));
+            verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1));
+            verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2, result.get(2));
+        }
+
+        // Make sure that 3-letter language code can be handled.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoEnUS);
+            subtypes.add(nonAutoFil);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FIL_PH, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoFil, result.get(0));
+        }
+
+        // Make sure that we never end up matching "fi" (finnish) with "fil" (filipino).
+        // Also make sure that the first subtype will be used as the last-resort candidate.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoJa);
+            subtypes.add(nonAutoEnUS);
+            subtypes.add(nonAutoFil);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FI, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoJa, result.get(0));
+        }
+
+        // Make sure that "in" and "id" conversion is taken into account.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoIn);
+            subtypes.add(nonAutoEnUS);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoIn, result.get(0));
+        }
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoIn);
+            subtypes.add(nonAutoEnUS);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoIn, result.get(0));
+        }
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoId);
+            subtypes.add(nonAutoEnUS);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoId, result.get(0));
+        }
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoId);
+            subtypes.add(nonAutoEnUS);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            final ArrayList<InputMethodSubtype> result =
+                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
+            assertEquals(1, result.size());
+            verifyEquality(nonAutoId, result.get(0));
+        }
+    }
+
+    @SmallTest
+    public void testContainsSubtypeOf() throws Exception {
+        final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoFilPH = createDummyInputMethodSubtype("fil_PH",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id",
+                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+
+        final boolean CHECK_COUNTRY = true;
+
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoEnUS);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_VOICE));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY,
+                    SUBTYPE_MODE_VOICE));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_ANY));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY,
+                    SUBTYPE_MODE_ANY));
+
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+        }
+
+        // Make sure that 3-letter language code ("fil") can be handled.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoFil);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+        }
+
+        // Make sure that 3-letter language code ("fil_PH") can be handled.
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoFilPH);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+        }
+
+        // Make sure that a subtype whose locale is "in" can be queried with "id".
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoIn);
+            subtypes.add(nonAutoEnUS);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+        }
+
+        // Make sure that a subtype whose locale is "id" can be queried with "in".
+        {
+            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes.add(nonAutoId);
+            subtypes.add(nonAutoEnUS);
+            final InputMethodInfo imi = createDummyInputMethodInfo(
+                    "com.android.apps.inputmethod.latin",
+                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+                    subtypes);
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            // TODO: This should be true but the current behavior is broken.
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+            // TODO: This should be true but the current behavior is broken.
+            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY,
+                    SUBTYPE_MODE_KEYBOARD));
+        }
+    }
+
+    private ArrayList<InputMethodSubtype> callGetImplicitlyApplicableSubtypesLockedWithLocale(
+            final Locale locale, final InputMethodInfo imi) {
+        final Context context = getInstrumentation().getTargetContext();
+        final Locale initialLocale = context.getResources().getConfiguration().locale;
+        try {
+            context.getResources().getConfiguration().setLocale(locale);
+            return InputMethodUtils.getImplicitlyApplicableSubtypesLocked(context.getResources(),
+                    imi);
+        } finally {
+            context.getResources().getConfiguration().setLocale(initialLocale);
+        }
+    }
+
     private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
             final Locale systemLocale, final boolean isSystemReady, String... expectedImeNames) {
         final Context context = getInstrumentation().getTargetContext();
-        final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesUnderWithLocale(
+        final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesWithLocale(
                 context, isSystemReady, preinstalledImes, systemLocale));
         assertEquals(expectedImeNames.length, actualImeNames.length);
         for (int i = 0; i < expectedImeNames.length; ++i) {
@@ -184,7 +603,7 @@
         }
     }
 
-    private static ArrayList<InputMethodInfo> callGetDefaultEnabledImesUnderWithLocale(
+    private static ArrayList<InputMethodInfo> callGetDefaultEnabledImesWithLocale(
             final Context context, final boolean isSystemReady,
             final ArrayList<InputMethodInfo> imis, final Locale locale) {
         final Locale initialLocale = context.getResources().getConfiguration().locale;
@@ -210,11 +629,15 @@
         for (int subtypeIndex = 0; subtypeIndex < expected.getSubtypeCount(); ++subtypeIndex) {
             final InputMethodSubtype expectedSubtype = expected.getSubtypeAt(subtypeIndex);
             final InputMethodSubtype actualSubtype = actual.getSubtypeAt(subtypeIndex);
-            assertEquals(expectedSubtype, actualSubtype);
-            assertEquals(expectedSubtype.hashCode(), actualSubtype.hashCode());
+            verifyEquality(expectedSubtype, actualSubtype);
         }
     }
 
+    private static void verifyEquality(InputMethodSubtype expected, InputMethodSubtype actual) {
+        assertEquals(expected, actual);
+        assertEquals(expected.hashCode(), actual.hashCode());
+    }
+
     private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name,
             CharSequence label, boolean isAuxIme, boolean isDefault,
             List<InputMethodSubtype> subtypes) {
@@ -236,13 +659,27 @@
 
     private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode,
             boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
-            boolean isAsciiCapable) {
+            boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) {
+
+        final StringBuilder subtypeExtraValue = new StringBuilder();
+        if (isEnabledWhenDefaultIsNotAsciiCapable) {
+            subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR);
+            subtypeExtraValue.append(EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+        }
+
+        // TODO: Remove following code. InputMethodSubtype#isAsciiCapable() has been publicly
+        // available since API level 19 (KitKat). We no longer need to rely on extra value.
+        if (isAsciiCapable) {
+            subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR);
+            subtypeExtraValue.append(EXTRA_VALUE_ASCII_CAPABLE);
+        }
+
         return new InputMethodSubtypeBuilder()
                 .setSubtypeNameResId(0)
                 .setSubtypeIconResId(0)
                 .setSubtypeLocale(locale)
                 .setSubtypeMode(mode)
-                .setSubtypeExtraValue("")
+                .setSubtypeExtraValue(subtypeExtraValue.toString())
                 .setIsAuxiliary(isAuxiliary)
                 .setOverridesImplicitlyEnabledSubtype(overridesImplicitlyEnabledSubtype)
                 .setIsAsciiCapable(isAsciiCapable)
@@ -253,10 +690,12 @@
         ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>();
         {
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
-            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_AUTO,
-                    !IS_ASCII_CAPABLE));
+            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX,
+                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultAutoVoiceIme",
                     "dummy.voice0", "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes));
         }
@@ -268,33 +707,39 @@
         ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>();
         {
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
-            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_AUTO,
-                    !IS_ASCII_CAPABLE));
+            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX,
+                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0",
                     "dummy.voice1", "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes));
         }
         {
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
-            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_AUTO,
-                    !IS_ASCII_CAPABLE));
+            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX,
+                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1",
                     "dummy.voice2", "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes));
         }
         {
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultVoiceIme2",
                     "dummy.voice3", "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes));
         }
         {
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultEnKeyboardIme",
                     "dummy.keyboard0", "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes));
         }
@@ -321,7 +766,8 @@
             final boolean isDefaultIme = false;
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX,
-                    IS_AUTO, !IS_ASCII_CAPABLE));
+                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice",
                     "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme,
                     subtypes));
@@ -332,9 +778,11 @@
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             // TODO: This subtype should be marked as IS_ASCII_CAPABLE
             subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi",
                     "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme,
                     subtypes));
@@ -345,7 +793,8 @@
             final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString);
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin",
                     "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme,
                     subtypes));
@@ -356,7 +805,8 @@
             final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString);
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean",
                     "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme,
                     subtypes));
@@ -368,13 +818,17 @@
                     new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString);
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin",
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme,
                     subtypes));
@@ -385,9 +839,11 @@
             final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString);
             final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
             subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             subtypes.add(createDummyInputMethodSubtype("emoji", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
-                    !IS_AUTO, !IS_ASCII_CAPABLE));
+                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
             preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese",
                     "com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX,
                     isDefaultIme, subtypes));
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index de5f91c..3c65705 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -228,21 +228,23 @@
     }
 
     // setup the first glyph position and adjust bounds if needed
+    int xBaseline = 0;
+    int yBaseline = 0;
     if (mCanvas->drawTextAbsolutePos()) {
         bounds.offset(x,y);
-        pointStorage[0].set(x, y);
-    } else {
-        pointStorage[0].set(0, 0);
+        xBaseline = x;
+        yBaseline = y;
     }
+    pointStorage[0].set(xBaseline, yBaseline);
 
     // setup the remaining glyph positions
     if (glyphs.paint.isVerticalText()) {
         for (int i = 1; i < glyphs.count; i++) {
-            pointStorage[i].set(x, glyphWidths[i-1] + pointStorage[i-1].fY);
+            pointStorage[i].set(xBaseline, glyphWidths[i-1] + pointStorage[i-1].fY);
         }
     } else {
         for (int i = 1; i < glyphs.count; i++) {
-            pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, y);
+            pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, yBaseline);
         }
     }
 
diff --git a/media/java/android/media/AudioDevicesManager.java b/media/java/android/media/AudioDevicesManager.java
index 4e52953..bce2100 100644
--- a/media/java/android/media/AudioDevicesManager.java
+++ b/media/java/android/media/AudioDevicesManager.java
@@ -130,8 +130,7 @@
     public ArrayList<AudioDeviceInfo> listDevices(int flags) {
         Slog.i(TAG, "AudioManager.listDevices(" + Integer.toHexString(flags) + ")");
 
-        //FIXME - Use ArrayList<AudioDevicePort> when mAudioManager.listAudioDevicePorts() is fixed.
-        ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+        ArrayList<AudioDevicePort> ports = new ArrayList<AudioDevicePort>();
         int status = mAudioManager.listAudioDevicePorts(ports);
 
         Slog.i(TAG, "  status:" + status + " numPorts:" + ports.size());
@@ -140,9 +139,9 @@
 
         if (status == AudioManager.SUCCESS) {
             deviceList = new ArrayList<AudioDeviceInfo>();
-             for (AudioPort port : ports) {
-                if (/*port instanceof AudioDevicePort &&*/ checkFlags((AudioDevicePort)port, flags)) {
-                    deviceList.add(new AudioDeviceInfo((AudioDevicePort)port));
+             for (AudioDevicePort port : ports) {
+                if (checkFlags(port, flags)) {
+                    deviceList.add(new AudioDeviceInfo(port));
                 }
             }
         }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index da89cf4..9876995 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3459,14 +3459,14 @@
      * @see listAudioPorts(ArrayList<AudioPort>)
      * @hide
      */
-    public int listAudioDevicePorts(ArrayList<AudioPort> devices) {
+    public int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
         ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
         int status = updateAudioPortCache(ports, null);
         if (status == SUCCESS) {
             devices.clear();
             for (int i = 0; i < ports.size(); i++) {
                 if (ports.get(i) instanceof AudioDevicePort) {
-                    devices.add(ports.get(i));
+                    devices.add((AudioDevicePort)ports.get(i));
                 }
             }
         }
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 5406130..cf69b8f 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -430,7 +430,7 @@
             pData = buffer->data;
             dataSize = Image_getJpegSize(buffer, usingRGBAOverride);
             break;
-        case HAL_PIXEL_FORMAT_RAW_SENSOR:
+        case HAL_PIXEL_FORMAT_RAW16:
             // Single plane 16bpp bayer data.
             bytesPerPixel = 2;
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
@@ -517,7 +517,7 @@
             pixelStride = 0;
             break;
         case HAL_PIXEL_FORMAT_Y16:
-        case HAL_PIXEL_FORMAT_RAW_SENSOR:
+        case HAL_PIXEL_FORMAT_RAW16:
         case HAL_PIXEL_FORMAT_RGB_565:
             // Single plane 16bpp data.
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
@@ -584,7 +584,7 @@
             rowStride = buffer->stride;
             break;
         case HAL_PIXEL_FORMAT_Y16:
-        case HAL_PIXEL_FORMAT_RAW_SENSOR:
+        case HAL_PIXEL_FORMAT_RAW16:
             // In native side, strides are specified in pixels, not in bytes.
             // Single plane 16bpp bayer data. even width/height,
             // row stride multiple of 16 pixels (32 bytes)
diff --git a/packages/Keyguard/res/layout/keyguard_password_view.xml b/packages/Keyguard/res/layout/keyguard_password_view.xml
index cff2fc7..7dcaf6d 100644
--- a/packages/Keyguard/res/layout/keyguard_password_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_password_view.xml
@@ -64,6 +64,7 @@
              android:layout_height="wrap_content"
              android:layout_marginBottom="12dp"
              android:src="@drawable/ic_lockscreen_ime"
+             android:contentDescription="@string/accessibility_ime_switch_button"
              android:clickable="true"
              android:padding="8dip"
              android:layout_gravity="end|center_vertical"
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 8b18b2ed..5047330 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -297,4 +297,7 @@
         This is displayed if the phone is not connected to a carrier.-->
     <string name="keyguard_carrier_default">No service.</string>
 
+    <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_ime_switch_button" msgid="5032926134740456424">Switch input method button.</string>
+
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 78f6ace..eac83d8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -32,14 +32,18 @@
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 
+import com.android.internal.widget.LockPatternUtils;
+
 import libcore.io.IoUtils;
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.CharArrayReader;
 import java.io.DataInputStream;
@@ -76,10 +80,11 @@
     private static final String KEY_SECURE = "secure";
     private static final String KEY_GLOBAL = "global";
     private static final String KEY_LOCALE = "locale";
+    private static final String KEY_LOCK_SETTINGS = "lock_settings";
 
     // Versioning of the state file.  Increment this version
     // number any time the set of state items is altered.
-    private static final int STATE_VERSION = 3;
+    private static final int STATE_VERSION = 4;
 
     // Slots in the checksum array.  Never insert new items in the middle
     // of this array; new slots must be appended.
@@ -89,20 +94,23 @@
     private static final int STATE_WIFI_SUPPLICANT = 3;
     private static final int STATE_WIFI_CONFIG     = 4;
     private static final int STATE_GLOBAL          = 5;
+    private static final int STATE_LOCK_SETTINGS   = 6;
 
-    private static final int STATE_SIZE            = 6; // The current number of state items
+    private static final int STATE_SIZE            = 7; // The current number of state items
 
     // Number of entries in the checksum array at various version numbers
     private static final int STATE_SIZES[] = {
         0,
         4,              // version 1
         5,              // version 2 added STATE_WIFI_CONFIG
-        STATE_SIZE      // version 3 added STATE_GLOBAL
+        6,              // version 3 added STATE_GLOBAL
+        STATE_SIZE      // version 4 added STATE_LOCK_SETTINGS
     };
 
     // Versioning of the 'full backup' format
-    private static final int FULL_BACKUP_VERSION = 2;
+    private static final int FULL_BACKUP_VERSION = 3;
     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
+    private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
 
     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
 
@@ -124,6 +132,10 @@
     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
     private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
 
+    // Keys within the lock settings section
+    private static final String KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED = "owner_info_enabled";
+    private static final String KEY_LOCK_SETTINGS_OWNER_INFO = "owner_info";
+
     // Name of the temporary file we use during full backup/restore.  This is
     // stored in the full-backup tarfile as well, so should not be changed.
     private static final String STAGE_FILE = "flattened-data";
@@ -367,6 +379,7 @@
         byte[] systemSettingsData = getSystemSettings();
         byte[] secureSettingsData = getSecureSettings();
         byte[] globalSettingsData = getGlobalSettings();
+        byte[] lockSettingsData   = getLockSettings();
         byte[] locale = mSettingsHelper.getLocaleData();
         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
         byte[] wifiConfigData = getFileData(mWifiConfigFile);
@@ -387,6 +400,9 @@
         stateChecksums[STATE_WIFI_CONFIG] =
             writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
                     data);
+        stateChecksums[STATE_LOCK_SETTINGS] =
+            writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
+                    lockSettingsData, data);
 
         writeNewChecksums(stateChecksums, newState);
     }
@@ -492,6 +508,8 @@
             } else if (KEY_WIFI_CONFIG.equals(key)) {
                 initWifiRestoreIfNecessary();
                 mWifiRestore.incorporateWifiConfigFile(data);
+            } else if (KEY_LOCK_SETTINGS.equals(key)) {
+                restoreLockSettings(data);
              } else {
                 data.skipEntityData();
             }
@@ -513,6 +531,7 @@
         byte[] systemSettingsData = getSystemSettings();
         byte[] secureSettingsData = getSecureSettings();
         byte[] globalSettingsData = getGlobalSettings();
+        byte[] lockSettingsData   = getLockSettings();
         byte[] locale = mSettingsHelper.getLocaleData();
         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
         byte[] wifiConfigData = getFileData(mWifiConfigFile);
@@ -547,6 +566,9 @@
             if (DEBUG_BACKUP) Log.d(TAG, wifiConfigData.length + " bytes of wifi config data");
             out.writeInt(wifiConfigData.length);
             out.write(wifiConfigData);
+            if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
+            out.writeInt(lockSettingsData.length);
+            out.write(lockSettingsData);
 
             out.flush();    // also flushes downstream
 
@@ -629,6 +651,16 @@
             in.readFully(buffer, 0, nBytes);
             restoreFileData(mWifiConfigFile, buffer, nBytes);
 
+            if (version >= FULL_BACKUP_ADDED_LOCK_SETTINGS) {
+                nBytes = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of lock settings data");
+                if (nBytes > buffer.length) buffer = new byte[nBytes];
+                if (nBytes > 0) {
+                    in.readFully(buffer, 0, nBytes);
+                    restoreLockSettings(buffer, nBytes);
+                }
+            }
+
             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
         } else {
             data.close();
@@ -676,6 +708,9 @@
             return oldChecksum;
         }
         try {
+            if (DEBUG_BACKUP) {
+                Log.v(TAG, "Writing entity " + key + " of size " + data.length);
+            }
             output.writeEntityHeader(key, data.length);
             output.writeEntityData(data, data.length);
         } catch (IOException ioe) {
@@ -714,6 +749,31 @@
         }
     }
 
+    /**
+     * Serialize the owner info settings
+     */
+    private byte[] getLockSettings() {
+        final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
+        final boolean ownerInfoEnabled = lockPatternUtils.isOwnerInfoEnabled();
+        final String ownerInfo = lockPatternUtils.getOwnerInfo(UserHandle.myUserId());
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream out = new DataOutputStream(baos);
+        try {
+            out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
+            out.writeUTF(ownerInfoEnabled ? "1" : "0");
+            if (ownerInfo != null) {
+                out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
+                out.writeUTF(ownerInfo != null ? ownerInfo : "");
+            }
+            // End marker
+            out.writeUTF("");
+            out.flush();
+        } catch (IOException ioe) {
+        }
+        return baos.toByteArray();
+    }
+
     private void restoreSettings(BackupDataInput data, Uri contentUri,
             HashSet<String> movedToGlobal) {
         byte[] settings = new byte[data.getDataSize()];
@@ -797,6 +857,50 @@
     }
 
     /**
+     * Restores the owner info enabled and owner info settings in LockSettings.
+     *
+     * @param buffer
+     * @param nBytes
+     */
+    private void restoreLockSettings(byte[] buffer, int nBytes) {
+        final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
+
+        ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
+        DataInputStream in = new DataInputStream(bais);
+        try {
+            String key;
+            // Read until empty string marker
+            while ((key = in.readUTF()).length() > 0) {
+                final String value = in.readUTF();
+                if (DEBUG_BACKUP) {
+                    Log.v(TAG, "Restoring lock_settings " + key + " = " + value);
+                }
+                switch (key) {
+                    case KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED:
+                        lockPatternUtils.setOwnerInfoEnabled("1".equals(value));
+                        break;
+                    case KEY_LOCK_SETTINGS_OWNER_INFO:
+                        lockPatternUtils.setOwnerInfo(value, UserHandle.myUserId());
+                        break;
+                }
+            }
+            in.close();
+        } catch (IOException ioe) {
+        }
+    }
+
+    private void restoreLockSettings(BackupDataInput data) {
+        final byte[] settings = new byte[data.getDataSize()];
+        try {
+            data.readEntityData(settings, 0, settings.length);
+        } catch (IOException ioe) {
+            Log.e(TAG, "Couldn't read entity data");
+            return;
+        }
+        restoreLockSettings(settings, settings.length);
+    }
+
+    /**
      * Given a cursor and a set of keys, extract the required keys and
      * values and write them to a byte array.
      *
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 808ded5..d8b6a82 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -217,8 +217,6 @@
     <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
     <string name="camera_label">open camera</string>
 
-    <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_ime_switch_button">Switch input method button.</string>
     <!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string>
 
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 912a181..b3b4651 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -626,6 +626,22 @@
                 pw.println("  voltage: " + mBatteryProps.batteryVoltage);
                 pw.println("  temperature: " + mBatteryProps.batteryTemperature);
                 pw.println("  technology: " + mBatteryProps.batteryTechnology);
+
+            } else if ("unplug".equals(args[0])) {
+                if (!mUpdatesStopped) {
+                    mLastBatteryProps.set(mBatteryProps);
+                }
+                mBatteryProps.chargerAcOnline = false;
+                mBatteryProps.chargerUsbOnline = false;
+                mBatteryProps.chargerWirelessOnline = false;
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    mUpdatesStopped = true;
+                    processValuesLocked(false);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+
             } else if (args.length == 3 && "set".equals(args[0])) {
                 String key = args[1];
                 String value = args[2];
@@ -662,6 +678,7 @@
                 } catch (NumberFormatException ex) {
                     pw.println("Bad value: " + value);
                 }
+
             } else if (args.length == 1 && "reset".equals(args[0])) {
                 long ident = Binder.clearCallingIdentity();
                 try {
@@ -676,6 +693,7 @@
             } else {
                 pw.println("Dump current battery state, or:");
                 pw.println("  set [ac|usb|wireless|status|level|invalid] <value>");
+                pw.println("  unplug");
                 pw.println("  reset");
             }
         }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 77b8b31..895a5c3 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.app.admin.DevicePolicyManager;
+import android.app.backup.BackupManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -46,6 +47,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.ILockSettings;
 import com.android.internal.widget.LockPatternUtils;
 
@@ -257,6 +259,9 @@
 
     private void setStringUnchecked(String key, int userId, String value) {
         mStorage.writeKeyValue(key, value, userId);
+        if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
+            BackupManager.dataChanged("com.android.providers.settings");
+        }
     }
 
     @Override
@@ -463,6 +468,11 @@
         Secure.LOCK_SCREEN_OWNER_INFO
     };
 
+    private static final String[] SETTINGS_TO_BACKUP = new String[] {
+        Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+        Secure.LOCK_SCREEN_OWNER_INFO
+    };
+
     private IMountService getMountService() {
         final IBinder service = ServiceManager.getService("mount");
         if (service != null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50820ff..4504ce4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2256,31 +2256,33 @@
             if (MONITOR_CPU_USAGE &&
                     mLastCpuTime.get() < (now-MONITOR_CPU_MIN_TIME)) {
                 mLastCpuTime.set(now);
-                haveNewCpuStats = true;
                 mProcessCpuTracker.update();
-                //Slog.i(TAG, mProcessCpu.printCurrentState());
-                //Slog.i(TAG, "Total CPU usage: "
-                //        + mProcessCpu.getTotalCpuPercent() + "%");
+                if (mProcessCpuTracker.hasGoodLastStats()) {
+                    haveNewCpuStats = true;
+                    //Slog.i(TAG, mProcessCpu.printCurrentState());
+                    //Slog.i(TAG, "Total CPU usage: "
+                    //        + mProcessCpu.getTotalCpuPercent() + "%");
 
-                // Slog the cpu usage if the property is set.
-                if ("true".equals(SystemProperties.get("events.cpu"))) {
-                    int user = mProcessCpuTracker.getLastUserTime();
-                    int system = mProcessCpuTracker.getLastSystemTime();
-                    int iowait = mProcessCpuTracker.getLastIoWaitTime();
-                    int irq = mProcessCpuTracker.getLastIrqTime();
-                    int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
-                    int idle = mProcessCpuTracker.getLastIdleTime();
+                    // Slog the cpu usage if the property is set.
+                    if ("true".equals(SystemProperties.get("events.cpu"))) {
+                        int user = mProcessCpuTracker.getLastUserTime();
+                        int system = mProcessCpuTracker.getLastSystemTime();
+                        int iowait = mProcessCpuTracker.getLastIoWaitTime();
+                        int irq = mProcessCpuTracker.getLastIrqTime();
+                        int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
+                        int idle = mProcessCpuTracker.getLastIdleTime();
 
-                    int total = user + system + iowait + irq + softIrq + idle;
-                    if (total == 0) total = 1;
+                        int total = user + system + iowait + irq + softIrq + idle;
+                        if (total == 0) total = 1;
 
-                    EventLog.writeEvent(EventLogTags.CPU,
-                            ((user+system+iowait+irq+softIrq) * 100) / total,
-                            (user * 100) / total,
-                            (system * 100) / total,
-                            (iowait * 100) / total,
-                            (irq * 100) / total,
-                            (softIrq * 100) / total);
+                        EventLog.writeEvent(EventLogTags.CPU,
+                                ((user+system+iowait+irq+softIrq) * 100) / total,
+                                (user * 100) / total,
+                                (system * 100) / total,
+                                (iowait * 100) / total,
+                                (irq * 100) / total,
+                                (softIrq * 100) / total);
+                    }
                 }
             }
 
@@ -2289,8 +2291,10 @@
             synchronized(bstats) {
                 synchronized(mPidsSelfLocked) {
                     if (haveNewCpuStats) {
-                        if (mOnBattery) {
-                            int perc = bstats.startAddingCpuLocked();
+                        final int perc = bstats.startAddingCpuLocked();
+                        if (perc >= 0) {
+                            int remainUTime = 0;
+                            int remainSTime = 0;
                             int totalUTime = 0;
                             int totalSTime = 0;
                             final int N = mProcessCpuTracker.countStats();
@@ -2302,17 +2306,18 @@
                                 ProcessRecord pr = mPidsSelfLocked.get(st.pid);
                                 int otherUTime = (st.rel_utime*perc)/100;
                                 int otherSTime = (st.rel_stime*perc)/100;
-                                totalUTime += otherUTime;
-                                totalSTime += otherSTime;
+                                remainUTime += otherUTime;
+                                remainSTime += otherSTime;
+                                totalUTime += st.rel_utime;
+                                totalSTime += st.rel_stime;
                                 if (pr != null) {
                                     BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
                                     if (ps == null || !ps.isActive()) {
                                         pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
                                                 pr.info.uid, pr.processName);
                                     }
-                                    ps.addCpuTimeLocked(st.rel_utime-otherUTime,
-                                            st.rel_stime-otherSTime);
-                                    ps.addSpeedStepTimes(cpuSpeedTimes);
+                                    ps.addCpuTimeLocked(st.rel_utime - otherUTime,
+                                            st.rel_stime - otherSTime, cpuSpeedTimes);
                                     pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
                                 } else {
                                     BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
@@ -2320,13 +2325,19 @@
                                         st.batteryStats = ps = bstats.getProcessStatsLocked(
                                                 bstats.mapUid(st.uid), st.name);
                                     }
-                                    ps.addCpuTimeLocked(st.rel_utime-otherUTime,
-                                            st.rel_stime-otherSTime);
-                                    ps.addSpeedStepTimes(cpuSpeedTimes);
+                                    ps.addCpuTimeLocked(st.rel_utime - otherUTime,
+                                            st.rel_stime - otherSTime, cpuSpeedTimes);
                                 }
                             }
-                            bstats.finishAddingCpuLocked(perc, totalUTime,
-                                    totalSTime, cpuSpeedTimes);
+                            final int userTime = mProcessCpuTracker.getLastUserTime();
+                            final int systemTime = mProcessCpuTracker.getLastSystemTime();
+                            final int iowaitTime = mProcessCpuTracker.getLastIoWaitTime();
+                            final int irqTime = mProcessCpuTracker.getLastIrqTime();
+                            final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
+                            final int idleTime = mProcessCpuTracker.getLastIdleTime();
+                            bstats.finishAddingCpuLocked(perc, remainUTime,
+                                    remainSTime, totalUTime, totalSTime, userTime, systemTime,
+                                    iowaitTime, irqTime, softIrqTime, idleTime, cpuSpeedTimes);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 50b2262..c5f4161 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -699,15 +699,14 @@
 
         private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
             sinks.clear();
-            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
+            ArrayList<AudioDevicePort> devicePorts = new ArrayList<AudioDevicePort>();
             if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
                 return;
             }
             int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
-            for (AudioPort port : devicePorts) {
-                AudioDevicePort devicePort = (AudioDevicePort) port;
-                if ((devicePort.type() & sinkDevice) != 0) {
-                    sinks.add(devicePort);
+            for (AudioDevicePort port : devicePorts) {
+                if ((port.type() & sinkDevice) != 0) {
+                    sinks.add(port);
                 }
             }
         }
@@ -716,14 +715,13 @@
             if (type == AudioManager.DEVICE_NONE) {
                 return null;
             }
-            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
+            ArrayList<AudioDevicePort> devicePorts = new ArrayList<AudioDevicePort>();
             if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
                 return null;
             }
-            for (AudioPort port : devicePorts) {
-                AudioDevicePort devicePort = (AudioDevicePort) port;
-                if (devicePort.type() == type && devicePort.address().equals(address)) {
-                    return devicePort;
+            for (AudioDevicePort port : devicePorts) {
+                if (port.type() == type && port.address().equals(address)) {
+                    return port;
                 }
             }
             return null;
diff --git a/services/core/java/com/android/server/wm/FocusedStackFrame.java b/services/core/java/com/android/server/wm/FocusedStackFrame.java
index f1f5fe8..826fe97 100644
--- a/services/core/java/com/android/server/wm/FocusedStackFrame.java
+++ b/services/core/java/com/android/server/wm/FocusedStackFrame.java
@@ -16,14 +16,14 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
+import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
 
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.util.Slog;
 import android.view.Display;
 import android.view.Surface.OutOfResourcesException;
@@ -35,14 +35,17 @@
 
 class FocusedStackFrame {
     private static final String TAG = "FocusedStackFrame";
-    private static final int THICKNESS = 10;
+    private static final boolean DEBUG = false;
+    private static final int THICKNESS = 2;
     private static final float ALPHA = 0.3f;
 
     private final SurfaceControl mSurfaceControl;
     private final Surface mSurface = new Surface();
+    private final Paint mInnerPaint = new Paint();
+    private final Paint mOuterPaint = new Paint();
+    private final Rect mBounds = new Rect();
     private final Rect mLastBounds = new Rect();
-    final Rect mBounds = new Rect();
-    private final Rect mTmpDrawRect = new Rect();
+    private int mLayer = -1;
 
     public FocusedStackFrame(Display display, SurfaceSession session) {
         SurfaceControl ctrl = null;
@@ -60,83 +63,84 @@
         } catch (OutOfResourcesException e) {
         }
         mSurfaceControl = ctrl;
+
+        mInnerPaint.setStyle(Paint.Style.STROKE);
+        mInnerPaint.setStrokeWidth(THICKNESS);
+        mInnerPaint.setColor(Color.WHITE);
+        mOuterPaint.setStyle(Paint.Style.STROKE);
+        mOuterPaint.setStrokeWidth(THICKNESS);
+        mOuterPaint.setColor(Color.BLACK);
     }
 
-    private void draw(Rect bounds, int color) {
-        if (false && DEBUG_STACK) Slog.i(TAG, "draw: bounds=" + bounds.toShortString() +
-                " color=" + Integer.toHexString(color));
-        mTmpDrawRect.set(bounds);
+    private void draw() {
+        if (mLastBounds.isEmpty()) {
+            // Currently unset. Set it.
+            mLastBounds.set(mBounds);
+        }
+
+        if (DEBUG) Slog.i(TAG, "draw: mBounds=" + mBounds + " mLastBounds=" + mLastBounds);
+
         Canvas c = null;
         try {
-            c = mSurface.lockCanvas(mTmpDrawRect);
+            c = mSurface.lockCanvas(mLastBounds);
         } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Unable to lock canvas", e);
         } catch (Surface.OutOfResourcesException e) {
+            Slog.e(TAG, "Unable to lock canvas", e);
         }
         if (c == null) {
+            if (DEBUG) Slog.w(TAG, "Canvas is null...");
             return;
         }
 
-        final int w = bounds.width();
-        final int h = bounds.height();
-
-        // Top
-        mTmpDrawRect.set(0, 0, w, THICKNESS);
-        c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
-        c.drawColor(color);
-        // Left (not including Top or Bottom stripe).
-        mTmpDrawRect.set(0, THICKNESS, THICKNESS, h - THICKNESS);
-        c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
-        c.drawColor(color);
-        // Right (not including Top or Bottom stripe).
-        mTmpDrawRect.set(w - THICKNESS, THICKNESS, w, h - THICKNESS);
-        c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
-        c.drawColor(color);
-        // Bottom
-        mTmpDrawRect.set(0, h - THICKNESS, w, h);
-        c.clipRect(mTmpDrawRect, Region.Op.REPLACE);
-        c.drawColor(color);
-
+        c.drawRect(0, 0, mBounds.width(), mBounds.height(), mOuterPaint);
+        c.drawRect(THICKNESS, THICKNESS, mBounds.width() - THICKNESS, mBounds.height() - THICKNESS,
+                mInnerPaint);
+        if (DEBUG) Slog.w(TAG, "c.width=" + c.getWidth() + " c.height=" + c.getHeight()
+                + " c.clip=" + c .getClipBounds());
         mSurface.unlockCanvasAndPost(c);
+        mLastBounds.set(mBounds);
     }
 
-    private void positionSurface(Rect bounds) {
-        if (false && DEBUG_STACK) Slog.i(TAG, "positionSurface: bounds=" + bounds.toShortString());
-        mSurfaceControl.setSize(bounds.width(), bounds.height());
-        mSurfaceControl.setPosition(bounds.left, bounds.top);
+    private void setupSurface(boolean visible) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setupSurface");
+        SurfaceControl.openTransaction();
+        try {
+            if (visible) {
+                mSurfaceControl.setPosition(mBounds.left, mBounds.top);
+                mSurfaceControl.setSize(mBounds.width(), mBounds.height());
+                mSurfaceControl.show();
+            } else {
+                mSurfaceControl.hide();
+            }
+        } finally {
+            SurfaceControl.closeTransaction();
+            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> CLOSE TRANSACTION setupSurface");
+        }
+    }
+
+    void setVisibility(TaskStack stack) {
+        if (stack == null || stack.isFullscreen()) {
+            setupSurface(false);
+        } else {
+            stack.getBounds(mBounds);
+            setupSurface(true);
+            if (!mBounds.equals(mLastBounds)) {
+                draw();
+            }
+        }
     }
 
     // Note: caller responsible for being inside
     // Surface.openTransaction() / closeTransaction()
-    public void setVisibility(boolean on) {
-        if (false && DEBUG_STACK) Slog.i(TAG, "setVisibility: on=" + on +
-                " mLastBounds=" + mLastBounds.toShortString() +
-                " mBounds=" + mBounds.toShortString());
-        if (mSurfaceControl == null) {
+    void setLayer(int layer) {
+        if (mLayer == layer) {
             return;
         }
-        if (on) {
-            if (!mLastBounds.equals(mBounds)) {
-                // Erase the previous rectangle.
-                positionSurface(mLastBounds);
-                draw(mLastBounds, Color.TRANSPARENT);
-                // Draw the latest rectangle.
-                positionSurface(mBounds);
-                draw(mBounds, Color.WHITE);
-                // Update the history.
-                mLastBounds.set(mBounds);
-            }
-            mSurfaceControl.show();
-        } else {
-            mSurfaceControl.hide();
-        }
-    }
-
-    public void setBounds(TaskStack stack) {
-        stack.getBounds(mBounds);
-        if (false && DEBUG_STACK) Slog.i(TAG, "setBounds: bounds=" + mBounds);
-    }
-
-    public void setLayer(int layer) {
-        mSurfaceControl.setLayer(layer);
+        mLayer = layer;
+        mSurfaceControl.setLayer(mLayer);
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 94fecc9..3e1c5ff 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -26,7 +26,6 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.TypedValue;
-import android.view.Display;
 import android.view.Surface;
 
 import com.android.server.EventLogTags;
@@ -371,7 +370,7 @@
             for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
                 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
                 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
-                    mService.removeWindowInnerLocked(null, appWindows.get(winNdx));
+                    mService.removeWindowInnerLocked(appWindows.get(winNdx));
                     doAnotherLayoutPass = true;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dd4bbb7..e238d30 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -64,11 +64,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
-import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
@@ -127,7 +125,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
-import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy;
@@ -136,7 +133,6 @@
 import android.view.WindowManagerPolicy.PointerEventListener;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
-import android.view.animation.Transformation;
 
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
@@ -181,7 +177,6 @@
     static final boolean DEBUG_CONFIGURATION = false;
     static final boolean DEBUG_APP_TRANSITIONS = false;
     static final boolean DEBUG_STARTING_WINDOW = false;
-    static final boolean DEBUG_REORDER = false;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
     static final boolean DEBUG_DRAG = false;
@@ -413,7 +408,7 @@
      * This is set when we have run out of memory, and will either be an empty
      * list or contain windows that need to be force removed.
      */
-    ArrayList<WindowState> mForceRemoves;
+    final ArrayList<WindowState> mForceRemoves = new ArrayList<>();
 
     /**
      * Windows that clients are waiting to have drawn.
@@ -1736,10 +1731,7 @@
         }
     }
 
-    static final int ADJUST_WALLPAPER_LAYERS_CHANGED = 1<<1;
-    static final int ADJUST_WALLPAPER_VISIBILITY_CHANGED = 1<<2;
-
-    int adjustWallpaperWindowsLocked() {
+    boolean adjustWallpaperWindowsLocked() {
         mInnerFields.mWallpaperMayChange = false;
         boolean targetChanged = false;
 
@@ -1966,13 +1958,12 @@
 
         // Start stepping backwards from here, ensuring that our wallpaper windows
         // are correctly placed.
-        int changed = 0;
+        boolean changed = false;
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
             WindowToken token = mWallpaperTokens.get(curTokenNdx);
             if (token.hidden == visible) {
                 if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
                         "Wallpaper token " + token + " hidden=" + !visible);
-                changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED;
                 token.hidden = !visible;
                 // Need to do a layout to ensure the wallpaper now has the correct size.
                 getDefaultDisplayContentLocked().layoutNeeded = true;
@@ -2033,7 +2024,7 @@
 
                 windows.add(insertionIndex, wallpaper);
                 mWindowsChanged = true;
-                changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
+                changed = true;
             }
         }
 
@@ -2654,7 +2645,7 @@
             }
         }
 
-        removeWindowInnerLocked(session, win);
+        removeWindowInnerLocked(win);
         // Removing a visible window will effect the computed orientation
         // So just update orientation if needed.
         if (wasVisible && updateOrientationFromAppTokensLocked(false)) {
@@ -2664,7 +2655,7 @@
         Binder.restoreCallingIdentity(origId);
     }
 
-    void removeWindowInnerLocked(Session session, WindowState win) {
+    void removeWindowInnerLocked(WindowState win) {
         if (win.mRemoved) {
             // Nothing to do.
             return;
@@ -2674,7 +2665,7 @@
             WindowState cwin = win.mChildWindows.get(i);
             Slog.w(TAG, "Force-removing child win " + cwin + " from container "
                     + win);
-            removeWindowInnerLocked(cwin.mSession, cwin);
+            removeWindowInnerLocked(cwin);
         }
 
         win.mRemoved = true;
@@ -3781,6 +3772,9 @@
 
     private Configuration updateOrientationFromAppTokensLocked(
             Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
+        if (!mDisplayReady) {
+            return null;
+        }
         Configuration config = null;
 
         if (updateOrientationFromAppTokensLocked(false)) {
@@ -3799,20 +3793,19 @@
             // the value of the previous configuration.
             mTempConfiguration.setToDefaults();
             mTempConfiguration.fontScale = currentConfig.fontScale;
-            if (computeScreenConfigurationLocked(mTempConfiguration)) {
-                if (currentConfig.diff(mTempConfiguration) != 0) {
-                    mWaitingForConfig = true;
-                    final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                    displayContent.layoutNeeded = true;
-                    int anim[] = new int[2];
-                    if (displayContent.isDimming()) {
-                        anim[0] = anim[1] = 0;
-                    } else {
-                        mPolicy.selectRotationAnimationLw(anim);
-                    }
-                    startFreezingDisplayLocked(false, anim[0], anim[1]);
-                    config = new Configuration(mTempConfiguration);
+            computeScreenConfigurationLocked(mTempConfiguration);
+            if (currentConfig.diff(mTempConfiguration) != 0) {
+                mWaitingForConfig = true;
+                final DisplayContent displayContent = getDefaultDisplayContentLocked();
+                displayContent.layoutNeeded = true;
+                int anim[] = new int[2];
+                if (displayContent.isDimming()) {
+                    anim[0] = anim[1] = 0;
+                } else {
+                    mPolicy.selectRotationAnimationLw(anim);
                 }
+                startFreezingDisplayLocked(false, anim[0], anim[1]);
+                config = new Configuration(mTempConfiguration);
             }
         }
 
@@ -3936,20 +3929,7 @@
         } else {
             stack = null;
         }
-        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setFocusedStackFrame");
-        SurfaceControl.openTransaction();
-        try {
-            if (stack == null) {
-                mFocusedStackFrame.setVisibility(false);
-            } else {
-                mFocusedStackFrame.setBounds(stack);
-                final boolean multipleStacks = !stack.isFullscreen();
-                mFocusedStackFrame.setVisibility(multipleStacks);
-            }
-        } finally {
-            SurfaceControl.closeTransaction();
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> CLOSE TRANSACTION setFocusedStackFrame");
-        }
+        mFocusedStackFrame.setVisibility(stack);
     }
 
     @Override
@@ -6917,9 +6897,11 @@
 
     public Configuration computeNewConfiguration() {
         synchronized (mWindowMap) {
+            if (!mDisplayReady) {
+                return null;
+            }
             Configuration config = computeNewConfigurationLocked();
-            if (config == null && mWaitingForConfig) {
-                // Nothing changed but we are waiting for something... stop that!
+            if (mWaitingForConfig) {
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
                 performLayoutAndPlaceSurfacesLocked();
@@ -6931,9 +6913,7 @@
     Configuration computeNewConfigurationLocked() {
         Configuration config = new Configuration();
         config.fontScale = 0;
-        if (!computeScreenConfigurationLocked(config)) {
-            return null;
-        }
+        computeScreenConfigurationLocked(config);
         return config;
     }
 
@@ -7040,11 +7020,8 @@
         return sw;
     }
 
+    /** Do not call if mDisplayReady == false */
     DisplayInfo updateDisplayAndOrientationLocked() {
-        if (!mDisplayReady) {
-            return null;
-        }
-
         // TODO(multidisplay): For now, apply Configuration to main screen only.
         final DisplayContent displayContent = getDefaultDisplayContentLocked();
 
@@ -7101,11 +7078,9 @@
         return displayInfo;
     }
 
-    boolean computeScreenConfigurationLocked(Configuration config) {
+    /** Do not call if mDisplayReady == false */
+    void computeScreenConfigurationLocked(Configuration config) {
         final DisplayInfo displayInfo = updateDisplayAndOrientationLocked();
-        if (displayInfo == null) {
-            return false;
-        }
 
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
@@ -7190,8 +7165,6 @@
         config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
         config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
         mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
-
-        return true;
     }
 
     public boolean isHardKeyboardAvailable() {
@@ -8320,17 +8293,17 @@
     // displayContent must not be null
     private void reconfigureDisplayLocked(DisplayContent displayContent) {
         // TODO: Multidisplay: for now only use with default display.
+        if (!mDisplayReady) {
+            return;
+        }
         configureDisplayPolicyLocked(displayContent);
         displayContent.layoutNeeded = true;
 
         boolean configChanged = updateOrientationFromAppTokensLocked(false);
         mTempConfiguration.setToDefaults();
         mTempConfiguration.fontScale = mCurConfiguration.fontScale;
-        if (computeScreenConfigurationLocked(mTempConfiguration)) {
-            if (mCurConfiguration.diff(mTempConfiguration) != 0) {
-                configChanged = true;
-            }
-        }
+        computeScreenConfigurationLocked(mTempConfiguration);
+        configChanged |= mCurConfiguration.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
             mWaitingForConfig = true;
@@ -8631,29 +8604,24 @@
 
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
         mInLayout = true;
-        boolean recoveringMemory = false;
 
-        try {
-            if (mForceRemoves != null) {
-                recoveringMemory = true;
-                // Wait a little bit for things to settle down, and off we go.
-                for (int i=0; i<mForceRemoves.size(); i++) {
-                    WindowState ws = mForceRemoves.get(i);
-                    Slog.i(TAG, "Force removing: " + ws);
-                    removeWindowInnerLocked(ws.mSession, ws);
-                }
-                mForceRemoves = null;
-                Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
-                Object tmp = new Object();
-                synchronized (tmp) {
-                    try {
-                        tmp.wait(250);
-                    } catch (InterruptedException e) {
-                    }
+        boolean recoveringMemory = false;
+        if (!mForceRemoves.isEmpty()) {
+            recoveringMemory = true;
+            // Wait a little bit for things to settle down, and off we go.
+            while (!mForceRemoves.isEmpty()) {
+                WindowState ws = mForceRemoves.remove(0);
+                Slog.i(TAG, "Force removing: " + ws);
+                removeWindowInnerLocked(ws);
+            }
+            Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
+            Object tmp = new Object();
+            synchronized (tmp) {
+                try {
+                    tmp.wait(250);
+                } catch (InterruptedException e) {
                 }
             }
-        } catch (RuntimeException e) {
-            Slog.wtf(TAG, "Unhandled exception while force removing for memory", e);
         }
 
         try {
@@ -9329,14 +9297,12 @@
 
     /**
      * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
-     *
-     * @param w WindowState this method is applied to.
-     * @param currentTime The time which animations use for calculating transitions.
+     *  @param w WindowState this method is applied to.
      * @param innerDw Width of app window.
      * @param innerDh Height of app window.
      */
-    private void handleNotObscuredLocked(final WindowState w, final long currentTime,
-                                         final int innerDw, final int innerDh) {
+    private void handleNotObscuredLocked(final WindowState w,
+            final int innerDw, final int innerDh) {
         final WindowManager.LayoutParams attrs = w.mAttrs;
         final int attrFlags = attrs.flags;
         final boolean canBeSeen = w.isDisplayedLw();
@@ -9455,8 +9421,6 @@
                     + Debug.getCallers(3));
         }
 
-        final long currentTime = SystemClock.uptimeMillis();
-
         int i;
         boolean updateInputWindowsNeeded = false;
 
@@ -9547,8 +9511,7 @@
 
                     if ((displayContent.pendingLayoutChanges &
                             WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
-                            (adjustWallpaperWindowsLocked() &
-                                    ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+                            adjustWallpaperWindowsLocked()) {
                         assignLayersLocked(windows);
                         displayContent.layoutNeeded = true;
                     }
@@ -9613,7 +9576,7 @@
                     // Update effect.
                     w.mObscured = mInnerFields.mObscured;
                     if (!mInnerFields.mObscured) {
-                        handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
+                        handleNotObscuredLocked(w, innerDw, innerDh);
                     }
 
                     if (stack != null && !stack.testDimmingTag()) {
@@ -9992,7 +9955,7 @@
             DisplayContentList displayList = new DisplayContentList();
             for (i = 0; i < N; i++) {
                 WindowState w = mPendingRemoveTmp[i];
-                removeWindowInnerLocked(w.mSession, w);
+                removeWindowInnerLocked(w);
                 final DisplayContent displayContent = w.getDisplayContent();
                 if (displayContent != null && !displayList.contains(displayContent)) {
                     displayList.add(displayContent);
@@ -10168,10 +10131,6 @@
         EventLog.writeEvent(EventLogTags.WM_NO_SURFACE_MEMORY, winAnimator.mWin.toString(),
                 winAnimator.mSession.mPid, operation);
 
-        if (mForceRemoves == null) {
-            mForceRemoves = new ArrayList<WindowState>();
-        }
-
         long callingIdentity = Binder.clearCallingIdentity();
         try {
             // There was some problem...   first, do a sanity check of the
@@ -10353,6 +10312,10 @@
                 + ", flags=" + win.mAttrs.flags
                 + ", canReceive=" + win.canReceiveKeys());
 
+            if (!win.canReceiveKeys()) {
+                continue;
+            }
+
             AppWindowToken wtoken = win.mAppToken;
 
             // If this window's application has been removed, just skip it.
@@ -10362,10 +10325,6 @@
                 continue;
             }
 
-            if (!win.canReceiveKeys()) {
-                continue;
-            }
-
             // Descend through all of the app tokens and find the first that either matches
             // win.mAppToken (return win) or mFocusedApp (return null).
             if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING &&
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
index f0e02bc..57ed876 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
@@ -39,7 +39,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -320,5 +322,23 @@
             this.admin = admin;
             this.packageName = admin.getPackageName();
         }
+        public void dump(String prefix, PrintWriter pw) {
+            pw.println(prefix + "admin=" + admin);
+            pw.println(prefix + "name=" + name);
+            pw.println();
+        }
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        if (mDeviceOwner != null) {
+            pw.println(prefix + "Device Owner: ");
+            mDeviceOwner.dump(prefix + "  ", pw);
+        }
+        if (mProfileOwners != null) {
+            for (Map.Entry<Integer, OwnerInfo> entry : mProfileOwners.entrySet()) {
+                pw.println(prefix + "Profile Owner (User " + entry.getKey() + "): ");
+                entry.getValue().dump(prefix + "  ", pw);
+            }
+        }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8170835..770da5b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4139,7 +4139,9 @@
 
         synchronized (this) {
             p.println("Current Device Policy Manager state:");
-
+            if (mDeviceOwner != null) {
+                mDeviceOwner.dump("  ", pw);
+            }
             int userCount = mUserData.size();
             for (int u = 0; u < userCount; u++) {
                 DevicePolicyData policy = getUserData(mUserData.keyAt(u));
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 570f5fd..4600b72 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -80,6 +80,9 @@
      * ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
      * collide with values generated on other phones or after a data wipe of a given phone.
      *
+     * Important: A non-unique identifier could cause non-deterministic call-log backup/restore
+     * behavior.
+     *
      * @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
      */
     public String getId() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 94691c0..dda9626 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.telecom.PhoneAccount;
 import android.util.Log;
 
 import com.android.internal.telecom.ITelecomService;
@@ -3765,7 +3766,7 @@
 
    /**
     * Returns the IMS Registration Status
-    *@hide
+    * @hide
     */
    public boolean isImsRegistered() {
        try {
@@ -4116,4 +4117,21 @@
                     ServiceState.rilRadioTechnologyToString(type));
         }
     }
+
+    /**
+     * Returns the subscription ID for the given phone account.
+     * @hide
+     */
+    public int getSubIdForPhoneAccount(PhoneAccount phoneAccount) {
+        int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                retval = service.getSubIdForPhoneAccount(phoneAccount);
+            }
+        } catch (RemoteException e) {
+        }
+
+        return retval;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bf3ee09..62c8746 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.telecom.PhoneAccount;
 import android.telephony.CellInfo;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.NeighboringCellInfo;
@@ -879,4 +880,9 @@
       *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
       */
     String getDeviceId();
+
+    /**
+     * Returns the subscription ID associated with the specified PhoneAccount.
+     */
+    int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
 }