Merge "Lightweight checkin output for network stats." into lmp-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index d25ae96..f6c3c17 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34459,6 +34459,7 @@
     method public final android.content.Context getContext();
     method public abstract android.view.View getCurrentFocus();
     method public abstract android.view.View getDecorView();
+    method public static int getDefaultFeatures(android.content.Context);
     method public android.transition.Transition getEnterTransition();
     method public android.transition.Transition getExitTransition();
     method protected final int getFeatures();
@@ -34560,7 +34561,7 @@
     method public abstract void takeKeyEvents(boolean);
     method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
     method public abstract void togglePanel(int, android.view.KeyEvent);
-    field protected static final int DEFAULT_FEATURES = 65; // 0x41
+    field protected static final deprecated int DEFAULT_FEATURES = 65; // 0x41
     field public static final int FEATURE_ACTION_BAR = 8; // 0x8
     field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
     field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 2a17fa6..d56dc1e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -23,8 +23,8 @@
  */
 public abstract class ActivityManagerInternal {
     // Called by the power manager.
-    public abstract void goingToSleep();
-    public abstract void wakingUp();
+    public abstract void onWakefulnessChanged(int wakefulness);
+
     public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
             String processName, String abiOverride, int uid, Runnable crashHandler);
 }
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 9d78360..6f31768 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -25,6 +25,58 @@
  */
 public abstract class PowerManagerInternal {
     /**
+     * Wakefulness: The device is asleep.  It can only be awoken by a call to wakeUp().
+     * The screen should be off or in the process of being turned off by the display controller.
+     * The device typically passes through the dozing state first.
+     */
+    public static final int WAKEFULNESS_ASLEEP = 0;
+
+    /**
+     * Wakefulness: The device is fully awake.  It can be put to sleep by a call to goToSleep().
+     * When the user activity timeout expires, the device may start dreaming or go to sleep.
+     */
+    public static final int WAKEFULNESS_AWAKE = 1;
+
+    /**
+     * Wakefulness: The device is dreaming.  It can be awoken by a call to wakeUp(),
+     * which ends the dream.  The device goes to sleep when goToSleep() is called, when
+     * the dream ends or when unplugged.
+     * User activity may brighten the screen but does not end the dream.
+     */
+    public static final int WAKEFULNESS_DREAMING = 2;
+
+    /**
+     * Wakefulness: The device is dozing.  It is almost asleep but is allowing a special
+     * low-power "doze" dream to run which keeps the display on but lets the application
+     * processor be suspended.  It can be awoken by a call to wakeUp() which ends the dream.
+     * The device fully goes to sleep if the dream cannot be started or ends on its own.
+     */
+    public static final int WAKEFULNESS_DOZING = 3;
+
+    public static String wakefulnessToString(int wakefulness) {
+        switch (wakefulness) {
+            case WAKEFULNESS_ASLEEP:
+                return "Asleep";
+            case WAKEFULNESS_AWAKE:
+                return "Awake";
+            case WAKEFULNESS_DREAMING:
+                return "Dreaming";
+            case WAKEFULNESS_DOZING:
+                return "Dozing";
+            default:
+                return Integer.toString(wakefulness);
+        }
+    }
+
+    /**
+     * Returns true if the wakefulness state represents an interactive state
+     * as defined by {@link android.os.PowerManager#isInteractive}.
+     */
+    public static boolean isInteractive(int wakefulness) {
+        return wakefulness == WAKEFULNESS_AWAKE || wakefulness == WAKEFULNESS_DREAMING;
+    }
+
+    /**
      * Used by the window manager to override the screen brightness based on the
      * current foreground activity.
      *
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0076abf..2e5c1e0 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
@@ -155,6 +156,7 @@
             "android:navigation:background";
 
     /** The default features enabled */
+    @Deprecated
     @SuppressWarnings({"PointlessBitwiseExpression"})
     protected static final int DEFAULT_FEATURES = (1 << FEATURE_OPTIONS_PANEL) |
             (1 << FEATURE_CONTEXT_MENU);
@@ -183,8 +185,8 @@
     private boolean mSetCloseOnTouchOutside = false;
     private int mForcedWindowFlags = 0;
 
-    private int mFeatures = DEFAULT_FEATURES;
-    private int mLocalFeatures = DEFAULT_FEATURES;
+    private int mFeatures;
+    private int mLocalFeatures;
 
     private boolean mHaveWindowFormat = false;
     private boolean mHaveDimAmount = false;
@@ -442,6 +444,7 @@
 
     public Window(Context context) {
         mContext = context;
+        mFeatures = mLocalFeatures = getDefaultFeatures(context);
     }
 
     /**
@@ -1270,6 +1273,25 @@
     }
 
     /**
+     * Return the feature bits set by default on a window.
+     * @param context The context used to access resources
+     */
+    public static int getDefaultFeatures(Context context) {
+        int features = 0;
+
+        final Resources res = context.getResources();
+        if (res.getBoolean(com.android.internal.R.bool.config_defaultWindowFeatureOptionsPanel)) {
+            features |= 1 << FEATURE_OPTIONS_PANEL;
+        }
+
+        if (res.getBoolean(com.android.internal.R.bool.config_defaultWindowFeatureContextMenu)) {
+            features |= 1 << FEATURE_CONTEXT_MENU;
+        }
+
+        return features;
+    }
+
+    /**
      * Query for the availability of a certain feature.
      *
      * @param feature The feature ID to check
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index defc26c..161ae7e 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -1613,7 +1613,11 @@
         equivalent to the single-source shortest paths problem on a digraph, for
         which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
         */
-        private void solve(Arc[] arcs, int[] locations) {
+        private boolean solve(Arc[] arcs, int[] locations) {
+            return solve(arcs, locations, true);
+        }
+
+        private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
             String axisName = horizontal ? "horizontal" : "vertical";
             int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
             boolean[] originalCulprits = null;
@@ -1631,10 +1635,14 @@
                         if (originalCulprits != null) {
                             logError(axisName, arcs, originalCulprits);
                         }
-                        return;
+                        return true;
                     }
                 }
 
+                if (!modifyOnError) {
+                    return false; // cannot solve with these constraints
+                }
+
                 boolean[] culprits = new boolean[arcs.length];
                 for (int i = 0; i < N; i++) {
                     for (int j = 0, length = arcs.length; j < length; j++) {
@@ -1658,6 +1666,7 @@
                     }
                 }
             }
+            return true;
         }
 
         private void computeMargins(boolean leading) {
@@ -1697,8 +1706,8 @@
             return trailingMargins;
         }
 
