Merge "AppWidgetManager: direct add widget support."
diff --git a/api/current.txt b/api/current.txt
index 709fc12c..a84bfb5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -46139,6 +46139,11 @@
     method public abstract android.view.View getFullScreenView(int, android.content.Context);
   }
 
+  public abstract class RenderProcessGoneDetail {
+    ctor public RenderProcessGoneDetail();
+    method public abstract boolean didCrash();
+  }
+
   public class ServiceWorkerClient {
     ctor public ServiceWorkerClient();
     method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebResourceRequest);
@@ -46663,6 +46668,7 @@
     method public void onReceivedHttpError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse);
     method public void onReceivedLoginRequest(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String);
     method public void onReceivedSslError(android.webkit.WebView, android.webkit.SslErrorHandler, android.net.http.SslError);
+    method public boolean onRenderProcessGone(android.webkit.WebView, android.webkit.RenderProcessGoneDetail);
     method public void onScaleChanged(android.webkit.WebView, float, float);
     method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message);
     method public void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent);
diff --git a/api/removed.txt b/api/removed.txt
index 10e6eb5..35b4859 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -99,67 +99,6 @@
 
 }
 
-package android.location {
-
-  public final class GnssMeasurement implements android.os.Parcelable {
-    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
-  }
-
-  public final class GnssMeasurementsEvent implements android.os.Parcelable {
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
-  }
-
-  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
-    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
-    method public int describeContents();
-    method public android.location.GnssNavigationMessage getNavigationMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
-  }
-
-  public static abstract class GnssNavigationMessageEvent.Callback {
-    ctor public GnssNavigationMessageEvent.Callback();
-    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
-    method public void onStatusChanged(int);
-  }
-
-  public abstract interface GnssNmeaListener {
-    method public abstract void onNmeaReceived(long, java.lang.String);
-  }
-
-  public final class GnssStatus {
-    method public int getNumSatellites();
-    method public boolean hasAlmanac(int);
-    method public boolean hasEphemeris(int);
-  }
-
-  public abstract class GnssStatusCallback {
-    ctor public GnssStatusCallback();
-    method public void onFirstFix(int);
-    method public void onSatelliteStatusChanged(android.location.GnssStatus);
-    method public void onStarted();
-    method public void onStopped();
-  }
-
-  public class LocationManager {
-    method public boolean addNmeaListener(android.location.GnssNmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
-    method public void removeNmeaListener(android.location.GnssNmeaListener);
-    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
-  }
-
-}
-
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 64e18ff..aeb17fb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26178,6 +26178,14 @@
     field public static final android.os.Parcelable.Creator<android.net.RecommendationRequest> CREATOR;
   }
 
+  public static final class RecommendationRequest.Builder {
+    ctor public RecommendationRequest.Builder();
+    method public android.net.RecommendationRequest build();
+    method public android.net.RecommendationRequest.Builder setCurrentRecommendedWifiConfig(android.net.wifi.WifiConfiguration);
+    method public android.net.RecommendationRequest.Builder setNetworkCapabilities(android.net.NetworkCapabilities);
+    method public android.net.RecommendationRequest.Builder setScanResults(android.net.wifi.ScanResult[]);
+  }
+
   public final class RecommendationResult implements android.os.Parcelable {
     method public static android.net.RecommendationResult createConnectRecommendation(android.net.wifi.WifiConfiguration);
     method public static android.net.RecommendationResult createDoNotConnectRecommendation();
@@ -26242,10 +26250,16 @@
     ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve);
     ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean);
     ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, android.os.Bundle);
+    method public int calculateBadge(int);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ATTRIBUTES_KEY_BADGING_CURVE = "android.net.attributes.key.BADGING_CURVE";
     field public static final java.lang.String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
     field public static final java.lang.String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
+    field public static final int BADGING_4K = 30; // 0x1e
+    field public static final int BADGING_HD = 20; // 0x14
+    field public static final int BADGING_NONE = 0; // 0x0
+    field public static final int BADGING_SD = 10; // 0xa
     field public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
     field public final android.os.Bundle attributes;
     field public final boolean meteredHint;
@@ -26253,6 +26267,9 @@
     field public final android.net.RssiCurve rssiCurve;
   }
 
+  public static abstract class ScoredNetwork.Badging implements java.lang.annotation.Annotation {
+  }
+
   public class TrafficStats {
     ctor public TrafficStats();
     method public static void clearThreadStatsTag();
@@ -49388,6 +49405,11 @@
     method public abstract android.view.View getFullScreenView(int, android.content.Context);
   }
 
+  public abstract class RenderProcessGoneDetail {
+    ctor public RenderProcessGoneDetail();
+    method public abstract boolean didCrash();
+  }
+
   public class ServiceWorkerClient {
     ctor public ServiceWorkerClient();
     method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebResourceRequest);
@@ -49983,6 +50005,7 @@
     method public void onReceivedHttpError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse);
     method public void onReceivedLoginRequest(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String);
     method public void onReceivedSslError(android.webkit.WebView, android.webkit.SslErrorHandler, android.net.http.SslError);
+    method public boolean onRenderProcessGone(android.webkit.WebView, android.webkit.RenderProcessGoneDetail);
     method public void onScaleChanged(android.webkit.WebView, float, float);
     method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message);
     method public void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 0919b8f..8016f58 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -97,67 +97,6 @@
 
 }
 
-package android.location {
-
-  public final class GnssMeasurement implements android.os.Parcelable {
-    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
-  }
-
-  public final class GnssMeasurementsEvent implements android.os.Parcelable {
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
-  }
-
-  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
-    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
-    method public int describeContents();
-    method public android.location.GnssNavigationMessage getNavigationMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
-  }
-
-  public static abstract class GnssNavigationMessageEvent.Callback {
-    ctor public GnssNavigationMessageEvent.Callback();
-    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
-    method public void onStatusChanged(int);
-  }
-
-  public abstract interface GnssNmeaListener {
-    method public abstract void onNmeaReceived(long, java.lang.String);
-  }
-
-  public final class GnssStatus {
-    method public int getNumSatellites();
-    method public boolean hasAlmanac(int);
-    method public boolean hasEphemeris(int);
-  }
-
-  public abstract class GnssStatusCallback {
-    ctor public GnssStatusCallback();
-    method public void onFirstFix(int);
-    method public void onSatelliteStatusChanged(android.location.GnssStatus);
-    method public void onStarted();
-    method public void onStopped();
-  }
-
-  public class LocationManager {
-    method public boolean addNmeaListener(android.location.GnssNmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
-    method public void removeNmeaListener(android.location.GnssNmeaListener);
-    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
-  }
-
-}
-
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index da4ccb1..ff70fee 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -46435,6 +46435,11 @@
     method public abstract android.view.View getFullScreenView(int, android.content.Context);
   }
 
+  public abstract class RenderProcessGoneDetail {
+    ctor public RenderProcessGoneDetail();
+    method public abstract boolean didCrash();
+  }
+
   public class ServiceWorkerClient {
     ctor public ServiceWorkerClient();
     method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebResourceRequest);
@@ -46959,6 +46964,7 @@
     method public void onReceivedHttpError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse);
     method public void onReceivedLoginRequest(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String);
     method public void onReceivedSslError(android.webkit.WebView, android.webkit.SslErrorHandler, android.net.http.SslError);
+    method public boolean onRenderProcessGone(android.webkit.WebView, android.webkit.RenderProcessGoneDetail);
     method public void onScaleChanged(android.webkit.WebView, float, float);
     method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message);
     method public void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 10e6eb5..35b4859 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -99,67 +99,6 @@
 
 }
 
-package android.location {
-
-  public final class GnssMeasurement implements android.os.Parcelable {
-    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
-  }
-
-  public final class GnssMeasurementsEvent implements android.os.Parcelable {
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
-  }
-
-  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
-    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
-    method public int describeContents();
-    method public android.location.GnssNavigationMessage getNavigationMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
-  }
-
-  public static abstract class GnssNavigationMessageEvent.Callback {
-    ctor public GnssNavigationMessageEvent.Callback();
-    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
-    method public void onStatusChanged(int);
-  }
-
-  public abstract interface GnssNmeaListener {
-    method public abstract void onNmeaReceived(long, java.lang.String);
-  }
-
-  public final class GnssStatus {
-    method public int getNumSatellites();
-    method public boolean hasAlmanac(int);
-    method public boolean hasEphemeris(int);
-  }
-
-  public abstract class GnssStatusCallback {
-    ctor public GnssStatusCallback();
-    method public void onFirstFix(int);
-    method public void onSatelliteStatusChanged(android.location.GnssStatus);
-    method public void onStarted();
-    method public void onStopped();
-  }
-
-  public class LocationManager {
-    method public boolean addNmeaListener(android.location.GnssNmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
-    method public void removeNmeaListener(android.location.GnssNmeaListener);
-    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
-  }
-
-}
-
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/compiled-classes-phone b/compiled-classes-phone
index f09bad9..7239a7f 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -632,6 +632,7 @@
 android.bluetooth.BluetoothAudioConfig
 android.bluetooth.BluetoothClass
 android.bluetooth.BluetoothClass$1
+android.bluetooth.BluetoothCodecConfig
 android.bluetooth.BluetoothDevice
 android.bluetooth.BluetoothDevice$1
 android.bluetooth.BluetoothDevice$2
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 547c710..d08bee5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -509,10 +509,23 @@
      * Common-path handling of app data dir creation
      */
     private static File ensurePrivateDirExists(File file) {
+        return ensurePrivateDirExists(file, 0771, -1);
+    }
+
+    private static File ensurePrivateCacheDirExists(File file) {
+        final int gid = UserHandle.getCacheAppGid(Process.myUid());
+        return ensurePrivateDirExists(file, 02771, gid);
+    }
+
+    private static File ensurePrivateDirExists(File file, int mode, int gid) {
         if (!file.exists()) {
+            final String path = file.getAbsolutePath();
             try {
-                Os.mkdir(file.getAbsolutePath(), 0771);
-                Os.chmod(file.getAbsolutePath(), 0771);
+                Os.mkdir(path, mode);
+                Os.chmod(path, mode);
+                if (gid != -1) {
+                    Os.chown(path, -1, gid);
+                }
             } catch (ErrnoException e) {
                 if (e.errno == OsConstants.EEXIST) {
                     // We must have raced with someone; that's okay
@@ -581,7 +594,7 @@
             if (mCacheDir == null) {
                 mCacheDir = new File(getDataDir(), "cache");
             }
-            return ensurePrivateDirExists(mCacheDir);
+            return ensurePrivateCacheDirExists(mCacheDir);
         }
     }
 
@@ -591,7 +604,7 @@
             if (mCodeCacheDir == null) {
                 mCodeCacheDir = new File(getDataDir(), "code_cache");
             }
-            return ensurePrivateDirExists(mCodeCacheDir);
+            return ensurePrivateCacheDirExists(mCodeCacheDir);
         }
     }
 
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 28ad01e..fd139f7 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -319,7 +319,12 @@
     void registerUserSwitchObserver(in IUserSwitchObserver observer, in String name);
     void unregisterUserSwitchObserver(in IUserSwitchObserver observer);
     int[] getRunningUserIds();
+
+    // Deprecated - This method is only used by a few internal components and it will soon be
+    // replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+    // No new code should be calling it.
     void requestBugReport(int bugreportType);
+
     long inputDispatchingTimedOut(int pid, boolean aboveSystem, in String reason);
     void clearPendingBackup();
     Intent getIntentForIntentSender(in IIntentSender sender);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 3de159a..1ce8007 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -103,6 +103,27 @@
         "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
 
     /**
+     * Intent used to broadcast the change in the Audio Codec state of the
+     * A2DP Source profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     *   <li> {@link #EXTRA_CODEC_CONFIG} - The current codec configuration. </li>
+     *   <li> {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration. </li>
+     *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
+     *   connected, otherwise it is not included.</li>
+     * </ul>
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CODEC_CONFIG_CHANGED =
+        "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
+
+    /**
      * A2DP sink device is streaming music. This state can be one of
      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
      * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
@@ -544,6 +565,54 @@
     }
 
     /**
+     * Gets the current codec configuration.
+     *
+     * @return the current codec configuration
+     * @hide
+     */
+    public BluetoothCodecConfig getCodecConfig() {
+        if (DBG) Log.d(TAG, "getCodecConfig");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getCodecConfig();
+            }
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+            }
+            return null;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to BT service in getCodecConfig()", e);
+            return null;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Sets the codec configuration preference.
+     *
+     * @param codecConfig the codec configuration preference
+     * @hide
+     */
+    public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
+        if (DBG) Log.d(TAG, "setCodecConfigPreference");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                mService.setCodecConfigPreference(codecConfig);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
+            return;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
      * Helper for converting a state to a string.
      *
      * For debug use only - strings are not internationalized.
diff --git a/location/java/android/location/GnssNavigationMessageEvent.aidl b/core/java/android/bluetooth/BluetoothCodecConfig.aidl
similarity index 74%
rename from location/java/android/location/GnssNavigationMessageEvent.aidl
rename to core/java/android/bluetooth/BluetoothCodecConfig.aidl
index e765739..553e66e 100644
--- a/location/java/android/location/GnssNavigationMessageEvent.aidl
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2014, The Android Open Source Project
+ * Copyright (C) 2016 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.location;
+package android.bluetooth;
 
-parcelable GnssNavigationMessageEvent;
+parcelable BluetoothCodecConfig;
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
new file mode 100644
index 0000000..5cc1277
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2016 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 android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents the codec configuration for a Bluetooth A2DP source device.
+ *
+ * {@see BluetoothA2dp}
+ *
+ * {@hide}
+ */
+public final class BluetoothCodecConfig implements Parcelable {
+
+    /**
+     * Extra for the codec configuration intents of the individual profiles.
+     *
+     * This extra represents the current codec configuration of the A2DP
+     * profile.
+     */
+    public static final String EXTRA_CODEC_CONFIG = "android.bluetooth.codec.extra.CODEC_CONFIG";
+
+    /**
+     * Extra for the codec configuration intents of the individual profiles.
+     *
+     * This extra represents the previous codec configuration of the A2DP
+     * profile.
+     */
+    public static final String EXTRA_PREVIOUS_CODEC_CONFIG =
+        "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG";
+
+    public static final int SOURCE_CODEC_TYPE_SBC     = 0;
+    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
+
+    public static final int CODEC_PRIORITY_DEFAULT = 0;
+    public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
+
+    public static final int SAMPLE_RATE_NONE   = 0;
+    public static final int SAMPLE_RATE_44100  = 0x1 << 0;
+    public static final int SAMPLE_RATE_48000  = 0x1 << 1;
+    public static final int SAMPLE_RATE_88200  = 0x1 << 2;
+    public static final int SAMPLE_RATE_96000  = 0x1 << 3;
+    public static final int SAMPLE_RATE_176400 = 0x1 << 4;
+    public static final int SAMPLE_RATE_192000 = 0x1 << 5;
+
+    public static final int BITS_PER_SAMPLE_NONE = 0;
+    public static final int BITS_PER_SAMPLE_16   = 0x1 << 0;
+    public static final int BITS_PER_SAMPLE_24   = 0x1 << 1;
+    public static final int BITS_PER_SAMPLE_32   = 0x1 << 2;
+
+    public static final int CHANNEL_MODE_NONE   = 0;
+    public static final int CHANNEL_MODE_MONO   = 0x1 << 0;
+    public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
+
+    private final int mCodecType;
+    private final int mCodecPriority;
+    private final int mSampleRate;
+    private final int mBitsPerSample;
+    private final int mChannelMode;
+    private final long mCodecSpecific1;
+    private final long mCodecSpecific2;
+    private final long mCodecSpecific3;
+    private final long mCodecSpecific4;
+
+    public BluetoothCodecConfig(int codecType, int codecPriority,
+                                int sampleRate, int bitsPerSample,
+                                int channelMode,long codecSpecific1,
+                                long codecSpecific2, long codecSpecific3,
+                                long codecSpecific4) {
+        mCodecType = codecType;
+        mCodecPriority = codecPriority;
+        mSampleRate = sampleRate;
+        mBitsPerSample = bitsPerSample;
+        mChannelMode = channelMode;
+        mCodecSpecific1 = codecSpecific1;
+        mCodecSpecific2 = codecSpecific2;
+        mCodecSpecific3 = codecSpecific3;
+        mCodecSpecific4 = codecSpecific4;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothCodecConfig) {
+            BluetoothCodecConfig other = (BluetoothCodecConfig)o;
+            return (other.mCodecType == mCodecType &&
+                    other.mCodecPriority == mCodecPriority &&
+                    other.mSampleRate == mSampleRate &&
+                    other.mBitsPerSample == mBitsPerSample &&
+                    other.mChannelMode == mChannelMode &&
+                    other.mCodecSpecific1 == mCodecSpecific1 &&
+                    other.mCodecSpecific2 == mCodecSpecific2 &&
+                    other.mCodecSpecific3 == mCodecSpecific3 &&
+                    other.mCodecSpecific4 == mCodecSpecific4);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
+                            mBitsPerSample, mChannelMode, mCodecSpecific1,
+                            mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
+    }
+
+    @Override
+    public String toString() {
+        return "{mCodecType:" + mCodecType +
+            ",mCodecPriority:" + mCodecPriority +
+            ",mSampleRate:" + String.format("0x%x", mSampleRate) +
+            ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) +
+            ",mChannelMode:" + String.format("0x%x", mChannelMode) +
+            ",mCodecSpecific1:" + mCodecSpecific1 +
+            ",mCodecSpecific2:" + mCodecSpecific2 +
+            ",mCodecSpecific3:" + mCodecSpecific3 +
+            ",mCodecSpecific4:" + mCodecSpecific4 + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothCodecConfig> CREATOR =
+            new Parcelable.Creator<BluetoothCodecConfig>() {
+        public BluetoothCodecConfig createFromParcel(Parcel in) {
+            final int codecType = in.readInt();
+            final int codecPriority = in.readInt();
+            final int sampleRate = in.readInt();
+            final int bitsPerSample = in.readInt();
+            final int channelMode = in.readInt();
+            final long codecSpecific1 = in.readLong();
+            final long codecSpecific2 = in.readLong();
+            final long codecSpecific3 = in.readLong();
+            final long codecSpecific4 = in.readLong();
+            return new BluetoothCodecConfig(codecType, codecPriority,
+                                            sampleRate, bitsPerSample,
+                                            channelMode, codecSpecific1,
+                                            codecSpecific2, codecSpecific3,
+                                            codecSpecific4);
+        }
+        public BluetoothCodecConfig[] newArray(int size) {
+            return new BluetoothCodecConfig[size];
+        }
+    };
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCodecType);
+        out.writeInt(mCodecPriority);
+        out.writeInt(mSampleRate);
+        out.writeInt(mBitsPerSample);
+        out.writeInt(mChannelMode);
+        out.writeLong(mCodecSpecific1);
+        out.writeLong(mCodecSpecific2);
+        out.writeLong(mCodecSpecific3);
+        out.writeLong(mCodecSpecific4);
+    }
+
+    /**
+     * Returns the codec type.
+     * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}.
+     *
+     * @return the codec type
+     */
+    public int getCodecType() {
+        return mCodecType;
+    }
+
+    /**
+     * Returns the codec selection priority.
+     * The codec selection priority is relative to other codecs: larger value
+     * means higher priority. If 0, reset to default.
+     *
+     * @return the codec priority
+     */
+    public int getCodecPriority() {
+        return mCodecPriority;
+    }
+
+    /**
+     * Returns the codec sample rate. The value can be a bitmask with all
+     * supported sample rates:
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or
+     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000}
+     *
+     * @return the codec sample rate
+     */
+    public int getSampleRate() {
+        return mSampleRate;
+    }
+
+    /**
+     * Returns the codec bits per sample. The value can be a bitmask with all
+     * bits per sample supported:
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or
+     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32}
+     *
+     * @return the codec bits per sample
+     */
+    public int getBitsPerSample() {
+        return mBitsPerSample;
+    }
+
+    /**
+     * Returns the codec channel mode. The value can be a bitmask with all
+     * supported channel modes:
+     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or
+     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or
+     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO}
+     *
+     * @return the codec channel mode
+     */
+    public int getChannelMode() {
+        return mChannelMode;
+    }
+
+    /**
+     * Returns a codec specific value1.
+     *
+     * @return a codec specific value1.
+     */
+    public long getCodecSpecific1() {
+        return mCodecSpecific1;
+    }
+
+    /**
+     * Returns a codec specific value2.
+     *
+     * @return a codec specific value2
+     */
+    public long getCodecSpecific2() {
+        return mCodecSpecific2;
+    }
+
+    /**
+     * Returns a codec specific value3.
+     *
+     * @return a codec specific value3
+     */
+    public long getCodecSpecific3() {
+        return mCodecSpecific3;
+    }
+
+    /**
+     * Returns a codec specific value4.
+     *
+     * @return a codec specific value4
+     */
+    public long getCodecSpecific4() {
+        return mCodecSpecific4;
+    }
+
+    /**
+     * Checks whether the audio feeding parameters are same.
+     *
+     * @param other the codec config to compare against
+     * @return true if the audio feeding parameters are same, otherwise false
+     */
+    public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) {
+        return (other != null && other.mSampleRate == mSampleRate &&
+                other.mBitsPerSample == mBitsPerSample &&
+                other.mChannelMode == mChannelMode);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
index 0f0e050..f519776 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -45,9 +45,9 @@
      *            <code>false</code> otherwise.
      */
     public void onAppStatusChanged(BluetoothDevice pluggedDevice,
-                                    BluetoothHidDeviceAppConfiguration config, boolean registered) {
-        Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + (pluggedDevice == null ?
-            null : pluggedDevice.toString()) + " registered=" + registered);
+            BluetoothHidDeviceAppConfiguration config, boolean registered) {
+        Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered="
+                + registered);
     }
 
     /**
@@ -60,13 +60,13 @@
      * @param state Connection state as defined in {@link BluetoothProfile}.
      */
     public void onConnectionStateChanged(BluetoothDevice device, int state) {
-        Log.d(TAG, "onConnectionStateChanged: device=" + device.toString() + " state=" + state);
+        Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
     }
 
     /**
      * Callback called when GET_REPORT is received from remote host. Should be
      * replied by application using
-     * {@link BluetoothHidDevice#replyReport(byte, byte, byte[])}.
+     * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}.
      *
      * @param type Requested Report Type.
      * @param id Requested Report Id, can be 0 if no Report Id are defined in
@@ -74,21 +74,22 @@
      * @param bufferSize Requested buffer size, application shall respond with
      *            at least given number of bytes.
      */
-    public void onGetReport(byte type, byte id, int bufferSize) {
-        Log.d(TAG, "onGetReport: type=" + type + " id=" + id + " bufferSize=" + bufferSize);
+    public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+        Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize="
+                + bufferSize);
     }
 
     /**
      * Callback called when SET_REPORT is received from remote host. In case
      * received data are invalid, application shall respond with
-     * {@link BluetoothHidDevice#reportError()}.
+     * {@link BluetoothHidDevice#reportError(BluetoothDevice)}.
      *
      * @param type Report Type.
      * @param id Report Id.
      * @param data Report data.
      */
-    public void onSetReport(byte type, byte id, byte[] data) {
-        Log.d(TAG, "onSetReport: type=" + type + " id=" + id);
+    public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+        Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
     }
 
     /**
@@ -99,8 +100,8 @@
      *
      * @param protocol Protocol Mode.
      */
-    public void onSetProtocol(byte protocol) {
-        Log.d(TAG, "onSetProtocol: protocol=" + protocol);
+    public void onSetProtocol(BluetoothDevice device, byte protocol) {
+        Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
     }
 
     /**
@@ -111,16 +112,17 @@
      * @param reportId Report Id.
      * @param data Report data.
      */
-    public void onIntrData(byte reportId, byte[] data) {
-        Log.d(TAG, "onIntrData: reportId=" + reportId);
+    public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
+        Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId);
     }
 
     /**
      * Callback called when Virtual Cable is removed. This can be either due to
-     * {@link BluetoothHidDevice#unplug()} or request from remote side. After
-     * this callback is received connection will be disconnected automatically.
+     * {@link BluetoothHidDevice#unplug(BluetoothDevice)} or request from remote
+     * side. After this callback is received connection will be disconnected
+     * automatically.
      */
-    public void onVirtualCableUnplug() {
-        Log.d(TAG, "onVirtualCableUnplug");
+    public void onVirtualCableUnplug(BluetoothDevice device) {
+        Log.d(TAG, "onVirtualCableUnplug: device=" + device);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothInputHost.java
index 129fe7e..68d105f 100644
--- a/core/java/android/bluetooth/BluetoothInputHost.java
+++ b/core/java/android/bluetooth/BluetoothInputHost.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -137,28 +138,28 @@
         }
 
         @Override
-        public void onGetReport(byte type, byte id, int bufferSize) {
-            mCallback.onGetReport(type, id, bufferSize);
+        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+            mCallback.onGetReport(device, type, id, bufferSize);
         }
 
         @Override
-        public void onSetReport(byte type, byte id, byte[] data) {
-            mCallback.onSetReport(type, id, data);
+        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+            mCallback.onSetReport(device, type, id, data);
         }
 
         @Override
-        public void onSetProtocol(byte protocol) {
-            mCallback.onSetProtocol(protocol);
+        public void onSetProtocol(BluetoothDevice device, byte protocol) {
+            mCallback.onSetProtocol(device, protocol);
         }
 
         @Override
-        public void onIntrData(byte reportId, byte[] data) {
-            mCallback.onIntrData(reportId, data);
+        public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
+            mCallback.onIntrData(device, reportId, data);
         }
 
         @Override
-        public void onVirtualCableUnplug() {
-            mCallback.onVirtualCableUnplug();
+        public void onVirtualCableUnplug(BluetoothDevice device) {
+            mCallback.onVirtualCableUnplug(device);
         }
     }
 
@@ -276,21 +277,59 @@
         mServiceListener = null;
     }
 
-    @Override
+    /**
+     * {@inheritDoc}
+     */
     public List<BluetoothDevice> getConnectedDevices() {
         Log.v(TAG, "getConnectedDevices()");
-        return null;
+
+        if (mService != null) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return new ArrayList<BluetoothDevice>();
     }
 
-    @Override
+    /**
+     * {@inheritDoc}
+     */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
-        return null;
+
+        if (mService != null) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return new ArrayList<BluetoothDevice>();
     }
 
-    @Override
+    /**
+     * {@inheritDoc}
+     */
     public int getConnectionState(BluetoothDevice device) {
-        Log.v(TAG, "getConnectionState(): device=" + device.getAddress());
+        Log.v(TAG, "getConnectionState(): device=" + device);
+
+        if (mService != null) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
 
         return STATE_DISCONNECTED;
     }
@@ -379,14 +418,12 @@
      * @param data Report data, not including Report Id.
      * @return
      */
