am 36c7d313: Merge "Import translations. DO NOT MERGE" into jb-mr1-dev

* commit '36c7d313af815c8842d7ca9002ff36bfe347716b':
  Import translations. DO NOT MERGE
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 43a163d..6382cee 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -858,6 +858,9 @@
      */
     public Intent getAssistIntent(Context context, int userHandle) {
         try {
+            if (mService == null) {
+                return null;
+            }
             ComponentName comp = mService.getAssistIntent(userHandle);
             if (comp == null) {
                 return null;
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index 0138b1c..2fd52b8 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -107,6 +107,15 @@
                 && Objects.equal(mDeviceAlias, other.mDeviceAlias);
     }
 
+    /**
+     * Returns true if the other display is not null and has the same address as this one.
+     * Can be used to perform identity comparisons on displays ignoring properties
+     * that might change during a connection such as the name or alias.
+     */
+    public boolean hasSameAddress(WifiDisplay other) {
+        return other != null && mDeviceAddress.equals(other.mDeviceAddress);
+    }
+
     @Override
     public int hashCode() {
         // The address on its own should be sufficiently unique for hashing purposes.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f07002e..6f1cc94 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -930,11 +930,13 @@
      */
     public void onConfigureWindow(Window win, boolean isFullscreen,
             boolean isCandidatesOnly) {
-        if (isFullscreen) {
-            mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
-        } else {
-            mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
+        final int currentHeight = mWindow.getWindow().getAttributes().height;
+        final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
+        if (mIsInputViewShown && currentHeight != newHeight) {
+            Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
+                    + currentHeight + " -> " + newHeight);
         }
+        mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
     }
     
     /**
@@ -997,10 +999,11 @@
     }
     
     void updateExtractFrameVisibility() {
-        int vis;
+        final int vis;
         if (isFullscreenMode()) {
             vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
-            mExtractFrame.setVisibility(View.VISIBLE);
+            // "vis" should be applied for the extract frame as well in the fullscreen mode.
+            mExtractFrame.setVisibility(vis);
         } else {
             vis = View.VISIBLE;
             mExtractFrame.setVisibility(View.GONE);
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 874e80a..8dc900e 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -351,6 +351,8 @@
         DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
 
         if (dhcpAction == DhcpAction.START) {
+            /* Stop any existing DHCP daemon before starting new */
+            NetworkUtils.stopDhcp(mInterfaceName);
             if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
             success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
             mDhcpInfo = dhcpInfoInternal;
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index ed51818..0ca9183 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -44,6 +44,7 @@
     public static final long TRACE_TAG_AUDIO = 1L << 8;
     public static final long TRACE_TAG_VIDEO = 1L << 9;
     public static final long TRACE_TAG_CAMERA = 1L << 10;
+    private static final long TRACE_TAG_NOT_READY = 1L << 63;
 
     public static final int TRACE_FLAGS_START_BIT = 1;
     public static final String[] TRACE_TAGS = {
@@ -53,11 +54,8 @@
 
     public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
 
-    // This works as a "not ready" flag because TRACE_TAG_ALWAYS is always set.
-    private static final long TRACE_FLAGS_NOT_READY = 0;
-
     // Must be volatile to avoid word tearing.
-    private static volatile long sEnabledTags = TRACE_FLAGS_NOT_READY;
+    private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
 
     private static native long nativeGetEnabledTags();
     private static native void nativeTraceCounter(long tag, String name, int value);
@@ -99,7 +97,7 @@
      */
     private static long cacheEnabledTags() {
         long tags = nativeGetEnabledTags();
-        if (tags == TRACE_FLAGS_NOT_READY) {
+        if (tags == TRACE_TAG_NOT_READY) {
             Log.w(TAG, "Unexpected value from nativeGetEnabledTags: " + tags);
             // keep going
         }
@@ -115,7 +113,7 @@
      */
     public static boolean isTagEnabled(long traceTag) {
         long tags = sEnabledTags;
-        if (tags == TRACE_FLAGS_NOT_READY) {
+        if (tags == TRACE_TAG_NOT_READY) {
             tags = cacheEnabledTags();
         }
         return (tags & traceTag) != 0;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b1a44c5..521e686 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -291,6 +291,7 @@
         mErrorWasChanged = true;
 
         if (mError == null) {
+            setErrorIcon(null);
             if (mErrorPopup != null) {
                 if (mErrorPopup.isShowing()) {
                     mErrorPopup.dismiss();
@@ -299,10 +300,11 @@
                 mErrorPopup = null;
             }
 
-            setErrorIcon(null);
-        } else if (mTextView.isFocused()) {
-            showError();
+        } else {
             setErrorIcon(icon);
+            if (mTextView.isFocused()) {
+                showError();
+            }
         }
     }
 
@@ -321,8 +323,6 @@
             if (mErrorPopup.isShowing()) {
                 mErrorPopup.dismiss();
             }
-
-            setErrorIcon(null);
         }
 
         mShowErrorAfterAttach = false;
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index e7c4c23..9537ac4 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -122,9 +122,9 @@
     return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0);
 }
 
-static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
+static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
 {
-    return (jboolean)(::wifi_stop_supplicant() == 0);
+    return (jboolean)(::wifi_stop_supplicant(p2pSupported) == 0);
 }
 
 static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject, jstring jIface)
@@ -204,7 +204,7 @@
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
     { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
-    { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
+    { "killSupplicant", "(Z)Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "(Ljava/lang/String;)Z",
             (void *)android_net_wifi_connectToSupplicant },
     { "closeSupplicantConnection", "(Ljava/lang/String;)V",
diff --git a/core/res/res/drawable-hdpi/ic_coins_l.png b/core/res/res/drawable-hdpi/ic_coins_l.png
new file mode 100644
index 0000000..e1e3e2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_coins_l.png b/core/res/res/drawable-mdpi/ic_coins_l.png
new file mode 100644
index 0000000..a6d7abb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_coins_l.png b/core/res/res/drawable-xhdpi/ic_coins_l.png
new file mode 100644
index 0000000..84e7e72
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/layout/sms_short_code_confirmation_dialog.xml b/core/res/res/layout/sms_short_code_confirmation_dialog.xml
index ec39d97..d82f560 100644
--- a/core/res/res/layout/sms_short_code_confirmation_dialog.xml
+++ b/core/res/res/layout/sms_short_code_confirmation_dialog.xml
@@ -30,9 +30,9 @@
         style="?android:attr/textAppearanceMedium"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="16dip"
-        android:paddingRight="16dip"
-        android:paddingTop="8dip"
+        android:paddingLeft="20dip"
+        android:paddingRight="20dip"
+        android:paddingTop="16dip"
         android:paddingBottom="16dip" />
 
     <TableLayout android:id="@+id/sms_short_code_detail_layout"
@@ -51,7 +51,7 @@
                 android:layout_height="wrap_content"
                 android:paddingLeft="8dip"
                 android:paddingRight="8dip"
-                android:src="@null" />
+                android:src="@drawable/ic_coins_l" />
             <TextView android:id="@+id/sms_short_code_detail_message"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
@@ -60,14 +60,19 @@
         <TableRow
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" >
-
-            <CheckBox android:id="@+id/sms_short_code_remember_choice_checkbox"
-                android:layout_width="wrap_content"
+            <RelativeLayout android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingRight="8dip" />
+                android:paddingTop="12dip"
+                android:paddingLeft="8dip" >
+            <CheckBox android:id="@+id/sms_short_code_remember_choice_checkbox"
+                android:paddingTop="11dip"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            </RelativeLayout>
             <TextView android:id="@+id/sms_short_code_remember_choice_text"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:paddingTop="18dip"
                 android:text="@string/sms_short_code_remember_choice" />
         </TableRow>
 
@@ -77,6 +82,7 @@
 
             <Space android:layout_gravity="fill" />
             <TextView android:id="@+id/sms_short_code_remember_undo_instruction"
+                android:paddingTop="10dip"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content" />
         </TableRow>
diff --git a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
new file mode 100644
index 0000000..e3f3144
--- /dev/null
+++ b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3b7d73a..ea28a51 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1000,4 +1000,25 @@
          provisioning on some carriers, working around a bug (7305641)
          where if the preferred is used we don't try the others. -->
     <bool name="config_dontPreferApn">false</bool>
+
+    <!-- Vibrator pattern to be used as the default for notifications
+         that specify DEFAULT_VIBRATE.
+     -->
+    <integer-array name="config_defaultNotificationVibePattern">
+        <item>0</item>
+        <item>150</item>
+        <item>200</item>
+        <item>250</item>
+    </integer-array>
+
+    <!-- Vibrator pattern to be used as the default for notifications
+         that do not specify vibration but vibrate anyway because the device
+         is in vibrate mode.
+     -->
+    <integer-array name="config_notificationFallbackVibePattern">
+        <item>0</item>
+        <item>33</item>
+        <item>150</item>
+        <item>50</item>
+    </integer-array>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 68a0289..6858732 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1525,6 +1525,8 @@
   <java-symbol type="array" name="radioAttributes" />
   <java-symbol type="array" name="config_oemUsbModeOverride" />
   <java-symbol type="array" name="config_locationProviderPackageNames" />
+  <java-symbol type="array" name="config_defaultNotificationVibePattern" />
+  <java-symbol type="array" name="config_notificationFallbackVibePattern" />
   <java-symbol type="bool" name="config_animateScreenLights" />
   <java-symbol type="bool" name="config_automatic_brightness_available" />
   <java-symbol type="bool" name="config_sf_limitedAlpha" />
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 315196e..22f699f 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -436,6 +436,8 @@
 
     private boolean mDockAudioMediaEnabled = true;
 
+    private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
     ///////////////////////////////////////////////////////////////////////////
     // Construction
     ///////////////////////////////////////////////////////////////////////////
@@ -3324,6 +3326,13 @@
                                 mBluetoothA2dpEnabled ?
                                         AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
                     }
+
+                    synchronized (mSettingsLock) {
+                        AudioSystem.setForceUse(AudioSystem.FOR_DOCK,
+                                mDockAudioMediaEnabled ?
+                                        AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE);
+                    }
+
                     // indicate the end of reconfiguration phase to audio HAL
                     AudioSystem.setParameters("restarting=false");
                     break;
@@ -3751,13 +3760,7 @@
                         config = AudioSystem.FORCE_BT_CAR_DOCK;
                         break;
                     case Intent.EXTRA_DOCK_STATE_LE_DESK:
-                        synchronized (mSettingsLock) {
-                            if (mDockAudioMediaEnabled) {
-                                config = AudioSystem.FORCE_ANALOG_DOCK;
-                            } else {
-                                config = AudioSystem.FORCE_NONE;
-                            }
-                        }
+                        config = AudioSystem.FORCE_ANALOG_DOCK;
                         break;
                     case Intent.EXTRA_DOCK_STATE_HE_DESK:
                         config = AudioSystem.FORCE_DIGITAL_DOCK;
@@ -3766,8 +3769,14 @@
                     default:
                         config = AudioSystem.FORCE_NONE;
                 }
-
-                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
+                // Low end docks have a menu to enable or disable audio
+                // (see mDockAudioMediaEnabled)
+                if (!((dockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                      ((dockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) &&
+                       (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK)))) {
+                    AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
+                }
+                mDockState = dockState;
             } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
                 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                                BluetoothProfile.STATE_DISCONNECTED);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4414191..169502b 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -50,7 +50,7 @@
  * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>
  * <tr><td>{@link #KEY_CHANNEL_COUNT}</td><td>Integer</td><td></td></tr>
  * <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr>
- * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if content is AAC audio, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr>
+ * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr>
  * <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr>
  * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>A mask of audio channel assignments</td></tr>
  * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr>
@@ -140,6 +140,8 @@
      * A key mapping to a value of 1 if the content is AAC audio and
      * audio frames are prefixed with an ADTS header.
      * The associated value is an integer (0 or 1).
+     * This key is only supported when _decoding_ content, it cannot
+     * be used to configure an encoder to emit ADTS output.
      */
     public static final String KEY_IS_ADTS = "is-adts";
 
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 2a5a16e..8701f36 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -862,7 +862,7 @@
     private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) {
         for (int i = 0; i < displays.length; i++) {
             final WifiDisplay other = displays[i];
-            if (d.getDeviceAddress().equals(other.getDeviceAddress())) {
+            if (d.hasSameAddress(other)) {
                 return other;
             }
         }
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 2669c7e..b1104cc 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -141,7 +141,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:layout_width="128dp"
             android:id="@+id/search_light"
             android:layout_height="match_parent"
@@ -282,7 +282,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:layout_width="162dp"
             android:id="@+id/search_light"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 440a4e1..da52d89 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -145,7 +145,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:layout_width="80dp"
             android:id="@+id/search_light"
             android:layout_height="match_parent"
@@ -289,7 +289,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:id="@+id/search_light"
             android:layout_height="80dp"
             android:layout_width="match_parent"
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 76ba811..4e8aba7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -129,12 +129,19 @@
         @Override
         protected void onConfigurationChanged(Configuration newConfig) {
             super.onConfigurationChanged(newConfig);
-            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
-                // only propagate configuration messages if we're currently showing
-                maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
-            } else {
-                if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
-            }
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (KeyguardViewManager.this) {
+                        if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+                            // only propagate configuration messages if we're currently showing
+                            maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
+                        } else {
+                            if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
+                        }
+                    }
+                }
+            });
         }
 
         @Override
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index df4c661..c227619 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.SearchManager;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -166,6 +167,9 @@
     /** UserManager for querying number of users */
     private UserManager mUserManager;
 
+    /** SearchManager for determining whether or not search assistant is available */
+    private SearchManager mSearchManager;
+
     /**
      * Used to keep the device awake while to ensure the keyguard finishes opening before
      * we sleep.
@@ -527,6 +531,7 @@
      * Let us know that the system is ready after startup.
      */
     public void onSystemReady() {
+        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "onSystemReady");
             mSystemReady = true;
@@ -1313,6 +1318,9 @@
                     // showing secure lockscreen; disable ticker.
                     flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER;
                 }
