Merge "Do not take screenshots when launching activities..." into klp-dev
diff --git a/api/current.txt b/api/current.txt
index 94b40279..078ed53 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13228,6 +13228,7 @@
     field public static final java.util.UUID EFFECT_TYPE_BASS_BOOST;
     field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB;
     field public static final java.util.UUID EFFECT_TYPE_EQUALIZER;
+    field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER;
     field public static final java.util.UUID EFFECT_TYPE_NS;
     field public static final java.util.UUID EFFECT_TYPE_PRESET_REVERB;
     field public static final java.util.UUID EFFECT_TYPE_VIRTUALIZER;
@@ -13385,6 +13386,7 @@
   }
 
   public class LoudnessEnhancer extends android.media.audiofx.AudioEffect {
+    ctor public LoudnessEnhancer(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.RuntimeException, java.lang.UnsupportedOperationException;
     method public float getTargetGain() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
     method public void setTargetGain(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
     field public static final int PARAM_TARGET_GAIN_MB = 0; // 0x0
@@ -25633,10 +25635,8 @@
     ctor public TransitionManager();
     method public static void beginDelayedTransition(android.view.ViewGroup);
     method public static void beginDelayedTransition(android.view.ViewGroup, android.transition.Transition);
-    method public static android.transition.Transition getDefaultTransition();
     method public static void go(android.transition.Scene);
     method public static void go(android.transition.Scene, android.transition.Transition);
-    method public void setDefaultTransition(android.transition.Transition);
     method public void setTransition(android.transition.Scene, android.transition.Transition);
     method public void setTransition(android.transition.Scene, android.transition.Scene, android.transition.Transition);
     method public void transitionTo(android.transition.Scene);
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 14f67c5..50fdb41 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -358,14 +358,20 @@
             mListener = listener;
         }
 
+        @Override
         public void addSensorEvent(Sensor sensor) {
             SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor,
                     mManager.mTargetSdkLevel));
-            mSensorsEvents.put(sensor.getHandle(), t);
+            synchronized (mSensorsEvents) {
+                mSensorsEvents.put(sensor.getHandle(), t);
+            }
         }
 
+        @Override
         public void removeSensorEvent(Sensor sensor) {
-            mSensorsEvents.delete(sensor.getHandle());
+            synchronized (mSensorsEvents) {
+                mSensorsEvents.delete(sensor.getHandle());
+            }
         }
 
         // Called from native code.
@@ -374,9 +380,14 @@
         protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
                 long timestamp) {
             final Sensor sensor = sHandleToSensor.get(handle);
-            SensorEvent t = mSensorsEvents.get(handle);
+            SensorEvent t = null;
+            synchronized (mSensorsEvents) {
+                t = mSensorsEvents.get(handle);
+            }
+
             if (t == null) {
-                Log.e(TAG, "Error: Sensor Event is null for Sensor: " + sensor);
+                // This may happen if the client has unregistered and there are pending events in
+                // the queue waiting to be delivered. Ignore.
                 return;
             }
             // Copy from the values array.
@@ -427,14 +438,20 @@
             mListener = listener;
         }
 
+        @Override
         public void addSensorEvent(Sensor sensor) {
             TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor,
                     mManager.mTargetSdkLevel));
-            mTriggerEvents.put(sensor.getHandle(), t);
+            synchronized (mTriggerEvents) {
+                mTriggerEvents.put(sensor.getHandle(), t);
+            }
         }
 
+        @Override
         public void removeSensorEvent(Sensor sensor) {
-            mTriggerEvents.delete(sensor.getHandle());
+            synchronized (mTriggerEvents) {
+                mTriggerEvents.delete(sensor.getHandle());
+            }
         }
 
         // Called from native code.
@@ -443,7 +460,10 @@
         protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
                 long timestamp) {
             final Sensor sensor = sHandleToSensor.get(handle);
-            TriggerEvent t = mTriggerEvents.get(handle);
+            TriggerEvent t = null;
+            synchronized (mTriggerEvents) {
+                t = mTriggerEvents.get(handle);
+            }
             if (t == null) {
                 Log.e(TAG, "Error: Trigger Event is null for Sensor: " + sensor);
                 return;
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 5a919fb..f9c1d31 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -35,10 +35,12 @@
     public static final Bundle EMPTY;
 
     static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+    static final Parcel EMPTY_PARCEL;
 
     static {
         EMPTY = new Bundle();
         EMPTY.mMap = ArrayMap.EMPTY;
+        EMPTY_PARCEL = Parcel.obtain();
     }
 
     // Invariant - exactly one of mMap / mParcelledData will be null
@@ -115,9 +117,13 @@
      */
     public Bundle(Bundle b) {
         if (b.mParcelledData != null) {
-            mParcelledData = Parcel.obtain();
-            mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize());
-            mParcelledData.setDataPosition(0);
+            if (b.mParcelledData == EMPTY_PARCEL) {
+                mParcelledData = EMPTY_PARCEL;
+            } else {
+                mParcelledData = Parcel.obtain();
+                mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize());
+                mParcelledData.setDataPosition(0);
+            }
         } else {
             mParcelledData = null;
         }
@@ -216,6 +222,18 @@
             return;
         }
 
+        if (mParcelledData == EMPTY_PARCEL) {
+            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                    + ": empty");
+            if (mMap == null) {
+                mMap = new ArrayMap<String, Object>(1);
+            } else {
+                mMap.erase();
+            }
+            mParcelledData = null;
+            return;
+        }
+
         int N = mParcelledData.readInt();
         if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                 + ": reading " + N + " maps");
@@ -1652,11 +1670,20 @@
         final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds);
         try {
             if (mParcelledData != null) {
-                int length = mParcelledData.dataSize();
-                parcel.writeInt(length);
-                parcel.writeInt(BUNDLE_MAGIC);
-                parcel.appendFrom(mParcelledData, 0, length);
+                if (mParcelledData == EMPTY_PARCEL) {
+                    parcel.writeInt(0);
+                } else {
+                    int length = mParcelledData.dataSize();
+                    parcel.writeInt(length);
+                    parcel.writeInt(BUNDLE_MAGIC);
+                    parcel.appendFrom(mParcelledData, 0, length);
+                }
             } else {
+                // Special case for empty bundles.
+                if (mMap == null || mMap.size() <= 0) {
+                    parcel.writeInt(0);
+                    return;
+                }
                 int lengthPos = parcel.dataPosition();
                 parcel.writeInt(-1); // dummy, will hold length
                 parcel.writeInt(BUNDLE_MAGIC);
@@ -1690,6 +1717,13 @@
     }
 
     void readFromParcelInner(Parcel parcel, int length) {
+        if (length == 0) {
+            // Empty Bundle or end of data.
+            mParcelledData = EMPTY_PARCEL;
+            mHasFds = false;
+            mFdsKnown = true;
+            return;
+        }
         int magic = parcel.readInt();
         if (magic != BUNDLE_MAGIC) {
             //noinspection ThrowableInstanceNeverThrown
@@ -1716,8 +1750,12 @@
     @Override
     public synchronized String toString() {
         if (mParcelledData != null) {
-            return "Bundle[mParcelledData.dataSize=" +
-                    mParcelledData.dataSize() + "]";
+            if (mParcelledData == EMPTY_PARCEL) {
+                return "Bundle[EMPTY_PARCEL]";
+            } else {
+                return "Bundle[mParcelledData.dataSize=" +
+                        mParcelledData.dataSize() + "]";
+            }
         }
         return "Bundle[" + mMap.toString() + "]";
     }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 02b1998..94b9617 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -611,11 +611,15 @@
             here.fillInStackTrace();
             Log.d(TAG, "Writing " + N + " ArrayMap entries", here);
         }
+        int startPos;
         for (int i=0; i<N; i++) {
-            if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Write #" + i + ": key=0x"
-                    + (val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0) + " " + val.keyAt(i));
+            if (DEBUG_ARRAY_MAP) startPos = dataPosition();
             writeValue(val.keyAt(i));
             writeValue(val.valueAt(i));
+            if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Write #" + i + " "
+                    + (dataPosition()-startPos) + " bytes: key=0x"
+                    + Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)
+                    + " " + val.keyAt(i));
         }
     }
 