-    public boolean sendReport(int id, byte[] data) {
-        Log.v(TAG, "sendReport(): id=" + id);
-
+    public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
         boolean result = false;
 
         if (mService != null) {
             try {
-                result = mService.sendReport(id, data);
+                result = mService.sendReport(device, id, data);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -399,21 +436,21 @@
 
     /**
      * Sends report to remote host as reply for GET_REPORT request from
-     * {@link BluetoothHidDeviceCallback#onGetReport(byte, byte, int)}.
+     * {@link BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}.
      *
      * @param type Report Type, as in request.
      * @param id Report Id, as in request.
      * @param data Report data, not including Report Id.
      * @return
      */
-    public boolean replyReport(byte type, byte id, byte[] data) {
-        Log.v(TAG, "replyReport(): type=" + type + " id=" + id);
+    public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+        Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
 
         boolean result = false;
 
         if (mService != null) {
             try {
-                result = mService.replyReport(type, id, data);
+                result = mService.replyReport(device, type, id, data);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -426,19 +463,19 @@
 
     /**
      * Sends error handshake message as reply for invalid SET_REPORT request
-     * from {@link BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])}.
+     * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
      *
      * @param error Error to be sent for SET_REPORT via HANDSHAKE.
      * @return
      */
-    public boolean reportError(byte error) {
-        Log.v(TAG, "reportError(): error = " + error);
+    public boolean reportError(BluetoothDevice device, byte error) {
+        Log.v(TAG, "reportError(): device=" + device + " error=" + error);
 
         boolean result = false;
 
         if (mService != null) {
             try {
-                result = mService.reportError(error);
+                result = mService.reportError(device, error);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -454,14 +491,14 @@
      *
      * @return
      */
-    public boolean unplug() {
-        Log.v(TAG, "unplug()");
+    public boolean unplug(BluetoothDevice device) {
+        Log.v(TAG, "unplug(): device=" + device);
 
         boolean result = false;
 
         if (mService != null) {
             try {
-                result = mService.unplug();
+                result = mService.unplug(device);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -478,14 +515,14 @@
      *
      * @return
      */
-    public boolean connect() {
-        Log.v(TAG, "connect()");
+    public boolean connect(BluetoothDevice device) {
+        Log.v(TAG, "connect(): device=" + device);
 
         boolean result = false;
 
         if (mService != null) {
             try {
-                result = mService.connect();
+                result = mService.connect(device);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -501,14 +538,14 @@
      *
      * @return
      */
-    public boolean disconnect() {
-        Log.v(TAG, "disconnect()");
+    public boolean disconnect(BluetoothDevice device) {
+        Log.v(TAG, "disconnect(): device=" + device);
 
         boolean result = false;
 
         if (mService != null) {
             try {
-                result = mService.disconnect();
+                result = mService.disconnect(device);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 26ff9e27..5b524eb 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.bluetooth.BluetoothCodecConfig;
 import android.bluetooth.BluetoothDevice;
 
 /**
@@ -36,4 +37,6 @@
     oneway void adjustAvrcpAbsoluteVolume(int direction);
     oneway void setAvrcpAbsoluteVolume(int volume);
     boolean isA2dpPlaying(in BluetoothDevice device);
+    BluetoothCodecConfig getCodecConfig();
+    oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig);
 }
diff --git a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
index 1252876..a737198 100644
--- a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
@@ -23,9 +23,9 @@
 interface IBluetoothHidDeviceCallback {
    void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered);
    void onConnectionStateChanged(in BluetoothDevice device, in int state);
-   void onGetReport(in byte type, in byte id, in int bufferSize);
-   void onSetReport(in byte type, in byte id, in byte[] data);
-   void onSetProtocol(in byte protocol);
-   void onIntrData(in byte reportId, in byte[] data);
-   void onVirtualCableUnplug();
+   void onGetReport(in BluetoothDevice device, in byte type, in byte id, in int bufferSize);
+   void onSetReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data);
+   void onSetProtocol(in BluetoothDevice device, in byte protocol);
+   void onIntrData(in BluetoothDevice device, in byte reportId, in byte[] data);
+   void onVirtualCableUnplug(in BluetoothDevice device);
 }
diff --git a/core/java/android/bluetooth/IBluetoothInputHost.aidl b/core/java/android/bluetooth/IBluetoothInputHost.aidl
index b2c421c..6c4993f 100644
--- a/core/java/android/bluetooth/IBluetoothInputHost.aidl
+++ b/core/java/android/bluetooth/IBluetoothInputHost.aidl
@@ -28,10 +28,13 @@
             in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos,
             in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback);
     boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config);
-    boolean sendReport(in int id, in byte[] data);
-    boolean replyReport(in byte type, in byte id, in byte[] data);
-    boolean reportError(byte error);
-    boolean unplug();
-    boolean connect();
-    boolean disconnect();
+    boolean sendReport(in BluetoothDevice device, in int id, in byte[] data);
+    boolean replyReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data);
+    boolean reportError(in BluetoothDevice device, byte error);
+    boolean unplug(in BluetoothDevice device);
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
 }
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index 9464222..e1eaf00 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -87,9 +87,9 @@
     {
         LocalSocketImpl acceptedImpl = new LocalSocketImpl();
 
-        impl.accept (acceptedImpl);
+        impl.accept(acceptedImpl);
 
-        return new LocalSocket(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
+        return LocalSocket.createLocalSocketForAccept(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
     }
 
     /**
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index e14facb1..3f8d9d3 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -75,17 +75,24 @@
         isConnected = true;
     }
 
-    /**
-     * for use with AndroidServerSocket
-     * @param impl a SocketImpl
-     */
-    /*package*/ LocalSocket(LocalSocketImpl impl, int sockType) {
+    private LocalSocket(LocalSocketImpl impl, int sockType) {
         this.impl = impl;
         this.sockType = sockType;
         this.isConnected = false;
         this.isBound = false;
     }
 
+    /**
+     * for use with LocalServerSocket.accept()
+     */
+    static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl, int sockType) {
+        LocalSocket socket = new LocalSocket(impl, sockType);
+        socket.isConnected = true;
+        socket.isBound = true;
+        socket.implCreated = true;
+        return socket;
+    }
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 0f0e9c4..d8f7821 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -235,29 +235,29 @@
      * @throws IOException
      */
     public void create(int sockType) throws IOException {
-        // no error if socket already created
-        // need this for LocalServerSocket.accept()
-        if (fd == null) {
-            int osType;
-            switch (sockType) {
-                case LocalSocket.SOCKET_DGRAM:
-                    osType = OsConstants.SOCK_DGRAM;
-                    break;
-                case LocalSocket.SOCKET_STREAM:
-                    osType = OsConstants.SOCK_STREAM;
-                    break;
-                case LocalSocket.SOCKET_SEQPACKET:
-                    osType = OsConstants.SOCK_SEQPACKET;
-                    break;
-                default:
-                    throw new IllegalStateException("unknown sockType");
-            }
-            try {
-                fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
-                mFdCreatedInternally = true;
-            } catch (ErrnoException e) {
-                e.rethrowAsIOException();
-            }
+        if (fd != null) {
+            throw new IOException("LocalSocketImpl already has an fd");
+        }
+
+        int osType;
+        switch (sockType) {
+            case LocalSocket.SOCKET_DGRAM:
+                osType = OsConstants.SOCK_DGRAM;
+                break;
+            case LocalSocket.SOCKET_STREAM:
+                osType = OsConstants.SOCK_STREAM;
+                break;
+            case LocalSocket.SOCKET_SEQPACKET:
+                osType = OsConstants.SOCK_SEQPACKET;
+                break;
+            default:
+                throw new IllegalStateException("unknown sockType");
+        }
+        try {
+            fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
+            mFdCreatedInternally = true;
+        } catch (ErrnoException e) {
+            e.rethrowAsIOException();
         }
     }
 
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index a96f90d..d013f64 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -41,6 +41,7 @@
      * Builder class for constructing {@link RecommendationRequest} instances.
      * @hide
      */
+    @SystemApi
     public static final class Builder {
         private ScanResult[] mScanResults;
         private WifiConfiguration mCurrentConfig;
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 94e5187..7e3dd77 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Bundle;
@@ -24,6 +25,8 @@
 
 import java.lang.Math;
 import java.lang.UnsupportedOperationException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -33,6 +36,15 @@
  */
 @SystemApi
 public class ScoredNetwork implements Parcelable {
+
+  /**
+     * Key used with the {@link #attributes} bundle to define the badging curve.
+     *
+     * <p>The badging curve is a {@link RssiCurve} used to map different RSSI values to {@link
+     * Badging} enums.
+     */
+    public static final String ATTRIBUTES_KEY_BADGING_CURVE =
+            "android.net.attributes.key.BADGING_CURVE";
     /**
      * Extra used with {@link #attributes} to specify whether the
      * network is believed to have a captive portal.
@@ -58,6 +70,15 @@
     public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET =
             "android.net.attributes.key.RANKING_SCORE_OFFSET";
 
+    @IntDef({BADGING_NONE, BADGING_SD, BADGING_HD, BADGING_4K})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Badging {}
+
+    public static final int BADGING_NONE = 0;
+    public static final int BADGING_SD = 10;
+    public static final int BADGING_HD = 20;
+    public static final int BADGING_4K = 30;
+
     /** A {@link NetworkKey} uniquely identifying this network. */
     public final NetworkKey networkKey;
 
@@ -249,6 +270,25 @@
         }
     }
 
+    /**
+     * Return the {@link Badging} enum for this network for the given RSSI, derived from the
+     * badging curve.
+     *
+     * <p>If no badging curve is present, {@link #BADGE_NONE} will be returned.
+     *
+     * @param rssi The rssi level for which the badge should be calculated
+     */
+    @Badging
+    public int calculateBadge(int rssi) {
+        if (attributes != null && attributes.containsKey(ATTRIBUTES_KEY_BADGING_CURVE)) {
+            RssiCurve badgingCurve =
+                    attributes.getParcelable(ATTRIBUTES_KEY_BADGING_CURVE);
+            return badgingCurve.lookupScore(rssi);
+        }
+
+        return BADGING_NONE;
+    }
+
     public static final Parcelable.Creator<ScoredNetwork> CREATOR =
             new Parcelable.Creator<ScoredNetwork>() {
                 @Override
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0d6d369..daf8b2d 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -818,7 +818,7 @@
      */
     public static boolean isBuildConsistent() {
         // Don't care on eng builds.  Incremental build may trigger false negative.
-        if ("eng".equals(TYPE)) return true;
+        if (IS_ENG) return true;
 
         final String system = SystemProperties.get("ro.build.fingerprint");
         final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
@@ -882,6 +882,10 @@
     public static final boolean IS_DEBUGGABLE =
             SystemProperties.getInt("ro.debuggable", 0) == 1;
 
+    /** {@hide} */
+    public static final boolean IS_ENG =
+            "eng".equals(getString("ro.build.type"));
+
     /**
      * Specifies whether the permissions needed by a legacy app should be
      * reviewed before any of its components can run. A legacy app is one
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 481b2dc..bcc3468 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -33,11 +33,14 @@
                 mNativeContext);
     }
 
+    @Override
     public final native void transact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
 
     public abstract void onTransact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
 
     public native final void registerService(
             ArrayList<String> interfaceChain,
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index e617e0a..2f89ce6 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -32,12 +32,15 @@
                 mNativeContext);
     }
 
+    @Override
     public IHwInterface queryLocalInterface(String descriptor) {
         return null;
     }
 
+    @Override
     public native final void transact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
 
     public native boolean linkToDeath(DeathRecipient recipient, long cookie);
     public native boolean unlinkToDeath(DeathRecipient recipient);
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index f93bfd7..2a65679 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -23,7 +23,8 @@
     public static final int FLAG_ONEWAY = 1;
 
     public void transact(
-            int code, HwParcel request, HwParcel reply, int flags);
+            int code, HwParcel request, HwParcel reply, int flags)
+        throws RemoteException;
 
     public IHwInterface queryLocalInterface(String descriptor);
 
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9cd1a42..d6688e3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -184,6 +184,11 @@
      */
     public static final int LAST_SHARED_APPLICATION_GID = 59999;
 
+    /** {@hide} */
+    public static final int FIRST_APPLICATION_CACHE_GID = 20000;
+    /** {@hide} */
+    public static final int LAST_APPLICATION_CACHE_GID = 29999;
+
     /**
      * Standard priority of application threads.
      * Use with {@link #setThreadPriority(int)} and
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index b04d149..4aec8ae 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -215,6 +215,15 @@
     }
 
     /**
+     * Returns the cache GID for a given UID or appId.
+     * @hide
+     */
+    public static int getCacheAppGid(int id) {
+        return Process.FIRST_APPLICATION_CACHE_GID + (id % PER_USER_RANGE)
+                - Process.FIRST_APPLICATION_UID;
+    }
+
+    /**
      * Generate a text representation of the uid, breaking out its individual
      * components -- user, app, isolated, etc.
      * @hide
diff --git a/core/java/android/speech/tts/EventLoggerV1.java b/core/java/android/speech/tts/EventLogger.java
similarity index 87%
rename from core/java/android/speech/tts/EventLoggerV1.java
rename to core/java/android/speech/tts/EventLogger.java
index 2b02b43..f03df31 100644
--- a/core/java/android/speech/tts/EventLoggerV1.java
+++ b/core/java/android/speech/tts/EventLogger.java
@@ -18,14 +18,14 @@
 import android.text.TextUtils;
 
 /**
- * Writes data about a given speech synthesis request for V1 API to the event
- * logs. The data that is logged includes the calling app, length of the
- * utterance, speech rate / pitch, the latency, and overall time taken.
+ * Writes data about a given speech synthesis request to the event logs. The data that is logged
+ * includes the calling app, length of the utterance, speech rate / pitch, the latency, and overall
+ * time taken.
  */
-class EventLoggerV1 extends AbstractEventLogger {
+class EventLogger extends AbstractEventLogger {
     private final SynthesisRequest mRequest;
 
-    EventLoggerV1(SynthesisRequest request, int callerUid, int callerPid, String serviceApp) {
+    EventLogger(SynthesisRequest request, int callerUid, int callerPid, String serviceApp) {
         super(callerUid, callerPid, serviceApp);
         mRequest = request;
     }
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 171a72c..55da52b 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -226,17 +226,14 @@
     protected abstract void onStop();
 
     /**
-     * Tells the service to synthesize speech from the given text. This method
-     * should block until the synthesis is finished. Used for requests from V1
-     * clients ({@link android.speech.tts.TextToSpeech}). Called on the synthesis
-     * thread.
+     * Tells the service to synthesize speech from the given text. This method should block until
+     * the synthesis is finished. Called on the synthesis thread.
      *
      * @param request The synthesis request.
-     * @param callback The callback that the engine must use to make data
-     *            available for playback or for writing to a file.
+     * @param callback The callback that the engine must use to make data available for playback or
+     *     for writing to a file.
      */
-    protected abstract void onSynthesizeText(SynthesisRequest request,
-            SynthesisCallback callback);
+    protected abstract void onSynthesizeText(SynthesisRequest request, SynthesisCallback callback);
 
     /**
      * Queries the service for a set of features supported for a given language.
@@ -713,8 +710,7 @@
         }
 
         /** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */
-        static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle,
-                boolean isSpeech) {
+        static AudioOutputParams createFromParamsBundle(Bundle paramsBundle, boolean isSpeech) {
             if (paramsBundle == null) {
                 return new AudioOutputParams();
             }
@@ -902,16 +898,19 @@
     }
 
     /**
-     * UtteranceSpeechItem for V1 API speech items. V1 API speech items keep
-     * synthesis parameters in a single Bundle passed as parameter. This class
-     * allow subclasses to access them conveniently.
+     * Synthesis parameters are kept in a single Bundle passed as parameter. This class allow
+     * subclasses to access them conveniently.
      */
-    private abstract class SpeechItemV1 extends UtteranceSpeechItem {
+    private abstract class UtteranceSpeechItemWithParams extends UtteranceSpeechItem {
         protected final Bundle mParams;
         protected final String mUtteranceId;
 
-        SpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, String utteranceId) {
+        UtteranceSpeechItemWithParams(
+                Object callerIdentity,
+                int callerUid,
+                int callerPid,
+                Bundle params,
+                String utteranceId) {
             super(callerIdentity, callerUid, callerPid);
             mParams = params;
             mUtteranceId = utteranceId;
@@ -935,11 +934,11 @@
         }
 
         AudioOutputParams getAudioParams() {
-            return AudioOutputParams.createFromV1ParamsBundle(mParams, true);
+            return AudioOutputParams.createFromParamsBundle(mParams, true);
         }
     }
 
-    class SynthesisSpeechItemV1 extends SpeechItemV1 {
+    class SynthesisSpeechItem extends UtteranceSpeechItemWithParams {
         // Never null.
         private final CharSequence mText;
         private final SynthesisRequest mSynthesisRequest;
@@ -947,19 +946,23 @@
         // Non null after synthesis has started, and all accesses
         // guarded by 'this'.
         private AbstractSynthesisCallback mSynthesisCallback;
-        private final EventLoggerV1 mEventLogger;
+        private final EventLogger mEventLogger;
         private final int mCallerUid;
 
-        public SynthesisSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, String utteranceId, CharSequence text) {
+        public SynthesisSpeechItem(
+                Object callerIdentity,
+                int callerUid,
+                int callerPid,
+                Bundle params,
+                String utteranceId,
+                CharSequence text) {
             super(callerIdentity, callerUid, callerPid, params, utteranceId);
             mText = text;
             mCallerUid = callerUid;
             mSynthesisRequest = new SynthesisRequest(mText, mParams);
             mDefaultLocale = getSettingsLocale();
             setRequestParams(mSynthesisRequest);
-            mEventLogger = new EventLoggerV1(mSynthesisRequest, callerUid, callerPid,
-                    mPackageName);
+            mEventLogger = new EventLogger(mSynthesisRequest, callerUid, callerPid, mPackageName);
         }
 
         public CharSequence getText() {
@@ -1053,11 +1056,16 @@
         }
     }
 
-    private class SynthesisToFileOutputStreamSpeechItemV1 extends SynthesisSpeechItemV1 {
+    private class SynthesisToFileOutputStreamSpeechItem extends SynthesisSpeechItem {
         private final FileOutputStream mFileOutputStream;
 
-        public SynthesisToFileOutputStreamSpeechItemV1(Object callerIdentity, int callerUid,
-                int callerPid, Bundle params, String utteranceId, CharSequence text,
+        public SynthesisToFileOutputStreamSpeechItem(
+                Object callerIdentity,
+                int callerUid,
+                int callerPid,
+                Bundle params,
+                String utteranceId,
+                CharSequence text,
                 FileOutputStream fileOutputStream) {
             super(callerIdentity, callerUid, callerPid, params, utteranceId, text);
             mFileOutputStream = fileOutputStream;
@@ -1080,11 +1088,16 @@
         }
     }
 
-    private class AudioSpeechItemV1 extends SpeechItemV1 {
+    private class AudioSpeechItem extends UtteranceSpeechItemWithParams {
         private final AudioPlaybackQueueItem mItem;
 
-        public AudioSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, String utteranceId, Uri uri) {
+        public AudioSpeechItem(
+                Object callerIdentity,
+                int callerUid,
+                int callerPid,
+                Bundle params,
+                String utteranceId,
+                Uri uri) {
             super(callerIdentity, callerUid, callerPid, params, utteranceId);
             mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(),
                     TextToSpeechService.this, uri, getAudioParams());
@@ -1112,7 +1125,7 @@
 
         @Override
         AudioOutputParams getAudioParams() {
-            return AudioOutputParams.createFromV1ParamsBundle(mParams, false);
+            return AudioOutputParams.createFromParamsBundle(mParams, false);
         }
     }
 
@@ -1219,202 +1232,252 @@
     }
 
     /**
-     * Binder returned from {@code #onBind(Intent)}. The methods in this class can be
-     * called called from several different threads.
+     * Binder returned from {@code #onBind(Intent)}. The methods in this class can be called called
+     * from several different threads.
      */
     // NOTE: All calls that are passed in a calling app are interned so that
     // they can be used as message objects (which are tested for equality using ==).
-    private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
-        @Override
-        public int speak(IBinder caller, CharSequence text, int queueMode, Bundle params,
-                String utteranceId) {
-            if (!checkNonNull(caller, text, params)) {
-                return TextToSpeech.ERROR;
-            }
+    private final ITextToSpeechService.Stub mBinder =
+            new ITextToSpeechService.Stub() {
+                @Override
+                public int speak(
+                        IBinder caller,
+                        CharSequence text,
+                        int queueMode,
+                        Bundle params,
+                        String utteranceId) {
+                    if (!checkNonNull(caller, text, params)) {
+                        return TextToSpeech.ERROR;
+                    }
 
-            SpeechItem item = new SynthesisSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text);
-            return mSynthHandler.enqueueSpeechItem(queueMode, item);
-        }
-
-        @Override
-        public int synthesizeToFileDescriptor(IBinder caller, CharSequence text, ParcelFileDescriptor
-                fileDescriptor, Bundle params, String utteranceId) {
-            if (!checkNonNull(caller, text, fileDescriptor, params)) {
-                return TextToSpeech.ERROR;
-            }
-
-            // In test env, ParcelFileDescriptor instance may be EXACTLY the same
-            // one that is used by client. And it will be closed by a client, thus
-            // preventing us from writing anything to it.
-            final ParcelFileDescriptor sameFileDescriptor = ParcelFileDescriptor.adoptFd(
-                    fileDescriptor.detachFd());
-
-            SpeechItem item = new SynthesisToFileOutputStreamSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text,
-                    new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor));
-            return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
-        }
-
-        @Override
-        public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params,
-                String utteranceId) {
-            if (!checkNonNull(caller, audioUri, params)) {
-                return TextToSpeech.ERROR;
-            }
-
-            SpeechItem item = new AudioSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, audioUri);
-            return mSynthHandler.enqueueSpeechItem(queueMode, item);
-        }
-
-        @Override
-        public int playSilence(IBinder caller, long duration, int queueMode, String utteranceId) {
-            if (!checkNonNull(caller)) {
-                return TextToSpeech.ERROR;
-            }
-
-            SpeechItem item = new SilenceSpeechItem(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), utteranceId, duration);
-            return mSynthHandler.enqueueSpeechItem(queueMode, item);
-        }
-
-        @Override
-        public boolean isSpeaking() {
-            return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking();
-        }
-
-        @Override
-        public int stop(IBinder caller) {
-            if (!checkNonNull(caller)) {
-                return TextToSpeech.ERROR;
-            }
-
-            return mSynthHandler.stopForApp(caller);
-        }
-
-        @Override
-        public String[] getLanguage() {
-            return onGetLanguage();
-        }
-
-        @Override
-        public String[] getClientDefaultLanguage() {
-            return getSettingsLocale();
-        }
-
-        /*
-         * If defaults are enforced, then no language is "available" except
-         * perhaps the default language selected by the user.
-         */
-        @Override
-        public int isLanguageAvailable(String lang, String country, String variant) {
-            if (!checkNonNull(lang)) {
-                return TextToSpeech.ERROR;
-            }
-
-            return onIsLanguageAvailable(lang, country, variant);
-        }
-
-        @Override
-        public String[] getFeaturesForLanguage(String lang, String country, String variant) {
-            Set<String> features = onGetFeaturesForLanguage(lang, country, variant);
-            String[] featuresArray = null;
-            if (features != null) {
-                featuresArray = new String[features.size()];
-                features.toArray(featuresArray);
-            } else {
-                featuresArray = new String[0];
-            }
-            return featuresArray;
-        }
-
-        /*
-         * There is no point loading a non default language if defaults
-         * are enforced.
-         */
-        @Override
-        public int loadLanguage(IBinder caller, String lang, String country, String variant) {
-            if (!checkNonNull(lang)) {
-                return TextToSpeech.ERROR;
-            }
-            int retVal = onIsLanguageAvailable(lang, country, variant);
-
-            if (retVal == TextToSpeech.LANG_AVAILABLE ||
-                    retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE ||
-                    retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
-
-                SpeechItem item = new LoadLanguageItem(caller, Binder.getCallingUid(),
-                        Binder.getCallingPid(), lang, country, variant);
-
-                if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) !=
-                        TextToSpeech.SUCCESS) {
-                    return TextToSpeech.ERROR;
+                    SpeechItem item =
+                            new SynthesisSpeechItem(
+                                    caller,
+                                    Binder.getCallingUid(),
+                                    Binder.getCallingPid(),
+                                    params,
+                                    utteranceId,
+                                    text);
+                    return mSynthHandler.enqueueSpeechItem(queueMode, item);
                 }
-            }
-            return retVal;
-        }
 
-        @Override
-        public List<Voice> getVoices() {
-            return onGetVoices();
-        }
+                @Override
+                public int synthesizeToFileDescriptor(
+                        IBinder caller,
+                        CharSequence text,
+                        ParcelFileDescriptor fileDescriptor,
+                        Bundle params,
+                        String utteranceId) {
+                    if (!checkNonNull(caller, text, fileDescriptor, params)) {
+                        return TextToSpeech.ERROR;
+                    }
 
-        @Override
-        public int loadVoice(IBinder caller, String voiceName) {
-            if (!checkNonNull(voiceName)) {
-                return TextToSpeech.ERROR;
-            }
-            int retVal = onIsValidVoiceName(voiceName);
+                    // In test env, ParcelFileDescriptor instance may be EXACTLY the same
+                    // one that is used by client. And it will be closed by a client, thus
+                    // preventing us from writing anything to it.
+                    final ParcelFileDescriptor sameFileDescriptor =
+                            ParcelFileDescriptor.adoptFd(fileDescriptor.detachFd());
 
-            if (retVal == TextToSpeech.SUCCESS) {
-                SpeechItem item = new LoadVoiceItem(caller, Binder.getCallingUid(),
-                        Binder.getCallingPid(), voiceName);
-                if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) !=
-                        TextToSpeech.SUCCESS) {
-                    return TextToSpeech.ERROR;
+                    SpeechItem item =
+                            new SynthesisToFileOutputStreamSpeechItem(
+                                    caller,
+                                    Binder.getCallingUid(),
+                                    Binder.getCallingPid(),
+                                    params,
+                                    utteranceId,
+                                    text,
+                                    new ParcelFileDescriptor.AutoCloseOutputStream(
+                                            sameFileDescriptor));
+                    return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
                 }
-            }
-            return retVal;
-        }
 
-        public String getDefaultVoiceNameFor(String lang, String country, String variant) {
-            if (!checkNonNull(lang)) {
-                return null;
-            }
-            int retVal = onIsLanguageAvailable(lang, country, variant);
+                @Override
+                public int playAudio(
+                        IBinder caller,
+                        Uri audioUri,
+                        int queueMode,
+                        Bundle params,
+                        String utteranceId) {
+                    if (!checkNonNull(caller, audioUri, params)) {
+                        return TextToSpeech.ERROR;
+                    }
 
-            if (retVal == TextToSpeech.LANG_AVAILABLE ||
-                    retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE ||
-                    retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
-                return onGetDefaultVoiceNameFor(lang, country, variant);
-            } else {
-                return null;
-            }
-        }
+                    SpeechItem item =
+                            new AudioSpeechItem(
+                                    caller,
+                                    Binder.getCallingUid(),
+                                    Binder.getCallingPid(),
+                                    params,
+                                    utteranceId,
+                                    audioUri);
+                    return mSynthHandler.enqueueSpeechItem(queueMode, item);
+                }
 
-        @Override
-        public void setCallback(IBinder caller, ITextToSpeechCallback cb) {
-            // Note that passing in a null callback is a valid use case.
-            if (!checkNonNull(caller)) {
-                return;
-            }
+                @Override
+                public int playSilence(
+                        IBinder caller, long duration, int queueMode, String utteranceId) {
+                    if (!checkNonNull(caller)) {
+                        return TextToSpeech.ERROR;
+                    }
 
-            mCallbacks.setCallback(caller, cb);
-        }
+                    SpeechItem item =
+                            new SilenceSpeechItem(
+                                    caller,
+                                    Binder.getCallingUid(),
+                                    Binder.getCallingPid(),
+                                    utteranceId,
+                                    duration);
+                    return mSynthHandler.enqueueSpeechItem(queueMode, item);
+                }
 
-        private String intern(String in) {
-            // The input parameter will be non null.
-            return in.intern();
-        }
+                @Override
+                public boolean isSpeaking() {
+                    return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking();
+                }
 
-        private boolean checkNonNull(Object... args) {
-            for (Object o : args) {
-                if (o == null) return false;
-            }
-            return true;
-        }
-    };
+                @Override
+                public int stop(IBinder caller) {
+                    if (!checkNonNull(caller)) {
+                        return TextToSpeech.ERROR;
+                    }
+
+                    return mSynthHandler.stopForApp(caller);
+                }
+
+                @Override
+                public String[] getLanguage() {
+                    return onGetLanguage();
+                }
+
+                @Override
+                public String[] getClientDefaultLanguage() {
+                    return getSettingsLocale();
+                }
+
+                /*
+                 * If defaults are enforced, then no language is "available" except
+                 * perhaps the default language selected by the user.
+                 */
+                @Override
+                public int isLanguageAvailable(String lang, String country, String variant) {
+                    if (!checkNonNull(lang)) {
+                        return TextToSpeech.ERROR;
+                    }
+
+                    return onIsLanguageAvailable(lang, country, variant);
+                }
+
+                @Override
+                public String[] getFeaturesForLanguage(
+                        String lang, String country, String variant) {
+                    Set<String> features = onGetFeaturesForLanguage(lang, country, variant);
+                    String[] featuresArray = null;
+                    if (features != null) {
+                        featuresArray = new String[features.size()];
+                        features.toArray(featuresArray);
+                    } else {
+                        featuresArray = new String[0];
+                    }
+                    return featuresArray;
+                }
+
+                /*
+                 * There is no point loading a non default language if defaults
+                 * are enforced.
+                 */
+                @Override
+                public int loadLanguage(
+                        IBinder caller, String lang, String country, String variant) {
+                    if (!checkNonNull(lang)) {
+                        return TextToSpeech.ERROR;
+                    }
+                    int retVal = onIsLanguageAvailable(lang, country, variant);
+
+                    if (retVal == TextToSpeech.LANG_AVAILABLE
+                            || retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE
+                            || retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
+
+                        SpeechItem item =
+                                new LoadLanguageItem(
+                                        caller,
+                                        Binder.getCallingUid(),
+                                        Binder.getCallingPid(),
+                                        lang,
+                                        country,
+                                        variant);
+
+                        if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item)
+                                != TextToSpeech.SUCCESS) {
+                            return TextToSpeech.ERROR;
+                        }
+                    }
+                    return retVal;
+                }
+
+                @Override
+                public List<Voice> getVoices() {
+                    return onGetVoices();
+                }
+
+                @Override
+                public int loadVoice(IBinder caller, String voiceName) {
+                    if (!checkNonNull(voiceName)) {
+                        return TextToSpeech.ERROR;
+                    }
+                    int retVal = onIsValidVoiceName(voiceName);
+
+                    if (retVal == TextToSpeech.SUCCESS) {
+                        SpeechItem item =
+                                new LoadVoiceItem(
+                                        caller,
+                                        Binder.getCallingUid(),
+                                        Binder.getCallingPid(),
+                                        voiceName);
+                        if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item)
+                                != TextToSpeech.SUCCESS) {
+                            return TextToSpeech.ERROR;
+                        }
+                    }
+                    return retVal;
+                }
+
+                public String getDefaultVoiceNameFor(String lang, String country, String variant) {
+                    if (!checkNonNull(lang)) {
+                        return null;
+                    }
+                    int retVal = onIsLanguageAvailable(lang, country, variant);
+
+                    if (retVal == TextToSpeech.LANG_AVAILABLE
+                            || retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE
+                            || retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
+                        return onGetDefaultVoiceNameFor(lang, country, variant);
+                    } else {
+                        return null;
+                    }
+                }
+
+                @Override
+                public void setCallback(IBinder caller, ITextToSpeechCallback cb) {
+                    // Note that passing in a null callback is a valid use case.
+                    if (!checkNonNull(caller)) {
+                        return;
+                    }
+
+                    mCallbacks.setCallback(caller, cb);
+                }
+
+                private String intern(String in) {
+                    // The input parameter will be non null.
+                    return in.intern();
+                }
+
+                private boolean checkNonNull(Object... args) {
+                    for (Object o : args) {
+                        if (o == null) return false;
+                    }
+                    return true;
+                }
+            };
 
     private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> {
         private final HashMap<IBinder, ITextToSpeechCallback> mCallerToCallback
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 412eba3..a8c3453 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -471,9 +471,7 @@
         String[] ret = new String[]{"","",""};
         try {
             // Note that the default locale might have an empty variant
-            // or language, and we take care that the construction is
-            // the same as {@link #getV1Locale} i.e no trailing delimiters
-            // or spaces.
+            // or language.
             ret[0] = locale.getISO3Language();
             ret[1] = locale.getISO3Country();
             ret[2] = locale.getVariant();
diff --git a/core/java/android/webkit/RenderProcessGoneDetail.java b/core/java/android/webkit/RenderProcessGoneDetail.java
new file mode 100644
index 0000000..77d8596
--- /dev/null
+++ b/core/java/android/webkit/RenderProcessGoneDetail.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 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 android.webkit;
+
+/**
+ * This class provides more specific information about why the render process
+ * exited. The application may use this to decide how to handle the situation.
+ **/
+public abstract class RenderProcessGoneDetail {
+    /**
+     * Indicates whether the render process was observed to crash, or whether
+     * it was killed by the system.
+     *
+     * If the render process was killed, this is most likely caused by the
+     * system being low on memory.
+     *
+     * @return True if render process crashed, otherwise it was killed by
+     *         system.
+     **/
+    public abstract boolean didCrash();
+}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 76d3fb1..8703468 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -466,4 +466,31 @@
     public void onReceivedLoginRequest(WebView view, String realm,
             String account, String args) {
     }
+
+    /**
+     * Notify host application that the given webview's render process has exited.
+     *
+     * Multiple WebView instances may be associated with a single render process;
+     * onRenderProcessGone will be called for each WebView that was affected.
+     * The application's implementation of this callback should only attempt to
+     * clean up the specific WebView given as a parameter, and should not assume
+     * that other WebView instances are affected.
+     *
+     * The given WebView can't be used, and should be removed from the view hierarchy,
+     * all references to it should be cleaned up, e.g any references in the Activity
+     * or other classes saved using findViewById and similar calls, etc
+     *
+     * To cause an render process crash for test purpose, the application can
+     * call loadUrl("chrome://crash") on the WebView. Note that multiple WebView
+     * instances may be affected if they share a render process, not just the
+     * specific WebView which loaded chrome://crash.
+     *
+     * @param view The WebView which needs to be cleaned up.
+     * @param detail the reason why it exited.
+     * @return true if the host application handled the situation that process has
+     *         exited, otherwise, application will crash.
+     */
+    public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
+        return false;
+    }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index c314cae..16a1251 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -69,6 +69,7 @@
 import android.widget.BaseAdapter;
 import android.widget.ListView;
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity.TargetInfo;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -93,9 +94,13 @@
     private IntentSender mRefinementIntentSender;
     private RefinementResultReceiver mRefinementResultReceiver;
     private ChooserTarget[] mCallerChooserTargets;
+    private ComponentName[] mFilteredComponentNames;
 
     private Intent mReferrerFillInIntent;
 
+    private long mChooserShownTime;
+    private boolean mIsSuccessfullySelected;
+
     private ChooserListAdapter mChooserListAdapter;
     private ChooserRowAdapter mChooserRowAdapter;
 
@@ -155,6 +160,8 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        final long intentReceivedTime = System.currentTimeMillis();
+        mIsSuccessfullySelected = false;
         Intent intent = getIntent();
         Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
         if (!(targetParcelable instanceof Intent)) {
@@ -235,7 +242,7 @@
                 }
                 names[i] = (ComponentName) pa[i];
             }
-            setFilteredComponents(names);
+            mFilteredComponentNames = names;
         }
 
         pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
@@ -257,6 +264,13 @@
                 null, false);
 
         MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
+
+        mChooserShownTime = System.currentTimeMillis();
+        final long systemCost = mChooserShownTime - intentReceivedTime;
+        MetricsLogger.histogram(null, "system_cost_for_smart_sharing", (int) systemCost);
+        if (DEBUG) {
+            Log.d(TAG, "System Time Cost is " + systemCost);
+        }
     }
 
     static SharedPreferences getPinnedSharedPrefs(Context context) {
@@ -410,6 +424,7 @@
 
     @Override
     public void startSelected(int which, boolean always, boolean filtered) {
+        final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
         super.startSelected(which, always, filtered);
 
         if (mChooserListAdapter != null) {
@@ -435,6 +450,17 @@
             if (cat != 0) {
                 MetricsLogger.action(this, cat, value);
             }
+
+            if (mIsSuccessfullySelected) {
+                if (DEBUG) {
+                    Log.d(TAG, "User Selection Time Cost is " + selectionCost);
+                    Log.d(TAG, "position of selected app/service/caller is " +
+                            Integer.toString(value));
+                }
+                MetricsLogger.histogram(null, "user_selection_cost_for_smart_sharing",
+                        (int) selectionCost);
+                MetricsLogger.histogram(null, "app_position_for_smart_sharing", value);
+            }
         }
     }
 
@@ -563,6 +589,7 @@
             if (ri != null && ri.activityInfo != null) {
                 usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
                         annotation, null, info.getResolvedIntent().getAction());
+                mResolverComparator.updateModel(info.getResolvedComponentName());
                 if (DEBUG) {
                     Log.d(TAG, "ResolveInfo Package is" + ri.activityInfo.packageName);
                 }
@@ -570,6 +597,7 @@
                 Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
             }
         }
+        mIsSuccessfullySelected = true;
     }
 
     void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
@@ -642,17 +670,65 @@
         }
     }
 
+    public class ChooserListController extends ResolverListController {
+        public ChooserListController(Context context,
+                PackageManager pm,
+                Intent targetIntent,
+                String referrerPackageName,
+                int launchedFromUid) {
+            super(context, pm, targetIntent, referrerPackageName, launchedFromUid);
+        }
+
+        @Override
+        boolean isComponentPinned(ComponentName name) {
+            return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
+        }
+
+        @Override
+        boolean isComponentFiltered(ComponentName name) {
+            if (mFilteredComponentNames == null) {
+                return false;
+            }
+            for (ComponentName filteredComponentName : mFilteredComponentNames) {
+                if (name.equals(filteredComponentName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public float getScore(DisplayResolveInfo target) {
+            if (target == null) {
+                return CALLER_TARGET_SCORE_BOOST;
+            }
+            float score = super.getScore(target);
+            if (target.isPinned()) {
+                score += PINNED_TARGET_SCORE_BOOST;
+            }
+            return score;
+        }
+    }
+
     @Override
     public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
             Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
             boolean filterLastUsed) {
         final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents,
-                initialIntents, rList, launchedFromUid, filterLastUsed);
-        if (DEBUG) Log.d(TAG, "Adapter created; querying services");
-        queryTargetServices(adapter);
+                initialIntents, rList, launchedFromUid, filterLastUsed, createListController());
         return adapter;
     }
 
+    @VisibleForTesting
+    protected ResolverListController createListController() {
+        return new ChooserListController(
+                this,
+                mPm,
+                getTargetIntent(),
+                getReferrerPackageName(),
+                mLaunchedFromUid);
+    }
+
     final class ChooserTargetInfo implements TargetInfo {
         private final DisplayResolveInfo mSourceInfo;
         private final ResolveInfo mBackupResolveInfo;
@@ -853,10 +929,11 @@
 
         public ChooserListAdapter(Context context, List<Intent> payloadIntents,
                 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
-                boolean filterLastUsed) {
+                boolean filterLastUsed, ResolverListController resolverListController) {
             // Don't send the initial intents through the shared ResolverActivity path,
             // we want to separate them into a different section.
-            super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed);
+            super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed,
+                    resolverListController);
 
             if (initialIntents != null) {
                 final PackageManager pm = getPackageManager();
@@ -922,18 +999,6 @@
         }
 
         @Override
-        public float getScore(DisplayResolveInfo target) {
-            if (target == null) {
-                return CALLER_TARGET_SCORE_BOOST;
-            }
-            float score = super.getScore(target);
-            if (target.isPinned()) {
-                score += PINNED_TARGET_SCORE_BOOST;
-            }
-            return score;
-        }
-
-        @Override
         public View onCreateView(ViewGroup parent) {
             return mInflater.inflate(
                     com.android.internal.R.layout.resolve_grid_item, parent, false);
@@ -944,6 +1009,8 @@
             if (mServiceTargets != null) {
                 pruneServiceTargets();
             }
+            if (DEBUG) Log.d(TAG, "List built querying services");
+            queryTargetServices(this);
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index b0e0373..5ce3e54 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -18,19 +18,23 @@
 
 import com.android.internal.R;
 
-import android.app.Dialog;
+import android.app.AlertDialog;
 import android.app.MediaRouteActionProvider;
 import android.app.MediaRouteButton;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteGroup;
 import android.media.MediaRouter.RouteInfo;
 import android.os.Bundle;
+import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.View;
-import android.view.Window;
-import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.SeekBar;
@@ -46,7 +50,7 @@
  *
  * TODO: Move this back into the API, as in the support library media router.
  */
-public class MediaRouteControllerDialog extends Dialog {
+public class MediaRouteControllerDialog extends AlertDialog {
     // Time to wait before updating the volume when the user lets go of the seek bar
     // to allow the route provider time to propagate the change and publish a new
     // route descriptor.
@@ -57,8 +61,9 @@
     private final MediaRouter.RouteInfo mRoute;
 
     private boolean mCreated;
-    private Drawable mMediaRouteConnectingDrawable;
-    private Drawable mMediaRouteOnDrawable;
+    private Drawable mMediaRouteButtonDrawable;
+    private int[] mMediaRouteConnectingState = { R.attr.state_checked, R.attr.state_enabled };
+    private int[] mMediaRouteOnState = { R.attr.state_activated, R.attr.state_enabled };
     private Drawable mCurrentIconDrawable;
 
     private boolean mVolumeControlEnabled = true;
@@ -68,8 +73,6 @@
 
     private View mControlView;
 
-    private Button mDisconnectButton;
-
     public MediaRouteControllerDialog(Context context, int theme) {
         super(context, theme);
 
@@ -132,14 +135,28 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        setTitle(mRoute.getName());
+        Resources res = getContext().getResources();
+        setButton(BUTTON_NEGATIVE, res.getString(R.string.media_route_controller_disconnect),
+                new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialogInterface, int id) {
+                        if (mRoute.isSelected()) {
+                            mRouter.getDefaultRoute().select();
+                        }
+                        dismiss();
+                    }
+                });
+        View customView = getLayoutInflater().inflate(R.layout.media_route_controller_dialog, null);
+        setView(customView, 0, 0, 0, 0);
         super.onCreate(savedInstanceState);
 
-        getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
-
-        setContentView(R.layout.media_route_controller_dialog);
-
-        mVolumeLayout = (LinearLayout)findViewById(R.id.media_route_volume_layout);
-        mVolumeSlider = (SeekBar)findViewById(R.id.media_route_volume_slider);
+        View customPanelView = getWindow().findViewById(R.id.customPanel);
+        if (customPanelView != null) {
+            customPanelView.setMinimumHeight(0);
+        }
+        mVolumeLayout = (LinearLayout) customView.findViewById(R.id.media_route_volume_layout);
+        mVolumeSlider = (SeekBar) customView.findViewById(R.id.media_route_volume_slider);
         mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             private final Runnable mStopTrackingTouch = new Runnable() {
                 @Override
@@ -176,22 +193,12 @@
             }
         });
 
-        mDisconnectButton = (Button)findViewById(R.id.media_route_disconnect_button);
-        mDisconnectButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mRoute.isSelected()) {
-                    mRouter.getDefaultRoute().select();
-                }
-                dismiss();
-            }
-        });
-
+        mMediaRouteButtonDrawable = obtainMediaRouteButtonDrawable();
         mCreated = true;
         if (update()) {
             mControlView = onCreateMediaControlView(savedInstanceState);
             FrameLayout controlFrame =
-                    (FrameLayout)findViewById(R.id.media_route_control_frame);
+                    (FrameLayout) customView.findViewById(R.id.media_route_control_frame);
             if (mControlView != null) {
                 controlFrame.addView(mControlView);
                 controlFrame.setVisibility(View.VISIBLE);
@@ -201,7 +208,6 @@
         }
     }
 
-
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -248,24 +254,41 @@
         Drawable icon = getIconDrawable();
         if (icon != mCurrentIconDrawable) {
             mCurrentIconDrawable = icon;
-            getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, icon);
+            if (icon instanceof AnimationDrawable) {
+                AnimationDrawable animDrawable = (AnimationDrawable) icon;
+                if (!animDrawable.isRunning()) {
+                    animDrawable.start();
+                }
+            }
+            setIcon(icon);
         }
         return true;
     }
 
+    private Drawable obtainMediaRouteButtonDrawable() {
+        Context context = getContext();
+        TypedValue value = new TypedValue();
+        if (!context.getTheme().resolveAttribute(R.attr.mediaRouteButtonStyle, value, true)) {
+            return null;
+        }
+        int[] drawableAttrs = new int[] { R.attr.externalRouteEnabledDrawable };
+        TypedArray a = context.obtainStyledAttributes(value.data, drawableAttrs);
+        Drawable drawable = a.getDrawable(0);
+        a.recycle();
+        return drawable;
+    }
+
     private Drawable getIconDrawable() {
-        if (mRoute.isConnecting()) {
-            if (mMediaRouteConnectingDrawable == null) {
-                mMediaRouteConnectingDrawable = getContext().getDrawable(
-                        R.drawable.ic_media_route_connecting_holo_dark);
-            }
-            return mMediaRouteConnectingDrawable;
+        if (!(mMediaRouteButtonDrawable instanceof StateListDrawable)) {
+            return mMediaRouteButtonDrawable;
+        } else if (mRoute.isConnecting()) {
+            StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+            stateListDrawable.setState(mMediaRouteConnectingState);
+            return stateListDrawable.getCurrent();
         } else {
-            if (mMediaRouteOnDrawable == null) {
-                mMediaRouteOnDrawable = getContext().getDrawable(
-                        R.drawable.ic_media_route_on_holo_dark);
-            }
-            return mMediaRouteOnDrawable;
+            StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+            stateListDrawable.setState(mMediaRouteOnState);
+            return stateListDrawable.getCurrent();
         }
     }
 
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
index 466c015..4c30501 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
@@ -39,7 +39,6 @@
      */
     public MediaRouteControllerDialogFragment() {
         setCancelable(true);
-        setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog);
     }
 
     /**
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index c516b5c..f2bd701 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -16,15 +16,14 @@
 
 package com.android.internal.app;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
+import android.annotation.UiThread;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.VoiceInteractor.PickOptionRequest;
 import android.app.VoiceInteractor.PickOptionRequest.Option;
 import android.app.VoiceInteractor.Prompt;
-import android.content.pm.ComponentInfo;
 import android.os.AsyncTask;
 import android.os.RemoteException;
 import android.provider.MediaStore;
@@ -33,6 +32,7 @@
 import android.util.Slog;
 import android.widget.AbsListView;
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
 
 import android.app.ActivityManager;
@@ -75,7 +75,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -83,21 +82,16 @@
 import java.util.Set;
 
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 
 /**
  * This activity is displayed when the system attempts to start an Intent for
  * which there is more than one matching activity, allowing the user to decide
  * which to go to.  It is not normally used directly by application developers.
  */
+@UiThread
 public class ResolverActivity extends Activity {
-    private static final String TAG = "ResolverActivity";
-    private static final boolean DEBUG = false;
 
-    private int mLaunchedFromUid;
-    private ResolveListAdapter mAdapter;
-    private PackageManager mPm;
+    protected ResolveListAdapter mAdapter;
     private boolean mSafeForwardingMode;
     private boolean mAlwaysUseOption;
     private AbsListView mAdapterView;
@@ -108,13 +102,19 @@
     private int mLastSelected = AbsListView.INVALID_POSITION;
     private boolean mResolvingHome = false;
     private int mProfileSwitchMessageId = -1;
+    private int mLayoutId;
     private final ArrayList<Intent> mIntents = new ArrayList<>();
-    private ResolverComparator mResolverComparator;
     private PickTargetOptionRequest mPickOptionRequest;
-    private ComponentName[] mFilteredComponents;
+    private String mReferrerPackage;
 
+    protected ResolverComparator mResolverComparator;
     protected ResolverDrawerLayout mResolverDrawerLayout;
     protected String mContentType;
+    protected PackageManager mPm;
+    protected int mLaunchedFromUid;
+
+    private static final String TAG = "ResolverActivity";
+    private static final boolean DEBUG = false;
 
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -261,6 +261,7 @@
 
         mPackageMonitor.register(this, getMainLooper(), false);
         mRegistered = true;
+        mReferrerPackage = getReferrerPackageName();
 
         final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
         mIconDpi = am.getLauncherLargeIconDensity();
@@ -268,11 +269,6 @@
         // Add our initial intent as the first item, regardless of what else has already been added.
         mIntents.add(0, new Intent(intent));
 
-        final String referrerPackage = getReferrerPackageName();
-
-        mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
-        mContentType = mResolverComparator.mContentType;
-
         if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
             return;
         }
@@ -306,11 +302,11 @@
             if (titleIcon != null) {
                 ApplicationInfo ai = null;
                 try {
-                    if (!TextUtils.isEmpty(referrerPackage)) {
-                        ai = mPm.getApplicationInfo(referrerPackage, 0);
+                    if (!TextUtils.isEmpty(mReferrerPackage)) {
+                        ai = mPm.getApplicationInfo(mReferrerPackage, 0);
                     }
                 } catch (NameNotFoundException e) {
-                    Log.e(TAG, "Could not find referrer package " + referrerPackage);
+                    Log.e(TAG, "Could not find referrer package " + mReferrerPackage);
                 }
 
                 if (ai != null) {
@@ -372,24 +368,6 @@
                         + (categories != null ? Arrays.toString(categories.toArray()) : ""));
     }
 
-    public final void setFilteredComponents(ComponentName[] components) {
-        mFilteredComponents = components;
-    }
-
-    public final boolean isComponentFiltered(ComponentInfo component) {
-        if (mFilteredComponents == null) {
-            return false;
-        }
-
-        final ComponentName checkName = component.getComponentName();
-        for (ComponentName name : mFilteredComponents) {
-            if (name.equals(checkName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * Perform any initialization needed for voice interaction.
      */
@@ -431,7 +409,7 @@
         return mIntents.isEmpty() ? null : mIntents.get(0);
     }
 
-    private String getReferrerPackageName() {
+    protected String getReferrerPackageName() {
         final Uri referrer = getReferrer();
         if (referrer != null && "android-app".equals(referrer.getScheme())) {
             return referrer.getHost();
@@ -689,7 +667,7 @@
         final Intent intent = target != null ? target.getResolvedIntent() : null;
 
         if (intent != null && (mAlwaysUseOption || mAdapter.hasFilteredItem())
-                && mAdapter.mOrigResolveList != null) {
+                && mAdapter.mUnfilteredResolveList != null) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
             Intent filterIntent;
@@ -774,11 +752,11 @@
             }
 
             if (filter != null) {
-                final int N = mAdapter.mOrigResolveList.size();
+                final int N = mAdapter.mUnfilteredResolveList.size();
                 ComponentName[] set = new ComponentName[N];
                 int bestMatch = 0;
                 for (int i=0; i<N; i++) {
-                    ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);
+                    ResolveInfo r = mAdapter.mUnfilteredResolveList.get(i).getResolveInfoAt(0);
                     set[i] = new ComponentName(r.activityInfo.packageName,
                             r.activityInfo.name);
                     if (r.match > bestMatch) bestMatch = r.match;
@@ -899,7 +877,17 @@
             Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
             boolean filterLastUsed) {
         return new ResolveListAdapter(context, payloadIntents, initialIntents, rList,
-                launchedFromUid, filterLastUsed);
+                launchedFromUid, filterLastUsed, createListController());
+    }
+
+    @VisibleForTesting
+    protected ResolverListController createListController() {
+        return new ResolverListController(
+                this,
+                mPm,
+                getTargetIntent(),
+                getReferrerPackageName(),
+                mLaunchedFromUid);
     }
 
     /**
@@ -914,32 +902,38 @@
         // to handle.
         mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
                 mLaunchedFromUid, alwaysUseOption && !isVoiceInteraction());
+        boolean rebuildCompleted = mAdapter.rebuildList();
 
-        final int layoutId;
         if (mAdapter.hasFilteredItem()) {
-            layoutId = R.layout.resolver_list_with_default;
+            mLayoutId = R.layout.resolver_list_with_default;
             alwaysUseOption = false;
         } else {
-            layoutId = getLayoutResource();
+            mLayoutId = getLayoutResource();
         }
         mAlwaysUseOption = alwaysUseOption;
 
         int count = mAdapter.getUnfilteredCount();
-        if (count == 1 && mAdapter.getOtherProfile() == null) {
-            // Only one target, so we're a candidate to auto-launch!
-            final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
-            if (shouldAutoLaunchSingleChoice(target)) {
-                safelyStartActivity(target);
-                mPackageMonitor.unregister();
-                mRegistered = false;
-                finish();
-                return true;
+
+        // We only rebuild asynchronously when we have multiple elements to sort. In the case where
+        // we're already done, we can check if we should auto-launch immediately.
+        if (rebuildCompleted) {
+            if (count == 1 && mAdapter.getOtherProfile() == null) {
+                // Only one target, so we're a candidate to auto-launch!
+                final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
+                if (shouldAutoLaunchSingleChoice(target)) {
+                    safelyStartActivity(target);
+                    mPackageMonitor.unregister();
+                    mRegistered = false;
+                    finish();
+                    return true;
+                }
             }
         }
-        if (count > 0) {
-            setContentView(layoutId);
+
+        if (count > 0 || !rebuildCompleted) {
+            setContentView(mLayoutId);
             mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
-            onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption);
+            onPrepareAdapterView(mAdapterView, mAdapter, mAlwaysUseOption);
         } else {
             setContentView(R.layout.resolver_list);
 
@@ -1236,20 +1230,21 @@
         private final List<ResolveInfo> mBaseResolveList;
         private ResolveInfo mLastChosen;
         private DisplayResolveInfo mOtherProfile;
-        private final int mLaunchedFromUid;
         private boolean mHasExtendedInfo;
+        private ResolverListController mResolverListController;
 
         protected final LayoutInflater mInflater;
 
         List<DisplayResolveInfo> mDisplayList;
-        List<ResolvedComponentInfo> mOrigResolveList;
+        List<ResolvedComponentInfo> mUnfilteredResolveList;
 
         private int mLastChosenPosition = -1;
         private boolean mFilterLastUsed;
 
         public ResolveListAdapter(Context context, List<Intent> payloadIntents,
                 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
-                boolean filterLastUsed) {
+                boolean filterLastUsed,
+                ResolverListController resolverListController) {
             mIntents = payloadIntents;
             mInitialIntents = initialIntents;
             mBaseResolveList = rList;
@@ -1257,12 +1252,11 @@
             mInflater = LayoutInflater.from(context);
             mDisplayList = new ArrayList<>();
             mFilterLastUsed = filterLastUsed;
-            rebuildList();
+            mResolverListController = resolverListController;
         }
 
         public void handlePackagesChanged() {
             rebuildList();
-            notifyDataSetChanged();
             if (getCount() == 0) {
                 // We no longer have any items...  just finish the activity.
                 finish();
@@ -1293,12 +1287,17 @@
         }
 
         public float getScore(DisplayResolveInfo target) {
-            return mResolverComparator.getScore(target.getResolvedComponentName());
+            return mResolverListController.getScore(target);
         }
 
-        private void rebuildList() {
+        /**
+         * Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
+         * to complete.
+         *
+         * @return Whether or not the list building is completed.
+         */
+        protected boolean rebuildList() {
             List<ResolvedComponentInfo> currentResolveList = null;
-
             try {
                 final Intent primaryIntent = getTargetIntent();
                 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
@@ -1312,84 +1311,88 @@
             mOtherProfile = null;
             mDisplayList.clear();
             if (mBaseResolveList != null) {
-                currentResolveList = mOrigResolveList = new ArrayList<>();
-                addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList);
+                currentResolveList = mUnfilteredResolveList = new ArrayList<>();
+                mResolverListController.addResolveListDedupe(currentResolveList,
+                        getTargetIntent(),
+                        mBaseResolveList);
             } else {
-                final boolean shouldGetResolvedFilter = shouldGetResolvedFilter();
-                final boolean shouldGetActivityMetadata = shouldGetActivityMetadata();
-                for (int i = 0, N = mIntents.size(); i < N; i++) {
-                    final Intent intent = mIntents.get(i);
-                    final List<ResolveInfo> infos = mPm.queryIntentActivities(intent,
-                            PackageManager.MATCH_DEFAULT_ONLY
-                            | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
-                            | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
-                    if (infos != null) {
-                        if (currentResolveList == null) {
-                            currentResolveList = mOrigResolveList = new ArrayList<>();
-                        }
-                        addResolveListDedupe(currentResolveList, intent, infos);
-                    }
+                currentResolveList =
+                        mResolverListController.getResolversForIntent(shouldGetResolvedFilter(),
+                                shouldGetActivityMetadata(),
+                                mIntents);
+                if (currentResolveList == null) {
+                    processSortedList(currentResolveList);
+                    return true;
                 }
-
-                // Filter out any activities that the launched uid does not
-                // have permission for.
-                // Also filter out those that are suspended because they couldn't
-                // be started. We don't do this when we have an explicit
-                // list of resolved activities, because that only happens when
-                // we are being subclassed, so we can safely launch whatever
-                // they gave us.
-                if (currentResolveList != null) {
-                    for (int i=currentResolveList.size()-1; i >= 0; i--) {
-                        ActivityInfo ai = currentResolveList.get(i)
-                                .getResolveInfoAt(0).activityInfo;
-                        int granted = ActivityManager.checkComponentPermission(
-                                ai.permission, mLaunchedFromUid,
-                                ai.applicationInfo.uid, ai.exported);
-                        boolean suspended = (ai.applicationInfo.flags
-                                & ApplicationInfo.FLAG_SUSPENDED) != 0;
-                        if (granted != PackageManager.PERMISSION_GRANTED || suspended
-                                || isComponentFiltered(ai)) {
-                            // Access not allowed!
-                            if (mOrigResolveList == currentResolveList) {
-                                mOrigResolveList = new ArrayList<>(mOrigResolveList);
-                            }
-                            currentResolveList.remove(i);
-                        }
-                    }
+                List<ResolvedComponentInfo> originalList =
+                        mResolverListController.filterIneligibleActivities(currentResolveList,
+                                true);
+                if (originalList != null) {
+                    mUnfilteredResolveList = originalList;
                 }
             }
             int N;
             if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
-                // Only display the first matches that are either of equal
-                // priority or have asked to be default options.
-                ResolvedComponentInfo rci0 = currentResolveList.get(0);
-                ResolveInfo r0 = rci0.getResolveInfoAt(0);
-                for (int i=1; i<N; i++) {
-                    ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0);
-                    if (DEBUG) Log.v(
-                        TAG,
-                        r0.activityInfo.name + "=" +
-                        r0.priority + "/" + r0.isDefault + " vs " +
-                        ri.activityInfo.name + "=" +
-                        ri.priority + "/" + ri.isDefault);
-                    if (r0.priority != ri.priority ||
-                        r0.isDefault != ri.isDefault) {
-                        while (i < N) {
-                            if (mOrigResolveList == currentResolveList) {
-                                mOrigResolveList = new ArrayList<>(mOrigResolveList);
-                            }
-                            currentResolveList.remove(i);
-                            N--;
-                        }
-                    }
+                // We only care about fixing the unfilteredList if the current resolve list and
+                // current resolve list are currently the same.
+                List<ResolvedComponentInfo> originalList =
+                        mResolverListController.filterLowPriority(currentResolveList,
+                                mUnfilteredResolveList == currentResolveList);
+                if (originalList != null) {
+                    mUnfilteredResolveList = originalList;
                 }
+
                 if (N > 1) {
-                    mResolverComparator.compute(currentResolveList);
-                    Collections.sort(currentResolveList, mResolverComparator);
+                    AsyncTask<List<ResolvedComponentInfo>,
+                            Void,
+                            List<ResolvedComponentInfo>> sortingTask =
+                            new AsyncTask<List<ResolvedComponentInfo>,
+                                    Void,
+                                    List<ResolvedComponentInfo>>() {
+                        @Override
+                        protected List<ResolvedComponentInfo> doInBackground(
+                                List<ResolvedComponentInfo>... params) {
+                            mResolverListController.sort(params[0]);
+                            return params[0];
+                        }
+
+                        @Override
+                        protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
+                            processSortedList(sortedComponents);
+                            onPrepareAdapterView(mAdapterView, mAdapter, mAlwaysUseOption);
+                            if (mProfileView != null) {
+                                bindProfileView();
+                            }
+                        }
+                    };
+                    sortingTask.execute(currentResolveList);
+                    return false;
+                } else {
+                    processSortedList(currentResolveList);
+                    return true;
                 }
+            } else {
+                processSortedList(currentResolveList);
+                return true;
+            }
+        }
+
+        private void disableLastChosenIfNeeded() {
+            // Layout doesn't handle both profile button and last chosen
+            // so disable last chosen if profile button is present.
+            if (mOtherProfile != null && mLastChosenPosition >= 0) {
+                mLastChosenPosition = -1;
+                mFilterLastUsed = false;
+            }
+        }
+
+
+        private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
+            int N;
+            if (sortedComponents != null && (N = sortedComponents.size()) != 0) {
                 // First put the initial items at the top.
                 if (mInitialIntents != null) {
-                    for (int i=0; i<mInitialIntents.length; i++) {
+                    for (int i = 0; i < mInitialIntents.length; i++) {
                         Intent ii = mInitialIntents[i];
                         if (ii == null) {
                             continue;
@@ -1405,7 +1408,7 @@
                         UserManager userManager =
                                 (UserManager) getSystemService(Context.USER_SERVICE);
                         if (ii instanceof LabeledIntent) {
-                            LabeledIntent li = (LabeledIntent)ii;
+                            LabeledIntent li = (LabeledIntent) ii;
                             ri.resolvePackageName = li.getSourcePackage();
                             ri.labelRes = li.getLabelResource();
                             ri.nonLocalizedLabel = li.getNonLocalizedLabel();
@@ -1423,16 +1426,16 @@
 
                 // Check for applications with same name and use application name or
                 // package name if necessary
-                rci0 = currentResolveList.get(0);
-                r0 = rci0.getResolveInfoAt(0);
+                ResolvedComponentInfo rci0 = sortedComponents.get(0);
+                ResolveInfo r0 = rci0.getResolveInfoAt(0);
                 int start = 0;
-                CharSequence r0Label =  r0.loadLabel(mPm);
+                CharSequence r0Label = r0.loadLabel(mPm);
                 mHasExtendedInfo = false;
                 for (int i = 1; i < N; i++) {
                     if (r0Label == null) {
                         r0Label = r0.activityInfo.packageName;
                     }
-                    ResolvedComponentInfo rci = currentResolveList.get(i);
+                    ResolvedComponentInfo rci = sortedComponents.get(i);
                     ResolveInfo ri = rci.getResolveInfoAt(0);
                     CharSequence riLabel = ri.loadLabel(mPm);
                     if (riLabel == null) {
@@ -1441,59 +1444,19 @@
                     if (riLabel.equals(r0Label)) {
                         continue;
                     }
-                    processGroup(currentResolveList, start, (i-1), rci0, r0Label);
+                    processGroup(sortedComponents, start, (i - 1), rci0, r0Label);
                     rci0 = rci;
                     r0 = ri;
                     r0Label = riLabel;
                     start = i;
                 }
                 // Process last group
-                processGroup(currentResolveList, start, (N-1), rci0, r0Label);
+                processGroup(sortedComponents, start, (N - 1), rci0, r0Label);
             }
-
-            // Layout doesn't handle both profile button and last chosen
-            // so disable last chosen if profile button is present.
-            if (mOtherProfile != null && mLastChosenPosition >= 0) {
-                mLastChosenPosition = -1;
-                mFilterLastUsed = false;
-            }
-
+            disableLastChosenIfNeeded();
             onListRebuilt();
         }
 
-        private void addResolveListDedupe(List<ResolvedComponentInfo> into, Intent intent,
-                List<ResolveInfo> from) {
-            final int fromCount = from.size();
-            final int intoCount = into.size();
-            for (int i = 0; i < fromCount; i++) {
-                final ResolveInfo newInfo = from.get(i);
-                boolean found = false;
-                // Only loop to the end of into as it was before we started; no dupes in from.
-                for (int j = 0; j < intoCount; j++) {
-                    final ResolvedComponentInfo rci = into.get(j);
-                    if (isSameResolvedComponent(newInfo, rci)) {
-                        found = true;
-                        rci.add(intent, newInfo);
-                        break;
-                    }
-                }
-                if (!found) {
-                    final ComponentName name = new ComponentName(
-                            newInfo.activityInfo.packageName, newInfo.activityInfo.name);
-                    final ResolvedComponentInfo rci = new ResolvedComponentInfo(name,
-                            intent, newInfo);
-                    rci.setPinned(isComponentPinned(name));
-                    into.add(rci);
-                }
-            }
-        }
-
-        private boolean isSameResolvedComponent(ResolveInfo a, ResolvedComponentInfo b) {
-            final ActivityInfo ai = a.activityInfo;
-            return ai.packageName.equals(b.name.getPackageName())
-                    && ai.name.equals(b.name.getClassName());
-        }
-
         public void onListRebuilt() {
             // This space for rent
         }
@@ -1715,7 +1678,8 @@
         }
     }
 
-    static final class ResolvedComponentInfo {
+    @VisibleForTesting
+    public static final class ResolvedComponentInfo {
         public final ComponentName name;
         private boolean mPinned;
         private final List<Intent> mIntents = new ArrayList<>();
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 75be906..45fad97 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -27,11 +27,16 @@
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.os.storage.StorageManager;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
+import java.io.File;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -54,6 +59,12 @@
 
     private static final float RECENCY_MULTIPLIER = 2.f;
 
+    // feature names used in ranking.
+    private static final String LAUNCH_SCORE = "launch";
+    private static final String TIME_SPENT_SCORE = "timeSpent";
+    private static final String RECENCY_SCORE = "recency";
+    private static final String CHOOSER_SCORE = "chooser";
+
     private final Collator mCollator;
     private final boolean mHttp;
     private final PackageManager mPm;
@@ -65,6 +76,7 @@
     private final String mReferrerPackage;
     public String mContentType;
     private String mAction;
+    private LogisticRegressionAppRanker mRanker;
 
     public ResolverComparator(Context context, Intent intent, String referrerPackage) {
         mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
@@ -80,6 +92,7 @@
         mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
         mContentType = intent.getType();
         mAction = intent.getAction();
+        mRanker = new LogisticRegressionAppRanker(context);
     }
 
     public void compute(List<ResolvedComponentInfo> targets) {
@@ -152,16 +165,13 @@
         for (ScoredTarget target : mScoredTargets.values()) {
             final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0)
                     / (mostRecentlyUsedTime - recentSinceTime);
-            final float recencyScore = recency * recency * RECENCY_MULTIPLIER;
-            final float usageTimeScore = (float) target.timeSpent / mostTimeSpent;
-            final float launchCountScore = (float) target.launchCount / mostLaunched;
-
-            target.score = recencyScore + usageTimeScore + launchCountScore;
+            target.setFeatures((float) target.launchCount / mostLaunched,
+                    (float) target.timeSpent / mostTimeSpent,
+                    recency * recency * RECENCY_MULTIPLIER,
+                    (float) target.chooserCount / mostSelected);
+            target.selectProb = mRanker.predict(target.getFeatures());
             if (DEBUG) {
-                Log.d(TAG, "Scores: recencyScore: " + recencyScore
-                        + " usageTimeScore: " + usageTimeScore
-                        + " launchCountScore: " + launchCountScore
-                        + " - " + target);
+                Log.d(TAG, "Scores: " + target);
             }
         }
     }
@@ -215,17 +225,11 @@
                 final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
                         rhs.activityInfo.packageName, rhs.activityInfo.name));
 
-                final int chooserCountDiff = Long.compare(
-                        rhsTarget.chooserCount, lhsTarget.chooserCount);
+                final int selectProbDiff = Float.compare(
+                        rhsTarget.selectProb, lhsTarget.selectProb);
 
-                if (chooserCountDiff != 0) {
-                    return chooserCountDiff > 0 ? 1 : -1;
-                }
-
-                final int diff = Float.compare(rhsTarget.score, lhsTarget.score);
-
-                if (diff != 0) {
-                    return diff > 0 ? 1 : -1;
+                if (selectProbDiff != 0) {
+                    return selectProbDiff > 0 ? 1 : -1;
                 }
             }
         }
@@ -241,32 +245,160 @@
     public float getScore(ComponentName name) {
         final ScoredTarget target = mScoredTargets.get(name);
         if (target != null) {
-            return target.score;
+            return target.selectProb;
         }
         return 0;
     }
 
     static class ScoredTarget {
         public final ComponentInfo componentInfo;
-        public float score;
         public long lastTimeUsed;
         public long timeSpent;
         public long launchCount;
         public long chooserCount;
+        public ArrayMap<String, Float> features;
+        public float selectProb;
 
         public ScoredTarget(ComponentInfo ci) {
             componentInfo = ci;
+            features = new ArrayMap<>(5);
         }
 
         @Override
         public String toString() {
             return "ScoredTarget{" + componentInfo
-                    + " score: " + score
                     + " lastTimeUsed: " + lastTimeUsed
                     + " timeSpent: " + timeSpent
                     + " launchCount: " + launchCount
                     + " chooserCount: " + chooserCount
+                    + " selectProb: " + selectProb
                     + "}";
         }
+
+        public void setFeatures(float launchCountScore, float usageTimeScore, float recencyScore,
+                                float chooserCountScore) {
+            features.put(LAUNCH_SCORE, launchCountScore);
+            features.put(TIME_SPENT_SCORE, usageTimeScore);
+            features.put(RECENCY_SCORE, recencyScore);
+            features.put(CHOOSER_SCORE, chooserCountScore);
+        }
+
+        public ArrayMap<String, Float> getFeatures() {
+            return features;
+        }
+    }
+
+    public void updateModel(ComponentName componentName) {
+        if (mScoredTargets == null || componentName == null ||
+                !mScoredTargets.containsKey(componentName)) {
+            return;
+        }
+        ScoredTarget selected = mScoredTargets.get(componentName);
+        for (ComponentName targetComponent : mScoredTargets.keySet()) {
+            if (targetComponent.equals(componentName)) {
+                continue;
+            }
+            ScoredTarget target = mScoredTargets.get(targetComponent);
+            // A potential point of optimization. Save updates or derive a closed form for the
+            // positive case, to avoid calculating them repeatedly.
+            if (target.selectProb >= selected.selectProb) {
+                mRanker.update(target.getFeatures(), target.selectProb, false);
+                mRanker.update(selected.getFeatures(), selected.selectProb, true);
+            }
+        }
+        mRanker.commitUpdate();
+    }
+
+    class LogisticRegressionAppRanker {
+        private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
+        private static final String BIAS_PREF_KEY = "bias";
+        private static final float LEARNING_RATE = 0.02f;
+        private static final float REGULARIZER_PARAM = 0.1f;
+        private SharedPreferences mParamSharedPref;
+        private ArrayMap<String, Float> mFeatureWeights;
+        private float mBias;
+
+        public LogisticRegressionAppRanker(Context context) {
+            mParamSharedPref = getParamSharedPref(context);
+        }
+
+        public float predict(ArrayMap<String, Float> target) {
+            if (target == null || mParamSharedPref == null) {
+                return 0.0f;
+            }
+            final int featureSize = target.size();
+            if (featureSize == 0) {
+                return 0.0f;
+            }
+            float sum = 0.0f;
+            if (mFeatureWeights == null) {
+                mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
+                mFeatureWeights = new ArrayMap<>(featureSize);
+                for (int i = 0; i < featureSize; i++) {
+                    String featureName = target.keyAt(i);
+                    float weight = mParamSharedPref.getFloat(featureName, 0.0f);
+                    sum += weight * target.valueAt(i);
+                    mFeatureWeights.put(featureName, weight);
+                }
+            } else {
+                for (int i = 0; i < featureSize; i++) {
+                    String featureName = target.keyAt(i);
+                    float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+                    sum += weight * target.valueAt(i);
+                }
+            }
+            return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
+        }
+
+        public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
+            if (target == null || target.size() == 0) {
+                return;
+            }
+            final int featureSize = target.size();
+            if (mFeatureWeights == null) {
+                mBias = 0.0f;
+                mFeatureWeights = new ArrayMap<>(featureSize);
+            }
+            float error = isSelected ? 1.0f - predict : -predict;
+            for (int i = 0; i < featureSize; i++) {
+                String featureName = target.keyAt(i);
+                float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+                mBias += LEARNING_RATE * error;
+                currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
+                        LEARNING_RATE * error * target.valueAt(i);
+                mFeatureWeights.put(featureName, currentWeight);
+            }
+            if (DEBUG) {
+                Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
+            }
+        }
+
+        public void commitUpdate() {
+            if (mFeatureWeights == null || mFeatureWeights.size() == 0) {
+                return;
+            }
+            SharedPreferences.Editor editor = mParamSharedPref.edit();
+            editor.putFloat(BIAS_PREF_KEY, mBias);
+            final int size = mFeatureWeights.size();
+            for (int i = 0; i < size; i++) {
+                editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
+            }
+            editor.apply();
+        }
+
+        private SharedPreferences getParamSharedPref(Context context) {
+            // The package info in the context isn't initialized in the way it is for normal apps,
+            // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
+            // build the path manually below using the same policy that appears in ContextImpl.
+            if (DEBUG) {
+                Log.d(TAG, "Context Package Name: " + context.getPackageName());
+            }
+            final File prefsFile = new File(new File(
+                    Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+                            context.getUserId(), context.getPackageName()),
+                    "shared_prefs"),
+                    PARAM_SHARED_PREF_NAME + ".xml");
+            return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
new file mode 100644
index 0000000..b91ecb6
--- /dev/null
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.internal.app;
+
+import android.annotation.WorkerThread;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper for the ResolverActivity that exposes methods to retrieve, filter and sort its list of
+ * resolvers.
+ */
+public class ResolverListController {
+
+    private final Context mContext;
+    private final PackageManager mpm;
+    private final int mLaunchedFromUid;
+
+    // Needed for sorting resolvers.
+    private final Intent mTargetIntent;
+    private final String mReferrerPackage;
+
+    private static final String TAG = "ResolverListController";
+    private static final boolean DEBUG = false;
+
+    private ResolverComparator mResolverComparator;
+
+    public ResolverListController(
+            Context context,
+            PackageManager pm,
+            Intent targetIntent,
+            String referrerPackage,
+            int launchedFromUid) {
+        mContext = context;
+        mpm = pm;
+        mLaunchedFromUid = launchedFromUid;
+        mTargetIntent = targetIntent;
+        mReferrerPackage = referrerPackage;
+    }
+
+    @VisibleForTesting
+    public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent(
+            boolean shouldGetResolvedFilter,
+            boolean shouldGetActivityMetadata,
+            List<Intent> intents) {
+        List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null;
+        for (int i = 0, N = intents.size(); i < N; i++) {
+            final Intent intent = intents.get(i);
+            final List<ResolveInfo> infos = mpm.queryIntentActivities(intent,
+                    PackageManager.MATCH_DEFAULT_ONLY
+                            | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
+                            | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
+            if (infos != null) {
+                if (resolvedComponents == null) {
+                    resolvedComponents = new ArrayList<>();
+                }
+                addResolveListDedupe(resolvedComponents, intent, infos);
+            }
+        }
+        return resolvedComponents;
+    }
+
+    @VisibleForTesting
+    public void addResolveListDedupe(List<ResolverActivity.ResolvedComponentInfo> into,
+            Intent intent,
+            List<ResolveInfo> from) {
+        final int fromCount = from.size();
+        final int intoCount = into.size();
+        for (int i = 0; i < fromCount; i++) {
+            final ResolveInfo newInfo = from.get(i);
+            boolean found = false;
+            // Only loop to the end of into as it was before we started; no dupes in from.
+            for (int j = 0; j < intoCount; j++) {
+                final ResolverActivity.ResolvedComponentInfo rci = into.get(j);
+                if (isSameResolvedComponent(newInfo, rci)) {
+                    found = true;
+                    rci.add(intent, newInfo);
+                    break;
+                }
+            }
+            if (!found) {
+                final ComponentName name = new ComponentName(
+                        newInfo.activityInfo.packageName, newInfo.activityInfo.name);
+                final ResolverActivity.ResolvedComponentInfo rci =
+                        new ResolverActivity.ResolvedComponentInfo(name, intent, newInfo);
+                rci.setPinned(isComponentPinned(name));
+                into.add(rci);
+            }
+        }
+    }
+
+    // Filter out any activities that the launched uid does not have permission for.
+    //
+    // Also filter out those that are suspended because they couldn't be started. We don't do this
+    // when we have an explicit list of resolved activities, because that only happens when
+    // we are being subclassed, so we can safely launch whatever they gave us.
+    //
+    // To preserve the inputList, optionally will return the original list if any modification has
+    // been made.
+    @VisibleForTesting
+    public ArrayList<ResolverActivity.ResolvedComponentInfo> filterIneligibleActivities(
+            List<ResolverActivity.ResolvedComponentInfo> inputList,
+            boolean returnCopyOfOriginalListIfModified) {
+        ArrayList<ResolverActivity.ResolvedComponentInfo> listToReturn = null;
+        for (int i = inputList.size()-1; i >= 0; i--) {
+            ActivityInfo ai = inputList.get(i)
+                    .getResolveInfoAt(0).activityInfo;
+            int granted = ActivityManager.checkComponentPermission(
+                    ai.permission, mLaunchedFromUid,
+                    ai.applicationInfo.uid, ai.exported);
+            boolean suspended = (ai.applicationInfo.flags
+                    & ApplicationInfo.FLAG_SUSPENDED) != 0;
+            if (granted != PackageManager.PERMISSION_GRANTED || suspended
+                    || isComponentFiltered(ai.getComponentName())) {
+                // Access not allowed! We're about to filter an item,
+                // so modify the unfiltered version if it hasn't already been modified.
+                if (returnCopyOfOriginalListIfModified && listToReturn == null) {
+                    listToReturn = new ArrayList<>(inputList);
+                }
+                inputList.remove(i);
+            }
+        }
+        return listToReturn;
+    }
+
+    // Filter out any low priority items.
+    //
+    // To preserve the inputList, optionally will return the original list if any modification has
+    // been made.
+    @VisibleForTesting
+    public ArrayList<ResolverActivity.ResolvedComponentInfo> filterLowPriority(
+            List<ResolverActivity.ResolvedComponentInfo> inputList,
+            boolean returnCopyOfOriginalListIfModified) {
+        ArrayList<ResolverActivity.ResolvedComponentInfo> listToReturn = null;
+        // Only display the first matches that are either of equal
+        // priority or have asked to be default options.
+        ResolverActivity.ResolvedComponentInfo rci0 = inputList.get(0);
+        ResolveInfo r0 = rci0.getResolveInfoAt(0);
+        int N = inputList.size();
+        for (int i = 1; i < N; i++) {
+            ResolveInfo ri = inputList.get(i).getResolveInfoAt(0);
+            if (DEBUG) Log.v(
+                    TAG,
+                    r0.activityInfo.name + "=" +
+                            r0.priority + "/" + r0.isDefault + " vs " +
+                            ri.activityInfo.name + "=" +
+                            ri.priority + "/" + ri.isDefault);
+            if (r0.priority != ri.priority ||
+                    r0.isDefault != ri.isDefault) {
+                while (i < N) {
+                    if (returnCopyOfOriginalListIfModified && listToReturn == null) {
+                        listToReturn = new ArrayList<>(inputList);
+                    }
+                    inputList.remove(i);
+                    N--;
+                }
+            }
+        }
+        return listToReturn;
+    }
+
+    @VisibleForTesting
+    @WorkerThread
+    public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
+        if (mResolverComparator == null) {
+            mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
+        }
+        mResolverComparator.compute(inputList);
+        Collections.sort(inputList, mResolverComparator);
+    }
+
+    private static boolean isSameResolvedComponent(ResolveInfo a,
+            ResolverActivity.ResolvedComponentInfo b) {
+        final ActivityInfo ai = a.activityInfo;
+        return ai.packageName.equals(b.name.getPackageName())
+                && ai.name.equals(b.name.getClassName());
+    }
+
+    boolean isComponentPinned(ComponentName name) {
+        return false;
+    }
+
+    boolean isComponentFiltered(ComponentName componentName) {
+        return false;
+    }
+
+    @VisibleForTesting
+    public float getScore(ResolverActivity.DisplayResolveInfo target) {
+        if (mResolverComparator == null) {
+            mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
+        }
+        return mResolverComparator.getScore(target.getResolvedComponentName());
+    }
+}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 8f74bf8..59cbc93 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -52,6 +52,7 @@
         mAllocationSize = mBitmap->getAllocationByteCount();
         mRowBytes = mBitmap->rowBytes();
         mGenerationId = mBitmap->getGenerationID();
+        mIsHardware = mBitmap->isHardware();
         mBitmap.reset();
     }
 
@@ -118,6 +119,13 @@
         return mGenerationId;
     }
 
+    bool isHardware() {
+        if (mBitmap) {
+            return mBitmap->isHardware();
+        }
+        return mIsHardware;
+    }
+
     ~BitmapWrapper() { }
 
 private:
@@ -127,6 +135,7 @@
     size_t mAllocationSize;
     size_t mRowBytes;
     uint32_t mGenerationId;
+    bool mIsHardware;
 };
 
 // Convenience class that does not take a global ref on the pixels, relying
@@ -775,7 +784,7 @@
 
 static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmap(bitmapHandle);
-    if (bitmap->bitmap().isHardware()) {
+    if (bitmap->isHardware()) {
         return GraphicsJNI::hardwareLegacyBitmapConfig();
     }
     return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType());
@@ -1208,7 +1217,7 @@
     // Paying the price for making Hardware Bitmap as Config:
     // later check for colorType will pass successfully,
     // because Hardware Config internally may be RGBA8888 or smth like that.
-    if (bitmap0->bitmap().isHardware() != bitmap1->bitmap().isHardware()) {
+    if (bitmap0->isHardware() != bitmap1->isHardware()) {
         return JNI_FALSE;
     }
 
@@ -1282,6 +1291,23 @@
     return static_cast<jint>(bitmapHandle->getAllocationByteCount());
 }
 
+static jobject Bitmap_nativeCopyPreserveInternalConfig(JNIEnv* env, jobject, jlong bitmapPtr) {
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(),
+            "Hardware config is only supported config in Bitmap_nativeCopyPreserveInternalConfig");
+    Bitmap& hwuiBitmap = bitmapHandle->bitmap();
+    SkBitmap src;
+    hwuiBitmap.getSkBitmap(&src);
+
+    SkBitmap result;
+    HeapAllocator allocator;
+    if (!src.copyTo(&result, hwuiBitmap.info().colorType(), &allocator)) {
+        doThrowRE(env, "Could not copy a hardware bitmap.");
+        return NULL;
+    }
+    return createBitmap(env, allocator.getStorageObjAndReset(), kBitmapCreateFlag_None);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 static jclass make_globalref(JNIEnv* env, const char classname[])
 {
@@ -1340,6 +1366,8 @@
     {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
     {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
     {   "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
+    {   "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_nativeCopyPreserveInternalConfig },
 };
 
 int register_android_graphics_Bitmap(JNIEnv* env)
diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp
index 035417e..92c7746 100644
--- a/core/jni/android/graphics/GIFMovie.cpp
+++ b/core/jni/android/graphics/GIFMovie.cpp
@@ -121,7 +121,7 @@
                      int transparent, int width)
 {
     for (; width > 0; width--, src++, dst++) {
-        if (*src != transparent) {
+        if (*src != transparent && *src < cmap->ColorCount) {
             const GifColorType& col = cmap->Colors[*src];
             *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
         }
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 664c7ea..226e9e3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -463,6 +463,18 @@
 }
 
 static jint
+android_media_AudioSystem_handleDeviceConfigChange(JNIEnv *env, jobject thiz, jint device, jstring device_address, jstring device_name)
+{
+    const char *c_address = env->GetStringUTFChars(device_address, NULL);
+    const char *c_name = env->GetStringUTFChars(device_name, NULL);
+    int status = check_AudioSystem_Command(AudioSystem::handleDeviceConfigChange(static_cast <audio_devices_t>(device),
+                                          c_address, c_name));
+    env->ReleaseStringUTFChars(device_address, c_address);
+    env->ReleaseStringUTFChars(device_name, c_name);
+    return (jint) status;
+}
+
+static jint
 android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
 {
     return (jint) check_AudioSystem_Command(AudioSystem::setPhoneState((audio_mode_t) state));
@@ -1764,6 +1776,7 @@
     {"newAudioPlayerId",    "()I",      (void *)android_media_AudioSystem_newAudioPlayerId},
     {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
     {"getDeviceConnectionState", "(ILjava/lang/String;)I",  (void *)android_media_AudioSystem_getDeviceConnectionState},
+    {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_handleDeviceConfigChange},
     {"setPhoneState",       "(I)I",     (void *)android_media_AudioSystem_setPhoneState},
     {"setForceUse",         "(II)I",    (void *)android_media_AudioSystem_setForceUse},
     {"getForceUse",         "(I)I",     (void *)android_media_AudioSystem_getForceUse},
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 95e031b..c456d62 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -52,6 +52,8 @@
     jmethodID get;
 } gArrayListMethods;
 
+static jclass gErrorClass;
+
 static struct fields_t {
     jfieldID contextID;
     jmethodID onTransactID;
@@ -144,6 +146,22 @@
             replyObj.get(),
             flags);
 
+    if (env->ExceptionCheck()) {
+        jthrowable excep = env->ExceptionOccurred();
+        env->ExceptionDescribe();
+
+        if (env->IsInstanceOf(excep, gErrorClass)) {
+            /* It's an error */
+            LOG(ERROR) << "Forcefully exiting";
+            exit(1);
+        } else {
+            env->ExceptionClear();
+            LOG(ERROR) << "Uncaught exception!";
+        }
+
+        env->DeleteLocalRef(excep);
+    }
+
     status_t err = OK;
 
     if (!replyContext->wasSent()) {
@@ -356,6 +374,9 @@
     gArrayListMethods.size = GetMethodIDOrDie(env, arrayListClass, "size", "()I");
     gArrayListMethods.get = GetMethodIDOrDie(env, arrayListClass, "get", "(I)Ljava/lang/Object;");
 
+    jclass errorClass = FindClassOrDie(env, "java/lang/Error");
+    gErrorClass = MakeGlobalRefOrDie(env, errorClass);
+
     return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a10d807..b9d810a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -49,7 +49,7 @@
 
 } gFields;
 
-void signalExceptionForError(JNIEnv *env, status_t err) {
+void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteException) {
     switch (err) {
         case OK:
             break;
@@ -114,8 +114,13 @@
 
         default:
         {
+            std::stringstream ss;
+            ss << "HwBinder Error: (" << err << ")";
+
             jniThrowException(
-                    env, "java/lang/RuntimeException", "Unknown error");
+                    env,
+                    canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
+                    ss.str().c_str());
 
             break;
         }
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
index 708bbba..f81de9b 100644
--- a/core/jni/android_os_HwParcel.h
+++ b/core/jni/android_os_HwParcel.h
@@ -67,7 +67,7 @@
     DISALLOW_COPY_AND_ASSIGN(JHwParcel);
 };
 
-void signalExceptionForError(JNIEnv *env, status_t err);
+void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteException = false);
 int register_android_os_HwParcel(JNIEnv *env);
 
 }  // namespace android
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index 0a7d84d..f2f8e52 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -347,7 +347,7 @@
         JHwParcel::GetNativeContext(env, replyObj)->getParcel();
 
     status_t err = binder->transact(code, *request, reply, flags);
-    signalExceptionForError(env, err);
+    signalExceptionForError(env, err, true /* canThrowRemoteException */);
 }
 
 static jboolean JHwRemoteBinder_linkToDeath(JNIEnv* env, jobject thiz,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68d8c55..4c9fb04 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -168,6 +168,8 @@
     <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
@@ -2075,6 +2077,7 @@
          @SystemApi @hide -->
     <permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"
         android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"/>
 
     <!-- ========================================= -->
     <!-- Permissions for special development tools -->
diff --git a/core/res/res/layout/media_route_controller_dialog.xml b/core/res/res/layout/media_route_controller_dialog.xml
index 0bf70da..24a2535 100644
--- a/core/res/res/layout/media_route_controller_dialog.xml
+++ b/core/res/res/layout/media_route_controller_dialog.xml
@@ -47,17 +47,5 @@
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:visibility="gone" />
-
-        <!-- Disconnect button. -->
-        <LinearLayout android:layout_width="match_parent"
-                      android:layout_height="wrap_content"
-                      style="?attr/buttonBarStyle">
-            <Button android:id="@+id/media_route_disconnect_button"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    style="?attr/buttonBarButtonStyle"
-                    android:gravity="center"
-                    android:text="@string/media_route_controller_disconnect" />
-        </LinearLayout>
     </LinearLayout>
-</ScrollView>
\ No newline at end of file
+</ScrollView>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 989a79d..d02e156 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1482,10 +1482,10 @@
   <java-symbol type="id" name="media_route_volume_layout" />
   <java-symbol type="id" name="media_route_volume_slider" />
   <java-symbol type="id" name="media_route_control_frame" />
-  <java-symbol type="id" name="media_route_disconnect_button" />
   <java-symbol type="id" name="media_route_extended_settings_button" />
   <java-symbol type="string" name="media_route_chooser_title" />
   <java-symbol type="string" name="media_route_chooser_title_for_remote_display" />
+  <java-symbol type="string" name="media_route_controller_disconnect" />
   <java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
 
   <java-symbol type="dimen" name="config_minScalingSpan" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index ba1a55d..cd41987 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1156,6 +1156,7 @@
         </activity>
         <activity android:name="android.app.EmptyActivity">
         </activity>
+        <activity android:name="com.android.internal.app.ChooserWrapperActivity"/>
 
         <receiver android:name="android.app.activity.AbortReceiver">
             <intent-filter android:priority="1">
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index 9c3346e..e818c56 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -166,4 +166,52 @@
         assertTrue(newNetwork.meteredHint);
         assertNull(newNetwork.attributes);
     }
+
+    @Test
+    public void calculateBadgeShouldReturnNoBadgeWhenNoAttributesBundle() {
+        ScoredNetwork network = new ScoredNetwork(KEY, CURVE);
+        assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnNoBadgeWhenNoBadgingCurveInBundle() {
+        ScoredNetwork network = new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+        assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturn4kBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_4K);
+        assertEquals(ScoredNetwork.BADGING_4K, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnHdBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_HD);
+        assertEquals(ScoredNetwork.BADGING_HD, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnSdBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_SD);
+        assertEquals(ScoredNetwork.BADGING_SD, network.calculateBadge(TEST_RSSI));
+    }
+
+    @Test
+    public void calculateBadgeShouldReturnNoBadge() {
+        ScoredNetwork network =
+            buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_NONE);
+        assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+    }
+
+    private ScoredNetwork buildScoredNetworkWithGivenBadgeForTestRssi(int badge) {
+        RssiCurve badgingCurve =
+               new RssiCurve(RSSI_START, 10, new byte[] {0, 0, 0, 0, 0, 0, (byte) badge});
+        Bundle attr = new Bundle();
+        attr.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, badgingCurve);
+        return new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
new file mode 100644
index 0000000..8a7b881
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+/**
+ * Chooser activity instrumentation tests
+ */
+@RunWith(AndroidJUnit4.class)
+public class ChooserActivityTest {
+    @Rule
+    public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
+            new ActivityTestRule<>(ChooserWrapperActivity.class, false,
+                    false);
+
+    @Before
+    public void cleanOverrideData() {
+        sOverrides.reset();
+    }
+
+    @Test
+    public void customTitle() throws InterruptedException {
+        Intent sendIntent = createSendImageIntent();
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(null);
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
+        waitForIdle();
+        onView(withId(R.id.title)).check(matches(withText("chooser test")));
+    }
+
+    @Test
+    public void emptyTitle() throws InterruptedException {
+        sOverrides.isVoiceInteraction = false;
+        Intent sendIntent = createSendImageIntent();
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(null);
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+        onView(withId(R.id.title))
+                .check(matches(withText(R.string.whichSendApplication)));
+    }
+
+    @Test
+    public void twoOptionsAndUserSelectsOne() throws InterruptedException {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        assertThat(activity.getAdapter().getCount(), is(2));
+        onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+        onView(withText(toChoose.activityInfo.name))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    @Test
+    public void noResultsFromPackageManager() {
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(null);
+        Intent sendIntent = createSendImageIntent();
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+        assertThat(activity.isFinishing(), is(false));
+
+        onView(withId(R.id.empty)).check(matches(isDisplayed()));
+        onView(withId(R.id.resolver_list)).check(matches(not(isDisplayed())));
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> activity.getAdapter().handlePackagesChanged()
+        );
+        // backward compatibility. looks like we finish when data is empty after package change
+        assertThat(activity.isFinishing(), is(true));
+    }
+
+    @Test
+    public void autoLaunchSingleResult() throws InterruptedException {
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        Intent sendIntent = createSendImageIntent();
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
+        assertThat(activity.isFinishing(), is(true));
+    }
+
+    private Intent createSendImageIntent() {
+        Intent sendIntent = new Intent();
+        sendIntent.setAction(Intent.ACTION_SEND);
+        sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+        sendIntent.setType("image/jpeg");
+        return sendIntent;
+    }
+
+    private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+        List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+        for (int i = 0; i < numberOfResults; i++) {
+            infoList.add(ChooserDataProvider.createResolvedComponentInfo(i));
+        }
+        return infoList;
+    }
+
+    private void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java
new file mode 100644
index 0000000..f6f63f1
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.app;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.service.chooser.ChooserTarget;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Utility class used by chooser tests to create mock data
+ */
+class ChooserDataProvider {
+
+    static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfo(int i) {
+        return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
+                createResolverIntent(i), createResolveInfo(i));
+    }
+
+    static ComponentName createComponentName(int i) {
+        final String name = "component" + i;
+        return new ComponentName("foo.bar." + name, name);
+    }
+
+    static ResolveInfo createResolveInfo(int i) {
+        final ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = createActivityInfo(i);
+        resolveInfo.targetUserId = UserHandle.USER_CURRENT;
+        return resolveInfo;
+    }
+
+    static ActivityInfo createActivityInfo(int i) {
+        ActivityInfo ai = new ActivityInfo();
+        ai.name = "activity_name" + i;
+        ai.packageName = "foo_bar" + i;
+        ai.enabled = true;
+        ai.exported = true;
+        ai.permission = null;
+        ai.applicationInfo = createApplicationInfo();
+        return ai;
+    }
+
+    static ApplicationInfo createApplicationInfo() {
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.name = "app_name";
+        ai.packageName = "foo.bar";
+        ai.enabled = true;
+        return ai;
+    }
+
+    static Intent createResolverIntent(int i) {
+        return new Intent("intentAction" + i);
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
new file mode 100644
index 0000000..66fb451
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.app;
+
+import android.content.pm.PackageManager;
+
+import java.util.function.Function;
+
+import static org.mockito.Mockito.mock;
+
+
+/**
+ * Simple wrapper around chooser activity to be able to initiate it under test
+ */
+public class ChooserWrapperActivity extends ChooserActivity {
+    static final OverrideData sOverrides = new OverrideData();
+
+    ResolveListAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    @Override
+    public boolean isVoiceInteraction() {
+        if (sOverrides.isVoiceInteraction != null) {
+            return sOverrides.isVoiceInteraction;
+        }
+        return super.isVoiceInteraction();
+    }
+
+    @Override
+    public void safelyStartActivity(TargetInfo cti) {
+        if (sOverrides.onSafelyStartCallback != null &&
+                sOverrides.onSafelyStartCallback.apply(cti)) {
+            return;
+        }
+        super.safelyStartActivity(cti);
+    }
+
+    @Override
+    protected ResolverListController createListController() {
+        return sOverrides.resolverListController;
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        if (sOverrides.createPackageManager != null) {
+            return sOverrides.createPackageManager.apply(super.getPackageManager());
+        }
+        return super.getPackageManager();
+    }
+
+    /**
+     * We cannot directly mock the activity created since instrumentation creates it.
+     * <p>
+     * Instead, we use static instances of this object to modify behavior.
+     */
+    static class OverrideData {
+        @SuppressWarnings("Since15")
+        public Function<PackageManager, PackageManager> createPackageManager;
+        public Function<TargetInfo, Boolean> onSafelyStartCallback;
+        public ResolverListController resolverListController;
+        public Boolean isVoiceInteraction;
+
+        public void reset() {
+            onSafelyStartCallback = null;
+            isVoiceInteraction = null;
+            createPackageManager = null;
+            resolverListController = mock(ResolverListController.class);
+        }
+    }
+}
\ No newline at end of file
diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd
index 9936489..1dc58e2 100644
--- a/docs/html/google/play/billing/billing_admin.jd
+++ b/docs/html/google/play/billing/billing_admin.jd
@@ -125,7 +125,10 @@
       </dd>
     </dl>
   </li>
-  <li><p>Enter additional information about the item, then click <strong>Save</strong>.</p>
+  <li><p>Enter additional information about the item. If you're adding a
+    subscription, also include
+    <a href="#billing-form-add-subscription">subscription-specific details</a>.
+    After you've provided this information, click <strong>Save</strong>.</p>
     <dl>
       <dt>Publishing State</dt>
       <dd>
@@ -185,6 +188,73 @@
   </figcaption>
 </figure>
 
+<h4 id="billing-form-add-subscription">
+  Adding subscription details
+</h4>
+
+<p>
+  When you add a subscription to a product list, you must define its billing
+  period. All other settings related to subscriptions are optional. The
+  following list shows each property that you can set:
+</p>
+
+<dl>
+  <dt>Billing period</dt>
+  <dd>
+    <p>
+      Sets the frequency at which a user is charged while their subscription
+      is active.
+    </p>
+
+    <p>
+      If the billing period for the new subscription is Seasonal, you must
+      specify when the season starts and ends using the <strong>Start
+      date</strong> and <strong>End date</strong> options. The subscription is
+      active only between these two dates.
+    </p>
+
+    <p>
+      To allow users to activate a seasonal subscription for a discounted price
+      after the season begins, you can provide prorated pricing by selecting
+      <strong>Add Prorated Price</strong>. In the text boxes that appear after
+      you select this option, specify a discounted price for your subscription,
+      as well as the first date during the season when users can activate the
+      subscription for the discounted price. You can set multiple prorated
+      prices for a single subscription, with each price point taking effect on
+      a different start date.
+    </p>
+
+    <p class="note">
+      <strong>Note:</strong> After you activate a subscription, you cannot
+      change the subscription's billing period. For a seasonal subscription,
+      this restriction means that you also cannot change the season's start and
+      end dates. However, you can still add, change, and remove the prorated
+      prices that you've associated with a seasonal subscription.
+    </p>
+  </dd>
+
+  <dt>Free trial period</dt>
+  <dd>
+    Sets the number of days that users can access the subscription for free
+    after they first activate it. The trial period must be at least 7 days.
+  </dd>
+
+  <dt>Grace period</dt>
+  <dd>
+    Determines the amount of time that the user can continue accessing the
+    subscription after their payment is declined. If the user doesn't fix their
+    payment issue after the grace period has ended, their access to the
+    subscription is revoked.
+  </dd>
+</dl>
+
+<p>
+  To learn more about how you can manage subscriptions using the In-app Billing
+  service, see the
+  <a href="/google/play/billing/billing_subscriptions.html">In-app
+  Subscriptions</a> guide.
+</p>
+
 <h3 id="billing-bulk-add">Adding a batch of items to a product list</h3>
 
 <p>To add a batch of items to a product list using a CSV file, you first need to
diff --git a/docs/html/google/play/billing/billing_testing.jd b/docs/html/google/play/billing/billing_testing.jd
index 44b7ad3..22480a2 100644
--- a/docs/html/google/play/billing/billing_testing.jd
+++ b/docs/html/google/play/billing/billing_testing.jd
@@ -199,6 +199,15 @@
 with a reserved product ID. Figure 1 shows the checkout flow for the reserved item that has the
 product ID android.test.purchased.</p>
 
+<p class="note">
+  <strong>Note:</strong> If you're testing subscription purchases, you must use
+  the product ID of an actual subscription, not a reserved product ID. To verify
+  that Google Play is processing the test subscription purchases correctly, you
+  should
+  <a href="/google/play/billing/billing/billing_admin.html#billing-testing-setup">set
+  up test accounts</a> for your app.
+</p>
+
 <img src="{@docRoot}images/billing_test_flow.png" height="381" id="figure1" />
 <p class="img-caption">
   <strong>Figure 1.</strong>Purchase flow for the special reserved item android.test.purchased.
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index b6db327..a259937 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -372,6 +372,16 @@
     }
 
     /**
+     * This is called by methods that want to throw an exception if the bitmap
+     * is {@link Config#HARDWARE}.
+     */
+    private void checkHardware(String errorMessage) {
+        if (getConfig() == Config.HARDWARE) {
+            throw new IllegalStateException(errorMessage);
+        }
+    }
+
+    /**
      * Common code for checking that x and y are >= 0
      *
      * @param x x coordinate to ensure is >= 0
@@ -512,8 +522,11 @@
      * <p>After this method returns, the current position of the buffer is
      * updated: the position is incremented by the number of elements written
      * in the buffer.</p>
+     * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
      */
     public void copyPixelsToBuffer(Buffer dst) {
+        checkHardware("unable to copyPixelsToBuffer, "
+                + "pixel access is not supported on Config#HARDWARE bitmaps");
         int elements = dst.remaining();
         int shift;
         if (dst instanceof ByteBuffer) {
@@ -550,9 +563,11 @@
      * updated: the position is incremented by the number of elements read from
      * the buffer. If you need to read the bitmap from the buffer again you must
      * first rewind the buffer.</p>
+     * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
      */
     public void copyPixelsFromBuffer(Buffer src) {
         checkRecycled("copyPixelsFromBuffer called on recycled bitmap");
+        checkHardware("unable to copyPixelsFromBuffer, Config#HARDWARE bitmaps are immutable");
 
         int elements = src.remaining();
         int shift;
@@ -753,6 +768,11 @@
             return source;
         }
 
+        boolean isHardware = source.getConfig() == Config.HARDWARE;
+        if (isHardware) {
+            source = nativeCopyPreserveInternalConfig(source.mNativePtr);
+        }
+
         int neww = width;
         int newh = height;
         Canvas canvas = new Canvas();
@@ -824,7 +844,9 @@
         canvas.setBitmap(bitmap);
         canvas.drawBitmap(source, srcR, dstR, paint);
         canvas.setBitmap(null);
-
+        if (isHardware) {
+            return bitmap.copy(Config.HARDWARE, false);
+        }
         return bitmap;
     }
 
@@ -1428,9 +1450,8 @@
     @ColorInt
     public int getPixel(int x, int y) {
         checkRecycled("Can't call getPixel() on a recycled bitmap");
-        if (getConfig() == Config.HARDWARE) {
-            throw new IllegalStateException("Can't access pixels in hardware Bitmaps");
-        }
+        checkHardware("unable to getPixel(), "
+                + "pixel access is not supported on Config#HARDWARE bitmaps");
         checkPixelAccess(x, y);
         return nativeGetPixel(mNativePtr, x, y);
     }
@@ -1462,9 +1483,8 @@
     public void getPixels(@ColorInt int[] pixels, int offset, int stride,
                           int x, int y, int width, int height) {
         checkRecycled("Can't call getPixels() on a recycled bitmap");
-        if (getConfig() == Config.HARDWARE) {
-            throw new IllegalStateException("Can't access pixels in hardware Bitmaps");
-        }
+        checkHardware("unable to getPixels(), "
+                + "pixel access is not supported on Config#HARDWARE bitmaps");
         if (width == 0 || height == 0) {
             return; // nothing to do
         }
@@ -1773,4 +1793,5 @@
     private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
     private static native void nativePrepareToDraw(long nativeBitmap);
     private static native int nativeGetAllocationByteCount(long nativeBitmap);
+    private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
 }
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 761ee22..7033900 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -63,7 +63,7 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({MULTIPATH_INDICATOR_UNKNOWN, MULTIPATH_INDICATOR_DETECTED,
-        MULTIPATH_INDICATOR_NOT_USED})
+            MULTIPATH_INDICATOR_NOT_DETECTED})
     public @interface MultipathIndicator {}
 
     /**
@@ -81,9 +81,6 @@
      */
     public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
 
-    /** @removed */
-    public static final int MULTIPATH_INDICATOR_NOT_USED = 2;
-
     /** This GNSS measurement's tracking state is invalid or unknown. */
     public static final int STATE_UNKNOWN = 0;
     /** This GNSS measurement's tracking state has code lock. */
@@ -803,8 +800,8 @@
                 return "Unknown";
             case MULTIPATH_INDICATOR_DETECTED:
                 return "Detected";
-            case MULTIPATH_INDICATOR_NOT_USED:
-                return "NotUsed";
+            case MULTIPATH_INDICATOR_NOT_DETECTED:
+                return "NotDetected";
             default:
                 return "<Invalid:" + mMultipathIndicator + ">";
         }
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index 7db0466..d66fd9c 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -34,13 +34,6 @@
  * Events are delivered to registered instances of {@link Callback}.
  */
 public final class GnssMeasurementsEvent implements Parcelable {
-    /** @removed */
-    public static final int STATUS_NOT_SUPPORTED = 0;
-    /** @removed */
-    public static final int STATUS_READY = 1;
-    /** @removed */
-    public static final int STATUS_GNSS_LOCATION_DISABLED = 2;
-
     private final GnssClock mClock;
     private final Collection<GnssMeasurement> mReadOnlyMeasurements;
 
diff --git a/location/java/android/location/GnssNavigationMessageEvent.java b/location/java/android/location/GnssNavigationMessageEvent.java
deleted file mode 100644
index f7e5665..0000000
--- a/location/java/android/location/GnssNavigationMessageEvent.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.location;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * A class implementing a container for data associated with a navigation message event.
- * Events are delivered to registered instances of {@link Callback}.
- * @removed
- */
-public final class GnssNavigationMessageEvent implements Parcelable {
-    /**
-     * The status of GNSS measurements event.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_GNSS_LOCATION_DISABLED})
-    public @interface GnssNavigationMessageStatus {}
-
-    /**
-     * The system does not support tracking of GNSS Navigation Messages.
-     *
-     * This status will not change in the future.
-     */
-    public static final int STATUS_NOT_SUPPORTED = 0;
-
-    /**
-     * GNSS Navigation Messages are successfully being tracked, it will receive updates once they
-     * are available.
-     */
-    public static final int STATUS_READY = 1;
-
-    /**
-     * GNSS provider or Location is disabled, updated will not be received until they are enabled.
-     */
-    public static final int STATUS_GNSS_LOCATION_DISABLED = 2;
-
-    private final GnssNavigationMessage mNavigationMessage;
-
-    /**
-     * Used for receiving GNSS satellite Navigation Messages from the GNSS engine.
-     *
-     * <p>You can implement this interface and call
-     * {@link LocationManager#registerGnssNavigationMessageCallback}.
-     */
-    public static abstract class Callback {
-
-        /**
-         * Returns the latest collected GNSS Navigation Message.
-         */
-        public void onGnssNavigationMessageReceived(GnssNavigationMessageEvent event) {}
-
-        /**
-         * Returns the latest status of the GNSS Navigation Messages sub-system.
-         */
-        public void onStatusChanged(@GnssNavigationMessageStatus int status) {}
-    }
-
-    public GnssNavigationMessageEvent(GnssNavigationMessage message) {
-        if (message == null) {
-            throw new InvalidParameterException("Parameter 'message' must not be null.");
-        }
-        mNavigationMessage = message;
-    }
-
-    @NonNull
-    public GnssNavigationMessage getNavigationMessage() {
-        return mNavigationMessage;
-    }
-
-    public static final Creator<GnssNavigationMessageEvent> CREATOR =
-            new Creator<GnssNavigationMessageEvent>() {
-                @Override
-                public GnssNavigationMessageEvent createFromParcel(Parcel in) {
-                    ClassLoader classLoader = getClass().getClassLoader();
-                    GnssNavigationMessage navigationMessage = in.readParcelable(classLoader);
-                    return new GnssNavigationMessageEvent(navigationMessage);
-                }
-
-                @Override
-                public GnssNavigationMessageEvent[] newArray(int size) {
-                    return new GnssNavigationMessageEvent[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mNavigationMessage, flags);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder("[ GnssNavigationMessageEvent:\n\n");
-        builder.append(mNavigationMessage.toString());
-        builder.append("\n]");
-        return builder.toString();
-    }
-}
diff --git a/location/java/android/location/GnssNmeaListener.java b/location/java/android/location/GnssNmeaListener.java
deleted file mode 100644
index 756ae49..0000000
--- a/location/java/android/location/GnssNmeaListener.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.location;
-
-/**
-* Used for receiving NMEA sentences from the GNSS.
-* NMEA 0183 is a standard for communicating with marine electronic devices
-* and is a common method for receiving data from a GNSS, typically over a serial port.
-* See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
-* You can implement this interface and call {@link LocationManager#addNmeaListener}
-* to receive NMEA data from the GNSS engine.
-* @removed
-*/
-public interface GnssNmeaListener {
-    /** Called when an NMEA message is received. */
-    void onNmeaReceived(long timestamp, String nmea);
-}
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index e68821b..f105144 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -111,11 +111,6 @@
         mAzimuths = azimuths;
     }
 
-    /** @removed */
-    public int getNumSatellites() {
-        return getSatelliteCount();
-    }
-
     /**
      * Gets the total number of satellites in satellite list.
      */
@@ -191,11 +186,6 @@
         return mAzimuths[satIndex];
     }
 
-    /** @removed */
-    public boolean hasEphemeris(int satIndex) {
-        return hasEphemerisData(satIndex);
-    }
-
     /**
      * Reports whether the satellite at the specified index has ephemeris data.
      *
@@ -205,11 +195,6 @@
         return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
     }
 
-    /** @removed */
-    public boolean hasAlmanac(int satIndex) {
-        return hasAlmanacData(satIndex);
-    }
-
     /**
      * Reports whether the satellite at the specified index has almanac data.
      *
diff --git a/location/java/android/location/GnssStatusCallback.java b/location/java/android/location/GnssStatusCallback.java
deleted file mode 100644
index bf295ef..0000000
--- a/location/java/android/location/GnssStatusCallback.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.location;
-
-/**
- * Used for receiving notifications when GNSS events happen.
- * @removed
- */
-public abstract class GnssStatusCallback {
-    /**
-     * Called when GNSS system has started.
-     */
-    public void onStarted() {}
-
-    /**
-     * Called when GNSS system has stopped.
-     */
-    public void onStopped() {}
-
-    /**
-     * Called when the GNSS system has received its first fix since starting.
-     * @param ttffMillis the time from start to first fix in milliseconds.
-     */
-    public void onFirstFix(int ttffMillis) {}
-
-    /**
-     * Called periodically to report GNSS satellite status.
-     * @param status the current status of all satellites.
-     */
-    public void onSatelliteStatusChanged(GnssStatus status) {}
-}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index da0e515..087e74f 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -70,16 +70,10 @@
             new HashMap<>();
     private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners =
             new HashMap<>();
-    private final HashMap<GnssStatusCallback, GnssStatusListenerTransport>
-            mOldGnssStatusListeners = new HashMap<>();
     private final HashMap<GnssStatus.Callback, GnssStatusListenerTransport> mGnssStatusListeners =
             new HashMap<>();
-    private final HashMap<GnssNmeaListener, GnssStatusListenerTransport> mOldGnssNmeaListeners =
-            new HashMap<>();
     private final HashMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
             new HashMap<>();
-    private final HashMap<GnssNavigationMessageEvent.Callback, GnssNavigationMessage.Callback>
-            mNavigationMessageBridge = new HashMap<>();
     private GnssStatus mGnssStatus;
     private int mTimeToFirstFix;
 
@@ -1398,9 +1392,7 @@
 
         private final GpsStatus.Listener mGpsListener;
         private final GpsStatus.NmeaListener mGpsNmeaListener;
-        private final GnssStatusCallback mOldGnssCallback;
         private final GnssStatus.Callback mGnssCallback;
-        private final GnssNmeaListener mOldGnssNmeaListener;
         private final OnNmeaMessageListener mGnssNmeaListener;
 
         private class GnssHandler extends Handler {
@@ -1464,7 +1456,6 @@
             mGnssHandler = new GnssHandler(handler);
             mGpsNmeaListener = null;
             mNmeaBuffer = null;
-            mOldGnssCallback = null;
             mGnssCallback = mGpsListener != null ? new GnssStatus.Callback() {
                 @Override
                 public void onStarted() {
@@ -1486,7 +1477,6 @@
                     mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
                 }
             } : null;
-            mOldGnssNmeaListener = null;
             mGnssNmeaListener = null;
         }
 
@@ -1499,9 +1489,7 @@
             mGnssHandler = new GnssHandler(handler);
             mGpsNmeaListener = listener;
             mNmeaBuffer = new ArrayList<Nmea>();
-            mOldGnssCallback = null;
             mGnssCallback = null;
-            mOldGnssNmeaListener = null;
             mGnssNmeaListener = mGpsNmeaListener != null ? new OnNmeaMessageListener() {
                 @Override
                 public void onNmeaMessage(String nmea, long timestamp) {
@@ -1510,85 +1498,26 @@
             } : null;
         }
 
-        GnssStatusListenerTransport(GnssStatusCallback callback) {
-            this(callback, null);
-        }
-
-        GnssStatusListenerTransport(GnssStatusCallback callback, Handler handler) {
-            mOldGnssCallback = callback;
-            mGnssCallback = mOldGnssCallback != null ? new GnssStatus.Callback() {
-                @Override
-                public void onStarted() {
-                    mOldGnssCallback.onStarted();
-                }
-
-                @Override
-                public void onStopped() {
-                    mOldGnssCallback.onStopped();
-                }
-
-                @Override
-                public void onFirstFix(int ttff) {
-                    mOldGnssCallback.onFirstFix(ttff);
-                }
-
-                @Override
-                public void onSatelliteStatusChanged(GnssStatus status) {
-                    mOldGnssCallback.onSatelliteStatusChanged(status);
-                }
-            } : null;
-            mGnssHandler = new GnssHandler(handler);
-            mOldGnssNmeaListener = null;
-            mGnssNmeaListener = null;
-            mNmeaBuffer = null;
-            mGpsListener = null;
-            mGpsNmeaListener = null;
-        }
-
         GnssStatusListenerTransport(GnssStatus.Callback callback) {
             this(callback, null);
         }
 
         GnssStatusListenerTransport(GnssStatus.Callback callback, Handler handler) {
-            mOldGnssCallback = null;
             mGnssCallback = callback;
             mGnssHandler = new GnssHandler(handler);
-            mOldGnssNmeaListener = null;
             mGnssNmeaListener = null;
             mNmeaBuffer = null;
             mGpsListener = null;
             mGpsNmeaListener = null;
         }
 
-        GnssStatusListenerTransport(GnssNmeaListener listener) {
-            this(listener, null);
-        }
-
-        GnssStatusListenerTransport(GnssNmeaListener listener, Handler handler) {
-            mGnssCallback = null;
-            mOldGnssCallback = null;
-            mGnssHandler = new GnssHandler(handler);
-            mOldGnssNmeaListener = listener;
-            mGnssNmeaListener = mOldGnssNmeaListener != null ? new OnNmeaMessageListener() {
-                @Override
-                public void onNmeaMessage(String message, long timestamp) {
-                    mOldGnssNmeaListener.onNmeaReceived(timestamp, message);
-                }
-            } : null;
-            mGpsListener = null;
-            mGpsNmeaListener = null;
-            mNmeaBuffer = new ArrayList<Nmea>();
-        }
-
         GnssStatusListenerTransport(OnNmeaMessageListener listener) {
             this(listener, null);
         }
 
         GnssStatusListenerTransport(OnNmeaMessageListener listener, Handler handler) {
-            mOldGnssCallback = null;
             mGnssCallback = null;
             mGnssHandler = new GnssHandler(handler);
-            mOldGnssNmeaListener = null;
             mGnssNmeaListener = listener;
             mGpsListener = null;
             mGpsNmeaListener = null;
@@ -1703,73 +1632,9 @@
     }
 
     /**
-     * Registers a GNSS status listener.
+     * Registers a GNSS status callback.
      *
-     * @param callback GNSS status listener object to register
-     *
-     * @return true if the listener was successfully added
-     *
-     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
-     * @removed
-     */
-    @RequiresPermission(ACCESS_FINE_LOCATION)
-    public boolean registerGnssStatusCallback(GnssStatusCallback callback) {
-        return registerGnssStatusCallback(callback, null);
-    }
-
-    /**
-     * Registers a GNSS status listener.
-     *
-     * @param callback GNSS status listener object to register
-     * @param handler the handler that the callback runs on.
-     *
-     * @return true if the listener was successfully added
-     *
-     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
-     * @removed
-     */
-    @RequiresPermission(ACCESS_FINE_LOCATION)
-    public boolean registerGnssStatusCallback(GnssStatusCallback callback, Handler handler) {
-        boolean result;
-        if (mOldGnssStatusListeners.get(callback) != null) {
-            // listener is already registered
-            return true;
-        }
-        try {
-            GnssStatusListenerTransport transport =
-                    new GnssStatusListenerTransport(callback, handler);
-            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
-            if (result) {
-                mOldGnssStatusListeners.put(callback, transport);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        return result;
-    }
-
-    /**
-     * Removes a GNSS status listener.
-     *
-     * @param callback GNSS status listener object to remove
-     * @removed
-     */
-    public void unregisterGnssStatusCallback(GnssStatusCallback callback) {
-        try {
-            GnssStatusListenerTransport transport = mOldGnssStatusListeners.remove(callback);
-            if (transport != null) {
-                mService.unregisterGnssStatusCallback(transport);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Registers a GNSS status listener.
-     *
-     * @param callback GNSS status listener object to register
+     * @param callback GNSS status callback object to register
      *
      * @return true if the listener was successfully added
      *
@@ -1781,9 +1646,9 @@
     }
 
     /**
-     * Registers a GNSS status listener.
+     * Registers a GNSS status callback.
      *
-     * @param callback GNSS status listener object to register
+     * @param callback GNSS status callback object to register
      * @param handler the handler that the callback runs on.
      *
      * @return true if the listener was successfully added
@@ -1812,9 +1677,9 @@
     }
 
     /**
-     * Removes a GNSS status listener.
+     * Removes a GNSS status callback.
      *
-     * @param callback GNSS status listener object to remove
+     * @param callback GNSS status callback object to remove
      */
     public void unregisterGnssStatusCallback(GnssStatus.Callback callback) {
         try {
@@ -1880,71 +1745,6 @@
     /**
      * Adds an NMEA listener.
      *
-     * @param listener a {@link GnssNmeaListener} object to register
-     *
-     * @return true if the listener was successfully added
-     *
-     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
-     * @removed
-     */
-    @RequiresPermission(ACCESS_FINE_LOCATION)
-    public boolean addNmeaListener(GnssNmeaListener listener) {
-        return addNmeaListener(listener, null);
-    }
-
-    /**
-     * Adds an NMEA listener.
-     *
-     * @param listener a {@link GnssNmeaListener} object to register
-     * @param handler the handler that the listener runs on.
-     *
-     * @return true if the listener was successfully added
-     *
-     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
-     * @removed
-     */
-    @RequiresPermission(ACCESS_FINE_LOCATION)
-    public boolean addNmeaListener(GnssNmeaListener listener, Handler handler) {
-        boolean result;
-
-        if (mGpsNmeaListeners.get(listener) != null) {
-            // listener is already registered
-            return true;
-        }
-        try {
-            GnssStatusListenerTransport transport =
-                    new GnssStatusListenerTransport(listener, handler);
-            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
-            if (result) {
-                mOldGnssNmeaListeners.put(listener, transport);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        return result;
-    }
-
-    /**
-     * Removes an NMEA listener.
-     *
-     * @param listener a {@link GnssNmeaListener} object to remove
-     * @removed
-     */
-    public void removeNmeaListener(GnssNmeaListener listener) {
-        try {
-            GnssStatusListenerTransport transport = mOldGnssNmeaListeners.remove(listener);
-            if (transport != null) {
-                mService.unregisterGnssStatusCallback(transport);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Adds an NMEA listener.
-     *
      * @param listener a {@link OnNmeaMessageListener} object to register
      *
      * @return true if the listener was successfully added
@@ -2088,58 +1888,6 @@
     /**
      * Registers a GNSS Navigation Message callback.
      *
-     * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
-     * @removed
-     */
-    public boolean registerGnssNavigationMessageCallback(
-            GnssNavigationMessageEvent.Callback callback) {
-        return registerGnssNavigationMessageCallback(callback, null);
-    }
-
-    /**
-     * Registers a GNSS Navigation Message callback.
-     *
-     * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
-     * @param handler the handler that the callback runs on.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
-     * @removed
-     */
-    @RequiresPermission(ACCESS_FINE_LOCATION)
-    public boolean registerGnssNavigationMessageCallback(
-            final GnssNavigationMessageEvent.Callback callback, Handler handler) {
-        GnssNavigationMessage.Callback bridge = new GnssNavigationMessage.Callback() {
-            @Override
-            public void onGnssNavigationMessageReceived(GnssNavigationMessage message) {
-                GnssNavigationMessageEvent event = new GnssNavigationMessageEvent(message);
-                callback.onGnssNavigationMessageReceived(event);
-            }
-
-            @Override
-            public void onStatusChanged(int status) {
-                callback.onStatusChanged(status);
-            }
-        };
-        mNavigationMessageBridge.put(callback, bridge);
-        return mGnssNavigationMessageCallbackTransport.add(bridge, handler);
-    }
-
-    /**
-     * Unregisters a GNSS Navigation Message callback.
-     *
-     * @param callback a {@link GnssNavigationMessageEvent.Callback} object to remove.
-     * @removed
-     */
-    public void unregisterGnssNavigationMessageCallback(
-            GnssNavigationMessageEvent.Callback callback) {
-        mGnssNavigationMessageCallbackTransport.remove(
-                mNavigationMessageBridge.remove(
-                        callback));
-    }
-
-    /**
-     * Registers a GNSS Navigation Message callback.
-     *
      * @param callback a {@link GnssNavigationMessage.Callback} object to register.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
      */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 23bb6f9..1d124c5 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3482,6 +3482,20 @@
         return delay;
     }
 
+     /**
+     * Indicate A2DP device configuration has changed.
+     * @param device Bluetooth device whose configuration has changed.
+     * {@hide}
+     */
+    public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) {
+        IAudioService service = getService();
+        try {
+            service.handleBluetoothA2dpDeviceConfigChange(device);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** {@hide} */
     public IRingtonePlayer getRingtonePlayer() {
         try {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 17928995..b44a710 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -689,6 +689,9 @@
     public static native int setDeviceConnectionState(int device, int state,
                                                       String device_address, String device_name);
     public static native int getDeviceConnectionState(int device, String device_address);
+    public static native int handleDeviceConfigChange(int device,
+                                                      String device_address,
+                                                      String device_name);
     public static native int setPhoneState(int state);
     public static native int setForceUse(int usage, int config);
     public static native int getForceUse(int usage);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d53c937..084675f 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -143,6 +143,8 @@
 
     int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
 
+    void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
+
     AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
 
     boolean isCameraSoundForced();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 77a0fbd..500556a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2291,7 +2291,7 @@
      * {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one).
      */
     private synchronized void setSubtitleAnchor() {
-        if (mSubtitleController == null) {
+        if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) {
             final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
             thread.start();
             Handler handler = new Handler(thread.getLooper());
@@ -2442,10 +2442,7 @@
     }
 
     private void scanInternalSubtitleTracks() {
-        if (mSubtitleController == null) {
-            Log.d(TAG, "setSubtitleAnchor in MediaPlayer");
-            setSubtitleAnchor();
-        }
+        setSubtitleAnchor();
 
         populateInbandTracks();
 
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
index a68a44e..abe310a 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -18,7 +18,7 @@
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="?android:attr/colorPrimaryDark">
+    android:background="?android:attr/statusBarColor">
     <!-- The main content view -->
     <LinearLayout
         android:id="@+id/content_parent"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5c2787b..5574753 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -427,6 +427,7 @@
             android:supportsPictureInPicture="true"
             android:stateNotNeeded="true"
             android:taskAffinity=""
+            android:launchMode="singleTop"
             androidprv:alwaysFocusable="true" />
 
         <!-- platform logo easter egg activity -->
diff --git a/packages/SystemUI/res/color/tint_color_selector.xml b/packages/SystemUI/res/color/tint_color_selector.xml
new file mode 100644
index 0000000..cb24425
--- /dev/null
+++ b/packages/SystemUI/res/color/tint_color_selector.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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="false"
+          android:alpha="0.25"
+          android:color="?android:attr/colorForeground"/>
+    <item android:state_activated="false"
+          android:alpha="0.3"
+          android:color="?android:attr/colorForeground"/>
+    <item android:color="?android:attr/colorForeground"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
index 736cfd8..83f46e5 100644
--- a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
+++ b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
@@ -20,7 +20,8 @@
     android:width="48dp"
     android:viewportHeight="48"
     android:viewportWidth="48"
-    android:tint="@color/qs_tile_tint_unavailable" >
+    android:alpha="0.25"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="ic_hotspot"
         android:translateX="23.97354"
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 054bfab..cadc09b 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -19,7 +19,7 @@
     android:id="@+id/menu"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="#33000000">
+    android:background="#66000000">
 
     <LinearLayout
         android:id="@+id/actions"
@@ -45,7 +45,7 @@
             android:text="@string/pip_phone_dismiss"
             android:background="?android:selectableItemBackground" />
         <TextView
-            android:id="@+id/minimize"
+            android:id="@+id/expand"
             android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_weight="1"
@@ -53,7 +53,7 @@
             android:textSize="12sp"
             android:textColor="#ffffffff"
             android:fontFamily="sans-serif"
-            android:text="@string/pip_phone_minimize"
+            android:text="@string/pip_phone_expand"
             android:background="?android:selectableItemBackground" />
     </LinearLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c7149df..ba8c644 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -144,10 +144,6 @@
 
     <color name="remote_input_accent">#eeeeee</color>
 
-    <color name="qs_tile_tint_unavailable">#40ffffff</color>
-    <color name="qs_tile_tint_inactive">#4dffffff</color>
-    <color name="qs_tile_tint_active">#ffffffff</color>
-
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
     <color name="ksh_keyword_color">#d9000000</color>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fc1d816..614472a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -68,7 +68,7 @@
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowContentOverlay">@null</item>
-        <item name="android:windowBackground">@drawable/forced_resizable_background</item>
+        <item name="android:windowBackground">@null</item>
         <item name="android:colorBackgroundCacheHint">@null</item>
         <item name="android:statusBarColor">@color/transparent</item>
         <item name="android:windowAnimationStyle">@style/Animation.PipPhoneOverlayControl</item>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index c4698c3..07ef5e0 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -491,6 +491,12 @@
 
     @Override
     public void setColorFilter(@Nullable ColorFilter colorFilter) {
+        mFramePaint.setColorFilter(colorFilter);
+        mBatteryPaint.setColorFilter(colorFilter);
+        mWarningTextPaint.setColorFilter(colorFilter);
+        mTextPaint.setColorFilter(colorFilter);
+        mBoltPaint.setColorFilter(colorFilter);
+        mPlusPaint.setColorFilter(colorFilter);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index fe8ee6f..e4f7816 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -16,13 +16,16 @@
 
 package com.android.systemui.pip.phone;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.PendingIntent.CanceledException;
 import android.app.RemoteAction;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.graphics.PointF;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -30,11 +33,15 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.WindowManager.LayoutParams;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
 import java.util.ArrayList;
@@ -47,28 +54,40 @@
 
     private static final String TAG = "PipMenuActivity";
 
-    public static final int MESSAGE_FINISH_SELF = 1;
-    public static final int MESSAGE_UPDATE_ACTIONS = 2;
+    public static final int MESSAGE_SHOW_MENU = 1;
+    public static final int MESSAGE_HIDE_MENU = 2;
+    public static final int MESSAGE_UPDATE_ACTIONS = 3;
 
     private static final long INITIAL_DISMISS_DELAY = 2000;
     private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
+    private static final long MENU_FADE_DURATION = 125;
 
-    private List<RemoteAction> mActions = new ArrayList<>();
+    private boolean mMenuVisible;
+    private final List<RemoteAction> mActions = new ArrayList<>();
+    private View mMenuContainer;
     private View mDismissButton;
-    private View mMinimizeButton;
+    private View mExpandButton;
 
+    private ObjectAnimator mMenuContainerAnimator;
+
+    private PointF mDownPosition = new PointF();
+    private PointF mDownDelta = new PointF();
+    private ViewConfiguration mViewConfig;
     private Handler mHandler = new Handler();
     private Messenger mToControllerMessenger;
     private Messenger mMessenger = new Messenger(new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case MESSAGE_SHOW_MENU:
+                    showMenu();
+                    break;
+                case MESSAGE_HIDE_MENU:
+                    hideMenu();
+                    break;
                 case MESSAGE_UPDATE_ACTIONS:
                     setActions(((ParceledListSlice) msg.obj).getList());
                     break;
-                case MESSAGE_FINISH_SELF:
-                    finish();
-                    break;
             }
         }
     });
@@ -76,12 +95,17 @@
     private final Runnable mFinishRunnable = new Runnable() {
         @Override
         public void run() {
-            finish();
+            hideMenu();
         }
     };
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
+        // Set the flags to allow us to watch for outside touches and also hide the menu and start
+        // manipulating the PIP in the same touch gesture
+        mViewConfig = ViewConfiguration.get(this);
+        getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_SLIPPERY);
+
         super.onCreate(savedInstanceState);
         setContentView(R.layout.pip_menu_activity);
 
@@ -94,44 +118,74 @@
             setActions(actions.getList());
         }
 
-        findViewById(R.id.menu).setOnClickListener((v) -> {
+        mMenuContainer = findViewById(R.id.menu);
+        mMenuContainer.setOnClickListener((v) -> {
             expandPip();
         });
         mDismissButton = findViewById(R.id.dismiss);
         mDismissButton.setOnClickListener((v) -> {
             dismissPip();
         });
-        mMinimizeButton = findViewById(R.id.minimize);
-        mMinimizeButton.setOnClickListener((v) -> {
-            minimizePip();
+        mExpandButton = findViewById(R.id.expand);
+        mExpandButton.setOnClickListener((v) -> {
+            expandPip();
         });
+
+        notifyActivityCallback(mMessenger);
+        showMenu();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        showMenu();
     }
 
     @Override
     protected void onStart() {
         super.onStart();
-        notifyActivityVisibility(true);
+        notifyMenuVisibility(true);
         repostDelayedFinish(INITIAL_DISMISS_DELAY);
     }
 
     @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        if (!isInPictureInPictureMode) {
+            finish();
+        }
+    }
+
+    @Override
     public void onUserInteraction() {
         repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
     }
 
     @Override
-    protected void onStop() {
-        super.onStop();
-        finish();
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        // On the first action outside the window, hide the menu
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_OUTSIDE:
+                hideMenu();
+                break;
+            case MotionEvent.ACTION_DOWN:
+                mDownPosition.set(ev.getX(), ev.getY());
+                break;
+            case MotionEvent.ACTION_MOVE:
+                mDownDelta.set(ev.getX() - mDownPosition.x, ev.getY() - mDownPosition.y);
+                if (mDownDelta.length() > mViewConfig.getScaledTouchSlop() && mMenuVisible) {
+                    hideMenu();
+                    mMenuVisible = false;
+                }
+        }
+        return super.dispatchTouchEvent(ev);
     }
 
     @Override
     public void finish() {
-        View v = getWindow().getDecorView();
-        v.removeCallbacks(mFinishRunnable);
-        notifyActivityVisibility(false);
+        notifyActivityCallback(null);
         super.finish();
-        overridePendingTransition(0, R.anim.forced_resizable_exit);
+        // Hide without an animation (the menu should already be invisible at this point)
+        overridePendingTransition(0, 0);
     }
 
     @Override
@@ -139,6 +193,51 @@
         // Do nothing
     }
 
+    private void showMenu() {
+        if (!mMenuVisible) {
+            if (mMenuContainerAnimator != null) {
+                mMenuContainerAnimator.cancel();
+            }
+
+            notifyMenuVisibility(true);
+            mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 1f);
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
+            mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
+            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    repostDelayedFinish(INITIAL_DISMISS_DELAY);
+                }
+            });
+            mMenuContainerAnimator.start();
+        }
+    }
+
+    private void hideMenu() {
+        hideMenu(null /* animationFinishedRunnable */);
+    }
+
+    private void hideMenu(final Runnable animationFinishedRunnable) {
+        if (mMenuVisible) {
+            cancelDelayedFinish();
+            notifyMenuVisibility(false);
+            mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 0f);
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+            mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
+            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (animationFinishedRunnable != null) {
+                        animationFinishedRunnable.run();
+                    }
+                }
+            });
+            mMenuContainerAnimator.start();
+        }
+    }
+
     private void setActions(List<RemoteAction> actions) {
         mActions.clear();
         mActions.addAll(actions);
@@ -173,17 +272,19 @@
         }
     }
 
-    private void notifyActivityVisibility(boolean visible) {
+    private void notifyMenuVisibility(boolean visible) {
+        mMenuVisible = visible;
         Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_ACTIVITY_VISIBILITY_CHANGED;
+        m.what = PipMenuActivityController.MESSAGE_MENU_VISIBILITY_CHANGED;
         m.arg1 = visible ? 1 : 0;
-        m.replyTo = visible ? mMessenger : null;
         sendMessage(m, "Could not notify controller of PIP menu visibility");
     }
 
     private void expandPip() {
-        sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
-                "Could not notify controller to expand PIP");
+        hideMenu(() -> {
+            sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
+                    "Could not notify controller to expand PIP");
+        });
     }
 
     private void minimizePip() {
@@ -192,8 +293,17 @@
     }
 
     private void dismissPip() {
-        sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
-                "Could not notify controller to dismiss PIP");
+        hideMenu(() -> {
+            sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
+                    "Could not notify controller to dismiss PIP");
+        });
+    }
+
+    private void notifyActivityCallback(Messenger callback) {
+        Message m = Message.obtain();
+        m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
+        m.replyTo = callback;
+        sendMessage(m, "Could not notify controller of activity finished");
     }
 
     private void sendEmptyMessage(int what, String errorMsg) {
@@ -210,6 +320,11 @@
         }
     }
 
+    private void cancelDelayedFinish() {
+        View v = getWindow().getDecorView();
+        v.removeCallbacks(mFinishRunnable);
+    }
+
     private void repostDelayedFinish(long delay) {
         View v = getWindow().getDecorView();
         v.removeCallbacks(mFinishRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 64e2d1a..0350cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -25,10 +25,11 @@
     public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
     public static final String EXTRA_ACTIONS = "actions";
 
-    public static final int MESSAGE_ACTIVITY_VISIBILITY_CHANGED = 100;
+    public static final int MESSAGE_MENU_VISIBILITY_CHANGED = 100;
     public static final int MESSAGE_EXPAND_PIP = 101;
     public static final int MESSAGE_MINIMIZE_PIP = 102;
     public static final int MESSAGE_DISMISS_PIP = 103;
+    public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
 
     /**
      * A listener interface to receive notification on changes in PIP.
@@ -67,34 +68,25 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MESSAGE_ACTIVITY_VISIBILITY_CHANGED: {
+                case MESSAGE_MENU_VISIBILITY_CHANGED: {
                     boolean visible = msg.arg1 > 0;
-                    int listenerCount = mListeners.size();
-                    for (int i = 0; i < listenerCount; i++) {
-                        mListeners.get(i).onPipMenuVisibilityChanged(visible);
-                    }
-                    mToActivityMessenger = msg.replyTo;
+                    mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
                     break;
                 }
                 case MESSAGE_EXPAND_PIP: {
-                    int listenerCount = mListeners.size();
-                    for (int i = 0; i < listenerCount; i++) {
-                        mListeners.get(i).onPipExpand();
-                    }
+                    mListeners.forEach(l -> l.onPipExpand());
                     break;
                 }
                 case MESSAGE_MINIMIZE_PIP: {
-                    int listenerCount = mListeners.size();
-                    for (int i = 0; i < listenerCount; i++) {
-                        mListeners.get(i).onPipMinimize();
-                    }
+                    mListeners.forEach(l -> l.onPipMinimize());
                     break;
                 }
                 case MESSAGE_DISMISS_PIP: {
-                    int listenerCount = mListeners.size();
-                    for (int i = 0; i < listenerCount; i++) {
-                        mListeners.get(i).onPipDismiss();
-                    }
+                    mListeners.forEach(l -> l.onPipDismiss());
+                    break;
+                }
+                case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
+                    mToActivityMessenger = msg.replyTo;
                     break;
                 }
             }
@@ -121,24 +113,34 @@
      * Shows the menu activity.
      */
     public void showMenu() {
-        // Start the menu activity on the top task of the pinned stack
-        try {
-            StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
-            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
-                    pinnedStackInfo.taskIds.length > 0) {
-                Intent intent = new Intent(mContext, PipMenuActivity.class);
-                intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
-                intent.putExtra(EXTRA_ACTIONS, mActions);
-                ActivityOptions options = ActivityOptions.makeBasic();
-                options.setLaunchTaskId(
-                        pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
-                options.setTaskOverlay(true);
-                mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
-            } else {
-                Log.e(TAG, "No PIP tasks found");
+        if (mToActivityMessenger != null) {
+            Message m = Message.obtain();
+            m.what = PipMenuActivity.MESSAGE_SHOW_MENU;
+            try {
+                mToActivityMessenger.send(m);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not notify menu to show", e);
             }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error showing PIP menu activity", e);
+        } else {
+            // Start the menu activity on the top task of the pinned stack
+            try {
+                StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+                if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
+                        pinnedStackInfo.taskIds.length > 0) {
+                    Intent intent = new Intent(mContext, PipMenuActivity.class);
+                    intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
+                    intent.putExtra(EXTRA_ACTIONS, mActions);
+                    ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+                    options.setLaunchTaskId(
+                            pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
+                    options.setTaskOverlay(true);
+                    mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+                } else {
+                    Log.e(TAG, "No PIP tasks found");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error showing PIP menu activity", e);
+            }
         }
     }
 
@@ -148,13 +150,12 @@
     public void hideMenu() {
         if (mToActivityMessenger != null) {
             Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_FINISH_SELF;
+            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
             try {
                 mToActivityMessenger.send(m);
             } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu activity to finish", e);
+                Log.e(TAG, "Could not notify menu to hide", e);
             }
-            mToActivityMessenger = null;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index ff3cc79..380e4683 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -174,7 +174,7 @@
 
         @Override
         public void onPipDismiss() {
-            animateDismissPinnedStack(mPinnedStackBounds);
+            BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack);
         }
     }
 
@@ -328,26 +328,31 @@
      * Registers the input consumer.
      */
     private void registerInputConsumer() {
-        final InputChannel inputChannel = new InputChannel();
-        try {
-            mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
-            mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to create PIP input consumer", e);
+        if (mInputEventReceiver == null) {
+            final InputChannel inputChannel = new InputChannel();
+            try {
+                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+                mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to create PIP input consumer", e);
+            }
+            mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
         }
-        mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
     }
 
     /**
      * Unregisters the input consumer.
      */
     private void unregisterInputConsumer() {
-        try {
-            mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to destroy PIP input consumer", e);
+        if (mInputEventReceiver != null) {
+            try {
+                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to destroy PIP input consumer", e);
+            }
+            mInputEventReceiver.dispose();
+            mInputEventReceiver = null;
         }
-        mInputEventReceiver.dispose();
     }
 
     /**
@@ -761,10 +766,6 @@
     private PipTouchGesture mTapThroughGesture = new PipTouchGesture() {
         @Override
         boolean onMove(PipTouchState touchState) {
-            if (mEnableTapThrough && touchState.startedDragging()) {
-                mIsTappingThrough = false;
-                mMenuController.hideMenu();
-            }
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index dc42adc..fdefcf9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -362,6 +362,11 @@
     private final TouchAnimator.Listener mNonFirstPageListener =
             new TouchAnimator.ListenerAdapter() {
                 @Override
+                public void onAnimationAtEnd() {
+                    mQuickQsPanel.setVisibility(View.INVISIBLE);
+                }
+
+                @Override
                 public void onAnimationStarted() {
                     mQuickQsPanel.setVisibility(View.VISIBLE);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 1fcb45b..9e3889b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -15,6 +15,9 @@
  */
 package com.android.systemui.qs.external;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,7 +27,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
@@ -37,19 +39,14 @@
 import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.view.IWindowManager;
-import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
 import com.android.systemui.statusbar.phone.QSTileHost;
 import libcore.util.Objects;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-
 public class CustomTile extends QSTile<QSTile.State> implements TileChangeListener {
     public static final String PREFIX = "custom(";
 
@@ -301,7 +298,7 @@
             tileState = Tile.STATE_UNAVAILABLE;
             drawable = mDefaultIcon.loadDrawable(mAppContext);
         }
-        int color = mContext.getColor(getColor(tileState));
+        final int color = TileColorPicker.getInstance(mContext).getColor(tileState);
         drawable.setTint(color);
         state.icon = mHasRes ? new DrawableIconWithRes(drawable, icon.getResId())
                 : new DrawableIcon(drawable);
@@ -335,18 +332,6 @@
         });
     }
 
-    private static int getColor(int state) {
-        switch (state) {
-            case Tile.STATE_UNAVAILABLE:
-                return R.color.qs_tile_tint_unavailable;
-            case Tile.STATE_INACTIVE:
-                return R.color.qs_tile_tint_inactive;
-            case Tile.STATE_ACTIVE:
-                return R.color.qs_tile_tint_active;
-        }
-        return 0;
-    }
-
     public static String toSpec(ComponentName name) {
         return PREFIX + name.flattenToShortString() + ")";
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
new file mode 100644
index 0000000..0cc17ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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.systemui.qs.external;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.service.quicksettings.Tile;
+import android.support.annotation.VisibleForTesting;
+import com.android.systemui.R;
+
+public class TileColorPicker {
+    @VisibleForTesting static final int[] DISABLE_STATE_SET = {-android.R.attr.state_enabled};
+    @VisibleForTesting static final int[] ENABLE_STATE_SET = {android.R.attr.state_enabled,
+            android.R.attr.state_activated};
+    @VisibleForTesting static final int[] INACTIVE_STATE_SET = {-android.R.attr.state_activated};
+    private static TileColorPicker sInstance;
+
+    private ColorStateList mColorStateList;
+
+    private TileColorPicker(Context context) {
+        mColorStateList = context.getResources().
+                getColorStateList(R.color.tint_color_selector, context.getTheme());
+    }
+
+    public static TileColorPicker getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new TileColorPicker(context);
+        }
+        return sInstance;
+    }
+
+    public int getColor(int state) {
+        final int defaultColor = 0;
+
+        switch (state) {
+            case Tile.STATE_UNAVAILABLE:
+                return mColorStateList.getColorForState(DISABLE_STATE_SET, defaultColor);
+            case Tile.STATE_INACTIVE:
+                return mColorStateList.getColorForState(INACTIVE_STATE_SET, defaultColor);
+            case Tile.STATE_ACTIVE:
+                return mColorStateList.getColorForState(ENABLE_STATE_SET, defaultColor);
+            default:
+                return mColorStateList.getColorForState(ENABLE_STATE_SET, defaultColor);
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index fc1c1ac..87b00a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -19,7 +19,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
+import android.service.quicksettings.Tile;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.RelativeSizeSpan;
@@ -40,6 +43,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import java.text.NumberFormat;
@@ -120,6 +124,8 @@
                         context.getColor(R.color.batterymeter_frame_color));
                 drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
                 drawable.onPowerSaveChanged(mPowerSave);
+                final int color = TileColorPicker.getInstance(context).getColor(Tile.STATE_ACTIVE);
+                drawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
                 return drawable;
             }
 
@@ -205,6 +211,11 @@
             mDrawable.onBatteryLevelChanged(100, false, false);
             mDrawable.onPowerSaveChanged(true);
             mDrawable.disableShowPercent();
+
+            final int color = TileColorPicker.getInstance(mCurrentView.getContext())
+                    .getColor(Tile.STATE_ACTIVE);
+            mDrawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
+
             ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
             Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
             checkbox.setChecked(mPowerSave);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 5b1638f..d86aebf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.provider.MediaStore;
+import android.service.quicksettings.Tile;
 import android.text.SpannableStringBuilder;
 import android.text.style.ForegroundColorSpan;
 import android.widget.Switch;
@@ -28,6 +29,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
 import com.android.systemui.statusbar.policy.FlashlightController;
 
 /** Quick settings tile: Control flashlight **/
@@ -104,7 +106,8 @@
         if (!mFlashlightController.isAvailable()) {
             Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_disable)
                     .mutate();
-            final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+            final int disabledColor = TileColorPicker.getInstance(mContext)
+                    .getColor(Tile.STATE_UNAVAILABLE);
             icon.setTint(disabledColor);
             state.icon = new DrawableIcon(icon);
             state.label = new SpannableStringBuilder().append(state.label,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index dcee659..99485bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -22,6 +22,7 @@
 
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
 import android.text.SpannableStringBuilder;
 import android.text.style.ForegroundColorSpan;
 import android.widget.Switch;
@@ -31,6 +32,7 @@
 import com.android.systemui.R;
 import com.android.systemui.qs.GlobalSetting;
 import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
 import com.android.systemui.statusbar.policy.HotspotController;
 
 /** Quick settings tile: Hotspot **/
@@ -124,7 +126,8 @@
         boolean wasAirplane = state.isAirplaneMode;
         state.isAirplaneMode = mAirplaneMode.getValue() != 0;
         if (state.isAirplaneMode) {
-            final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+            final int disabledColor = TileColorPicker.getInstance(mContext)
+                    .getColor(Tile.STATE_UNAVAILABLE);
             state.label = new SpannableStringBuilder().append(state.label,
                     new ForegroundColorSpan(disabledColor),
                     SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
new file mode 100644
index 0000000..ba451e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 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.systemui.qs.external;
+
+import android.content.res.ColorStateList;
+import android.service.quicksettings.Tile;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.systemui.SysuiTestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.systemui.R;
+
+import static junit.framework.Assert.assertEquals;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TileColorPickerTest extends SysuiTestCase {
+    private static final int DEFAULT_COLOR = 0;
+
+    private TileColorPicker mTileColorPicker;
+    private ColorStateList mTintColorStateList;
+
+    @Before
+    public void setUp() {
+        mTileColorPicker = TileColorPicker.getInstance(mContext);
+        mTintColorStateList = mContext.getResources().
+                getColorStateList(R.color.tint_color_selector, mContext.getTheme());
+    }
+
+    @Test
+    public void testGetColor_StateUnavailable_ReturnUnavailableColor() {
+        final int color = mTileColorPicker.getColor(Tile.STATE_UNAVAILABLE);
+        final int expectedColor = mTintColorStateList.getColorForState(
+                TileColorPicker.DISABLE_STATE_SET, DEFAULT_COLOR);
+
+        assertEquals(expectedColor, color);
+    }
+
+    @Test
+    public void testGetColor_StateInactive_ReturnInactiveColor() {
+        final int color = mTileColorPicker.getColor(Tile.STATE_INACTIVE);
+        final int expectedColor = mTintColorStateList.getColorForState(
+                TileColorPicker.INACTIVE_STATE_SET, DEFAULT_COLOR);
+
+        assertEquals(expectedColor, color);
+    }
+
+    @Test
+    public void testGetColor_StateActive_ReturnActiveColor() {
+        final int color = mTileColorPicker.getColor(Tile.STATE_ACTIVE);
+        final int expectedColor = mTintColorStateList.getColorForState(
+                TileColorPicker.ENABLE_STATE_SET, DEFAULT_COLOR);
+
+        assertEquals(expectedColor, color);
+    }
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index b8a2340..3419963 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -826,25 +826,21 @@
                         + " not in uid " + callingUid);
             }
 
-            // Legacy apps in permission review mode trigger a user prompt
-            if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
-                Intent intent = new Intent(intentAction);
-                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                try {
-                    mContext.startActivity(intent);
-                } catch (ActivityNotFoundException e) {
-                    // Shouldn't happen
-                    Slog.e(TAG, "Intent to handle action " + intentAction + " missing");
-                    return false;
-                }
-                return true;
+            Intent intent = new Intent(intentAction);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            try {
+                mContext.startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                // Shouldn't happen
+                Slog.e(TAG, "Intent to handle action " + intentAction + " missing");
+                return false;
             }
+            return true;
         } catch (PackageManager.NameNotFoundException e) {
             throw new RemoteException(e.getMessage());
         }
-        return false;
     }
 
     public void unbindAndFinish() {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index eb2cd0b..d42fe11 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -154,7 +154,8 @@
 3110 unknown_sources_enabled (value|1)
 # Package Manager critical info
 3120 pm_critical_info (msg|3)
-
+# Disk usage stats for verifying quota correctness
+3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2)
 
 # ---------------------------
 # WindowManagerService.java
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 87b4221..6625846 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -215,6 +215,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.BootTimingsTraceLog;
 import android.util.DebugUtils;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -3629,13 +3630,14 @@
                  * resources like shared libraries and access user-wide resources
                  */
                 if (ArrayUtils.isEmpty(permGids)) {
-                    gids = new int[2];
+                    gids = new int[3];
                 } else {
-                    gids = new int[permGids.length + 2];
-                    System.arraycopy(permGids, 0, gids, 2, permGids.length);
+                    gids = new int[permGids.length + 3];
+                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
                 }
                 gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
-                gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
             }
             checkTime(startTime, "startProcess: building args");
             if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
@@ -12056,6 +12058,12 @@
         }
     }
 
+    /**
+     * @deprecated This method is only used by a few internal components and it will soon be
+     * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+     * No new code should be calling it.
+     */
+    @Deprecated
     public void requestBugReport(int bugreportType) {
         String extraOptions = null;
         switch (bugreportType) {
@@ -13205,7 +13213,8 @@
         }
     }
 
-    public void systemReady(final Runnable goingCallback) {
+    public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
+        traceLog.traceBegin("PhaseActivityManagerReady");
         synchronized(this) {
             if (mSystemReady) {
                 // If we're done calling all the receivers, run the next "boot phase" passed in
@@ -13302,7 +13311,7 @@
         }
 
         if (goingCallback != null) goingCallback.run();
-
+        traceLog.traceBegin("ActivityManagerStartApps");
         mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
                 Integer.toString(currentUserId), currentUserId);
         mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
@@ -13377,6 +13386,8 @@
             }
             mStackSupervisor.resumeFocusedStackTopActivityLocked();
             mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
+            traceLog.traceEnd(); // ActivityManagerStartApps
+            traceLog.traceEnd(); // PhaseActivityManagerReady
         }
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 788a28c..9ef793d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -232,6 +232,7 @@
     private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 100;
     private static final int MSG_SET_A2DP_SRC_CONNECTION_STATE = 101;
     private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102;
+    private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103;
     // end of messages handled under wakelock
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -3183,7 +3184,7 @@
                             queueMsgUnderWakeLock(mAudioHandler,
                                     MSG_SET_A2DP_SINK_CONNECTION_STATE,
                                     state,
-                                    0,
+                                    0 /* arg2 unused */,
                                     btDevice,
                                     delay);
                         }
@@ -3200,7 +3201,7 @@
                         queueMsgUnderWakeLock(mAudioHandler,
                                 MSG_SET_A2DP_SRC_CONNECTION_STATE,
                                 state,
-                                0,
+                                0 /* arg2 unused */,
                                 btDevice,
                                 0 /* delay */);
                     }
@@ -3848,8 +3849,8 @@
             int delay = checkSendBecomingNoisyIntent(type, state);
             queueMsgUnderWakeLock(mAudioHandler,
                     MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
-                    0,
-                    0,
+                    0 /* arg1 unused */,
+                    0 /* arg2 unused */,
                     new WiredDeviceConnectionState(type, state, address, name, caller),
                     delay);
         }
@@ -3872,13 +3873,25 @@
                     (profile == BluetoothProfile.A2DP ?
                         MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
                     state,
-                    0,
+                    0 /* arg2 unused */,
                     device,
                     delay);
         }
         return delay;
     }
 
+    public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device)
+    {
+        synchronized (mConnectedDevices) {
+            queueMsgUnderWakeLock(mAudioHandler,
+                    MSG_A2DP_DEVICE_CONFIG_CHANGE,
+                    0 /* arg1 unused */,
+                    0 /* arg1 unused */,
+                    device,
+                    0 /* delay */);
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Inner classes
     ///////////////////////////////////////////////////////////////////////////
@@ -4691,6 +4704,11 @@
                     mAudioEventWakeLock.release();
                     break;
 
+                case MSG_A2DP_DEVICE_CONFIG_CHANGE:
+                    onBluetoothA2dpDeviceConfigChange((BluetoothDevice)msg.obj);
+                    mAudioEventWakeLock.release();
+                    break;
+
                 case MSG_REPORT_NEW_ROUTES: {
                     int N = mRoutesObservers.beginBroadcast();
                     if (N > 0) {
@@ -4913,7 +4931,7 @@
     private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state)
     {
         if (DEBUG_VOL) {
-            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice="+btDevice+"state="+state);
+            Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice+"state=" + state);
         }
         if (btDevice == null) {
             return;
@@ -4924,9 +4942,9 @@
         }
 
         synchronized (mConnectedDevices) {
-            String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                                           btDevice.getAddress());
-            DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+            final String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                                                 btDevice.getAddress());
+            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
             boolean isConnected = deviceSpec != null;
 
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
@@ -4977,7 +4995,7 @@
     private void onSetA2dpSourceConnectionState(BluetoothDevice btDevice, int state)
     {
         if (DEBUG_VOL) {
-            Log.d(TAG, "onSetA2dpSourceConnectionState btDevice="+btDevice+" state="+state);
+            Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state=" + state);
         }
         if (btDevice == null) {
             return;
@@ -4988,8 +5006,8 @@
         }
 
         synchronized (mConnectedDevices) {
-            String key = makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
-            DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+            final String key = makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
+            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
             boolean isConnected = deviceSpec != null;
 
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
@@ -5000,6 +5018,31 @@
         }
     }
 
+    private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice)
+    {
+        if (DEBUG_VOL) {
+            Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
+        }
+        if (btDevice == null) {
+            return;
+        }
+        String address = btDevice.getAddress();
+        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+            address = "";
+        }
+
+        int device = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+        synchronized (mConnectedDevices) {
+            final String key = makeDeviceListKey(device, address);
+            final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+            if (deviceSpec != null) {
+                // Device is connected
+                AudioSystem.handleDeviceConfigChange(device, address,
+                        btDevice.getName());
+            }
+        }
+    }
+
     public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
         // address is not used for now, but may be used when multiple a2dp devices are supported
         synchronized (mA2dpAvrcpLock) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 4526ab74..7c5550a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -250,7 +250,8 @@
     @ServiceThreadOnly
     boolean dispatchMessage(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (mService.isPowerStandby() && mStandbyHandler.handleCommand(message)) {
+        if (mService.isPowerStandby() && !mService.isWakeUpMessageReceived()
+                && mStandbyHandler.handleCommand(message)) {
             return true;
         }
         return super.onMessage(message);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 18f1b6c..4c6b832 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -967,6 +967,9 @@
     }
 
     void setAudioStatus(boolean mute, int volume) {
+        if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated()) {
+            return;
+        }
         AudioManager audioManager = getAudioManager();
         boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
         if (mute) {
@@ -2014,6 +2017,10 @@
         // the intent, the sequence will continue at onStandby().
     }
 
+    boolean isWakeUpMessageReceived() {
+        return mWakeUpMessageReceived;
+    }
+
     @ServiceThreadOnly
     private void onWakeUp() {
         assertRunOnServiceThread();
@@ -2116,7 +2123,6 @@
             device.onStandby(mStandbyMessageReceived, standbyAction);
         }
         mStandbyMessageReceived = false;
-        mAddressAllocated = false;
         mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
         mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 039fc50..aaba00a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -270,6 +270,7 @@
             new ArrayList<NotificationRecord>();
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
             new ArrayMap<String, NotificationRecord>();
+    final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
     final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
@@ -1614,35 +1615,60 @@
             int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), incomingUserId, true, false,
                     "getAppActiveNotifications", pkg);
-
-            final ArrayList<StatusBarNotification> list
-                    = new ArrayList<StatusBarNotification>(mNotificationList.size());
+            final ArrayMap<String, StatusBarNotification> map
+                    = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
 
             synchronized (mNotificationList) {
                 final int N = mNotificationList.size();
                 for (int i = 0; i < N; i++) {
-                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
-                    if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
-                            && (sbn.getNotification().flags
-                            & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
-                        // We could pass back a cloneLight() but clients might get confused and
-                        // try to send this thing back to notify() again, which would not work
-                        // very well.
-                        final StatusBarNotification sbnOut = new StatusBarNotification(
-                                sbn.getPackageName(),
-                                sbn.getOpPkg(),
-                                sbn.getNotificationChannel(),
-                                sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
-                                sbn.getNotification().clone(),
-                                sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
-                        list.add(sbnOut);
+                    StatusBarNotification sbn = sanitizeSbn(pkg, userId,
+                            mNotificationList.get(i).sbn);
+                    if (sbn != null) {
+                        map.put(sbn.getKey(), sbn);
+                    }
+                }
+                for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
+                    StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
+                    if (sbn != null) {
+                        map.put(sbn.getKey(), sbn);
+                    }
+                }
+            }
+            synchronized (mEnqueuedNotifications) {
+                final int N = mEnqueuedNotifications.size();
+                for (int i = 0; i < N; i++) {
+                    StatusBarNotification sbn = sanitizeSbn(pkg, userId,
+                            mEnqueuedNotifications.get(i).sbn);
+                    if (sbn != null) {
+                        map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
                     }
                 }
             }
 
+            final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
+            list.addAll(map.values());
             return new ParceledListSlice<StatusBarNotification>(list);
         }
 
+        private StatusBarNotification sanitizeSbn(String pkg, int userId,
+                StatusBarNotification sbn) {
+            if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
+                    && (sbn.getNotification().flags
+                    & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
+                // We could pass back a cloneLight() but clients might get confused and
+                // try to send this thing back to notify() again, which would not work
+                // very well.
+                return new StatusBarNotification(
+                        sbn.getPackageName(),
+                        sbn.getOpPkg(),
+                        sbn.getNotificationChannel(),
+                        sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+                        sbn.getNotification().clone(),
+                        sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+            }
+            return null;
+        }
+
         /**
          * System-only API for getting a list of recent (cleared, no longer shown) notifications.
          *
@@ -2835,6 +2861,9 @@
 
         // setup local book-keeping
         final NotificationRecord r = new NotificationRecord(getContext(), n);
+        synchronized (mEnqueuedNotifications) {
+            mEnqueuedNotifications.add(r);
+        }
         mHandler.post(new EnqueueNotificationRunnable(userId, r));
 
         idOut[0] = id;
@@ -2851,121 +2880,126 @@
 
         @Override
         public void run() {
-
-            synchronized (mNotificationList) {
-                if (mSnoozeHelper.isSnoozed(userId, r.sbn.getPackageName(), r.getKey())) {
-                    // TODO: log to event log
-                    if (DBG) {
-                        Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
-                    }
-                    mSnoozeHelper.update(userId, r);
-                    savePolicyFile();
-                    return;
-                }
-
-                final StatusBarNotification n = r.sbn;
-                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
-                NotificationRecord old = mNotificationsByKey.get(n.getKey());
-                if (old != null) {
-                    // Retain ranking information from previous record
-                    r.copyRankingInformation(old);
-                }
-
-                final int callingUid = n.getUid();
-                final int callingPid = n.getInitialPid();
-                final Notification notification = n.getNotification();
-                final String pkg = n.getPackageName();
-                final int id = n.getId();
-                final String tag = n.getTag();
-                final boolean isSystemNotification = isUidSystem(callingUid) ||
-                        ("android".equals(pkg));
-
-                // Handle grouped notifications and bail out early if we
-                // can to avoid extracting signals.
-                handleGroupedNotificationLocked(r, old, callingUid, callingPid);
-
-                // This conditional is a dirty hack to limit the logging done on
-                //     behalf of the download manager without affecting other apps.
-                if (!pkg.equals("com.android.providers.downloads")
-                        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
-                    int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
-                    if (old != null) {
-                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
-                    }
-                    EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
-                            pkg, id, tag, userId, notification.toString(),
-                            enqueueStatus);
-                }
-
-                mRankingHelper.extractSignals(r);
-
-                // blocked apps
-                if (isBlocked(r, mUsageStats)) {
-                    return;
-                }
-
-                // tell the assistant service about the notification
-                if (mNotificationAssistants.isEnabled()) {
-                    mNotificationAssistants.onNotificationEnqueued(r);
-                    // TODO delay the code below here for 100ms or until there is an answer
-                }
-
-
-                int index = indexOfNotificationLocked(n.getKey());
-                if (index < 0) {
-                    mNotificationList.add(r);
-                    mUsageStats.registerPostedByApp(r);
-                } else {
-                    old = mNotificationList.get(index);
-                    mNotificationList.set(index, r);
-                    mUsageStats.registerUpdatedByApp(r, old);
-                    // Make sure we don't lose the foreground service state.
-                    notification.flags |=
-                            old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
-                    r.isUpdate = true;
-                }
-
-                mNotificationsByKey.put(n.getKey(), r);
-
-                // Ensure if this is a foreground service that the proper additional
-                // flags are set.
-                if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-                    notification.flags |= Notification.FLAG_ONGOING_EVENT
-                            | Notification.FLAG_NO_CLEAR;
-                }
-
-                applyZenModeLocked(r);
-                mRankingHelper.sort(mNotificationList);
-
-                if (notification.getSmallIcon() != null) {
-                    StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
-                    mListeners.notifyPostedLocked(n, oldSbn);
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            mGroupHelper.onNotificationPosted(n);
+            try {
+                synchronized (mNotificationList) {
+                    if (mSnoozeHelper.isSnoozed(userId, r.sbn.getPackageName(), r.getKey())) {
+                        // TODO: log to event log
+                        if (DBG) {
+                            Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
                         }
-                    });
-                } else {
-                    Slog.e(TAG, "Not posting notification without small icon: " + notification);
-                    if (old != null && !old.isCanceled) {
-                        mListeners.notifyRemovedLocked(n,
-                                NotificationListenerService.REASON_DELEGATE_ERROR);
+                        mSnoozeHelper.update(userId, r);
+                        savePolicyFile();
+                        return;
+                    }
+
+                    final StatusBarNotification n = r.sbn;
+                    if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
+                    NotificationRecord old = mNotificationsByKey.get(n.getKey());
+                    if (old != null) {
+                        // Retain ranking information from previous record
+                        r.copyRankingInformation(old);
+                    }
+
+                    final int callingUid = n.getUid();
+                    final int callingPid = n.getInitialPid();
+                    final Notification notification = n.getNotification();
+                    final String pkg = n.getPackageName();
+                    final int id = n.getId();
+                    final String tag = n.getTag();
+                    final boolean isSystemNotification = isUidSystem(callingUid) ||
+                            ("android".equals(pkg));
+
+                    // Handle grouped notifications and bail out early if we
+                    // can to avoid extracting signals.
+                    handleGroupedNotificationLocked(r, old, callingUid, callingPid);
+
+                    // This conditional is a dirty hack to limit the logging done on
+                    //     behalf of the download manager without affecting other apps.
+                    if (!pkg.equals("com.android.providers.downloads")
+                            || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
+                        int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
+                        if (old != null) {
+                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
+                        }
+                        EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
+                                pkg, id, tag, userId, notification.toString(),
+                                enqueueStatus);
+                    }
+
+                    mRankingHelper.extractSignals(r);
+
+                    // blocked apps
+                    if (isBlocked(r, mUsageStats)) {
+                        return;
+                    }
+
+                    // tell the assistant service about the notification
+                    if (mNotificationAssistants.isEnabled()) {
+                        mNotificationAssistants.onNotificationEnqueued(r);
+                        // TODO delay the code below here for 100ms or until there is an answer
+                    }
+
+
+                    int index = indexOfNotificationLocked(n.getKey());
+                    if (index < 0) {
+                        mNotificationList.add(r);
+                        mUsageStats.registerPostedByApp(r);
+                    } else {
+                        old = mNotificationList.get(index);
+                        mNotificationList.set(index, r);
+                        mUsageStats.registerUpdatedByApp(r, old);
+                        // Make sure we don't lose the foreground service state.
+                        notification.flags |=
+                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+                        r.isUpdate = true;
+                    }
+
+                    mNotificationsByKey.put(n.getKey(), r);
+
+                    // Ensure if this is a foreground service that the proper additional
+                    // flags are set.
+                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+                        notification.flags |= Notification.FLAG_ONGOING_EVENT
+                                | Notification.FLAG_NO_CLEAR;
+                    }
+
+                    applyZenModeLocked(r);
+                    mRankingHelper.sort(mNotificationList);
+
+                    if (notification.getSmallIcon() != null) {
+                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
+                        mListeners.notifyPostedLocked(n, oldSbn);
                         mHandler.post(new Runnable() {
                             @Override
                             public void run() {
-                                mGroupHelper.onNotificationRemoved(n);
+                                mGroupHelper.onNotificationPosted(n);
                             }
                         });
+                    } else {
+                        Slog.e(TAG, "Not posting notification without small icon: " + notification);
+                        if (old != null && !old.isCanceled) {
+                            mListeners.notifyRemovedLocked(n,
+                                    NotificationListenerService.REASON_DELEGATE_ERROR);
+                            mHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    mGroupHelper.onNotificationRemoved(n);
+                                }
+                            });
+                        }
+                        // ATTENTION: in a future release we will bail out here
+                        // so that we do not play sounds, show lights, etc. for invalid
+                        // notifications
+                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
+                                + n.getPackageName());
                     }
-                    // ATTENTION: in a future release we will bail out here
-                    // so that we do not play sounds, show lights, etc. for invalid
-                    // notifications
-                    Slog.e(TAG, "WARNING: In a future release this will crash the app: "
-                            + n.getPackageName());
-                }
 
-                buzzBeepBlinkLocked(r);
+                    buzzBeepBlinkLocked(r);
+                }
+            } finally {
+                synchronized (mEnqueuedNotifications) {
+                    mEnqueuedNotifications.remove(r);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 409eef4..733ff67 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -36,6 +36,9 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Map;
 import java.util.Objects;
@@ -84,6 +87,14 @@
                 && mSnoozedNotifications.get(userId).get(pkg).containsKey(key);
     }
 
+    protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
+        if (mSnoozedNotifications.containsKey(userId)
+                && mSnoozedNotifications.get(userId).containsKey(pkg)) {
+            mSnoozedNotifications.get(userId).get(pkg).values();
+        }
+        return Collections.EMPTY_LIST;
+    }
+
     /**
      * Snoozes a notification and schedules an alarm to repost at that time.
      */
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1f83d9e..605fa5d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -54,6 +54,7 @@
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
     public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+    public static final int FLAG_USE_QUOTA = 1 << 12;
 
     private final boolean mIsolated;
 
@@ -198,12 +199,13 @@
         }
     }
 