+                if (!isAssistantAvailable()) {
+                    flags |= StatusBarManager.DISABLE_SEARCH;
+                }
             }
 
             if (DEBUG) {
@@ -1410,4 +1418,8 @@
         mKeyguardViewManager.showAssistant();
     }
 
+    private boolean isAssistantAvailable() {
+        return mSearchManager != null
+                && mSearchManager.getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
+    }
 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index ad1dfb2..a7c4d73 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2686,18 +2686,8 @@
                             state + "/" + info.getDetailedState());
                     }
 
-                    // Connectivity state changed:
-                    // [31-14] Reserved for future use
-                    // [13-10] Network subtype (for mobile network, as defined
-                    //         by TelephonyManager)
-                    // [9-4] Detailed state ordinal (as defined by
-                    //         NetworkInfo.DetailedState)
-                    // [3-0] Network type (as defined by ConnectivityManager)
-                    int eventLogParam = (info.getType() & 0xf) |
-                            ((info.getDetailedState().ordinal() & 0x3f) << 4) |
-                            (info.getSubtype() << 10);
-                    EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED,
-                            eventLogParam);
+                    EventLogTags.writeConnectivityStateChanged(
+                            info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
 
                     if (info.getDetailedState() ==
                             NetworkInfo.DetailedState.FAILED) {
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 0fe66fc..8bc2da2 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -135,12 +135,8 @@
 # ---------------------------
 # ConnectivityService.java
 # ---------------------------
-# Connectivity state changed:
-# [31-14] Reserved for future use
-# [13-10] Network subtype (for mobile network, as defined by TelephonyManager)
-# [ 9- 4] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
-# [ 3- 0] Network type (as defined by ConnectivityManager)
-50020 connectivity_state_changed (custom|1|5)
+# Connectivity state changed
+50020 connectivity_state_changed (type|1),(subtype|1),(state|1)
 
 
 # ---------------------------
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 89fa6d0..7a55497c 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -506,7 +506,7 @@
                 }
             } else {
                 Intent statusChanged = new Intent();
-                statusChanged.putExtras(extras);
+                statusChanged.putExtras(new Bundle(extras));
                 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
                 try {
                     synchronized (this) {
@@ -541,7 +541,7 @@
                 }
             } else {
                 Intent locationChanged = new Intent();
-                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
+                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location));
                 try {
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index f3a38f0..fa84f486 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -101,6 +101,7 @@
     private static final int SHORT_DELAY = 2000; // 2 seconds
 
     private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
+    private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
 
     private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
     private static final boolean SCORE_ONGOING_HIGHER = false;
@@ -125,6 +126,9 @@
     private int mDefaultNotificationLedOn;
     private int mDefaultNotificationLedOff;
 
+    private long[] mDefaultVibrationPattern;
+    private long[] mFallbackVibrationPattern;
+
     private boolean mSystemReady;
     private int mDisabledNotifications;
 
@@ -596,6 +600,19 @@
         }
     }
 