@@ -2303,11 +2307,14 @@
             here.fillInStackTrace();
             Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
         }
+        int startPos;
         while (N > 0) {
+            if (DEBUG_ARRAY_MAP) startPos = dataPosition();
             Object key = readValue(loader);
-            if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Read #" + (N-1) + ": key=0x"
-                    + (key != null ? key.hashCode() : 0) + " " + key);
             Object value = readValue(loader);
+            if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Read #" + (N-1) + " "
+                    + (dataPosition()-startPos) + " bytes: key=0x"
+                    + Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
             outVal.append(key, value);
             N--;
         }
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 5800bd5..e1f1896 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -57,7 +57,7 @@
                 com.android.internal.R.id.scene_layoutid_cache);
         if (scenes == null) {
             scenes = new SparseArray<Scene>();
-            sceneRoot.setTag(com.android.internal.R.id.scene_layoutid_cache, scenes);
+            sceneRoot.setTagInternal(com.android.internal.R.id.scene_layoutid_cache, scenes);
         }
         Scene scene = scenes.get(layoutId);
         if (scene != null) {
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 9b1494d..3bf6790 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -82,6 +82,8 @@
      * an {@link AutoTransition} instance.
      *
      * @param transition The default transition to be used for scene changes.
+     *
+     * @hide pending later changes
      */
     public void setDefaultTransition(Transition transition) {
         sDefaultTransition = transition;
@@ -93,6 +95,8 @@
      *
      * @return The current default transition.
      * @see #setDefaultTransition(Transition)
+     *
+     * @hide pending later changes
      */
     public static Transition getDefaultTransition() {
         return sDefaultTransition;
@@ -105,7 +109,7 @@
      * transition to run.
      * @param transition The transition that will play when the given scene is
      * entered. A value of null will result in the default behavior of
-     * using the {@link #getDefaultTransition() default transition} instead.
+     * using the default transition instead.
      */
     public void setTransition(Scene scene, Transition transition) {
         mSceneTransitions.put(scene, transition);
@@ -121,7 +125,7 @@
      * be run
      * @param transition The transition that will play when the given scene is
      * entered. A value of null will result in the default behavior of
-     * using the {@link #getDefaultTransition() default transition} instead.
+     * using the default transition instead.
      */
     public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
         ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
@@ -139,8 +143,8 @@
      *
      * @param scene The scene being entered
      * @return The Transition to be used for the given scene change. If no
-     * Transition was specified for this scene change, the {@link #getDefaultTransition()
-     * default transition} will be used instead.
+     * Transition was specified for this scene change, the default transition
+     * will be used instead.
      */
     private Transition getTransition(Scene scene) {
         Transition transition = null;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index cd853b6..591267e 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -181,11 +181,12 @@
             } else {
                 mAlwaysUseOption = false;
             }
-        }
-        final int initialHighlight = mAdapter.getInitialHighlight();
-        if (initialHighlight >= 0) {
-            mListView.setItemChecked(initialHighlight, true);
-            onItemClick(null, null, initialHighlight, 0); // Other entries are not used
+            // Set the initial highlight if there was a preferred or last used choice
+            final int initialHighlight = mAdapter.getInitialHighlight();
+            if (initialHighlight >= 0) {
+                mListView.setItemChecked(initialHighlight, true);
+                onItemClick(null, null, initialHighlight, 0); // Other entries are not used
+            }
         }
     }
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e82ad1e..490eab8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -97,6 +97,13 @@
          the SIM card. -->
     <string name="needPuk">Your SIM card is PUK-locked. Type the PUK code to unlock it.</string>
     <string name="needPuk2">Type PUK2 to unblock SIM card.</string>
+    <!-- Displayed when user attempts to change SIM PIN1 without enabling PIN1. -->
+    <string name="enablePin">Unsuccessful, enable SIM/RUIM Lock.</string>
+    <!-- Displayed when a SIM PIN/PUK is entered incorrectly. -->
+    <plurals name="pinpuk_attempts">
+        <item quantity="one">You have <xliff:g id="number">%d</xliff:g> remaining attempt before SIM is locked.</item>
+        <item quantity="other">You have <xliff:g id="number">%d</xliff:g> remaining attempts before SIM is locked.</item>
+    </plurals>
     <!-- Title for the dialog used to display the user's IMEI number [CHAR LIMIT=10] -->
     <string name="imei">IMEI</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dc0841a..a5573c9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -612,6 +612,7 @@
   <java-symbol type="string" name="more_item_label" />
   <java-symbol type="string" name="needPuk" />
   <java-symbol type="string" name="needPuk2" />
+  <java-symbol type="string" name="enablePin" />
   <java-symbol type="string" name="new_app_action" />
   <java-symbol type="string" name="new_app_description" />
   <java-symbol type="string" name="noApplications" />
@@ -965,6 +966,7 @@
   <java-symbol type="plurals" name="num_minutes_ago" />
   <java-symbol type="plurals" name="num_seconds_ago" />
   <java-symbol type="plurals" name="restr_pin_countdown" />
+  <java-symbol type="plurals" name="pinpuk_attempts" />
 
   <java-symbol type="array" name="carrier_properties" />
   <java-symbol type="array" name="config_data_usage_network_types" />
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index 072b842..c5b9c67 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index 74919ff..0320214 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 4642d6f..38ba570 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index 13bf13a..271606b 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index 130672a..17ef355 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 0ba95c9..7469063 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index 309c22d..74efe4d 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index 0b53ba4..f08ea51 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index f0fd409..1252d00 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index e67b02b0..e914a07 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index a08414b..8a570cf 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index 713fd30..a16b9cb 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
index 25efe1e..ad084c2 100644
--- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -346,6 +346,7 @@
         RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
         if (fragment == null) {
             fragment = new RetainFragment();
+            fm.beginTransaction().add(fragment, TAG).commit();
         }
         return fragment;
     }
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 12f7bd9..1e1ef8c 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -120,8 +120,6 @@
             .fromString("58b4b260-8e06-11e0-aa8e-0002a5d5c51b");
 
     /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
      * UUID for Loudness Enhancer
      */
     public static final UUID EFFECT_TYPE_LOUDNESS_ENHANCER = UUID
diff --git a/media/java/android/media/audiofx/LoudnessEnhancer.java b/media/java/android/media/audiofx/LoudnessEnhancer.java
index eb2fb75..7dc4175 100644
--- a/media/java/android/media/audiofx/LoudnessEnhancer.java
+++ b/media/java/android/media/audiofx/LoudnessEnhancer.java
@@ -65,7 +65,6 @@
     private final Object mParamListenerLock = new Object();
 
     /**
-     * @hide
      * Class constructor.
      * @param audioSession system-wide unique audio session identifier. The LoudnessEnhancer
      * will be attached to the MediaPlayer or AudioTrack in the same audio session.
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 6faf7f8..179bcd1 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -50,6 +50,14 @@
             android:authorities="com.android.documentsui.recents"
             android:exported="false" />
 
+        <receiver android:name=".PackageReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
+                <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+                <data android:scheme="package" />
+            </intent-filter>
+        </receiver>
+
         <!-- TODO: remove when we have real clients -->
         <activity android:name=".TestActivity" android:enabled="false">
             <intent-filter>
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 891f0a0..d601194 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -51,15 +51,31 @@
             android:singleLine="true"
             android:selectAllOnFocus="true" />
 
-        <Button
-            android:id="@android:id/button1"
+        <FrameLayout
             android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:background="?android:attr/selectableItemBackground"
-            android:text="@string/menu_save"
-            android:textAllCaps="true"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:padding="8dp" />
+            android:layout_height="match_parent">
+
+            <Button
+                android:id="@android:id/button1"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:background="?android:attr/selectableItemBackground"
+                android:text="@string/menu_save"
+                android:textAllCaps="true"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:padding="8dp" />
+
+            <ProgressBar
+                android:id="@android:id/progress"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:visibility="gone"
+                android:indeterminate="true"
+                android:padding="8dp"
+                style="?android:attr/progressBarStyle" />
+
+        </FrameLayout>
 
     </LinearLayout>
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 90be197..ba8c35f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -95,6 +95,11 @@
         }
 
         @Override
+        protected void onPreExecute() {
+            mActivity.setPending(true);
+        }
+
+        @Override
         protected DocumentInfo doInBackground(Void... params) {
             final ContentResolver resolver = mActivity.getContentResolver();
             ContentProviderClient client = null;
@@ -120,6 +125,8 @@
             } else {
                 Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show();
             }
+
+            mActivity.setPending(false);
         }
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index d675e8d..a9278d7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -661,6 +661,13 @@
         DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
     }
 
+    public void setPending(boolean pending) {
+        final SaveFragment save = SaveFragment.get(getFragmentManager());
+        if (save != null) {
+            save.setPending(pending);
+        }
+    }
+
     @Override
     public void onBackPressed() {
         if (!mState.stackTouched) {
@@ -1051,6 +1058,11 @@
         }
 
         @Override
+        protected void onPreExecute() {
+            setPending(true);
+        }
+
+        @Override
         protected Uri doInBackground(Void... params) {
             final ContentResolver resolver = getContentResolver();
             final DocumentInfo cwd = getCurrentDirectory();
@@ -1083,6 +1095,8 @@
                 Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
                         .show();
             }
+
+            setPending(false);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
new file mode 100644
index 0000000..aef63af
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You 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.documentsui;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+/**
+ * Clean up {@link RecentsProvider} when packages are removed.
+ */
+public class PackageReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final ContentResolver resolver = context.getContentResolver();
+
+        final String action = intent.getAction();
+        if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+            resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE, null, null);
+
+        } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
+            final Uri data = intent.getData();
+            if (data != null) {
+                final String packageName = data.getSchemeSpecificPart();
+                resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE_PACKAGE,
+                        packageName, null);
+            }
+        }
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 4313fa7..f6e4349 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -16,24 +16,40 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.content.UriMatcher;
+import android.content.pm.ResolveInfo;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
 import android.text.format.DateUtils;
 import android.util.Log;
 
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+import com.android.internal.util.Predicate;
+import com.google.android.collect.Sets;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.util.Set;
+
 public class RecentsProvider extends ContentProvider {
     private static final String TAG = "RecentsProvider";
 
-    public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
+    private static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
 
     private static final String AUTHORITY = "com.android.documentsui.recents";
 
@@ -43,6 +59,9 @@
     private static final int URI_STATE = 2;
     private static final int URI_RESUME = 3;
 
+    public static final String METHOD_PURGE = "purge";
+    public static final String METHOD_PURGE_PACKAGE = "purgePackage";
+
     static {
         sMatcher.addURI(AUTHORITY, "recent", URI_RECENT);
         // state/authority/rootId/docId
@@ -231,4 +250,116 @@
     public int delete(Uri uri, String selection, String[] selectionArgs) {
         throw new UnsupportedOperationException("Unsupported Uri " + uri);
     }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        if (METHOD_PURGE.equals(method)) {
+            // Purge references to unknown authorities
+            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+            final Set<String> knownAuth = Sets.newHashSet();
+            for (ResolveInfo info : getContext()
+                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
+                knownAuth.add(info.providerInfo.authority);
+            }
+
+            purgeByAuthority(new Predicate<String>() {
+                @Override
+                public boolean apply(String authority) {
+                    // Purge unknown authorities
+                    return !knownAuth.contains(authority);
+                }
+            });
+
+            return null;
+
+        } else if (METHOD_PURGE_PACKAGE.equals(method)) {
+            // Purge references to authorities in given package
+            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+            intent.setPackage(arg);
+            final Set<String> packageAuth = Sets.newHashSet();
+            for (ResolveInfo info : getContext()
+                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
+                packageAuth.add(info.providerInfo.authority);
+            }
+
+            if (!packageAuth.isEmpty()) {
+                purgeByAuthority(new Predicate<String>() {
+                    @Override
+                    public boolean apply(String authority) {
+                        // Purge authority matches
+                        return packageAuth.contains(authority);
+                    }
+                });
+            }
+
+            return null;
+
+        } else {
+            return super.call(method, arg, extras);
+        }
+    }
+
+    /**
+     * Purge all internal data whose authority matches the given
+     * {@link Predicate}.
+     */
+    private void purgeByAuthority(Predicate<String> predicate) {
+        final SQLiteDatabase db = mHelper.getWritableDatabase();
+        final DocumentStack stack = new DocumentStack();
+
+        Cursor cursor = db.query(TABLE_RECENT, null, null, null, null, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                try {
+                    final byte[] rawStack = cursor.getBlob(
+                            cursor.getColumnIndex(RecentColumns.STACK));
+                    DurableUtils.readFromArray(rawStack, stack);
+
+                    if (stack.root != null && predicate.apply(stack.root.authority)) {
+                        final String key = getCursorString(cursor, RecentColumns.KEY);
+                        db.delete(TABLE_RECENT, RecentColumns.KEY + "=?", new String[] { key });
+                    }
+                } catch (IOException ignored) {
+                }
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+
+        cursor = db.query(TABLE_STATE, new String[] {
+                StateColumns.AUTHORITY }, null, null, StateColumns.AUTHORITY, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final String authority = getCursorString(cursor, StateColumns.AUTHORITY);
+                if (predicate.apply(authority)) {
+                    db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] {
+                            authority });
+                    Log.d(TAG, "Purged state for " + authority);
+                }
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+
+        cursor = db.query(TABLE_RESUME, null, null, null, null, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                try {
+                    final byte[] rawStack = cursor.getBlob(
+                            cursor.getColumnIndex(ResumeColumns.STACK));
+                    DurableUtils.readFromArray(rawStack, stack);
+
+                    if (stack.root != null && predicate.apply(stack.root.authority)) {
+                        final String packageName = getCursorString(
+                                cursor, ResumeColumns.PACKAGE_NAME);
+                        db.delete(TABLE_RESUME, ResumeColumns.PACKAGE_NAME + "=?",
+                                new String[] { packageName });
+                    }
+                } catch (IOException ignored) {
+                }
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index fdbc3ab..58c5daf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
+
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -25,7 +27,9 @@
 import android.content.Loader;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.Formatter;
 import android.view.LayoutInflater;
@@ -33,6 +37,7 @@
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.ListView;
@@ -133,6 +138,13 @@
         final State state = ((DocumentsActivity) context).getDisplayState();
         state.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(context);
 
+        if (state.action == ACTION_GET_CONTENT) {
+            mList.setOnItemLongClickListener(mItemLongClickListener);
+        } else {
+            mList.setOnItemLongClickListener(null);
+            mList.setLongClickable(false);
+        }
+
         getLoaderManager().restartLoader(2, null, mCallbacks);
     }
 
@@ -152,6 +164,13 @@
         }
     }
 