-        private void solve(int[] a) {
-            solve(getArcs(), a);
+        private boolean solve(int[] a) {
+            return solve(getArcs(), a);
         }
 
         private boolean computeHasWeights() {
@@ -1740,28 +1749,18 @@
             return deltas;
         }
 
-        private void shareOutDelta() {
-            int totalDelta = 0;
-            float totalWeight = 0;
+        private void shareOutDelta(int totalDelta, float totalWeight) {
+            Arrays.fill(deltas, 0);
             for (int i = 0, N = getChildCount(); i < N; i++) {
                 View c = getChildAt(i);
                 LayoutParams lp = getLayoutParams(c);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                 float weight = spec.weight;
                 if (weight != 0) {
-                    int delta = getMeasurement(c, horizontal) - getOriginalMeasurements()[i];
-                    totalDelta += delta;
-                    totalWeight += weight;
-                }
-            }
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                LayoutParams lp = getLayoutParams(getChildAt(i));
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                float weight = spec.weight;
-                if (weight != 0) {
                     int delta = Math.round((weight * totalDelta / totalWeight));
                     deltas[i] = delta;
-                    // the two adjustments below are to counter the above rounding and avoid off-by-ones at the end
+                    // the two adjustments below are to counter the above rounding and avoid
+                    // off-by-ones at the end
                     totalDelta -= delta;
                     totalWeight -= weight;
                 }
@@ -1771,12 +1770,46 @@
         private void solveAndDistributeSpace(int[] a) {
             Arrays.fill(getDeltas(), 0);
             solve(a);
-            shareOutDelta();
-            arcsValid = false;
-            forwardLinksValid = false;
-            backwardLinksValid = false;
-            groupBoundsValid = false;
-            solve(a);
+            int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
+            if (deltaMax < 2) {
+                return; //don't have any delta to distribute
+            }
+            int deltaMin = 0; //inclusive
+
+            float totalWeight = calculateTotalWeight();
+
+            int validDelta = -1; //delta for which a solution exists
+            boolean validSolution = true;
+            // do a binary search to find the max delta that won't conflict with constraints
+            while(deltaMin < deltaMax) {
+                final int delta = (deltaMin + deltaMax) / 2;
+                invalidateValues();
+                shareOutDelta(delta, totalWeight);
+                validSolution = solve(getArcs(), a, false);
+                if (validSolution) {
+                    validDelta = delta;
+                    deltaMin = delta + 1;
+                } else {
+                    deltaMax = delta;
+                }
+            }
+            if (validDelta > 0 && !validSolution) {
+                // last solution was not successful but we have a successful one. Use it.
+                invalidateValues();
+                shareOutDelta(validDelta, totalWeight);
+                solve(a);
+            }
+        }
+
+        private float calculateTotalWeight() {
+            float totalWeight = 0f;
+            for (int i = 0, N = getChildCount(); i < N; i++) {
+                View c = getChildAt(i);
+                LayoutParams lp = getLayoutParams(c);
+                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
+                totalWeight += spec.weight;
+            }
+            return totalWeight;
         }
 
         private void computeLocations(int[] a) {
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 6052fb0..3eede32 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -40,4 +40,7 @@
          Reduced intentionally for watches to retain minimal memory footprint -->
     <integer name="config_notificationServiceArchiveSize">1</integer>
 
+    <!-- Flags enabling default window features. See Window.java -->
+    <bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
+    <bool name="config_defaultWindowFeatureContextMenu">false</bool>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6e635f31..bb2ee48 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1927,6 +1927,10 @@
     <!-- Show the next-alarm as a zen exit condition if it occurs in the next n hours. -->
     <integer name="config_next_alarm_condition_lookahead_threshold_hrs">12</integer>
 
+    <!-- Flags enabling default window features. See Window.java -->
+    <bool name="config_defaultWindowFeatureOptionsPanel">true</bool>
+    <bool name="config_defaultWindowFeatureContextMenu">true</bool>
+
     <!-- This config is used to check if the carrier requires converting destination
          number before sending out a SMS.
          Formats for this configuration as below:
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 25ead90..7bb57ce 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2120,6 +2120,9 @@
   <java-symbol type="bool" name="config_switch_phone_on_voice_reg_state_change" />
   <java-symbol type="string" name="whichHomeApplicationNamed" />
   <java-symbol type="bool" name="config_sms_force_7bit_encoding" />
+  <java-symbol type="bool" name="config_defaultWindowFeatureOptionsPanel" />
+  <java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
+
   <java-symbol type="layout" name="simple_account_item" />
   <java-symbol type="id" name="scrollIndicatorUp" />
   <java-symbol type="id" name="scrollIndicatorDown" />
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index a136acf..fbe3712 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -240,12 +240,16 @@
     <string name="kg_pattern_instructions">Draw your pattern</string>
     <!-- Instructions for using the SIM PIN unlock screen -->
     <string name="kg_sim_pin_instructions">Enter SIM PIN</string>
+    <!-- Instructions for using the SIM PIN unlock screen when there's more than one SIM -->
+    <string name="kg_sim_pin_instructions_multi">Enter SIM PIN for \"<xliff:g id="carrier" example="CARD 1">%1$s</xliff:g>\"</string>
     <!-- Instructions for using the PIN unlock screen -->
     <string name="kg_pin_instructions">Enter PIN</string>
     <!-- Instructions for using the password unlock screen -->
     <string name="kg_password_instructions">Enter Password</string>
     <!-- Hint shown in the PUK screen that asks the user to enter the PUK code given to them by their provider -->
     <string name="kg_puk_enter_puk_hint">SIM is now disabled. Enter PUK code to continue. Contact carrier for details.</string>
+    <!-- Hint shown when there are multiple SIMs in the device to ask the user to enter the PUK code given to them by their provider -->
+    <string name="kg_puk_enter_puk_hint_multi">SIM \"<xliff:g id="carrier" example="CARD 1">%1$s</xliff:g>\" is now disabled. Enter PUK code to continue. Contact carrier for details.</string>
     <!-- Hint shown in the PUK unlock screen PIN TextView -->
     <string name="kg_puk_enter_pin_hint">Enter desired PIN code</string>
     <!-- Message shown when the user needs to confirm the PIN they just entered in the PUK screen -->
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index ad07a7a..55bfe49 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -48,7 +48,7 @@
         }
 
         @Override
-        public void onSimStateChanged(IccCardConstants.State simState) {
+        public void onSimStateChanged(int subId, int slotId, State simState) {
             mSimState = simState;
             updateCarrierText(mSimState, mPlmn, mSpn);
         }
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index e0507a8..50ac261 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -36,22 +36,18 @@
  * allows the user to return to the call.
  */
 public class EmergencyButton extends Button {
-
-    private static final int EMERGENCY_CALL_TIMEOUT = 10000; // screen timeout after starting e.d.
     private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
 
     KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
         @Override
-        public void onSimStateChanged(State simState) {
-            int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState();
-            updateEmergencyCallButton(simState, phoneState);
+        public void onSimStateChanged(int subId, int slotId, State simState) {
+            updateEmergencyCallButton();
         }
 
         @Override
         public void onPhoneStateChanged(int phoneState) {
-            State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState();
-            updateEmergencyCallButton(simState, phoneState);
+            updateEmergencyCallButton();
         }
     };
     private LockPatternUtils mLockPatternUtils;
@@ -87,9 +83,7 @@
                 takeEmergencyCallAction();
             }
         });
-        int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState();
-        State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState();
-        updateEmergencyCallButton(simState, phoneState);
+        updateEmergencyCallButton();
     }
 
     /**
@@ -112,12 +106,12 @@
         }
     }
 
-    private void updateEmergencyCallButton(State simState, int phoneState) {
+    private void updateEmergencyCallButton() {
         boolean enabled = false;
         if (mLockPatternUtils.isInCall()) {
             enabled = true; // always show "return to call" if phone is off-hook
         } else if (mLockPatternUtils.isEmergencyCallCapable()) {
-            boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimLocked();
+            final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimPinVoiceSecure();
             if (simLocked) {
                 // Some countries can't handle emergency calls while SIM is locked.
                 enabled = mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java b/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java
index 77643bd..10baf23 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java
@@ -25,5 +25,6 @@
      * Turns on debugging information for the whole Keyguard. This is very verbose and should only
      * be used temporarily for debugging.
      */
-    public static final boolean DEBUG = false;
+    public static final boolean DEBUG = true;
+    public static final boolean DEBUG_SIM_STATES = true;
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
index 3166ad4..a5d260d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -17,11 +17,15 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.widget.LockPatternUtils;
 
+import java.util.List;
+
 public class KeyguardSecurityModel {
 
     /**
@@ -75,12 +79,13 @@
     }
 
     SecurityMode getSecurityMode() {
-        KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-        final IccCardConstants.State simState = updateMonitor.getSimState();
+        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
         SecurityMode mode = SecurityMode.None;
-        if (simState == IccCardConstants.State.PIN_REQUIRED) {
+        if (monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED)
+                != SubscriptionManager.INVALID_SUB_ID) {
             mode = SecurityMode.SimPin;
-        } else if (simState == IccCardConstants.State.PUK_REQUIRED
+        } else if (monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED)
+                != SubscriptionManager.INVALID_SUB_ID
                 && mLockPatternUtils.isPukUnlockScreenEnable()) {
             mode = SecurityMode.SimPuk;
         } else {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index 5a0fdb2..d8feaf8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -17,15 +17,21 @@
 package com.android.keyguard;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.telephony.PhoneConstants;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.app.AlertDialog;
 import android.app.AlertDialog.Builder;
 import android.app.Dialog;
 import android.app.ProgressDialog;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.WindowManager;
@@ -35,13 +41,22 @@
  */
 public class KeyguardSimPinView extends KeyguardPinBasedInputView {
     private static final String LOG_TAG = "KeyguardSimPinView";
-    private static final boolean DEBUG = KeyguardConstants.DEBUG;
+    private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
     public static final String TAG = "KeyguardSimPinView";
 
     private ProgressDialog mSimUnlockProgressDialog = null;
     private CheckSimPin mCheckSimPinThread;
 
     private AlertDialog mRemainingAttemptsDialog;
+    private int mSubId;
+
+    KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onSimStateChanged(int subId, int slotId, State simState) {
+           if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+           resetState();
+       };
+    };
 
     public KeyguardSimPinView(Context context) {
         this(context, null);
@@ -53,7 +68,22 @@
 
     public void resetState() {
         super.resetState();
-        mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true);
+        if (DEBUG) Log.v(TAG, "Resetting state");
+        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+        mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED);
+        if (mSubId != SubscriptionManager.INVALID_SUB_ID) {
+            int count = TelephonyManager.getDefault().getSimCount();
+            Resources rez = getResources();
+            final String msg;
+            if (count < 2) {
+                msg = rez.getString(R.string.kg_sim_pin_instructions);
+            } else {
+                SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId);
+                CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
+                msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+            }
+            mSecurityMessageDisplay.setMessage(msg, true);
+        }
     }
 
     private String getPinPasswordErrorMessage(int attemptsRemaining) {
@@ -95,6 +125,18 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback);
+    }
+
+    @Override
     public void showUsabilityHint() {
     }
 