-    public void getAppSize(String uuid, String packageName, int userId, int flags, long ceDataInode,
-            String codePath, PackageStats stats) throws InstallerException {
+    public void getAppSize(String uuid, String packageName, int userId, int flags, int appId,
+            long ceDataInode, String codePath, String externalUuid, PackageStats stats)
+            throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, ceDataInode,
-                    codePath);
+            final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, appId,
+                    ceDataInode, codePath, externalUuid);
             stats.codeSize += res[0];
             stats.dataSize += res[1];
             stats.cacheSize += res[2];
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c85e1d8..d1aed3e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -703,8 +703,11 @@
         for (File addedFile : addedFiles) {
             final ApkLite apk;
             try {
-                apk = PackageParser.parseApkLite(
-                        addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+                int flags = PackageParser.PARSE_COLLECT_CERTIFICATES;
+                if ((params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
+                    flags |= PackageParser.PARSE_IS_EPHEMERAL;
+                }
+                apk = PackageParser.parseApkLite(addedFile, flags);
             } catch (PackageParserException e) {
                 throw PackageManagerException.from(e);
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 88c5a17..2dd9503 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -381,6 +381,9 @@
     private static final boolean DISABLE_EPHEMERAL_APPS = false;
     private static final boolean HIDE_EPHEMERAL_APIS = true;
 
+    private static final boolean ENABLE_QUOTA =
+            SystemProperties.getBoolean("persist.fw.quota", false);
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -17392,6 +17395,11 @@
         mHandler.sendMessage(msg);
     }
 
+    private boolean equals(PackageStats a, PackageStats b) {
+        return (a.codeSize == b.codeSize) && (a.dataSize == b.dataSize)
+                && (a.cacheSize == b.cacheSize);
+    }
+
     private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
         final PackageSetting ps;
         synchronized (mPackages) {
@@ -17401,20 +17409,51 @@
                 return false;
             }
         }
+
+        final long ceDataInode = ps.getCeDataInode(userId);
+        final PackageStats quotaStats = new PackageStats(stats.packageName, stats.userHandle);
+
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        final String externalUuid = storage.getPrimaryStorageUuid();
         try {
-            mInstaller.getAppSize(ps.volumeUuid, packageName, userId,
-                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE,
-                    ps.getCeDataInode(userId), ps.codePathString, stats);
+            final long start = SystemClock.elapsedRealtimeNanos();
+            mInstaller.getAppSize(ps.volumeUuid, packageName, userId, 0,
+                    ps.appId, ceDataInode, ps.codePathString, externalUuid, stats);
+            final long stopManual = SystemClock.elapsedRealtimeNanos();
+            if (ENABLE_QUOTA) {
+                mInstaller.getAppSize(ps.volumeUuid, packageName, userId, Installer.FLAG_USE_QUOTA,
+                        ps.appId, ceDataInode, ps.codePathString, externalUuid, quotaStats);
+            }
+            final long stopQuota = SystemClock.elapsedRealtimeNanos();
+
+            // For now, ignore code size of packages on system partition
+            if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
+                stats.codeSize = 0;
+                quotaStats.codeSize = 0;
+            }
+
+            if (ENABLE_QUOTA && Build.IS_ENG && !ps.isSharedUser()) {
+                if (!equals(stats, quotaStats)) {
+                    Log.w(TAG, "Found discrepancy between statistics:");
+                    Log.w(TAG, "Manual: " + stats);
+                    Log.w(TAG, "Quota:  " + quotaStats);
+                }
+                final long manualTime = stopManual - start;
+                final long quotaTime = stopQuota - stopManual;
+                EventLogTags.writePmPackageStats(manualTime, quotaTime,
+                        stats.dataSize, quotaStats.dataSize,
+                        stats.cacheSize, quotaStats.cacheSize);
+            }
+
+            // External clients expect these to be tracked separately
+            stats.dataSize -= stats.cacheSize;
+            quotaStats.dataSize -= quotaStats.cacheSize;
+
         } catch (InstallerException e) {
             Slog.w(TAG, String.valueOf(e));
             return false;
         }
 
-        // For now, ignore code size of packages on system partition
-        if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
-            stats.codeSize = 0;
-        }
-
         return true;
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9737b36..22f9f3a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -544,8 +544,10 @@
             if (Intent.ACTION_USER_UNLOCKED.equals(action)
                     || Intent.ACTION_USER_STARTED.equals(action)
                     || KeyChain.ACTION_TRUST_STORE_CHANGED.equals(action)) {
-                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
-                new MonitoringCertNotificationTask().execute(userId);
+                if (!StorageManager.inCryptKeeperBounce()) {
+                    new MonitoringCertNotificationTask().execute(
+                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL));
+                }
             }
             if (Intent.ACTION_USER_ADDED.equals(action)) {
                 sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
@@ -5978,8 +5980,9 @@
             throw new IllegalArgumentException("Invalid component " + admin
                     + " for device owner");
         }