+    private void showAppDetails(ResolveInfo ri) {
+        final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+        intent.setData(Uri.fromParts("package", ri.activityInfo.packageName, null));
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+        startActivity(intent);
+    }
+
     private OnItemClickListener mItemListener = new OnItemClickListener() {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -167,6 +186,19 @@
         }
     };
 
+    private OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
+        @Override
+        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+            final Item item = mAdapter.getItem(position);
+            if (item instanceof AppItem) {
+                showAppDetails(((AppItem) item).info);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    };
+
     private static abstract class Item {
         private final int mLayoutId;
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index 23e047c..9d70c51 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -30,6 +30,7 @@
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.ImageView;
+import android.widget.ProgressBar;
 
 import com.android.documentsui.model.DocumentInfo;
 
@@ -42,6 +43,7 @@
     private DocumentInfo mReplaceTarget;
     private EditText mDisplayName;
     private Button mSave;
+    private ProgressBar mProgress;
     private boolean mIgnoreNextEdit;
 
     private static final String EXTRA_MIME_TYPE = "mime_type";
@@ -83,6 +85,8 @@
         mSave.setOnClickListener(mSaveListener);
         mSave.setEnabled(false);
 
+        mProgress = (ProgressBar) view.findViewById(android.R.id.progress);
+
         return view;
     }
 