@@ -113,9 +155,11 @@
      */
     private abstract class CheckSimPin extends Thread {
         private final String mPin;
+        private int mSubId;
 
-        protected CheckSimPin(String pin) {
+        protected CheckSimPin(String pin, int subId) {
             mPin = pin;
+            mSubId = subId;
         }
 
         abstract void onSimCheckResponse(final int result, final int attemptsRemaining);
@@ -123,10 +167,14 @@
         @Override
         public void run() {
             try {
-                Log.v(TAG, "call supplyPinReportResult()");
+                if (DEBUG) {
+                    Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
+                }
                 final int[] result = ITelephony.Stub.asInterface(ServiceManager
-                        .checkService("phone")).supplyPinReportResult(mPin);
-                Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
+                        .checkService("phone")).supplyPinReportResultForSubscriber(mSubId, mPin);
+                if (DEBUG) {
+                    Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
+                }
                 post(new Runnable() {
                     public void run() {
                         onSimCheckResponse(result[0], result[1]);
@@ -187,15 +235,17 @@
         getSimUnlockProgressDialog().show();
 
         if (mCheckSimPinThread == null) {
-            mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText()) {
+            mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
                 void onSimCheckResponse(final int result, final int attemptsRemaining) {
                     post(new Runnable() {
                         public void run() {
                             if (mSimUnlockProgressDialog != null) {
                                 mSimUnlockProgressDialog.hide();
                             }
+                            resetPasswordText(true /* animate */);
                             if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
-                                KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
+                                KeyguardUpdateMonitor.getInstance(getContext())
+                                        .reportSimUnlocked(mSubId);
                                 mCallback.dismiss(true);
                             } else {
                                 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
@@ -216,7 +266,6 @@
                                 if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
                                         + " CheckSimPin.onSimCheckResponse: " + result
                                         + " attemptsRemaining=" + attemptsRemaining);
-                                resetPasswordText(true /* animate */);
                             }
                             mCallback.userActivity();
                             mCheckSimPinThread = null;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
index f0c5805..c5d940c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -17,18 +17,24 @@
 package com.android.keyguard;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.ProgressDialog;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.WindowManager;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.IccCardConstants.State;
 
 
 /**
@@ -45,6 +51,23 @@
     private String mPinText;
     private StateMachine mStateMachine = new StateMachine();
     private AlertDialog mRemainingAttemptsDialog;
+    private int mSubId;
+
+    KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onSimStateChanged(int subId, int slotId, State simState) {
+           if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+           resetState();
+       };
+    };
+
+    public KeyguardSimPukView(Context context) {
+        this(context, null);
+    }
+
+    public KeyguardSimPukView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
 
     private class StateMachine {
         final int ENTER_PUK = 0;
@@ -89,7 +112,21 @@
             mPinText="";
             mPukText="";
             state = ENTER_PUK;
-            mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true);
+            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+            mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED);
+            if (mSubId != SubscriptionManager.INVALID_SUB_ID) {
+                int count = TelephonyManager.getDefault().getSimCount();
+                Resources rez = getResources();
+                final String msg;
+                if (count < 2) {
+                    msg = rez.getString(R.string.kg_puk_enter_puk_hint);
+                } else {
+                    SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId);
+                    CharSequence displayName = info != null ? info.getDisplayName() : "";
+                    msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+                }
+                mSecurityMessageDisplay.setMessage(msg, true);
+            }
             mPasswordEntry.requestFocus();
         }
     }
@@ -111,14 +148,6 @@
         return displayMessage;
     }
 
-    public KeyguardSimPukView(Context context) {
-        this(context, null);
-    }
-
-    public KeyguardSimPukView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
     public void resetState() {
         super.resetState();
         mStateMachine.reset();
@@ -146,6 +175,18 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback);
+    }
+
+    @Override
     public void showUsabilityHint() {
     }
 
@@ -165,10 +206,12 @@
     private abstract class CheckSimPuk extends Thread {
 
         private final String mPin, mPuk;
+        private final int mSubId;
 
-        protected CheckSimPuk(String puk, String pin) {
+        protected CheckSimPuk(String puk, String pin, int subId) {
             mPuk = puk;
             mPin = pin;
+            mSubId = subId;
         }
 
         abstract void onSimLockChangedResponse(final int result, final int attemptsRemaining);
@@ -176,10 +219,12 @@
         @Override
         public void run() {
             try {
-                Log.v(TAG, "call supplyPukReportResult()");
+                if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
                 final int[] result = ITelephony.Stub.asInterface(ServiceManager
-                        .checkService("phone")).supplyPukReportResult(mPuk, mPin);
-                Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]);
+                    .checkService("phone")).supplyPukReportResultForSubscriber(mSubId, mPuk, mPin);
+                if (DEBUG) {
+                    Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]);
+                }
                 post(new Runnable() {
                     public void run() {
                         onSimLockChangedResponse(result[0], result[1]);
@@ -254,15 +299,17 @@
         getSimUnlockProgressDialog().show();
 
         if (mCheckSimPukThread == null) {
-            mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText) {
+            mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
                 void onSimLockChangedResponse(final int result, final int attemptsRemaining) {
                     post(new Runnable() {
                         public void run() {
                             if (mSimUnlockProgressDialog != null) {
                                 mSimUnlockProgressDialog.hide();
                             }
+                            resetPasswordText(true /* animate */);
                             if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
-                                KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
+                                KeyguardUpdateMonitor.getInstance(getContext())
+                                        .reportSimUnlocked(mSubId);
                                 mCallback.dismiss(true);
                             } else {
                                 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index aa5819e..48b2eac 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -51,11 +51,17 @@
 import android.provider.Settings;
 
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
 
 import android.service.fingerprint.FingerprintManager;
 import android.service.fingerprint.FingerprintManagerReceiver;
 import android.service.fingerprint.FingerprintUtils;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionListener;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.util.SparseBooleanArray;
@@ -64,6 +70,9 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map.Entry;
 
 /**
  * Watches for updates that may be interesting to the keyguard, and provides
@@ -79,7 +88,7 @@
 
     private static final String TAG = "KeyguardUpdateMonitor";
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
-    private static final boolean DEBUG_SIM_STATES = DEBUG || false;
+    private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
     private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3;
     private static final int LOW_BATTERY_THRESHOLD = 20;
 
@@ -113,13 +122,13 @@
     private static final int MSG_FINGERPRINT_PROCESSED = 323;
     private static final int MSG_FINGERPRINT_ACQUIRED = 324;
     private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 325;
+    private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 326;
 
     private static KeyguardUpdateMonitor sInstance;
 
     private final Context mContext;
+    HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
 
-    // Telephony state
-    private IccCardConstants.State mSimState = IccCardConstants.State.READY;
     private CharSequence mTelephonyPlmn;
     private CharSequence mTelephonySpn;
     private int mRingMode;
@@ -149,6 +158,7 @@
     private boolean mSwitchingUser;
 
     private boolean mScreenOn;
+    protected List<SubscriptionInfo> mSubscriptionInfo;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -164,7 +174,7 @@
                     handleCarrierInfoUpdate();
                     break;
                 case MSG_SIM_STATE_CHANGE:
-                    handleSimStateChange((SimArgs) msg.obj);
+                    handleSimStateChange(msg.arg1, msg.arg2, (State) msg.obj);
                     break;
                 case MSG_RINGER_MODE_CHANGED:
                     handleRingerModeChange(msg.arg1);
@@ -220,10 +230,20 @@
                 case MSG_FACE_UNLOCK_STATE_CHANGED:
                     handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2);
                     break;
+                case MSG_SIM_SUBSCRIPTION_INFO_CHANGED:
+                    handleSimSubscriptionInfoChanged();
+                    break;
             }
         }
     };
 
+    private SubscriptionListener mSubscriptionListener = new SubscriptionListener() {
+        @Override
+        public void onSubscriptionInfoChanged() {
+            mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
+        }
+    };
+
     private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
     private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
     private SparseBooleanArray mUserFingerprintRecognized = new SparseBooleanArray();
@@ -244,6 +264,40 @@
         }
     }
 
+    protected void handleSimSubscriptionInfoChanged() {
+        if (DEBUG_SIM_STATES) {
+            Log.v(TAG, "onSubscriptionInfoChanged()");
+            for (SubscriptionInfo subInfo : SubscriptionManager.getActiveSubscriptionInfoList()) {
+                Log.v(TAG, "SubInfo:" + subInfo);
+            }
+        }
+        List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
+
+        // Hack level over 9000: Because the subscription id is not yet valid when we see the
+        // first update in handleSimStateChange, we need to force refresh all all SIM states
+        // so the subscription id for them is consistent.
+        for (int i = 0; i < subscriptionInfos.size(); i++) {
+            SubscriptionInfo info = subscriptionInfos.get(i);
+            refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex());
+        }
+        for (int i = 0; i < subscriptionInfos.size(); i++) {
+            SimData data = mSimDatas.get(mSubscriptionInfo.get(i).getSubscriptionId());
+            for (int j = 0; j < mCallbacks.size(); j++) {
+                KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
+                if (cb != null) {
+                    cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                }
+            }
+        }
+    }
+
+    List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
+        if (mSubscriptionInfo == null || forceReload) {
+            mSubscriptionInfo = SubscriptionManager.getActiveSubscriptionInfoList();
+        }
+        return mSubscriptionInfo;
+    }
+
     @Override
     public void onTrustManagedChanged(boolean managed, int userId) {
         mUserTrustIsManaged.put(userId, managed);
@@ -382,12 +436,14 @@
                         MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health));
                 mHandler.sendMessage(msg);
             } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+                SimData args = SimData.fromIntent(intent);
                 if (DEBUG_SIM_STATES) {
-                    Log.v(TAG, "action " + action + " state" +
-                        intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE));
+                    Log.v(TAG, "action " + action
+                        + " state: " + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)
+                        + " slotId: " + args.slotId + " subid: " + args.subId);
                 }
-                mHandler.sendMessage(mHandler.obtainMessage(
-                        MSG_SIM_STATE_CHANGE, SimArgs.fromIntent(intent)));
+                mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, args.subId, args.slotId, args.simState)
+                        .sendToTarget();
             } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED,
                         intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
@@ -449,19 +505,26 @@
      * we need a single object to pass to the handler.  This class helps decode
      * the intent and provide a {@link SimCard.State} result.
      */
-    private static class SimArgs {
-        public final IccCardConstants.State simState;
+    private static class SimData {
+        public State simState;
+        public int slotId;
+        public int subId;
 
-        SimArgs(IccCardConstants.State state) {
+        SimData(State state, int slot, int id) {
             simState = state;
+            slotId = slot;
+            subId = id;
         }
 
-        static SimArgs fromIntent(Intent intent) {
-            IccCardConstants.State state;
+        static SimData fromIntent(Intent intent) {
+            State state;
             if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
                 throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
             }
             String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+            int slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, 0);
+            int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                    SubscriptionManager.INVALID_SUB_ID);
             if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
                 final String absentReason = intent
                     .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
@@ -494,11 +557,11 @@
             } else {
                 state = IccCardConstants.State.UNKNOWN;
             }
-            return new SimArgs(state);
+            return new SimData(state, slotId, subId);
         }
 
         public String toString() {
-            return simState.toString();
+            return "SimData{state=" + simState + ",slotId=" + slotId + ",subId=" + subId + "}";
         }
     }
 
@@ -605,7 +668,6 @@
         }
 
         // Take a guess at initial SIM state, battery status and PLMN until we get an update
-        mSimState = IccCardConstants.State.NOT_READY;
         mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0);
         mTelephonyPlmn = getDefaultPlmn();
 
@@ -636,6 +698,8 @@
         context.registerReceiverAsUser(mBroadcastAllReceiver, UserHandle.ALL, allUserFilter,
                 null, null);
 
+        SubscriptionManager.register(mContext, mSubscriptionListener,
+                SubscriptionListener.LISTEN_SUBSCRIPTION_INFO_LIST_CHANGED);
         try {
             ActivityManagerNative.getDefault().registerUserSwitchObserver(
                     new IUserSwitchObserver.Stub() {
@@ -881,20 +945,35 @@
     /**
      * Handle {@link #MSG_SIM_STATE_CHANGE}
      */
-    private void handleSimStateChange(SimArgs simArgs) {
-        final IccCardConstants.State state = simArgs.simState;
+    private void handleSimStateChange(int subId, int slotId, State state) {
 
-        if (DEBUG) {
-            Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " "
-                    + "state resolved to " + state.toString());
+        if (DEBUG_SIM_STATES) {
+            Log.d(TAG, "handleSimStateChange(subId=" + subId + ", slotId="
+                    + slotId + ", state=" + state +")");
         }
 
-        if (state != IccCardConstants.State.UNKNOWN && state != mSimState) {
-            mSimState = state;
+        if (subId == SubscriptionManager.INVALID_SUB_ID) {
+            Log.w(TAG, "invalid subId in handleSimStateChange()");
+            return;
+        }
+
+        SimData data = mSimDatas.get(subId);
+        final boolean changed;
+        if (data == null) {
+            data = new SimData(state, slotId, subId);
+            mSimDatas.put(subId, data);
+            changed = true; // no data yet; force update
+        } else {
+            changed = (data.simState != state || data.subId != subId || data.slotId != slotId);
+            data.simState = state;
+            data.subId = subId;
+            data.slotId = slotId;
+        }
+        if (changed && state != State.UNKNOWN) {
             for (int i = 0; i < mCallbacks.size(); i++) {
                 KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
                 if (cb != null) {
-                    cb.onSimStateChanged(state);
+                    cb.onSimStateChanged(subId, slotId, state);
                 }
             }
         }
@@ -1070,7 +1149,10 @@
         callback.onPhoneStateChanged(mPhoneState);
         callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
         callback.onClockVisibilityChanged();
-        callback.onSimStateChanged(mSimState);
+        for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
+            final SimData state = data.getValue();
+            callback.onSimStateChanged(state.subId, state.slotId, state.simState);
+        }
     }
 
     public void sendKeyguardVisibilityChanged(boolean showing) {
@@ -1095,10 +1177,6 @@
         mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget();
     }
 
-    public IccCardConstants.State getSimState() {
-        return mSimState;
-    }
-
     /**
      * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we
      * have the information earlier than waiting for the intent
@@ -1107,8 +1185,10 @@
      * NOTE: Because handleSimStateChange() invokes callbacks immediately without going
      * through mHandler, this *must* be called from the UI thread.
      */
-    public void reportSimUnlocked() {
-        handleSimStateChange(new SimArgs(IccCardConstants.State.READY));
+    public void reportSimUnlocked(int subId) {
+        if (DEBUG_SIM_STATES) Log.v(TAG, "reportSimUnlocked(subId=" + subId + ")");
+        int slotId = SubscriptionManager.getSlotId(subId);
+        handleSimStateChange(subId, slotId, State.READY);
     }
 
     /**
@@ -1184,18 +1264,44 @@
         mAlternateUnlockEnabled = enabled;
     }
 
-    public boolean isSimLocked() {
-        return isSimLocked(mSimState);
-    }
-
-    public static boolean isSimLocked(IccCardConstants.State state) {
-        return state == IccCardConstants.State.PIN_REQUIRED
-        || state == IccCardConstants.State.PUK_REQUIRED
-        || state == IccCardConstants.State.PERM_DISABLED;
+    public boolean isSimPinVoiceSecure() {
+        // TODO: only count SIMs that handle voice
+        return isSimPinSecure();
     }
 
     public boolean isSimPinSecure() {
-        return isSimPinSecure(mSimState);
+        // True if any SIM is pin secure
+        for (SubscriptionInfo info : getSubscriptionInfo(false /* forceReload */)) {
+            if (isSimPinSecure(getSimState(info.getSubscriptionId()))) return true;
+        }
+        return false;
+    }
+
+    private State getSimState(int subId) {
+        if (mSimDatas.containsKey(subId)) {
+            return mSimDatas.get(subId).simState;
+        } else {
+            return State.UNKNOWN;
+        }
+    }
+
+    private void refreshSimState(int subId, int slotId) {
+
+        // This is awful. It exists because there are two APIs for getting the SIM status
+        // that don't return the complete set of values and have different types. In Keyguard we
+        // need IccCardConstants, but TelephonyManager would only give us
+        // TelephonyManager.SIM_STATE*, so we retrieve it manually.
+        final int phoneId = SubscriptionManager.getPhoneId(subId);
+        final String stateString = TelephonyManager.getTelephonyProperty(phoneId,
+                TelephonyProperties.PROPERTY_SIM_STATE, "");
+        State state;
+        try {
+            state = State.valueOf(stateString);
+        } catch(IllegalArgumentException ex) {
+            Log.w(TAG, "Unknown sim state: " + stateString);
+            state = State.UNKNOWN;
+        }
+        mSimDatas.put(subId, new SimData(state, slotId, subId));
     }
 
     public static boolean isSimPinSecure(IccCardConstants.State state) {
@@ -1228,4 +1334,34 @@
     public boolean isScreenOn() {
         return mScreenOn;
     }
+
+    /**
+     * Find the next SubscriptionId for a SIM in the given state, favoring lower slot numbers first.
+     * @param state
+     * @return subid or {@link SubscriptionManager#INVALID_SUB_ID} if none found
+     */
+    public int getNextSubIdForState(State state) {
+        List<SubscriptionInfo> list = getSubscriptionInfo(false /* forceReload */);
+        int resultId = SubscriptionManager.INVALID_SUB_ID;
+        int bestSlotId = Integer.MAX_VALUE; // Favor lowest slot first
+        for (int i = 0; i < list.size(); i++) {
+            final SubscriptionInfo info = list.get(i);
+            final int id = info.getSubscriptionId();
+            int slotId = SubscriptionManager.getSlotId(id);
+            if (state == getSimState(id) && bestSlotId > slotId ) {
+                resultId = id;
+                bestSlotId = slotId;
+            }
+        }
+        return resultId;
+    }
+
+    public SubscriptionInfo getSubscriptionInfoForSubId(int subId) {
+        List<SubscriptionInfo> list = getSubscriptionInfo(false /* forceReload */);
+        for (int i = 0; i < list.size(); i++) {
+            SubscriptionInfo info = list.get(i);
+            if (subId == info.getSubscriptionId()) return info;
+        }
+        return null; // not found
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 2b40903..de72ddd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -121,9 +121,10 @@
 
     /**
      * Called when the SIM state changes.
+     * @param slotId
      * @param simState
      */
-    public void onSimStateChanged(IccCardConstants.State simState) { }
+    public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) { }
 
     /**
      * Called when a user is removed.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 5f92dc6..8213127 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -43,6 +43,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.EventLog;
 import android.util.Log;
@@ -57,6 +58,7 @@
 import com.android.internal.policy.IKeyguardShowCallback;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardDisplayManager;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -116,7 +118,8 @@
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
 
-    final static boolean DEBUG = false;
+    private static final boolean DEBUG = KeyguardConstants.DEBUG;
+    private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
     private final static boolean DBG_WAKE = false;
 
     private final static String TAG = "KeyguardViewMediator";
@@ -360,8 +363,12 @@
         }
 
         @Override
-        public void onSimStateChanged(IccCardConstants.State simState) {
-            if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState);
+        public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) {
+
+            if (DEBUG_SIM_STATES) {
+                Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId
+                        + ",state=" + simState + ")");
+            }
 
             switch (simState) {
                 case NOT_READY:
@@ -371,7 +378,7 @@
                     synchronized (this) {
                         if (shouldWaitForProvisioning()) {
                             if (!isShowing()) {
-                                if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
+                                if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing,"
                                         + " we need to show the keyguard since the "
                                         + "device isn't provisioned yet.");
                                 doKeyguardLocked(null);
@@ -385,7 +392,8 @@
                 case PUK_REQUIRED:
                     synchronized (this) {
                         if (!isShowing()) {
-                            if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
+                            if (DEBUG_SIM_STATES) Log.d(TAG,
+                                    "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
                                     + "showing; need to show keyguard so user can enter sim pin");
                             doKeyguardLocked(null);
                         } else {
@@ -396,11 +404,11 @@
                 case PERM_DISABLED:
                     synchronized (this) {
                         if (!isShowing()) {
-                            if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
+                            if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED and "
                                   + "keygaurd isn't showing.");
                             doKeyguardLocked(null);
                         } else {
-                            if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+                            if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
                                   + "show permanently disabled message in lockscreen.");
                             resetStateLocked();
                         }
@@ -413,6 +421,9 @@
                         }
                     }
                     break;
+                default:
+                    if (DEBUG_SIM_STATES) Log.v(TAG, "Ignoring state: " + simState);
+                    break;
             }
         }
 
@@ -913,13 +924,13 @@
         }
 
         // if the setup wizard hasn't run yet, don't show
-        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
-                false);
-        final IccCardConstants.State state = mUpdateMonitor.getSimState();
-        final boolean lockedOrMissing = state.isPinLocked()
-                || ((state == IccCardConstants.State.ABSENT
-                || state == IccCardConstants.State.PERM_DISABLED)
-                && requireSim);
+        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
+        final boolean absent = mUpdateMonitor.getNextSubIdForState(
+                IccCardConstants.State.ABSENT) != SubscriptionManager.INVALID_SUB_ID;
+        final boolean disabled = mUpdateMonitor.getNextSubIdForState(
+                IccCardConstants.State.PERM_DISABLED) != SubscriptionManager.INVALID_SUB_ID;
+        final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
+                || ((absent || disabled) && requireSim);
 
         if (!lockedOrMissing && shouldWaitForProvisioning()) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f0fb9e6..40563fe 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -173,6 +173,7 @@
 import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -958,9 +959,9 @@
     private boolean mRunningVoice = false;
 
     /**
-     * State of external calls telling us if the device is asleep.
+     * State of external calls telling us if the device is awake or asleep.
      */
-    private boolean mWentToSleep = false;
+    private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
 
     static final int LOCK_SCREEN_HIDDEN = 0;
     static final int LOCK_SCREEN_LEAVING = 1;
@@ -6061,6 +6062,7 @@
                 mWindowManager.keyguardWaitingForActivityDrawn();
                 if (mLockScreenShown == LOCK_SCREEN_SHOWN) {
                     mLockScreenShown = LOCK_SCREEN_LEAVING;
+                    updateSleepIfNeededLocked();
                 }
             }
         } finally {
@@ -9925,32 +9927,55 @@
         return mSleeping;
     }
 
-    void goingToSleep() {
+    void onWakefulnessChanged(int wakefulness) {
         synchronized(this) {
-            mWentToSleep = true;
-            goToSleepIfNeededLocked();
+            mWakefulness = wakefulness;
+            updateSleepIfNeededLocked();
         }
     }
 
     void finishRunningVoiceLocked() {
         if (mRunningVoice) {
             mRunningVoice = false;
-            goToSleepIfNeededLocked();
+            updateSleepIfNeededLocked();
         }
     }
 
-    void goToSleepIfNeededLocked() {
-        if (mWentToSleep && !mRunningVoice) {
-            if (!mSleeping) {
-                mSleeping = true;
-                mStackSupervisor.goingToSleepLocked();
+    void updateSleepIfNeededLocked() {
+        if (mSleeping && !shouldSleepLocked()) {
+            mSleeping = false;
+            mStackSupervisor.comeOutOfSleepIfNeededLocked();
+        } else if (!mSleeping && shouldSleepLocked()) {
+            mSleeping = true;
+            mStackSupervisor.goingToSleepLocked();
 
-                // Initialize the wake times of all processes.
-                checkExcessivePowerUsageLocked(false);
-                mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
-                mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
-            }
+            // Initialize the wake times of all processes.
+            checkExcessivePowerUsageLocked(false);
+            mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+            Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+            mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
+        }
+    }
+
+    private boolean shouldSleepLocked() {
+        // Resume applications while running a voice interactor.
+        if (mRunningVoice) {
+            return false;
+        }
+
+        switch (mWakefulness) {
+            case PowerManagerInternal.WAKEFULNESS_AWAKE:
+            case PowerManagerInternal.WAKEFULNESS_DREAMING:
+                // If we're interactive but applications are already paused then defer
+                // resuming them until the lock screen is hidden.
+                return mSleeping && mLockScreenShown != LOCK_SCREEN_HIDDEN;
+            case PowerManagerInternal.WAKEFULNESS_DOZING:
+                // If we're dozing then pause applications whenever the lock screen is shown.
+                return mLockScreenShown != LOCK_SCREEN_HIDDEN;
+            case PowerManagerInternal.WAKEFULNESS_ASLEEP:
+            default:
+                // If we're asleep then pause applications unconditionally.
+                return true;
         }
     }
 
@@ -10016,31 +10041,16 @@
     }
 
     void logLockScreen(String msg) {
-        if (DEBUG_LOCKSCREEN) Slog.d(TAG, Debug.getCallers(2) + ":" + msg +
-                " mLockScreenShown=" + lockScreenShownToString() + " mWentToSleep=" +
-                mWentToSleep + " mSleeping=" + mSleeping);
-    }
-
-    void comeOutOfSleepIfNeededLocked() {
-        if ((!mWentToSleep && mLockScreenShown == LOCK_SCREEN_HIDDEN) || mRunningVoice) {
-            if (mSleeping) {
-                mSleeping = false;
-                mStackSupervisor.comeOutOfSleepIfNeededLocked();
-            }
-        }
-    }
-
-    void wakingUp() {
-        synchronized(this) {
-            mWentToSleep = false;
-            comeOutOfSleepIfNeededLocked();
-        }
+        if (DEBUG_LOCKSCREEN) Slog.d(TAG, Debug.getCallers(2) + ":" + msg
+                + " mLockScreenShown=" + lockScreenShownToString() + " mWakefulness="
+                + PowerManagerInternal.wakefulnessToString(mWakefulness)
+                + " mSleeping=" + mSleeping);
     }
 
     void startRunningVoiceLocked() {
         if (!mRunningVoice) {
             mRunningVoice = true;
-            comeOutOfSleepIfNeededLocked();
+            updateSleepIfNeededLocked();
         }
     }
 
@@ -10060,7 +10070,7 @@
             try {
                 if (DEBUG_LOCKSCREEN) logLockScreen(" shown=" + shown);
                 mLockScreenShown = shown ? LOCK_SCREEN_SHOWN : LOCK_SCREEN_HIDDEN;
-                comeOutOfSleepIfNeededLocked();
+                updateSleepIfNeededLocked();
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -12830,13 +12840,11 @@
             }
         }
         if (dumpPackage == null) {
-            if (mSleeping || mWentToSleep || mLockScreenShown != LOCK_SCREEN_HIDDEN) {
-                pw.println("  mSleeping=" + mSleeping + " mWentToSleep=" + mWentToSleep
-                        + " mLockScreenShown " + lockScreenShownToString());
-            }
-            if (mShuttingDown || mRunningVoice) {
-                pw.print("  mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
-            }
+            pw.println("  mWakefulness="
+                    + PowerManagerInternal.wakefulnessToString(mWakefulness));
+            pw.println("  mSleeping=" + mSleeping + " mLockScreenShown="
+                    + lockScreenShownToString());
+            pw.print("  mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
         }
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
@@ -19204,13 +19212,8 @@
 
     private final class LocalService extends ActivityManagerInternal {
         @Override
-        public void goingToSleep() {
-            ActivityManagerService.this.goingToSleep();
-        }
-
-        @Override
-        public void wakingUp() {
-            ActivityManagerService.this.wakingUp();
+        public void onWakefulnessChanged(int wakefulness) {
+            ActivityManagerService.this.onWakefulnessChanged(wakefulness);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c8b7205..5a948f9 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1477,7 +1477,7 @@
             mStackSupervisor.inResumeTopActivity = true;
             if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
                 mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
-                mService.comeOutOfSleepIfNeededLocked();
+                mService.updateSleepIfNeededLocked();
             }
             result = resumeTopActivityInnerLocked(prev, options);
         } finally {
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index 2562ffc..7f6971f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.annotation.Nullable;
+import android.os.Build;
 import android.os.SystemClock;
 import android.util.Pair;
 import android.util.Slog;
@@ -41,7 +42,7 @@
     // Logging duration for same error message.
     private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000;  // 20s
 
-    private static final boolean DEBUG = false;
+    private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
 
     private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>();
 
@@ -82,7 +83,7 @@
     }
 
     private void debugInternal(String logMessage) {
-        if (!DEBUG) {
+        if (IS_USER_BUILD) {
             return;
         }
         Slog.d(TAG, logMessage);
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 94a628d..1349926 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -38,6 +38,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -70,9 +71,9 @@
 
     private static final boolean DEBUG = false;
 
-    private static final int POWER_STATE_UNKNOWN = 0;
-    private static final int POWER_STATE_AWAKE = 1;
-    private static final int POWER_STATE_ASLEEP = 2;
+    private static final int INTERACTIVE_STATE_UNKNOWN = 0;
+    private static final int INTERACTIVE_STATE_AWAKE = 1;
+    private static final int INTERACTIVE_STATE_ASLEEP = 2;
 
     private static final int MSG_USER_ACTIVITY = 1;
     private static final int MSG_BROADCAST = 2;
@@ -92,17 +93,17 @@
     private final Intent mScreenOnIntent;
     private final Intent mScreenOffIntent;
 
-    // The current power state.
-    private int mActualPowerState;
-    private int mLastGoToSleepReason;
+    // The current interactive state.
+    private int mActualInteractiveState;
+    private int mLastReason;
 
     // True if there is a pending transition that needs to be reported.
     private boolean mPendingWakeUpBroadcast;
     private boolean mPendingGoToSleepBroadcast;
 
-    // The currently broadcasted power state.  This reflects what other parts of the
+    // The currently broadcasted interactive state.  This reflects what other parts of the
     // system have observed.
-    private int mBroadcastedPowerState;
+    private int mBroadcastedInteractiveState;
     private boolean mBroadcastInProgress;
     private long mBroadcastStartTime;
 
@@ -236,62 +237,83 @@
     }
 
     /**
-     * Notifies that the device is changing interactive state.
+     * Notifies that the device is changing wakefulness.
      */
-    public void onInteractiveStateChangeStarted(boolean interactive, final int reason) {
+    public void onWakefulnessChangeStarted(int wakefulness, int reason) {
         if (DEBUG) {
-            Slog.d(TAG, "onInteractiveChangeStarted: interactive=" + interactive
+            Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
                     + ", reason=" + reason);
         }
 
+        // We handle interactive state changes once they start so that the system can
+        // set everything up or the user to begin interacting with applications.
+        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
+        if (interactive) {
+            handleWakefulnessChange(wakefulness, interactive, reason);
+        } else {
+            mLastReason = reason;
+        }
+
+        // Start input as soon as we start waking up or going to sleep.
+        mInputManagerInternal.setInteractive(interactive);
+    }
+
+    /**
+     * Notifies that the device has finished changing wakefulness.
+     */
+    public void onWakefulnessChangeFinished(int wakefulness) {
+        if (DEBUG) {
+            Slog.d(TAG, "onWakefulnessChangeFinished: wakefulness=" + wakefulness);
+        }
+
+        // Handle interactive state changes once they are finished so that the system can
+        // finish pending transitions (such as turning the screen off) before causing
+        // applications to change state visibly.
+        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
+        if (!interactive) {
+            handleWakefulnessChange(wakefulness, interactive, mLastReason);
+        }
+    }
+
+    private void handleWakefulnessChange(final int wakefulness, boolean interactive,
+            final int reason) {
+        // Tell the activity manager about changes in wakefulness, not just interactivity.
+        // It needs more granularity than other components.
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mActivityManagerInternal.onWakefulnessChanged(wakefulness);
+            }
+        });
+
+        // Handle changes in the overall interactive state.
+        boolean interactiveChanged = false;
         synchronized (mLock) {
+            // Broadcast interactive state changes.
             if (interactive) {
                 // Waking up...
-                if (mActualPowerState != POWER_STATE_AWAKE) {
-                    mActualPowerState = POWER_STATE_AWAKE;
+                interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_AWAKE);
+                if (interactiveChanged) {
+                    mActualInteractiveState = INTERACTIVE_STATE_AWAKE;
                     mPendingWakeUpBroadcast = true;
                     mHandler.post(new Runnable() {
                         @Override
                         public void run() {
                             EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
                             mPolicy.wakingUp();
-                            mActivityManagerInternal.wakingUp();
                         }
                     });
                     updatePendingBroadcastLocked();
                 }
             } else {
                 // Going to sleep...
-                mLastGoToSleepReason = reason;
-            }
-        }
-
-        mInputManagerInternal.setInteractive(interactive);
-
-        if (interactive) {
-            try {
-                mBatteryStats.noteInteractive(true);
-            } catch (RemoteException ex) { }
-        }
-    }
-
-    /**
-     * Notifies that the device has finished changing interactive state.
-     */
-    public void onInteractiveStateChangeFinished(boolean interactive) {
-        if (DEBUG) {
-            Slog.d(TAG, "onInteractiveChangeFinished");
-        }
-
-        synchronized (mLock) {
-            if (!interactive) {
-                // Finished going to sleep...
                 // This is a good time to make transitions that we don't want the user to see,
                 // such as bringing the key guard to focus.  There's no guarantee for this,
                 // however because the user could turn the device on again at any time.
                 // Some things may need to be protected by other mechanisms that defer screen on.
-                if (mActualPowerState != POWER_STATE_ASLEEP) {
-                    mActualPowerState = POWER_STATE_ASLEEP;
+                interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_ASLEEP);
+                if (interactiveChanged) {
+                    mActualInteractiveState = INTERACTIVE_STATE_ASLEEP;
                     mPendingGoToSleepBroadcast = true;
                     if (mUserActivityPending) {
                         mUserActivityPending = false;
@@ -301,7 +323,7 @@
                         @Override
                         public void run() {
                             int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
-                            switch (mLastGoToSleepReason) {
+                            switch (reason) {
                                 case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
                                     why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
                                     break;
@@ -311,7 +333,6 @@
                             }
                             EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
                             mPolicy.goingToSleep(why);
-                            mActivityManagerInternal.goingToSleep();
                         }
                     });
                     updatePendingBroadcastLocked();
@@ -319,9 +340,10 @@
             }
         }
 
-        if (!interactive) {
+        // Notify battery stats.
+        if (interactiveChanged) {
             try {
-                mBatteryStats.noteInteractive(false);
+                mBatteryStats.noteInteractive(interactive);
             } catch (RemoteException ex) { }
         }
     }
@@ -366,9 +388,9 @@
 
     private void updatePendingBroadcastLocked() {
         if (!mBroadcastInProgress
-                && mActualPowerState != POWER_STATE_UNKNOWN
+                && mActualInteractiveState != INTERACTIVE_STATE_UNKNOWN
                 && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
-                        || mActualPowerState != mBroadcastedPowerState)) {
+                        || mActualInteractiveState != mBroadcastedInteractiveState)) {
             mBroadcastInProgress = true;
             mSuspendBlocker.acquire();
             Message msg = mHandler.obtainMessage(MSG_BROADCAST);
@@ -396,16 +418,16 @@
     private void sendNextBroadcast() {
         final int powerState;
         synchronized (mLock) {
-            if (mBroadcastedPowerState == POWER_STATE_UNKNOWN) {
+            if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
                 // Broadcasted power state is unknown.  Send wake up.
                 mPendingWakeUpBroadcast = false;
-                mBroadcastedPowerState = POWER_STATE_AWAKE;
-            } else if (mBroadcastedPowerState == POWER_STATE_AWAKE) {
+                mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
+            } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
                 // Broadcasted power state is awake.  Send asleep if needed.
                 if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
-                        || mActualPowerState == POWER_STATE_ASLEEP) {
+                        || mActualInteractiveState == INTERACTIVE_STATE_ASLEEP) {
                     mPendingGoToSleepBroadcast = false;
-                    mBroadcastedPowerState = POWER_STATE_ASLEEP;
+                    mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
                 } else {
                     finishPendingBroadcastLocked();
                     return;
@@ -413,9 +435,9 @@
             } else {
                 // Broadcasted power state is asleep.  Send awake if needed.
                 if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
-                        || mActualPowerState == POWER_STATE_AWAKE) {
+                        || mActualInteractiveState == INTERACTIVE_STATE_AWAKE) {
                     mPendingWakeUpBroadcast = false;
-                    mBroadcastedPowerState = POWER_STATE_AWAKE;
+                    mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
                 } else {
                     finishPendingBroadcastLocked();
                     return;
@@ -423,12 +445,12 @@
             }
 
             mBroadcastStartTime = SystemClock.uptimeMillis();
-            powerState = mBroadcastedPowerState;
+            powerState = mBroadcastedInteractiveState;
         }
 
         EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
 
-        if (powerState == POWER_STATE_AWAKE) {
+        if (powerState == INTERACTIVE_STATE_AWAKE) {
             sendWakeUpBroadcast();
         } else {
             sendGoToSleepBroadcast();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4d8b98f..4d1ab4c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -61,7 +61,6 @@
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
 import android.util.EventLog;
-import android.util.Log;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.view.Display;
@@ -73,6 +72,11 @@
 
 import libcore.util.Objects;
 
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
+
 /**
  * The power manager service is responsible for coordinating power management
  * functions on the device.
@@ -116,24 +120,6 @@
     // Dirty bit: brightness boost changed
     private static final int DIRTY_SCREEN_BRIGHTNESS_BOOST = 1 << 11;
 
-    // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
-    // The screen should be off or in the process of being turned off by the display controller.
-    // The device typically passes through the dozing state first.
-    private static final int WAKEFULNESS_ASLEEP = 0;
-    // Wakefulness: The device is fully awake.  It can be put to sleep by a call to goToSleep().
-    // When the user activity timeout expires, the device may start dreaming or go to sleep.
-    private static final int WAKEFULNESS_AWAKE = 1;
-    // Wakefulness: The device is dreaming.  It can be awoken by a call to wakeUp(),
-    // which ends the dream.  The device goes to sleep when goToSleep() is called, when
-    // the dream ends or when unplugged.
-    // User activity may brighten the screen but does not end the dream.
-    private static final int WAKEFULNESS_DREAMING = 2;
-    // Wakefulness: The device is dozing.  It is almost asleep but is allowing a special
-    // low-power "doze" dream to run which keeps the display on but lets the application
-    // processor be suspended.  It can be awoken by a call to wakeUp() which ends the dream.
-    // The device fully goes to sleep if the dream cannot be started or ends on its own.
-    private static final int WAKEFULNESS_DOZING = 3;
-
     // Summarizes the state of all active wakelocks.
     private static final int WAKE_LOCK_CPU = 1 << 0;
     private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1;
@@ -187,6 +173,7 @@
     // Indicates whether the device is awake or asleep or somewhere in between.
     // This is distinct from the screen power state, which is managed separately.
     private int mWakefulness;
+    private boolean mWakefulnessChanging;
 
     // True if the sandman has just been summoned for the first time since entering the
     // dreaming or dozing state.  Indicates whether a new dream should begin.
@@ -205,10 +192,6 @@
     // A bitfield that summarizes the state of all active wakelocks.
     private int mWakeLockSummary;
 
-    // True if the device is in an interactive state.
-    private boolean mInteractive;
-    private boolean mInteractiveChanging;
-
     // If true, instructs the display controller to wait for the proximity sensor to
     // go negative before turning the screen on.
     private boolean mRequestWaitForNegativeProximity;
@@ -467,7 +450,6 @@
             mHalInteractiveModeEnabled = true;
 
             mWakefulness = WAKEFULNESS_AWAKE;
-            mInteractive = true;
 
             nativeInit();
             nativeSetAutoSuspend(false);
@@ -1047,9 +1029,7 @@
             }
 
             mLastWakeTime = eventTime;
-            mDirty |= DIRTY_WAKEFULNESS;
-            mWakefulness = WAKEFULNESS_AWAKE;
-            setInteractiveStateLocked(true, 0);
+            setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
 
             userActivityNoUpdateLocked(
                     eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
@@ -1109,10 +1089,8 @@
             }
 
             mLastSleepTime = eventTime;
-            mDirty |= DIRTY_WAKEFULNESS;
-            mWakefulness = WAKEFULNESS_DOZING;
             mSandmanSummoned = true;
-            setInteractiveStateLocked(false, reason);
+            setWakefulnessLocked(WAKEFULNESS_DOZING, reason);
 
             // Report the number of wake locks that will be cleared by going to sleep.
             int numWakeLocksCleared = 0;
@@ -1161,10 +1139,8 @@
         try {
             Slog.i(TAG, "Nap time (uid " + uid +")...");
 
-            mDirty |= DIRTY_WAKEFULNESS;
-            mWakefulness = WAKEFULNESS_DREAMING;
             mSandmanSummoned = true;
-            setInteractiveStateLocked(true, 0);
+            setWakefulnessLocked(WAKEFULNESS_DREAMING, 0);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -1187,29 +1163,28 @@
         try {
             Slog.i(TAG, "Sleeping (uid " + uid +")...");
 
-            mDirty |= DIRTY_WAKEFULNESS;
-            mWakefulness = WAKEFULNESS_ASLEEP;
-            setInteractiveStateLocked(false, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+            setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
         return true;
     }
 
-    private void setInteractiveStateLocked(boolean interactive, int reason) {
-        if (mInteractive != interactive) {
-            finishInteractiveStateChangeLocked();
+    private void setWakefulnessLocked(int wakefulness, int reason) {
+        if (mWakefulness != wakefulness) {
+            finishWakefulnessChangeLocked();
 
-            mInteractive = interactive;
-            mInteractiveChanging = true;
-            mNotifier.onInteractiveStateChangeStarted(interactive, reason);
+            mWakefulness = wakefulness;
+            mWakefulnessChanging = true;
+            mDirty |= DIRTY_WAKEFULNESS;
+            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
         }
     }
 
-    private void finishInteractiveStateChangeLocked() {
-        if (mInteractiveChanging) {
-            mNotifier.onInteractiveStateChangeFinished(mInteractive);
-            mInteractiveChanging = false;
+    private void finishWakefulnessChangeLocked() {
+        if (mWakefulnessChanging) {
+            mNotifier.onWakefulnessChangeFinished(mWakefulness);
+            mWakefulnessChanging = false;
         }
     }
 
@@ -1261,7 +1236,7 @@
 
             // Phase 4: Send notifications, if needed.
             if (mDisplayReady) {
-                finishInteractiveStateChangeLocked();
+                finishWakefulnessChangeLocked();
             }
 
             // Phase 5: Update suspend blocker.
@@ -1450,7 +1425,7 @@
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
-                        + wakefulnessToString(mWakefulness)
+                        + PowerManagerInternal.wakefulnessToString(mWakefulness)
                         + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
             }
         }
@@ -1527,7 +1502,7 @@
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
-                        + wakefulnessToString(mWakefulness)
+                        + PowerManagerInternal.wakefulnessToString(mWakefulness)
                         + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
                         + ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
             }
@@ -2150,7 +2125,7 @@
 
     private boolean isInteractiveInternal() {
         synchronized (mLock) {
-            return mInteractive;
+            return PowerManagerInternal.isInteractive(mWakefulness);
         }
     }
 
@@ -2428,8 +2403,8 @@
         synchronized (mLock) {
             pw.println("Power Manager State:");
             pw.println("  mDirty=0x" + Integer.toHexString(mDirty));
-            pw.println("  mWakefulness=" + wakefulnessToString(mWakefulness));
-            pw.println("  mInteractive=" + mInteractive);
+            pw.println("  mWakefulness=" + PowerManagerInternal.wakefulnessToString(mWakefulness));
+            pw.println("  mWakefulnessChanging=" + mWakefulnessChanging);
             pw.println("  mIsPowered=" + mIsPowered);
             pw.println("  mPlugType=" + mPlugType);
             pw.println("  mBatteryLevel=" + mBatteryLevel);
@@ -2564,21 +2539,6 @@
         return suspendBlocker;
     }
 
-    private static String wakefulnessToString(int wakefulness) {
-        switch (wakefulness) {
-            case WAKEFULNESS_ASLEEP:
-                return "Asleep";
-            case WAKEFULNESS_AWAKE:
-                return "Awake";
-            case WAKEFULNESS_DREAMING:
-                return "Dreaming";
-            case WAKEFULNESS_DOZING:
-                return "Dozing";
-            default:
-                return Integer.toString(wakefulness);
-        }
-    }
-
     private static WorkSource copyWorkSource(WorkSource workSource) {
         return workSource != null ? new WorkSource(workSource) : null;
     }