+        final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userId, admin);
         synchronized (this) {
-            enforceCanSetDeviceOwnerLocked(admin, userId);
+            enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccounts);
             final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
             if (activeAdmin == null
                     || getUserData(userId).mRemovingAdmins.contains(admin)) {
@@ -6215,8 +6218,9 @@
             throw new IllegalArgumentException("Component " + who
                     + " not installed for userId:" + userHandle);
         }
+        final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userHandle, who);
         synchronized (this) {
-            enforceCanSetProfileOwnerLocked(who, userHandle);
+            enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccounts);
 
             if (getActiveAdminUncheckedLocked(who, userHandle) == null
                     || getUserData(userHandle).mRemovingAdmins.contains(who)) {
@@ -6549,9 +6553,10 @@
      * The profile owner can only be set before the user setup phase has completed,
      * except for:
      * - SYSTEM_UID
-     * - adb if there are no accounts. (But see {@link #hasIncompatibleAccountsLocked})
+     * - adb unless hasIncompatibleAccounts is true.
      */
-    private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) {
+    private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle,
+            boolean hasIncompatibleAccounts) {
         UserInfo info = getUserInfo(userHandle);
         if (info == null) {
             // User doesn't exist.
@@ -6571,7 +6576,7 @@
         }
         if (isAdb()) {
             if ((mIsWatch || hasUserSetupCompleted(userHandle))
-                    && hasIncompatibleAccountsLocked(userHandle, owner)) {
+                    && hasIncompatibleAccounts) {
                 throw new IllegalStateException("Not allowed to set the profile owner because "
                         + "there are already some accounts on the profile");
             }
@@ -6588,12 +6593,14 @@
      * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
      * permission.
      */
-    private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId) {
+    private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId,
+            boolean hasIncompatibleAccounts) {
         if (!isAdb()) {
             enforceCanManageProfileAndDeviceOwners();
         }
 
-        final int code = checkDeviceOwnerProvisioningPreConditionLocked(owner, userId, isAdb());
+        final int code = checkDeviceOwnerProvisioningPreConditionLocked(
+                owner, userId, isAdb(), hasIncompatibleAccounts);
         switch (code) {
             case CODE_OK:
                 return;
@@ -8902,7 +8909,7 @@
      * except for adb command if no accounts or additional users are present on the device.
      */
     private int checkDeviceOwnerProvisioningPreConditionLocked(@Nullable ComponentName owner,
-            int deviceOwnerUserId, boolean isAdb) {
+            int deviceOwnerUserId, boolean isAdb, boolean hasIncompatibleAccounts) {
         if (mOwners.hasDeviceOwner()) {
             return CODE_HAS_DEVICE_OWNER;
         }
@@ -8922,7 +8929,7 @@
                     if (mUserManager.getUserCount() > 1) {
                         return CODE_NONSYSTEM_USER_EXISTS;
                     }
-                    if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) {
+                    if (hasIncompatibleAccounts) {
                         return CODE_ACCOUNTS_NOT_EMPTY;
                     }
                 } else {
@@ -8949,8 +8956,9 @@
 
     private int checkDeviceOwnerProvisioningPreCondition(int deviceOwnerUserId) {
         synchronized (this) {
+            // hasIncompatibleAccounts doesn't matter since the caller is not adb.
             return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null,
-                    deviceOwnerUserId, /* isAdb= */ false);
+                    deviceOwnerUserId, /* isAdb= */ false, /* hasIncompatibleAccounts=*/ true);
         }
     }
 
@@ -9848,8 +9856,15 @@
      * - Otherwise, if there's any account that does not have ..._ALLOWED, or does have
      *   ..._DISALLOWED, return true.
      * - Otherwise return false.
+     *
+     * DO NOT CALL IT WITH THE DPMS LOCK HELD.
      */
-    private boolean hasIncompatibleAccountsLocked(int userId, @Nullable ComponentName owner) {
+    private boolean hasIncompatibleAccountsNoLock(int userId, @Nullable ComponentName owner) {
+        if (Thread.holdsLock(this)) {
+            Slog.wtf(LOG_TAG, "hasIncompatibleAccountsNoLock() called with the DPMS lock held.");
+            return true;
+        }
+
         final long token = mInjector.binderClearCallingIdentity();
         try {
             final AccountManager am = AccountManager.get(mContext);
@@ -9857,22 +9872,30 @@
             if (accounts.length == 0) {
                 return false;
             }
+            synchronized (this) {
+                if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
+                    Log.w(LOG_TAG,
+                            "Non test-only owner can't be installed with existing accounts.");
+                    return true;
+                }
+            }
+
             final String[] feature_allow =
                     { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
             final String[] feature_disallow =
                     { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
 
-            // Even if we find incompatible accounts along the way, we still check all accounts
-            // for logging.
             boolean compatible = true;
             for (Account account : accounts) {
                 if (hasAccountFeatures(am, account, feature_disallow)) {
                     Log.e(LOG_TAG, account + " has " + feature_disallow[0]);
                     compatible = false;
+                    break;
                 }
                 if (!hasAccountFeatures(am, account, feature_allow)) {
                     Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]);
                     compatible = false;
+                    break;
                 }
             }
             if (compatible) {
@@ -9880,28 +9903,6 @@
             } else {
                 Log.e(LOG_TAG, "Found incompatible accounts");
             }
-
-            // Then check if the owner is test-only.
-            String log;
-            if (owner == null) {
-                // Owner is unknown.  Suppose it's not test-only
-                compatible = false;
-                log = "Only test-only device/profile owner can be installed with accounts";
-            } else if (isAdminTestOnlyLocked(owner, userId)) {
-                if (compatible) {
-                    log = "Installing test-only owner " + owner;
-                } else {
-                    log = "Can't install test-only owner " + owner + " with incompatible accounts";
-                }
-            } else {
-                compatible = false;
-                log = "Can't install non test-only owner " + owner + " with accounts";
-            }
-            if (compatible) {
-                Log.w(LOG_TAG, log);
-            } else {
-                Log.e(LOG_TAG, log);
-            }
             return !compatible;
         } finally {
             mInjector.binderRestoreCallingIdentity(token);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index be13499..7ccae2c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -710,10 +710,12 @@
                 traceEnd();
             }
 
+            traceBeginAndSlog("SetWindowManagerService");
             mActivityManagerService.setWindowManager(wm);
+            traceEnd();
 
-            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
             traceBeginAndSlog("StartInputManager");
+            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
             inputManager.start();
             traceEnd();
 
@@ -1238,8 +1240,10 @@
             }
 
             if (!disableNetwork && !disableNonCoreServices && EmergencyAffordanceManager.ENABLED) {
-                // EmergencyMode sevice
+                // EmergencyMode service
+                traceBeginAndSlog("StartEmergencyAffordanceService");
                 mSystemServiceManager.startService(EmergencyAffordanceService.class);
+                traceEnd();
             }
 
             if (!disableNonCoreServices) {
@@ -1482,176 +1486,167 @@
         // where third party code can really run (but before it has actually
         // started launching the initial applications), for us to complete our
         // initialization.
-        mActivityManagerService.systemReady(new Runnable() {
-            @Override
-            public void run() {
-                Slog.i(TAG, "Making services ready");
-                traceBeginAndSlog("StartActivityManagerReadyPhase");
-                mSystemServiceManager.startBootPhase(
-                        SystemService.PHASE_ACTIVITY_MANAGER_READY);
-                traceEnd();
-                traceBeginAndSlog("PhaseActivityManagerReady");
-
-                traceBeginAndSlog("StartObservingNativeCrashes");
-                try {
-                    mActivityManagerService.startObservingNativeCrashes();
-                } catch (Throwable e) {
-                    reportWtf("observing native crashes", e);
-                }
-                traceEnd();
-
-                if (!mOnlyCore) {
-                    Slog.i(TAG, "WebViewFactory preparation");
-                    traceBeginAndSlog("WebViewFactoryPreparation");
-                    mWebViewUpdateService.prepareWebViewInSystemServer();
-                    traceEnd();
-                }
-
-                traceBeginAndSlog("StartSystemUI");
-                try {
-                    startSystemUi(context);
-                } catch (Throwable e) {
-                    reportWtf("starting System UI", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeNetworkScoreReady");
-                try {
-                    if (networkScoreF != null) networkScoreF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Network Score Service ready", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeNetworkManagementServiceReady");
-                try {
-                    if (networkManagementF != null) networkManagementF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Network Managment Service ready", e);
-                }
-                CountDownLatch networkPolicyInitReadySignal = null;
-                if (networkPolicyF != null) {
-                    networkPolicyInitReadySignal = networkPolicyF
-                            .networkScoreAndNetworkManagementServiceReady();
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeNetworkStatsServiceReady");
-                try {
-                    if (networkStatsF != null) networkStatsF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Network Stats Service ready", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeConnectivityServiceReady");
-                try {
-                    if (connectivityF != null) connectivityF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Connectivity Service ready", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeNetworkPolicyServiceReady");
-                try {
-                    if (networkPolicyF != null) {
-                        networkPolicyF.systemReady(networkPolicyInitReadySignal);
-                    }
-                } catch (Throwable e) {
-                    reportWtf("making Network Policy Service ready", e);
-                }
-                traceEnd();
-
-                traceBeginAndSlog("StartWatchdog");
-                Watchdog.getInstance().start();
-                traceEnd();
-
-                // It is now okay to let the various system services start their
-                // third party code...
-                traceBeginAndSlog("PhaseThirdPartyAppsCanStart");
-                mSystemServiceManager.startBootPhase(
-                        SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-                traceEnd();
-
-                traceBeginAndSlog("MakeLocationServiceReady");
-                try {
-                    if (locationF != null) locationF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying Location Service running", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeCountryDetectionServiceReady");
-                try {
-                    if (countryDetectorF != null) countryDetectorF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying CountryDetectorService running", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeNetworkTimeUpdateReady");
-                try {
-                    if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying NetworkTimeService running", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeCommonTimeManagementServiceReady");
-                try {
-                    if (commonTimeMgmtServiceF != null) {
-                        commonTimeMgmtServiceF.systemRunning();
-                    }
-                } catch (Throwable e) {
-                    reportWtf("Notifying CommonTimeManagementService running", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeInputManagerServiceReady");
-                try {
-                    // TODO(BT) Pass parameter to input manager
-                    if (inputManagerF != null) inputManagerF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying InputManagerService running", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeTelephonyRegistryReady");
-                try {
-                    if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying TelephonyRegistry running", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeMediaRouterServiceReady");
-                try {
-                    if (mediaRouterF != null) mediaRouterF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying MediaRouterService running", e);
-                }
-                traceEnd();
-                traceBeginAndSlog("MakeMmsServiceReady");
-                try {
-                    if (mmsServiceF != null) mmsServiceF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying MmsService running", e);
-                }
-                traceEnd();
-
-                traceBeginAndSlog("MakeNetworkScoreServiceReady");
-                try {
-                    if (networkScoreF != null) networkScoreF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying NetworkScoreService running", e);
-                }
-                traceEnd();
-
-                traceBeginAndSlog("IncidentDaemonReady");
-                try {
-                    // TODO: Switch from checkService to getService once it's always
-                    // in the build and should reliably be there.
-                    final IIncidentManager incident = IIncidentManager.Stub.asInterface(
-                            ServiceManager.checkService("incident"));
-                    if (incident != null) incident.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying incident daemon running", e);
-                }
-                traceEnd();
-                
-
-                traceEnd();  // PhaseActivityManagerReady
+        mActivityManagerService.systemReady(() -> {
+            Slog.i(TAG, "Making services ready");
+            traceBeginAndSlog("StartActivityManagerReadyPhase");
+            mSystemServiceManager.startBootPhase(
+                    SystemService.PHASE_ACTIVITY_MANAGER_READY);
+            traceEnd();
+            traceBeginAndSlog("StartObservingNativeCrashes");
+            try {
+                mActivityManagerService.startObservingNativeCrashes();
+            } catch (Throwable e) {
+                reportWtf("observing native crashes", e);
             }
-        });
+            traceEnd();
+
+            if (!mOnlyCore) {
+                Slog.i(TAG, "WebViewFactory preparation");
+                traceBeginAndSlog("WebViewFactoryPreparation");
+                mWebViewUpdateService.prepareWebViewInSystemServer();
+                traceEnd();
+            }
+
+            traceBeginAndSlog("StartSystemUI");
+            try {
+                startSystemUi(context);
+            } catch (Throwable e) {
+                reportWtf("starting System UI", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeNetworkScoreReady");
+            try {
+                if (networkScoreF != null) networkScoreF.systemReady();
+            } catch (Throwable e) {
+                reportWtf("making Network Score Service ready", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeNetworkManagementServiceReady");
+            try {
+                if (networkManagementF != null) networkManagementF.systemReady();
+            } catch (Throwable e) {
+                reportWtf("making Network Managment Service ready", e);
+            }
+            CountDownLatch networkPolicyInitReadySignal = null;
+            if (networkPolicyF != null) {
+                networkPolicyInitReadySignal = networkPolicyF
+                        .networkScoreAndNetworkManagementServiceReady();
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeNetworkStatsServiceReady");
+            try {
+                if (networkStatsF != null) networkStatsF.systemReady();
+            } catch (Throwable e) {
+                reportWtf("making Network Stats Service ready", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeConnectivityServiceReady");
+            try {
+                if (connectivityF != null) connectivityF.systemReady();
+            } catch (Throwable e) {
+                reportWtf("making Connectivity Service ready", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeNetworkPolicyServiceReady");
+            try {
+                if (networkPolicyF != null) {
+                    networkPolicyF.systemReady(networkPolicyInitReadySignal);
+                }
+            } catch (Throwable e) {
+                reportWtf("making Network Policy Service ready", e);
+            }
+            traceEnd();
+
+            traceBeginAndSlog("StartWatchdog");
+            Watchdog.getInstance().start();
+            traceEnd();
+
+            // It is now okay to let the various system services start their
+            // third party code...
+            traceBeginAndSlog("PhaseThirdPartyAppsCanStart");
+            mSystemServiceManager.startBootPhase(
+                    SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
+            traceEnd();
+
+            traceBeginAndSlog("MakeLocationServiceReady");
+            try {
+                if (locationF != null) locationF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying Location Service running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeCountryDetectionServiceReady");
+            try {
+                if (countryDetectorF != null) countryDetectorF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying CountryDetectorService running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeNetworkTimeUpdateReady");
+            try {
+                if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying NetworkTimeService running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeCommonTimeManagementServiceReady");
+            try {
+                if (commonTimeMgmtServiceF != null) {
+                    commonTimeMgmtServiceF.systemRunning();
+                }
+            } catch (Throwable e) {
+                reportWtf("Notifying CommonTimeManagementService running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeInputManagerServiceReady");
+            try {
+                // TODO(BT) Pass parameter to input manager
+                if (inputManagerF != null) inputManagerF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying InputManagerService running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeTelephonyRegistryReady");
+            try {
+                if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying TelephonyRegistry running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeMediaRouterServiceReady");
+            try {
+                if (mediaRouterF != null) mediaRouterF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying MediaRouterService running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("MakeMmsServiceReady");
+            try {
+                if (mmsServiceF != null) mmsServiceF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying MmsService running", e);
+            }
+            traceEnd();
+
+            traceBeginAndSlog("MakeNetworkScoreServiceReady");
+            try {
+                if (networkScoreF != null) networkScoreF.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying NetworkScoreService running", e);
+            }
+            traceEnd();
+            traceBeginAndSlog("IncidentDaemonReady");
+            try {
+                // TODO: Switch from checkService to getService once it's always
+                // in the build and should reliably be there.
+                final IIncidentManager incident = IIncidentManager.Stub.asInterface(
+                        ServiceManager.checkService("incident"));
+                if (incident != null) incident.systemRunning();
+            } catch (Throwable e) {
+                reportWtf("Notifying incident daemon running", e);
+            }
+            traceEnd();
+        }, BOOT_TIMINGS_TRACE_LOG);
     }
 
     static final void startSystemUi(Context context) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 69d27f2..9a9f81e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -53,6 +53,7 @@
 import android.net.INetworkRecommendationProvider;
 import android.net.INetworkScoreCache;
 import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppManager;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
 import android.net.RecommendationRequest;
@@ -134,9 +135,10 @@
 
         mNetworkScoreService.systemRunning();
 
-        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
-                new ComponentName(NEW_SCORER.packageName,
-                    NEW_SCORER.recommendationServiceClassName))),
+        verify(mContext).bindServiceAsUser(MockUtils.checkIntent(
+                new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+                        .setComponent(new ComponentName(NEW_SCORER.packageName,
+                                NEW_SCORER.recommendationServiceClassName))),
                 any(ServiceConnection.class),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                 eq(UserHandle.SYSTEM));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 1247b2d..65255d9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -16,6 +16,8 @@
 
 package com.android.server.devicepolicy;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.app.IActivityManager;
 import android.app.NotificationManager;
 import android.app.backup.IBackupManager;
@@ -273,6 +275,7 @@
     public final SettingsForMock settings;
     public final MockContentResolver contentResolver;
     public final TelephonyManager telephonyManager;
+    public final AccountManager accountManager;
 
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
@@ -315,6 +318,7 @@
         wifiManager = mock(WifiManager.class);
         settings = mock(SettingsForMock.class);
         telephonyManager = mock(TelephonyManager.class);
+        accountManager = mock(AccountManager.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(context.getPackageManager());
@@ -375,6 +379,7 @@
                     }
                 }
         );
+        when(accountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]);
 
         // Create a data directory.
         final File dir = new File(dataDir, "user" + userId);
@@ -464,6 +469,8 @@
                 return powerManager;
             case Context.WIFI_SERVICE:
                 return wifiManager;
+            case Context.ACCOUNT_SERVICE:
+                return accountManager;
         }
         throw new UnsupportedOperationException();
     }
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 989f72c..10cb7c9 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -239,6 +239,13 @@
      */
     public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57;
 
+    /**
+     * The network does not accept the emergency call request because IMEI was used as
+     * identification and this cability is not supported by the network.
+     * {@hide}
+     */
+    public static final int IMEI_NOT_ACCEPTED = 58;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Please assign the new type the next id value below.
@@ -247,14 +254,14 @@
     // 4) Update toString() with the newly added disconnect type.
     // 5) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause.
     //
-    // NextId: 58
+    // NextId: 59
     //*********************************************************************************************
 
     /** Smallest valid value for call disconnect codes. */
     public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
 
     /** Largest valid value for call disconnect codes. */
-    public static final int MAXIMUM_VALID_VALUE = DIALED_CALL_FORWARDING_WHILE_ROAMING;
+    public static final int MAXIMUM_VALID_VALUE = IMEI_NOT_ACCEPTED;
 
     /** Private constructor to avoid class instantiation. */
     private DisconnectCause() {
@@ -378,6 +385,8 @@
             return "DIALED_ON_WRONG_SLOT";
         case DIALED_CALL_FORWARDING_WHILE_ROAMING:
             return "DIALED_CALL_FORWARDING_WHILE_ROAMING";
+        case IMEI_NOT_ACCEPTED:
+            return "IMEI_NOT_ACCEPTED";
         default:
             return "INVALID: " + cause;
         }