@@ -92,7 +96,6 @@
             if (mIgnoreNextEdit) {
                 mIgnoreNextEdit = false;
             } else {
-                Log.d(TAG, "onTextChanged!");
                 mReplaceTarget = null;
             }
         }
@@ -140,4 +143,9 @@
     public void setSaveEnabled(boolean enabled) {
         mSave.setEnabled(enabled);
     }
+
+    public void setPending(boolean pending) {
+        mSave.setVisibility(pending ? View.INVISIBLE : View.VISIBLE);
+        mProgress.setVisibility(pending ? View.VISIBLE : View.GONE);
+    }
 }
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
index 71ce4dd..af6ff01 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
@@ -127,7 +127,7 @@
         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
         final RowBuilder row = result.newRow();
         row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
-        row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
+        row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS | Root.FLAG_SUPPORTS_CREATE);
         row.add(Root.COLUMN_TITLE, "_Test title which is really long");
         row.add(Root.COLUMN_SUMMARY,
                 SystemClock.elapsedRealtime() + " summary which is also super long text");
@@ -147,6 +147,14 @@
         return result;
     }
 
+    @Override
+    public String createDocument(String parentDocumentId, String mimeType, String displayName)
+            throws FileNotFoundException {
+        if (LAG) lagUntilCanceled(null);
+
+        return super.createDocument(parentDocumentId, mimeType, displayName);
+    }
+
     /**
      * Holds any outstanding or finished "network" fetching.
      */
@@ -386,6 +394,7 @@
 
         if (MY_DOC_ID.equals(docId)) {
             row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+            row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_SUPPORTS_CREATE);
         } else if (MY_DOC_NULL.equals(docId)) {
             // No MIME type
         } else {
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png
deleted file mode 100644
index 6402d3d..0000000
--- a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png
deleted file mode 100644
index 83be046..0000000
--- a/packages/Keyguard/res/drawable-hdpi/ic_lockscreen_forgotpassword_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png
deleted file mode 100644
index a7e063a..0000000
--- a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png
deleted file mode 100644
index 53af5a5..0000000
--- a/packages/Keyguard/res/drawable-mdpi/ic_lockscreen_forgotpassword_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png
deleted file mode 100644
index e4172ce..0000000
--- a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png b/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png
deleted file mode 100644
index e2c76217..0000000
--- a/packages/Keyguard/res/drawable-xhdpi/ic_lockscreen_forgotpassword_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml b/packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml
deleted file mode 100644
index 6c081bf..0000000
--- a/packages/Keyguard/res/drawable/lockscreen_forgot_password_button.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="true" android:drawable="@drawable/ic_lockscreen_forgotpassword_normal" />
-    <item android:state_pressed="true" android:drawable="@drawable/ic_lockscreen_forgotpassword_pressed" />
-    <item android:drawable="@drawable/ic_lockscreen_forgotpassword_normal" />
-</selector>
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
index 313fe9f..8be15cb 100644
--- a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -42,7 +42,7 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="-10dip"
+        android:layout_marginTop="@dimen/eca_overlap"
         style="?android:attr/buttonBarStyle"
         android:orientation="horizontal"
         android:gravity="center"
@@ -66,12 +66,10 @@
             android:layout_width="0dip"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:drawableLeft="@drawable/lockscreen_forgot_password_button"
             style="?android:attr/buttonBarButtonStyle"
             android:textSize="@dimen/kg_status_line_font_size"
             android:textColor="?android:attr/textColorSecondary"
             android:textAppearance="?android:attr/textAppearanceMedium"
-            android:drawablePadding="8dip"
             android:visibility="gone"
             android:textAllCaps="@bool/kg_use_all_caps" />
     </LinearLayout>
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index f8a1362..ea5ef27 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -66,4 +66,7 @@
     <dimen name="widget_label_font_size">16dp</dimen>
     <dimen name="widget_big_font_size">141dp</dimen>
 
+    <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
+         Should be 0 on devices with plenty of room (e.g. tablets) -->
+    <dimen name="eca_overlap">0dip</dimen>
 </resources>
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index a1ad120..71e9924 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -150,6 +150,10 @@
     security mode. -->
     <dimen name="kg_small_widget_height">160dp</dimen>
 
+    <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
+         Should be 0 on devices with plenty of room (e.g. tablets) -->
+    <dimen name="eca_overlap">-10dip</dimen>
+
     <!-- Default clock parameters -->
     <dimen name="bottom_text_spacing_digital">-8dp</dimen>
     <dimen name="label_font_size">14dp</dimen>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index abc4483..4738049 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -24,12 +24,12 @@
 
     <!-- Instructions telling the user to enter their SIM PUK to unlock the keyguard.
          Displayed in one line in a large font.  -->
-    <string name="keyguard_password_enter_puk_code">Type PUK and new PIN code</string>
+    <string name="keyguard_password_enter_puk_code">Type SIM PUK and new PIN code</string>
 
     <!-- Prompt to enter SIM PUK in Edit Text Box in unlock screen -->
-    <string name="keyguard_password_enter_puk_prompt">PUK code</string>
+    <string name="keyguard_password_enter_puk_prompt">SIM PUK code</string>
     <!-- Prompt to enter New SIM PIN in Edit Text Box in unlock screen -->
-    <string name="keyguard_password_enter_pin_prompt">New PIN code</string>
+    <string name="keyguard_password_enter_pin_prompt">New SIM PIN code</string>
 
     <!-- Displayed as hint in passwordEntry EditText on PasswordUnlockScreen [CHAR LIMIT=30]-->
     <string name="keyguard_password_entry_touch_hint"><font size="17">Touch to type password</font></string>
@@ -249,8 +249,6 @@
     <string name="kg_enter_confirm_pin_hint">Confirm desired PIN code</string>
     <!-- Message shown in dialog while the device is unlocking the SIM card -->
     <string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
-    <!-- Message shown when the user enters the wrong PIN code -->
-    <string name="kg_password_wrong_pin_code">Incorrect PIN code.</string>
     <!-- Message shown when the user enters an invalid SIM pin password in PUK screen -->
     <string name="kg_invalid_sim_pin_hint">Type a PIN that is 4 to 8 numbers.</string>
     <!-- Message shown when the user enters an invalid PUK code in the PUK screen -->
@@ -333,6 +331,34 @@
     <!-- The delete-widget drop target button text -->
     <string name="kg_reordering_delete_drop_target_text">Remove</string>
 
+    <!-- Instructions telling the user that they entered the wrong SIM PIN for the last time.
+         Displayed in a dialog box.  -->
+    <string name="kg_password_wrong_pin_code_pukked">Incorrect SIM PIN code you must now contact your carrier to unlock your device.</string>
+    <!-- Instructions telling the user that they entered the wrong SIM PIN while trying
+         to unlock the keyguard.  Displayed in a dialog box.  -->
+    <plurals name="kg_password_wrong_pin_code">
+        <item quantity="one">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
+        <item quantity="other">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempts.</item>
+    </plurals>
+
+    <!-- Instructions telling the user that they have exhausted SIM PUK retries and the SIM is now unusable.
+         Displayed in a dialog box.  -->
+    <string name="kg_password_wrong_puk_code_dead">SIM is unusable. Contact your carrier.</string>
+    <!-- Instructions telling the user that they entered the wrong puk while trying
+         to unlock the keyguard.  Displayed in a dialog box.  -->
+    <plurals name="kg_password_wrong_puk_code">
+        <item quantity="one">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item>
+        <item quantity="other">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
+    </plurals>
+    <!-- Instructions telling the user that the operation to unlock the keyguard
+         with SIM PIN failed. Displayed in one line in a large font.  -->
+    <string name="kg_password_pin_failed">SIM PIN operation failed!</string>
+    <!-- Instructions telling the user that the operation to unlock the keyguard
+         with PUK failed. Displayed in one line in a large font.  -->
+    <string name="kg_password_puk_failed">SIM PUK operation failed!</string>
+    <!-- Notification telling the user that the PIN1 they entered is valid -->
+    <string name="kg_pin_accepted">Code Accepted!</string>
+
     <!-- Transport control strings -->
     <!-- Shown on transport control of lockscreen. Pressing button goes to previous track. -->
     <string name="keyguard_transport_prev_description">Previous track button</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index 5059407..e39622a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -17,11 +17,16 @@
 package com.android.keyguard;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
 
 import android.content.Context;
+import android.content.DialogInterface;
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
 import android.app.Dialog;
 import android.app.ProgressDialog;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.Editable;
@@ -30,6 +35,7 @@
 import android.text.method.DigitsKeyListener;
 import android.util.AttributeSet;
 import android.view.View;
+import android.util.Log;
 import android.view.WindowManager;
 import android.widget.TextView.OnEditorActionListener;
 
@@ -38,10 +44,14 @@
  */
 public class KeyguardSimPinView extends KeyguardAbsKeyInputView
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+    private static final String LOG_TAG = "KeyguardSimPinView";
+    private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
 
     private ProgressDialog mSimUnlockProgressDialog = null;
     private volatile boolean mSimCheckInProgress;
 
+    private AlertDialog mRemainingAttemptsDialog;
+
     public KeyguardSimPinView(Context context) {
         this(context, null);
     }
@@ -55,6 +65,23 @@
         mPasswordEntry.setEnabled(true);
     }
 