+    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
+        int[] ar = r.getIntArray(resid);
+        if (ar == null) {
+            return def;
+        }
+        final int len = ar.length > maxlen ? maxlen : ar.length;
+        long[] out = new long[len];
+        for (int i=0; i<len; i++) {
+            out[i] = ar[i];
+        }
+        return out;
+    }
+
     NotificationManagerService(Context context, StatusBarManagerService statusBar,
             LightsService lights)
     {
@@ -622,6 +639,16 @@
         mDefaultNotificationLedOff = resources.getInteger(
                 com.android.internal.R.integer.config_defaultNotificationLedOff);
 
+        mDefaultVibrationPattern = getLongArray(resources,
+                com.android.internal.R.array.config_defaultNotificationVibePattern,
+                VIBRATE_PATTERN_MAXLEN,
+                DEFAULT_VIBRATE_PATTERN);
+
+        mFallbackVibrationPattern = getLongArray(resources,
+                com.android.internal.R.array.config_notificationFallbackVibePattern,
+                VIBRATE_PATTERN_MAXLEN,
+                DEFAULT_VIBRATE_PATTERN);
+
         // Don't start allowing notifications until the setup wizard has run once.
         // After that, including subsequent boots, init with notifications turned on.
         // This works on the first boot because the setup wizard will toggle this
@@ -1050,16 +1077,27 @@
 
                 final AudioManager audioManager = (AudioManager) mContext
                 .getSystemService(Context.AUDIO_SERVICE);
+
                 // sound
                 final boolean useDefaultSound =
                     (notification.defaults & Notification.DEFAULT_SOUND) != 0;
-                if (useDefaultSound || notification.sound != null) {
-                    Uri uri;
-                    if (useDefaultSound) {
-                        uri = Settings.System.DEFAULT_NOTIFICATION_URI;
-                    } else {
-                        uri = notification.sound;
-                    }
+
+                Uri soundUri = null;
+                boolean hasValidSound = false;
+
+                if (useDefaultSound) {
+                    soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+                    // check to see if the default notification sound is silent
+                    ContentResolver resolver = mContext.getContentResolver();
+                    hasValidSound = Settings.System.getString(resolver,
+                           Settings.System.NOTIFICATION_SOUND) != null;
+                } else if (notification.sound != null) {
+                    soundUri = notification.sound;
+                    hasValidSound = (soundUri != null);
+                }
+
+                if (hasValidSound) {
                     boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
                     int audioStreamType;
                     if (notification.audioStreamType >= 0) {
@@ -1076,7 +1114,7 @@
                         try {
                             final IRingtonePlayer player = mAudioService.getRingtonePlayer();
                             if (player != null) {
-                                player.playAsync(uri, user, looping, audioStreamType);
+                                player.playAsync(soundUri, user, looping, audioStreamType);
                             }
                         } catch (RemoteException e) {
                         } finally {
@@ -1086,24 +1124,40 @@
                 }
 
                 // vibrate
+                // Does the notification want to specify its own vibration?
+                final boolean hasCustomVibrate = notification.vibrate != null;
+
                 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
-                // we always vibrate, even if no vibration was specified
+                // and no other vibration is specified, we apply the default vibration anyway
                 final boolean convertSoundToVibration =
-                           notification.vibrate == null
-                        && (useDefaultSound || notification.sound != null)
+                           !hasCustomVibrate
+                        && hasValidSound
                         && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
 
+                // The DEFAULT_VIBRATE flag trumps any custom vibration.
                 final boolean useDefaultVibrate =
-                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
-                    || convertSoundToVibration;
+                        (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
 
-                if ((useDefaultVibrate || notification.vibrate != null)
+                if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
                         && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
                     mVibrateNotification = r;
 
-                    mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN
-                                                        : notification.vibrate,
-                              ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
+                    if (useDefaultVibrate || convertSoundToVibration) {
+                        // Escalate privileges so we can use the vibrator even if the notifying app
+                        // does not have the VIBRATE permission.
+                        long identity = Binder.clearCallingIdentity();
+                        try {
+                            mVibrator.vibrate(convertSoundToVibration ? mFallbackVibrationPattern
+                                                                      : mDefaultVibrationPattern,
+                                ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                    } else if (notification.vibrate.length > 1) {
+                        // If you want your own vibration pattern, you need the VIBRATE permission
+                        mVibrator.vibrate(notification.vibrate,
+                            ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
+                    }
                 }
             }
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d2cd646..db64a9a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -14229,6 +14229,7 @@
                     startHomeActivityLocked(userId);
                 }
 
+                EventLogTags.writeAmSwitchUser(userId);
                 getUserManagerLocked().userForeground(userId);
                 sendUserSwitchBroadcastsLocked(oldUserId, userId);
                 if (needStart) {
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 6ee7507..88c0c03 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -83,3 +83,6 @@
 30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
 # Log.wtf() called
 30040 am_wtf (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3)
+
+# User switched
+30041 am_switch_user (id|1|5)
diff --git a/services/java/com/android/server/display/PersistentDataStore.java b/services/java/com/android/server/display/PersistentDataStore.java
index 3a6e1a6..105c253 100644
--- a/services/java/com/android/server/display/PersistentDataStore.java
+++ b/services/java/com/android/server/display/PersistentDataStore.java
@@ -81,6 +81,15 @@
         }
     }
 
+    public WifiDisplay getRememberedWifiDisplay(String deviceAddress) {
+        loadIfNeeded();
+        int index = findRememberedWifiDisplay(deviceAddress);
+        if (index >= 0) {
+            return mRememberedWifiDisplays.get(index);
+        }
+        return null;
+    }
+
     public WifiDisplay[] getRememberedWifiDisplays() {
         loadIfNeeded();
         return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
@@ -137,22 +146,6 @@
         return true;
     }
 
-    public boolean renameWifiDisplay(String deviceAddress, String alias) {
-        int index = findRememberedWifiDisplay(deviceAddress);
-        if (index >= 0) {
-            WifiDisplay display = mRememberedWifiDisplays.get(index);
-            if (Objects.equal(display.getDeviceAlias(), alias)) {
-                return false; // already has this alias
-            }
-            WifiDisplay renamedDisplay = new WifiDisplay(deviceAddress,
-                    display.getDeviceName(), alias);
-            mRememberedWifiDisplays.set(index, renamedDisplay);
-            setDirty();
-            return true;
-        }
-        return false;
-    }
-
     public boolean forgetWifiDisplay(String deviceAddress) {
         int index = findRememberedWifiDisplay(deviceAddress);
         if (index >= 0) {
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 45fff30..c8a44d2 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -45,6 +45,8 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 
+import libcore.util.Objects;
+
 /**
  * Connects to Wifi displays that implement the Miracast protocol.
  * <p>
@@ -224,16 +226,18 @@
             }
         }
 
-        if (mPersistentDataStore.renameWifiDisplay(address, alias)) {
-            mPersistentDataStore.saveIfNeeded();
-            updateRememberedDisplaysLocked();
-            scheduleStatusChangedBroadcastLocked();
+        WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
+        if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) {
+            display = new WifiDisplay(address, display.getDeviceName(), alias);
+            if (mPersistentDataStore.rememberWifiDisplay(display)) {
+                mPersistentDataStore.saveIfNeeded();
+                updateRememberedDisplaysLocked();
+                scheduleStatusChangedBroadcastLocked();
+            }
         }
 
-        if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)
-                && mDisplayDevice != null) {
-            mDisplayDevice.setNameLocked(mActiveDisplay.getFriendlyDisplayName());
-            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
+        if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
+            renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName());
         }
     }
 
@@ -272,9 +276,42 @@
         mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
     }
 
-    private void handleConnectLocked(WifiDisplay display,
+    private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
+        // It may happen that a display name has changed since it was remembered.
+        // Consult the list of available displays and update the name if needed.
+        // We don't do anything special for the active display here.  The display
+        // controller will send a separate event when it needs to be updates.
+        boolean changed = false;
+        for (int i = 0; i < mRememberedDisplays.length; i++) {
+            WifiDisplay rememberedDisplay = mRememberedDisplays[i];
+            WifiDisplay availableDisplay = findAvailableDisplayLocked(
+                    rememberedDisplay.getDeviceAddress());
+            if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: "
+                            + "updating remembered display to " + availableDisplay);
+                }
+                mRememberedDisplays[i] = availableDisplay;
+                changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);
+            }
+        }
+        if (changed) {
+            mPersistentDataStore.saveIfNeeded();
+        }
+    }
+
+    private WifiDisplay findAvailableDisplayLocked(String address) {
+        for (WifiDisplay display : mAvailableDisplays) {
+            if (display.getDeviceAddress().equals(address)) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    private void addDisplayDeviceLocked(WifiDisplay display,
             Surface surface, int width, int height, int flags) {
-        handleDisconnectLocked();
+        removeDisplayDeviceLocked();
 
         if (mPersistentDataStore.rememberWifiDisplay(display)) {
             mPersistentDataStore.saveIfNeeded();
@@ -303,7 +340,7 @@
         scheduleUpdateNotificationLocked();
     }
 
-    private void handleDisconnectLocked() {
+    private void removeDisplayDeviceLocked() {
         if (mDisplayDevice != null) {
             mDisplayDevice.clearSurfaceLocked();
             sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
@@ -313,6 +350,13 @@
         }
     }
 
+    private void renameDisplayDeviceLocked(String name) {
+        if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) {
+            mDisplayDevice.setNameLocked(name);
+            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
+        }
+    }
+
     private void scheduleStatusChangedBroadcastLocked() {
         mCurrentStatus = null;
         if (!mPendingStatusChangeBroadcast) {
@@ -446,6 +490,7 @@
                         || !Arrays.equals(mAvailableDisplays, availableDisplays)) {
                     mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
                     mAvailableDisplays = availableDisplays;
+                    fixRememberedDisplayNamesFromAvailableDisplaysLocked();
                     scheduleStatusChangedBroadcastLocked();
                 }
             }
@@ -483,7 +528,7 @@
                 int width, int height, int flags) {
             synchronized (getSyncRoot()) {
                 display = mPersistentDataStore.applyWifiDisplayAlias(display);
-                handleConnectLocked(display, surface, width, height, flags);
+                addDisplayDeviceLocked(display, surface, width, height, flags);
 
                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
                         || mActiveDisplay == null
@@ -496,10 +541,24 @@
         }
 
         @Override
+        public void onDisplayChanged(WifiDisplay display) {
+            synchronized (getSyncRoot()) {
+                display = mPersistentDataStore.applyWifiDisplayAlias(display);
+                if (mActiveDisplay != null
+                        && mActiveDisplay.hasSameAddress(display)
+                        && !mActiveDisplay.equals(display)) {
+                    mActiveDisplay = display;
+                    renameDisplayDeviceLocked(display.getFriendlyDisplayName());
+                    scheduleStatusChangedBroadcastLocked();
+                }
+            }
+        }
+
+        @Override
         public void onDisplayDisconnected() {
             // Stop listening.
             synchronized (getSyncRoot()) {
-                handleDisconnectLocked();
+                removeDisplayDeviceLocked();
 
                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
                         || mActiveDisplay != null) {
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index 39d042f..886e049 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -120,6 +120,12 @@
     // or are not trying to connect.
     private WifiP2pDevice mConnectingDevice;
 
+    // The device from which we are currently disconnecting.
+    private WifiP2pDevice mDisconnectingDevice;
+
+    // The device to which we were previously trying to connect and are now canceling.
+    private WifiP2pDevice mCancelingDevice;
+
     // The device to which we are currently connected, which means we have an active P2P group.
     private WifiP2pDevice mConnectedDevice;
 
@@ -186,6 +192,7 @@
         updateWfdEnableState();
     }
 
+    @Override
     public void dump(PrintWriter pw) {
         pw.println("mWifiDisplayOnSetting=" + mWifiDisplayOnSetting);
         pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled);
@@ -196,6 +203,8 @@
         pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft);
         pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
         pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
+        pw.println("mDisconnectingDisplay=" + describeWifiP2pDevice(mDisconnectingDevice));
+        pw.println("mCancelingDisplay=" + describeWifiP2pDevice(mCancelingDevice));
         pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice));
         pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft);
         pw.println("mRemoteDisplay=" + mRemoteDisplay);
@@ -384,7 +393,9 @@
         final int count = mAvailableWifiDisplayPeers.size();
         final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
         for (int i = 0; i < count; i++) {
-            displays[i] = createWifiDisplay(mAvailableWifiDisplayPeers.get(i));
+            WifiP2pDevice device = mAvailableWifiDisplayPeers.get(i);
+            displays[i] = createWifiDisplay(device);
+            updateDesiredDevice(device);
         }
 
         mHandler.post(new Runnable() {
@@ -395,6 +406,23 @@
         });
     }
 
+    private void updateDesiredDevice(WifiP2pDevice device) {
+        // Handle the case where the device to which we are connecting or connected
+        // may have been renamed or reported different properties in the latest scan.
+        final String address = device.deviceAddress;
+        if (mDesiredDevice != null && mDesiredDevice.deviceAddress.equals(address)) {
+            if (DEBUG) {
+                Slog.d(TAG, "updateDesiredDevice: new information "
+                        + describeWifiP2pDevice(device));
+            }
+            mDesiredDevice.update(device);
+            if (mAdvertisedDisplay != null
+                    && mAdvertisedDisplay.getDeviceAddress().equals(address)) {
+                readvertiseDisplay(createWifiDisplay(mDesiredDevice));
+            }
+        }
+    }
+
     private void connect(final WifiP2pDevice device) {
         if (mDesiredDevice != null
                 && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {
@@ -459,12 +487,17 @@
         }
 
         // Step 2. Before we try to connect to a new device, disconnect from the old one.
+        if (mDisconnectingDevice != null) {
+            return; // wait for asynchronous callback
+        }
         if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
             Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName);
+            mDisconnectingDevice = mConnectedDevice;
+            mConnectedDevice = null;
 
             unadvertiseDisplay();
 
-            final WifiP2pDevice oldDevice = mConnectedDevice;
+            final WifiP2pDevice oldDevice = mDisconnectingDevice;
             mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
                 @Override
                 public void onSuccess() {
@@ -480,8 +513,8 @@
                 }
 
                 private void next() {
-                    if (mConnectedDevice == oldDevice) {
-                        mConnectedDevice = null;
+                    if (mDisconnectingDevice == oldDevice) {
+                        mDisconnectingDevice = null;
                         updateConnection();
                     }
                 }
@@ -491,13 +524,18 @@
 
         // Step 3. Before we try to connect to a new device, stop trying to connect
         // to the old one.
+        if (mCancelingDevice != null) {
+            return; // wait for asynchronous callback
+        }
         if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
             Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName);
+            mCancelingDevice = mConnectingDevice;
+            mConnectingDevice = null;
 
             unadvertiseDisplay();
             mHandler.removeCallbacks(mConnectionTimeout);
 
-            final WifiP2pDevice oldDevice = mConnectingDevice;
+            final WifiP2pDevice oldDevice = mCancelingDevice;
             mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() {
                 @Override
                 public void onSuccess() {
@@ -513,8 +551,8 @@
                 }
 
                 private void next() {
-                    if (mConnectingDevice == oldDevice) {
-                        mConnectingDevice = null;
+                    if (mCancelingDevice == oldDevice) {
+                        mCancelingDevice = null;
                         updateConnection();
                     }
                 }
@@ -763,13 +801,17 @@
                 public void run() {
                     if (oldSurface != null && surface != oldSurface) {
                         mListener.onDisplayDisconnected();
-                    } else if (oldDisplay != null && !Objects.equal(display, oldDisplay)) {
+                    } else if (oldDisplay != null && !oldDisplay.hasSameAddress(display)) {
                         mListener.onDisplayConnectionFailed();
                     }
 
                     if (display != null) {
-                        if (!Objects.equal(display, oldDisplay)) {
+                        if (!display.hasSameAddress(oldDisplay)) {
                             mListener.onDisplayConnecting(display);
+                        } else if (!display.equals(oldDisplay)) {
+                            // The address is the same but some other property such as the
+                            // name must have changed.
+                            mListener.onDisplayChanged(display);
                         }
                         if (surface != null && surface != oldSurface) {
                             mListener.onDisplayConnected(display, surface, width, height, flags);
@@ -784,6 +826,12 @@
         advertiseDisplay(null, null, 0, 0, 0);
     }
 
+    private void readvertiseDisplay(WifiDisplay display) {
+        advertiseDisplay(display, mAdvertisedDisplaySurface,
+                mAdvertisedDisplayWidth, mAdvertisedDisplayHeight,
+                mAdvertisedDisplayFlags);
+    }
+
     private static Inet4Address getInterfaceAddress(WifiP2pGroup info) {
         NetworkInterface iface;
         try {
@@ -885,6 +933,7 @@
 
         void onDisplayConnecting(WifiDisplay display);
         void onDisplayConnectionFailed();
+        void onDisplayChanged(WifiDisplay display);
         void onDisplayConnected(WifiDisplay display,
                 Surface surface, int width, int height, int flags);
         void onDisplayDisconnected();
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index e05442b..66c3ce9 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -16,8 +16,7 @@
 
 package com.android.server.pm;
 
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -26,7 +25,6 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -34,6 +32,7 @@
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.Handler;
 import android.os.IUserManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -42,9 +41,17 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -54,13 +61,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 public class UserManagerService extends IUserManager.Stub {
 
     private static final String LOG_TAG = "UserManagerService";
@@ -95,19 +97,24 @@
     private final Object mInstallLock;
     private final Object mPackagesLock;
 
+    private final Handler mHandler;
+
     private final File mUsersDir;
     private final File mUserListFile;
     private final File mBaseUserPath;
 
-    private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
-    private HashSet<Integer> mRemovingUserIds = new HashSet<Integer>();
+    private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+
+    /**
+     * Set of user IDs being actively removed. Removed IDs linger in this set
+     * for several seconds to work around a VFS caching issue.
+     */
+    // @GuardedBy("mPackagesLock")
+    private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
 
     private int[] mUserIds;
     private boolean mGuestEnabled;
     private int mNextSerialNumber;
-    // This resets on a reboot. Otherwise it keeps incrementing so that user ids are
-    // not reused in quick succession
-    private int mNextUserId = MIN_USER_ID;
     private int mUserVersion = 0;
 
     private static UserManagerService sInstance;
@@ -147,6 +154,7 @@
         mPm = pm;
         mInstallLock = installLock;
         mPackagesLock = packagesLock;
+        mHandler = new Handler();
         synchronized (mInstallLock) {
             synchronized (mPackagesLock) {
                 mUsersDir = new File(dataDir, USER_INFO_DIR);
@@ -190,7 +198,7 @@
                 if (ui.partial) {
                     continue;
                 }
-                if (!excludeDying || !mRemovingUserIds.contains(ui.id)) {
+                if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
                     users.add(ui);
                 }
             }
@@ -212,7 +220,7 @@
     private UserInfo getUserInfoLocked(int userId) {
         UserInfo ui = mUsers.get(userId);
         // If it is partial and not in the process of being removed, return as unknown user.
-        if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) {
+        if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
             Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
             return null;
         }
@@ -502,7 +510,7 @@
 
     private void fallbackToSingleUserLocked() {
         // Create the primary user
-        UserInfo primary = new UserInfo(0, 
+        UserInfo primary = new UserInfo(0,
                 mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
                 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
         mUsers.put(0, primary);
@@ -749,7 +757,7 @@
             if (userHandle == 0 || user == null) {
                 return false;
             }
-            mRemovingUserIds.add(userHandle);
+            mRemovingUserIds.put(userHandle, true);
             // Set this to a partially created user, so that the user will be purged
             // on next startup, in case the runtime stops now before stopping and
             // removing the user completely.
@@ -813,13 +821,25 @@
         }
     }
 
-    private void removeUserStateLocked(int userHandle) {
+    private void removeUserStateLocked(final int userHandle) {
         // Cleanup package manager settings
         mPm.cleanUpUserLILPw(userHandle);
 
         // Remove this user from the list
         mUsers.remove(userHandle);
-        mRemovingUserIds.remove(userHandle);
+
+        // Have user ID linger for several seconds to let external storage VFS
+        // cache entries expire. This must be greater than the 'entry_valid'
+        // timeout used by the FUSE daemon.
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mPackagesLock) {
+                    mRemovingUserIds.delete(userHandle);
+                }
+            }
+        }, MINUTE_IN_MILLIS);
+
         // Remove user file
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
         userFile.delete();
@@ -906,14 +926,13 @@
      */
     private int getNextAvailableIdLocked() {
         synchronized (mPackagesLock) {
-            int i = mNextUserId;
+            int i = MIN_USER_ID;
             while (i < Integer.MAX_VALUE) {
-                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) {
+                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
                     break;
                 }
                 i++;
             }
-            mNextUserId = i + 1;
             return i;
         }
     }
@@ -938,7 +957,7 @@
                 UserInfo user = mUsers.valueAt(i);
                 if (user == null) continue;
                 pw.print("  "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
-                if (mRemovingUserIds.contains(mUsers.keyAt(i))) pw.print(" <removing> ");
+                if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> ");
                 if (user.partial) pw.print(" <partial>");
                 pw.println();
                 pw.print("    Created: ");
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b871cdc..0e29882 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -508,6 +508,10 @@
     private Messenger mWifiServiceMessenger;
     private final CountDownLatch mConnected = new CountDownLatch(1);
 
+    private static Object sThreadRefLock = new Object();
+    private static int sThreadRefCount;
+    private static HandlerThread sHandlerThread;
+
     /**
      * Create a new WifiManager instance.
      * Applications will almost always want to use
@@ -1365,9 +1369,14 @@
             return;
         }
 
-        HandlerThread t = new HandlerThread("WifiManager");
-        t.start();
-        mHandler = new ServiceHandler(t.getLooper());
+        synchronized (sThreadRefLock) {
+            if (++sThreadRefCount == 1) {
+                sHandlerThread = new HandlerThread("WifiManager");
+                sHandlerThread.start();
+            }
+        }
+
+        mHandler = new ServiceHandler(sHandlerThread.getLooper());
         mAsyncChannel.connect(mContext, mHandler, mWifiServiceMessenger);
         try {
             mConnected.await();
@@ -1983,8 +1992,10 @@
 
     protected void finalize() throws Throwable {
         try {
-            if (mHandler != null && mHandler.getLooper() != null) {
-                mHandler.getLooper().quit();
+            synchronized (sThreadRefLock) {
+                if (--sThreadRefCount == 0 && sHandlerThread != null) {
+                    sHandlerThread.getLooper().quit();
+                }
             }
         } finally {
             super.finalize();
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 4c5fc5d..5e25623 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -61,7 +61,7 @@
 
     /* Sends a kill signal to supplicant. To be used when we have lost connection
        or when the supplicant is hung */
-    public native static boolean killSupplicant();
+    public native static boolean killSupplicant(boolean p2pSupported);
 
     private native boolean connectToSupplicant(String iface);
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 040ff24..dafa8e8 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1944,6 +1944,7 @@
                 case CMD_STOP_DRIVER:
                 case CMD_DELAYED_STOP_DRIVER:
                 case CMD_DRIVER_START_TIMED_OUT:
+                case CMD_CAPTIVE_CHECK_COMPLETE:
                 case CMD_START_AP:
                 case CMD_START_AP_SUCCESS:
                 case CMD_START_AP_FAILURE:
@@ -2189,6 +2190,13 @@
                         loge("Unable to change interface settings: " + ie);
                     }
 
+                    /* Stop a running supplicant after a runtime restart
+                     * Avoids issues with drivers that do not handle interface down
+                     * on a running supplicant properly.
+                     */
+                    if (DBG) log("Kill any running supplicant");
+                    mWifiNative.killSupplicant(mP2pSupported);
+
                     if(mWifiNative.startSupplicant(mP2pSupported)) {
                         if (DBG) log("Supplicant start successful");
                         mWifiMonitor.startMonitoring();
@@ -2384,7 +2392,7 @@
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
                     if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
                         loge("Failed to setup control channel, restart supplicant");
-                        mWifiNative.killSupplicant();
+                        mWifiNative.killSupplicant(mP2pSupported);
                         transitionTo(mDriverLoadedState);
                         sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     } else {
@@ -2451,7 +2459,7 @@
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
                     loge("Connection lost, restart supplicant");
-                    mWifiNative.killSupplicant();
+                    mWifiNative.killSupplicant(mP2pSupported);
                     mWifiNative.closeSupplicantConnection();
                     mNetworkInfo.setIsAvailable(false);
                     handleNetworkDisconnect();
@@ -2605,14 +2613,14 @@
                     /* Socket connection can be lost when we do a graceful shutdown
                      * or when the driver is hung. Ensure supplicant is stopped here.
                      */
-                    mWifiNative.killSupplicant();
+                    mWifiNative.killSupplicant(mP2pSupported);
                     mWifiNative.closeSupplicantConnection();
                     transitionTo(mDriverLoadedState);
                     break;
                 case CMD_STOP_SUPPLICANT_FAILED:
                     if (message.arg1 == mSupplicantStopFailureToken) {
                         loge("Timed out on a supplicant stop, kill and proceed");
-                        mWifiNative.killSupplicant();
+                        mWifiNative.killSupplicant(mP2pSupported);
                         mWifiNative.closeSupplicantConnection();
                         transitionTo(mDriverLoadedState);
                     }