+    private String getPinPasswordErrorMessage(int attemptsRemaining) {
+        String displayMessage;
+
+        if (attemptsRemaining == 0) {
+            displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
+        } else if (attemptsRemaining > 0) {
+            displayMessage = getContext().getResources()
+                    .getQuantityString(R.plurals.kg_password_wrong_pin_code, attemptsRemaining,
+                            attemptsRemaining);
+        } else {
+            displayMessage = getContext().getString(R.string.kg_password_pin_failed);
+        }
+        if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
+                + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+        return displayMessage;
+    }
+
     @Override
     protected boolean shouldLockout(long deadline) {
         // SIM PIN doesn't have a timed lockout
@@ -109,6 +136,8 @@
                 | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
 
         mPasswordEntry.requestFocus();
+
+        mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
     }
 
     @Override
@@ -135,22 +164,22 @@
             mPin = pin;
         }
 
-        abstract void onSimCheckResponse(boolean success);
+        abstract void onSimCheckResponse(final int result, final int attemptsRemaining);
 
         @Override
         public void run() {
             try {
-                final boolean result = ITelephony.Stub.asInterface(ServiceManager
-                        .checkService("phone")).supplyPin(mPin);
+                final int[] result = ITelephony.Stub.asInterface(ServiceManager
+                        .checkService("phone")).supplyPinReportResult(mPin);
                 post(new Runnable() {
                     public void run() {
-                        onSimCheckResponse(result);
+                        onSimCheckResponse(result[0], result[1]);
                     }
                 });
             } catch (RemoteException e) {
                 post(new Runnable() {
                     public void run() {
-                        onSimCheckResponse(false);
+                        onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
                     }
                 });
             }
@@ -164,14 +193,28 @@
                     mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
             mSimUnlockProgressDialog.setIndeterminate(true);
             mSimUnlockProgressDialog.setCancelable(false);
-            if (!(mContext instanceof Activity)) {
-                mSimUnlockProgressDialog.getWindow().setType(
-                        WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-            }
+            mSimUnlockProgressDialog.getWindow().setType(
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         }
         return mSimUnlockProgressDialog;
     }
 
+    private Dialog getSimRemainingAttemptsDialog(int remaining) {
+        String msg = getPinPasswordErrorMessage(remaining);
+        if (mRemainingAttemptsDialog == null) {
+            Builder builder = new AlertDialog.Builder(mContext);
+            builder.setMessage(msg);
+            builder.setCancelable(false);
+            builder.setNeutralButton(R.string.ok, null);
+            mRemainingAttemptsDialog = builder.create();
+            mRemainingAttemptsDialog.getWindow().setType(
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+        } else {
+            mRemainingAttemptsDialog.setMessage(msg);
+        }
+        return mRemainingAttemptsDialog;
+    }
+
     @Override
     protected void verifyPasswordAndUnlock() {
         String entry = mPasswordEntry.getText().toString();
@@ -189,20 +232,34 @@
         if (!mSimCheckInProgress) {
             mSimCheckInProgress = true; // there should be only one
             new CheckSimPin(mPasswordEntry.getText().toString()) {
-                void onSimCheckResponse(final boolean success) {
+                void onSimCheckResponse(final int result, final int attemptsRemaining) {
                     post(new Runnable() {
                         public void run() {
                             if (mSimUnlockProgressDialog != null) {
                                 mSimUnlockProgressDialog.hide();
                             }
-                            if (success) {
-                                // before closing the keyguard, report back that the sim is unlocked
-                                // so it knows right away.
+                            if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
                                 KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
                                 mCallback.dismiss(true);
                             } else {
-                                mSecurityMessageDisplay.setMessage
-                                    (R.string.kg_password_wrong_pin_code, true);
+                                if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
+                                    if (attemptsRemaining <= 2) {
+                                        // this is getting critical - show dialog
+                                        getSimRemainingAttemptsDialog(attemptsRemaining).show();
+                                    } else {
+                                        // show message
+                                        mSecurityMessageDisplay.setMessage(
+                                                getPinPasswordErrorMessage(attemptsRemaining), true);
+                                    }
+                                } else {
+                                    // "PIN operation failed!" - no idea what this was and no way to
+                                    // find out. :/
+                                    mSecurityMessageDisplay.setMessage(getContext().getString(
+                                            R.string.kg_password_pin_failed), true);
+                                }
+                                if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+                                        + " CheckSimPin.onSimCheckResponse: " + result
+                                        + " attemptsRemaining=" + attemptsRemaining);
                                 mPasswordEntry.setText("");
                             }
                             mCallback.userActivity(0);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
index 2ae4cc7..31518a1 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -16,10 +16,10 @@
 
 package com.android.keyguard;
 
-import com.android.internal.telephony.ITelephony;
-
 import android.content.Context;
+import android.animation.AnimatorSet.Builder;
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.ProgressDialog;
 import android.os.RemoteException;
@@ -29,21 +29,29 @@
 import android.text.TextWatcher;
 import android.text.method.DigitsKeyListener;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.TextView.OnEditorActionListener;
 
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
+
+
 /**
  * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
  */
 public class KeyguardSimPukView extends KeyguardAbsKeyInputView
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+    private static final String LOG_TAG = "KeyguardSimPukView";
+    private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
 
     private ProgressDialog mSimUnlockProgressDialog = null;
     private volatile boolean mCheckInProgress;
     private String mPukText;
     private String mPinText;
     private StateMachine mStateMachine = new StateMachine();
+    private AlertDialog mRemainingAttemptsDialog;
 
     private class StateMachine {
         final int ENTER_PUK = 0;
@@ -93,6 +101,23 @@
         }
     }
 
+    private String getPukPasswordErrorMessage(int attemptsRemaining) {
+        String displayMessage;
+
+        if (attemptsRemaining == 0) {
+            displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead);
+        } else if (attemptsRemaining > 0) {
+            displayMessage = getContext().getResources()
+                    .getQuantityString(R.plurals.kg_password_wrong_puk_code, attemptsRemaining,
+                            attemptsRemaining);
+        } else {
+            displayMessage = getContext().getString(R.string.kg_password_puk_failed);
+        }
+        if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
+                + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+        return displayMessage;
+    }
+
     public KeyguardSimPukView(Context context) {
         this(context, null);
     }
@@ -190,23 +215,23 @@
             mPin = pin;
         }
 
-        abstract void onSimLockChangedResponse(boolean success);
+        abstract void onSimLockChangedResponse(final int result, final int attemptsRemaining);
 
         @Override
         public void run() {
             try {
-                final boolean result = ITelephony.Stub.asInterface(ServiceManager
-                        .checkService("phone")).supplyPuk(mPuk, mPin);
+                final int[] result = ITelephony.Stub.asInterface(ServiceManager
+                        .checkService("phone")).supplyPukReportResult(mPuk, mPin);
 
                 post(new Runnable() {
                     public void run() {
-                        onSimLockChangedResponse(result);
+                        onSimLockChangedResponse(result[0], result[1]);
                     }
                 });
             } catch (RemoteException e) {
                 post(new Runnable() {
                     public void run() {
-                        onSimLockChangedResponse(false);
+                        onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
                     }
                 });
             }
@@ -228,6 +253,22 @@
         return mSimUnlockProgressDialog;
     }
 
+    private Dialog getPukRemainingAttemptsDialog(int remaining) {
+        String msg = getPukPasswordErrorMessage(remaining);
+        if (mRemainingAttemptsDialog == null) {
+            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+            builder.setMessage(msg);
+            builder.setCancelable(false);
+            builder.setNeutralButton(R.string.ok, null);
+            mRemainingAttemptsDialog = builder.create();
+            mRemainingAttemptsDialog.getWindow().setType(
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+        } else {
+            mRemainingAttemptsDialog.setMessage(msg);
+        }
+        return mRemainingAttemptsDialog;
+    }
+
     private boolean checkPuk() {
         // make sure the puk is at least 8 digits long.
         if (mPasswordEntry.getText().length() >= 8) {
@@ -257,17 +298,33 @@
         if (!mCheckInProgress) {
             mCheckInProgress = true;
             new CheckSimPuk(mPukText, mPinText) {
-                void onSimLockChangedResponse(final boolean success) {
+                void onSimLockChangedResponse(final int result, final int attemptsRemaining) {
                     post(new Runnable() {
                         public void run() {
                             if (mSimUnlockProgressDialog != null) {
                                 mSimUnlockProgressDialog.hide();
                             }
-                            if (success) {
+                            if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
+                                KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
                                 mCallback.dismiss(true);
                             } else {
+                                if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
+                                    if (attemptsRemaining <= 2) {
+                                        // this is getting critical - show dialog
+                                        getPukRemainingAttemptsDialog(attemptsRemaining).show();
+                                    } else {
+                                        // show message
+                                        mSecurityMessageDisplay.setMessage(
+                                                getPukPasswordErrorMessage(attemptsRemaining), true);
+                                    }
+                                } else {
+                                    mSecurityMessageDisplay.setMessage(getContext().getString(
+                                            R.string.kg_password_puk_failed), true);
+                                }
+                                if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+                                        + " UpdateSim.onSimCheckResponse: "
+                                        + " attemptsRemaining=" + attemptsRemaining);
                                 mStateMachine.reset();
-                                mSecurityMessageDisplay.setMessage(R.string.kg_invalid_puk, true);
                             }
                             mCheckInProgress = false;
                         }
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 616090e..8054788 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -58,7 +58,14 @@
     // Set this to true to have the watchdog record kernel thread stacks when it fires
     static final boolean RECORD_KERNEL_THREADS = true;
 
-    static final int TIME_TO_WAIT = DB ? 5*1000 : 30*1000;
+    static final long DEFAULT_TIMEOUT = DB ? 10*1000 : 60*1000;
+    static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2;
+
+    // These are temporally ordered: larger values as lateness increases
+    static final int COMPLETED = 0;
+    static final int WAITING = 1;
+    static final int WAITED_HALF = 2;
+    static final int OVERDUE = 3;
 
     static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
         "/system/bin/mediaserver",
@@ -87,13 +94,17 @@
     public final class HandlerChecker implements Runnable {
         private final Handler mHandler;
         private final String mName;
+        private final long mWaitMax;
         private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
         private boolean mCompleted;
         private Monitor mCurrentMonitor;
+        private long mStartTime;
 
-        HandlerChecker(Handler handler, String name) {
+        HandlerChecker(Handler handler, String name, long waitMaxMillis) {
             mHandler = handler;
             mName = name;
+            mWaitMax = waitMaxMillis;
+            mCompleted = true;
         }
 
         public void addMonitor(Monitor monitor) {
@@ -111,13 +122,34 @@
                 mCompleted = true;
                 return;
             }
+
+            if (!mCompleted) {
+                // we already have a check in flight, so no need
+                return;
+            }
+
             mCompleted = false;
             mCurrentMonitor = null;
+            mStartTime = SystemClock.uptimeMillis();
             mHandler.postAtFrontOfQueue(this);
         }
 
-        public boolean isCompletedLocked() {
-            return mCompleted;
+        public boolean isOverdueLocked() {
+            return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);
+        }
+
+        public int getCompletionStateLocked() {
+            if (mCompleted) {
+                return COMPLETED;
+            } else {
+                long latency = SystemClock.uptimeMillis() - mStartTime;
+                if (latency < mWaitMax/2) {
+                    return WAITING;
+                } else if (latency < mWaitMax) {
+                    return WAITED_HALF;
+                }
+            }
+            return OVERDUE;
         }
 
         public Thread getThread() {
@@ -186,16 +218,19 @@
 
         // The shared foreground thread is the main checker.  It is where we
         // will also dispatch monitor checks and do other work.
-        mMonitorChecker = new HandlerChecker(FgThread.getHandler(), "foreground thread");
+        mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
+                "foreground thread", DEFAULT_TIMEOUT);
         mHandlerCheckers.add(mMonitorChecker);
         // Add checker for main thread.  We only do a quick check since there
         // can be UI running on the thread.
         mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
-                "main thread"));
+                "main thread", DEFAULT_TIMEOUT));
         // Add checker for shared UI thread.
-        mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread"));
+        mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
+                "ui thread", DEFAULT_TIMEOUT));
         // And also check IO thread.
-        mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread"));
+        mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
+                "i/o thread", DEFAULT_TIMEOUT));
     }
 
     public void init(Context context, BatteryService battery,
@@ -242,11 +277,15 @@
     }
 
     public void addThread(Handler thread, String name) {
+        addThread(thread, name, DEFAULT_TIMEOUT);
+    }
+
+    public void addThread(Handler thread, String name, long timeoutMillis) {
         synchronized (this) {
             if (isAlive()) {
                 throw new RuntimeException("Threads can't be added once the Watchdog is running");
             }
-            mHandlerCheckers.add(new HandlerChecker(thread, name));
+            mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
         }
     }
 
@@ -259,21 +298,20 @@
         pms.reboot(false, reason, false);
     }
 
-    private boolean haveAllCheckersCompletedLocked() {
+    private int evaluateCheckerCompletionLocked() {
+        int state = COMPLETED;
         for (int i=0; i<mHandlerCheckers.size(); i++) {
             HandlerChecker hc = mHandlerCheckers.get(i);
-            if (!hc.isCompletedLocked()) {
-                return false;
-            }
+            state = Math.max(state, hc.getCompletionStateLocked());
         }
-        return true;
+        return state;
     }
 
     private ArrayList<HandlerChecker> getBlockedCheckersLocked() {
         ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
         for (int i=0; i<mHandlerCheckers.size(); i++) {
             HandlerChecker hc = mHandlerCheckers.get(i);
-            if (!hc.isCompletedLocked()) {
+            if (hc.isOverdueLocked()) {
                 checkers.add(hc);
             }
         }
@@ -299,14 +337,12 @@
             final String subject;
             final boolean allowRestart;
             synchronized (this) {
-                long timeout = TIME_TO_WAIT;
-                if (!waitedHalf) {
-                    // If we are not at the half-point of waiting, perform a
-                    // new set of checks.  Otherwise we are still waiting for a previous set.
-                    for (int i=0; i<mHandlerCheckers.size(); i++) {
-                        HandlerChecker hc = mHandlerCheckers.get(i);
-                        hc.scheduleCheckLocked();
-                    }
+                long timeout = CHECK_INTERVAL;
+                // Make sure we (re)spin the checkers that have become idle within
+                // this wait-and-check interval
+                for (int i=0; i<mHandlerCheckers.size(); i++) {
+                    HandlerChecker hc = mHandlerCheckers.get(i);
+                    hc.scheduleCheckLocked();
                 }
 
                 // NOTE: We use uptimeMillis() here because we do not want to increment the time we
@@ -320,26 +356,31 @@
                     } catch (InterruptedException e) {
                         Log.wtf(TAG, e);
                     }
-                    timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
+                    timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
                 }
 
-                if (haveAllCheckersCompletedLocked()) {
-                    // The monitors have returned.
+                final int waitState = evaluateCheckerCompletionLocked();
+                if (waitState == COMPLETED) {
+                    // The monitors have returned; reset
                     waitedHalf = false;
                     continue;
-                }
-
-                if (!waitedHalf) {
-                    // We've waited half the deadlock-detection interval.  Pull a stack
-                    // trace and wait another half.
-                    ArrayList<Integer> pids = new ArrayList<Integer>();
-                    pids.add(Process.myPid());
-                    ActivityManagerService.dumpStackTraces(true, pids, null, null,
-                            NATIVE_STACKS_OF_INTEREST);
-                    waitedHalf = true;
+                } else if (waitState == WAITING) {
+                    // still waiting but within their configured intervals; back off and recheck
+                    continue;
+                } else if (waitState == WAITED_HALF) {
+                    if (!waitedHalf) {
+                        // We've waited half the deadlock-detection interval.  Pull a stack
+                        // trace and wait another half.
+                        ArrayList<Integer> pids = new ArrayList<Integer>();
+                        pids.add(Process.myPid());
+                        ActivityManagerService.dumpStackTraces(true, pids, null, null,
+                                NATIVE_STACKS_OF_INTEREST);
+                        waitedHalf = true;
+                    }
                     continue;
                 }
 
+                // something is overdue!
                 blockedCheckers = getBlockedCheckersLocked();
                 subject = describeCheckersLocked(blockedCheckers);
                 allowRestart = mAllowRestart;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index e075862..a781d5f 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -221,6 +221,14 @@
     static final int REMOVE_CHATTY = 1<<16;
 
     /**
+     * Timeout (in milliseconds) after which the watchdog should declare that
+     * our handler thread is wedged.  The usual default for such things is one
+     * minute but we sometimes do very lengthy I/O operations on this thread,
+     * such as installing multi-gigabyte applications, so ours needs to be longer.
+     */
+    private static final long WATCHDOG_TIMEOUT = 1000*60*10;     // ten minutes
+
+    /**
      * Whether verification is enabled by default.
      */
     private static final boolean DEFAULT_VERIFY_ENABLE = true;
@@ -1115,7 +1123,8 @@
         synchronized (mPackages) {
             mHandlerThread.start();
             mHandler = new PackageHandler(mHandlerThread.getLooper());
-            Watchdog.getInstance().addThread(mHandler, mHandlerThread.getName());
+            Watchdog.getInstance().addThread(mHandler, mHandlerThread.getName(),
+                    WATCHDOG_TIMEOUT);
 
             File dataDir = Environment.getDataDirectory();
             mAppDataDir = new File(dataDir, "data");
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index b0cfd7e..d3ccba6 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -2070,8 +2070,10 @@
                 if (intent.getAction() != null) {
                     filter.addAction(intent.getAction());
                 }
-                for (String cat : intent.getCategories()) {
-                    filter.addCategory(cat);
+                if (intent.getCategories() != null) {
+                    for (String cat : intent.getCategories()) {
+                        filter.addCategory(cat);
+                    }
                 }
                 if ((flags&PackageManager.MATCH_DEFAULT_ONLY) != 0) {
                     filter.addCategory(Intent.CATEGORY_DEFAULT);
@@ -2088,6 +2090,13 @@
                 if (path != null) {
                     filter.addDataPath(path);
                 }
+                if (intent.getType() != null) {
+                    try {
+                        filter.addDataType(intent.getType());
+                    } catch (IntentFilter.MalformedMimeTypeException ex) {
+                        Slog.w(TAG, "Malformed mimetype " + intent.getType() + " for " + cn);
+                    }
+                }
                 PreferredActivity pa = new PreferredActivity(filter, match, set, cn, true);
                 editPreferredActivitiesLPw(userId).addFilter(pa);
             } else if (!haveNonSys) {
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index afa4f78..52f2325 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -107,7 +107,7 @@
     Region mTouchExcludeRegion = new Region();
 
     /** Save allocating when retrieving tasks */
-    ArrayList<Task> mTmpTasks = new ArrayList<Task>();
+    ArrayList<Task> mTaskHistory = new ArrayList<Task>();
 
     /** Save allocating when calculating rects */
     Rect mTmpRect = new Rect();
@@ -163,7 +163,7 @@
     void moveStack(TaskStack stack, boolean toTop) {
         mStackHistory.remove(stack);
         mStackHistory.add(toTop ? mStackHistory.size() : 0, stack);
-        mService.moveStackWindowsLocked(stack);
+        mService.moveStackWindowsLocked(this);
     }
 
     public boolean isPrivate() {
@@ -175,14 +175,35 @@
      * @return All the Tasks, in order, on this display.
      */
     ArrayList<Task> getTasks() {
-        mTmpTasks.clear();
-        final int numStacks = mStackHistory.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            mTmpTasks.addAll(mStackHistory.get(stackNdx).getTasks());
+        return mTaskHistory;
+    }
+
+    void addTask(Task task, boolean toTop) {
+        mTaskHistory.remove(task);
+
+        final int userId = task.mUserId;
+        int taskNdx;
+        final int numTasks = mTaskHistory.size();
+        if (toTop) {
+            for (taskNdx = numTasks - 1; taskNdx >= 0; --taskNdx) {
+                if (mTaskHistory.get(taskNdx).mUserId == userId) {
+                    break;
+                }
+            }
+            ++taskNdx;
+        } else {
+            for (taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
+                if (mTaskHistory.get(taskNdx).mUserId == userId) {
+                    break;
+                }
+            }
         }
-        if (WindowManagerService.DEBUG_LAYERS) Slog.i(TAG, "getTasks: mStackHistory=" +
-                mStackHistory);
-        return mTmpTasks;
+
+        mTaskHistory.add(taskNdx, task);
+    }
+
+    void removeTask(Task task) {
+        mTaskHistory.remove(task);
     }
 
     TaskStack getHomeStack() {
@@ -205,10 +226,9 @@
 
     /** @return The number of tokens in all of the Tasks on this display. */
     int numTokens() {
-        getTasks();
         int count = 0;
-        for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            count += mTmpTasks.get(taskNdx).mAppTokens.size();
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            count += mTaskHistory.get(taskNdx).mAppTokens.size();
         }
         return count;
     }
@@ -469,8 +489,8 @@
             pw.println();
             pw.println("  Application tokens in Z order:");
             getTasks();
-            for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                AppTokenList tokens = mTmpTasks.get(taskNdx).mAppTokens;
+            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+                AppTokenList tokens = mTaskHistory.get(taskNdx).mAppTokens;
                 for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
                     final AppWindowToken wtoken = tokens.get(tokenNdx);
                     pw.print("  App #"); pw.print(ndx--);
diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java
index 2347a19..34bef68 100644
--- a/services/java/com/android/server/wm/TaskStack.java
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -120,6 +120,7 @@
         mTasks.add(stackNdx, task);
 
         task.mStack = this;
+        mDisplayContent.addTask(task, toTop);
         return mDisplayContent.moveHomeStackBox(mStackId == HOME_STACK_ID);
     }
 
@@ -145,6 +146,7 @@
         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
         mStackBox.makeDirty();
         mTasks.remove(task);
+        mDisplayContent.removeTask(task);
     }
 
     int remove() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index abc2b2b..63e09db 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4733,11 +4733,9 @@
         return index;
     }
 
-    void moveStackWindowsLocked(TaskStack stack) {
-        DisplayContent displayContent = stack.getDisplayContent();
-
+    void moveStackWindowsLocked(DisplayContent displayContent) {
         // First remove all of the windows from the list.
-        final ArrayList<Task> tasks = stack.getTasks();
+        final ArrayList<Task> tasks = displayContent.getTasks();
         final int numTasks = tasks.size();
         for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
             AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
@@ -4813,7 +4811,7 @@
                 }
                 final TaskStack stack = task.mStack;
                 stack.moveTaskToBottom(task);
-                moveStackWindowsLocked(stack);
+                moveStackWindowsLocked(stack.getDisplayContent());
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -5724,7 +5722,7 @@
         canvas.drawBitmap(rawss, matrix, null);
         canvas.setBitmap(null);
 
-        if (true || DEBUG_SCREENSHOT) {
+        if (DEBUG_SCREENSHOT) {
             // TEST IF IT's ALL BLACK
             int[] buffer = new int[bm.getWidth() * bm.getHeight()];
             bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
@@ -8434,20 +8432,6 @@
                         && !mWallpaperTarget.mWinAnimator.isDummyAnimation()
                     ? null : mWallpaperTarget;
 
-            // If there is a wallpaper target and the target is neither opening nor closing, then
-            // there exists an app on top of the wallpaper target that has a translucent
-            // background.
-            // If the pending transition is an exit, we should add the wallpaper target to the list
-            // of opening apps so that the translucent app on top of it will animate correctly.
-            final AppWindowToken wallpaperTargetAppToken =
-                    mWallpaperTarget != null ? mWallpaperTarget.mAppToken : null;
-            if (wallpaperTargetAppToken != null
-                    && !mClosingApps.contains(wallpaperTargetAppToken)
-                    && !mOpeningApps.contains(wallpaperTargetAppToken)
-                    && (transit & AppTransition.TRANSIT_EXIT_MASK) != 0) {
-                mOpeningApps.add(wallpaperTargetAppToken);
-                NN++;
-            }
             mInnerFields.mWallpaperMayChange = false;
 
             // The top-most window will supply the layout params,
@@ -8533,7 +8517,8 @@
                         break;
                 }
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit: " + transit);
-            } else if ((oldWallpaper != null) && !mOpeningApps.contains(oldWallpaper.mAppToken)) {
+            } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty()
+                    && !mOpeningApps.contains(oldWallpaper.mAppToken)) {
                 // We are transitioning from an activity with
                 // a wallpaper to one without.
                 transit = AppTransition.TRANSIT_WALLPAPER_CLOSE;
@@ -8602,8 +8587,7 @@
                 wtoken.mAppAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
                 wtoken.mAppAnimator.animation = null;
-                setTokenVisibilityLocked(wtoken, animLp, false,
-                        transit, false);
+                setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
                 wtoken.updateReportedVisibilityLocked();
                 wtoken.waitingToHide = false;
                 // Force the allDrawn flag, because we want to start
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 584411b..7bd2c84 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -155,6 +155,26 @@
     boolean supplyPuk(String puk, String pin);
 
     /**
+     * Supply a pin to unlock the SIM.  Blocks until a result is determined.
+     * Returns a specific success/error code.
+     * @param pin The pin to check.
+     * @return retValue[0] = Phone.PIN_RESULT_SUCCESS on success. Otherwise error code
+     *         retValue[1] = number of attempts remaining if known otherwise -1
+     */
+    int[] supplyPinReportResult(String pin);
+
+    /**
+     * Supply puk to unlock the SIM and set SIM pin to new pin.
+     * Blocks until a result is determined.
+     * Returns a specific success/error code
+     * @param puk The puk to check
+     *        pin The pin to check.
+     * @return retValue[0] = Phone.PIN_RESULT_SUCCESS on success. Otherwise error code
+     *         retValue[1] = number of attempts remaining if known otherwise -1
+     */
+    int[] supplyPukReportResult(String puk, String pin);
+
+    /**
      * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
      * without SEND (so <code>dial</code> is not appropriate).
      *
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 4a4a62b..4163255 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -86,6 +86,14 @@
     public static final String REASON_LINK_PROPERTIES_CHANGED = "linkPropertiesChanged";
 
     /**
+     * Return codes for supplyPinReturnResult and
+     * supplyPukReturnResult APIs
+     */
+    public static final int PIN_RESULT_SUCCESS = 0;
+    public static final int PIN_PASSWORD_INCORRECT = 1;
+    public static final int PIN_GENERAL_FAILURE = 2;
+
+    /**
      * Return codes for <code>enableApnType()</code>
      */
     public static final int APN_ALREADY_ACTIVE     = 0;