Merge "Icon: a clean, parcelable place for images." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index f8d3ef3..09b2b6a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18457,6 +18457,7 @@
     method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
     method public void onLosing(android.net.Network, int);
     method public void onLost(android.net.Network);
+    method public void onPreCheck(android.net.Network);
   }
 
   public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -27182,7 +27183,8 @@
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final java.lang.String APN = "apn";
     field public static final java.lang.String AUTH_TYPE = "authtype";
-    field public static final java.lang.String BEARER = "bearer";
+    field public static final deprecated java.lang.String BEARER = "bearer";
+    field public static final java.lang.String BEARER_BITMASK = "bearer_bitmask";
     field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String CURRENT = "current";
@@ -30276,6 +30278,7 @@
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
     method public void registerCallback(android.telecom.Call.Callback);
+    method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
     method public void reject(boolean, java.lang.String);
     method public void splitFromConference();
     method public void stopDtmfTone();
@@ -30583,6 +30586,7 @@
   public static abstract class InCallService.VideoCall {
     ctor public InCallService.VideoCall();
     method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback, android.os.Handler);
     method public abstract void requestCallDataUsage();
     method public abstract void requestCameraCapabilities();
     method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
@@ -30593,7 +30597,7 @@
     method public abstract void setPauseImage(java.lang.String);
     method public abstract void setPreviewSurface(android.view.Surface);
     method public abstract void setZoom(float);
-    method public abstract void unregisterCallback();
+    method public abstract void unregisterCallback(android.telecom.InCallService.VideoCall.Callback);
   }
 
   public static abstract class InCallService.VideoCall.Callback {
@@ -30681,6 +30685,7 @@
     method public void merge();
     method public void playDtmfTone(char);
     method public final void registerCallback(android.telecom.RemoteConference.Callback);
+    method public final void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
     method public void separate(android.telecom.RemoteConnection);
     method public void setAudioState(android.telecom.AudioState);
     method public void stopDtmfTone();
@@ -30720,6 +30725,7 @@
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
     method public void registerCallback(android.telecom.RemoteConnection.Callback);
+    method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler);
     method public void reject();
     method public void setAudioState(android.telecom.AudioState);
     method public void stopDtmfTone();
diff --git a/api/system-current.txt b/api/system-current.txt
index 87f53dc..3e84fd4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19925,6 +19925,7 @@
     method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
     method public void onLosing(android.net.Network, int);
     method public void onLost(android.net.Network);
+    method public void onPreCheck(android.net.Network);
   }
 
   public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -29195,7 +29196,8 @@
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final java.lang.String APN = "apn";
     field public static final java.lang.String AUTH_TYPE = "authtype";
-    field public static final java.lang.String BEARER = "bearer";
+    field public static final deprecated java.lang.String BEARER = "bearer";
+    field public static final java.lang.String BEARER_BITMASK = "bearer_bitmask";
     field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String CURRENT = "current";
@@ -32390,6 +32392,7 @@
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
     method public void registerCallback(android.telecom.Call.Callback);
+    method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
     method public void reject(boolean, java.lang.String);
     method public deprecated void removeListener(android.telecom.Call.Listener);
     method public void splitFromConference();
@@ -32706,6 +32709,7 @@
   public static abstract class InCallService.VideoCall {
     ctor public InCallService.VideoCall();
     method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback, android.os.Handler);
     method public abstract void requestCallDataUsage();
     method public abstract void requestCameraCapabilities();
     method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
@@ -32716,7 +32720,7 @@
     method public abstract void setPauseImage(java.lang.String);
     method public abstract void setPreviewSurface(android.view.Surface);
     method public abstract void setZoom(float);
-    method public abstract void unregisterCallback();
+    method public abstract void unregisterCallback(android.telecom.InCallService.VideoCall.Callback);
   }
 
   public static abstract class InCallService.VideoCall.Callback {
@@ -32824,6 +32828,7 @@
     method public void merge();
     method public void playDtmfTone(char);
     method public final void registerCallback(android.telecom.RemoteConference.Callback);
+    method public final void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
     method public void separate(android.telecom.RemoteConnection);
     method public void setAudioState(android.telecom.AudioState);
     method public void stopDtmfTone();
@@ -32863,6 +32868,7 @@
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
     method public void registerCallback(android.telecom.RemoteConnection.Callback);
+    method public void registerCallback(android.telecom.RemoteConnection.Callback, android.os.Handler);
     method public void reject();
     method public void setAudioState(android.telecom.AudioState);
     method public void stopDtmfTone();
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 9d1d312..b0fda9c 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -26,6 +26,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.text.TextUtils;
+import libcore.util.ZoneInfoDB;
+
+import java.io.IOException;
 
 /**
  * This class provides access to the system alarm services.  These allow you
@@ -151,6 +155,7 @@
 
     private final IAlarmManager mService;
     private final boolean mAlwaysExact;
+    private final int mTargetSdkVersion;
 
 
     /**
@@ -159,8 +164,8 @@
     AlarmManager(IAlarmManager service, Context ctx) {
         mService = service;
 
-        final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
-        mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
+        mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
+        mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT);
     }
 
     private long legacyExactLength() {
@@ -585,12 +590,38 @@
     }
 
     /**
-     * Set the system default time zone.
-     * Requires the permission android.permission.SET_TIME_ZONE.
+     * Sets the system's persistent default time zone. This is the time zone for all apps, even
+     * after a reboot. Use {@link java.util.TimeZone#setDefault} if you just want to change the
+     * time zone within your app, and even then prefer to pass an explicit
+     * {@link java.util.TimeZone} to APIs that require it rather than changing the time zone for
+     * all threads.
      *
-     * @param timeZone in the format understood by {@link java.util.TimeZone}
+     * <p> On android M and above, it is an error to pass in a non-Olson timezone to this
+     * function. Note that this is a bad idea on all Android releases because POSIX and
+     * the {@code TimeZone} class have opposite interpretations of {@code '+'} and {@code '-'}
+     * in the same non-Olson ID.
+     *
+     * @param timeZone one of the Olson ids from the list returned by
+     *     {@link java.util.TimeZone#getAvailableIDs}
      */
     public void setTimeZone(String timeZone) {
+        if (TextUtils.isEmpty(timeZone)) {
+            return;
+        }
+
+        // Reject this timezone if it isn't an Olson zone we recognize.
+        if (mTargetSdkVersion >= Build.VERSION_CODES.MNC) {
+            boolean hasTimeZone = false;
+            try {
+                hasTimeZone = ZoneInfoDB.getInstance().hasTimeZone(timeZone);
+            } catch (IOException ignored) {
+            }
+
+            if (!hasTimeZone) {
+                throw new IllegalArgumentException("Timezone: " + timeZone + " is not an Olson ID");
+            }
+        }
+
         try {
             mService.setTimeZone(timeZone);
         } catch (RemoteException ex) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cf9813f..9f71ea5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2926,7 +2926,7 @@
      *         the user has already been set up.
      */
     @SystemApi
-    public boolean setActiveProfileOwner(ComponentName admin, String ownerName)
+    public boolean setActiveProfileOwner(ComponentName admin, @Deprecated String ownerName)
             throws IllegalArgumentException {
         if (mService != null) {
             try {
@@ -2992,8 +2992,8 @@
      * @throws IllegalArgumentException if admin is null, the package isn't installed, or the
      * preconditions mentioned are not met.
      */
-    public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle)
-            throws IllegalArgumentException {
+    public boolean setProfileOwner(ComponentName admin, @Deprecated String ownerName,
+            int userHandle) throws IllegalArgumentException {
         if (admin == null) {
             throw new NullPointerException("admin cannot be null");
         }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 3044a94..9b4dcc6 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -34,6 +34,7 @@
 import android.os.ServiceManager;
 import android.app.ActivityThread;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.os.Binder;
 import android.util.Log;
 import android.util.Pair;
@@ -389,7 +390,7 @@
      * @hide
      */
     public static final String ACTION_BLE_STATE_CHANGED =
-        "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED";
+        "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
 
     /**
      * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
@@ -632,24 +633,6 @@
     }
 
     /**
-     * Returns true if LE only mode is enabled, that is apps
-     * have authorization to turn only BT ON and the calling
-     * app has privilage to do so
-     */
-    private boolean isLEAlwaysOnEnabled() {
-        boolean ret = false;
-        if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) {
-            Log.v(TAG, "LE always on mode is enabled");
-            // TODO: System API authorization check
-            ret = true;
-        } else {
-            Log.v(TAG, "LE always on mode is disabled");
-            ret = false;
-        }
-        return ret;
-    }
-
-    /**
      * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE().
      *
      * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
@@ -676,7 +659,7 @@
      * @hide
      */
     public boolean disableBLE() {
-        if (isLEAlwaysOnEnabled() != true) return false;
+        if (!isBleScanAlwaysAvailable()) return false;
 
         int state = getLeState();
         if (state == BluetoothAdapter.STATE_ON) {
@@ -738,7 +721,7 @@
      * @hide
      */
     public boolean enableBLE() {
-        if (isLEAlwaysOnEnabled() != true) return false;
+        if (!isBleScanAlwaysAvailable()) return false;
 
         if (isLeEnabled() == true) {
             if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!");
@@ -1243,8 +1226,12 @@
      */
     @SystemApi
     public boolean isBleScanAlwaysAvailable() {
-        // TODO: implement after Settings UI change.
-        return false;
+        try {
+            return mManagerService.isBleScanAlwaysAvailable();
+        } catch (RemoteException e) {
+            Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e);
+            return false;
+        }
     }
 
     /**
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 8d1ce99..0b81ee8 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -44,6 +44,8 @@
 
     String getAddress();
     String getName();
+
+    boolean isBleScanAlwaysAvailable();
     int updateBleAppCount(IBinder b, boolean enable);
     boolean isBleAppPresent();
 }
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 123514e..dad486d 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -187,7 +187,7 @@
         mScanResultType = scanResultType;
         mReportDelayMillis = reportDelayMillis;
         mNumOfMatchesPerFilter = numOfMatchesPerFilter;
-        mMatchMode = numOfMatchesPerFilter;
+        mMatchMode = matchMode;
     }
 
     private ScanSettings(Parcel in) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c531e7e..2d63e3f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -341,7 +341,8 @@
      * one.  This is used by applications needing to talk to the carrier's
      * Multimedia Messaging Service servers.
      *
-     * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+     * @deprecated Applications should instead use
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
      */
     public static final int TYPE_MOBILE_MMS  = 2;
@@ -351,7 +352,8 @@
      * one.  This is used by applications needing to talk to the carrier's
      * Secure User Plane Location servers for help locating the device.
      *
-     * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+     * @deprecated Applications should instead use
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
      */
     public static final int TYPE_MOBILE_SUPL = 3;
@@ -367,7 +369,8 @@
      * same network interface as {@link #TYPE_MOBILE} but the routing setup
      * is different.
      *
-     * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+     * @deprecated Applications should instead use
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
      */
     public static final int TYPE_MOBILE_HIPRI = 5;
@@ -910,7 +913,8 @@
      * implementation+feature combination, except that the value {@code -1}
      * always indicates failure.
      *
-     * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+     * @deprecated Deprecated in favor of the cleaner
+     *             {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
      * @removed
      */
     public int startUsingNetworkFeature(int networkType, String feature) {
@@ -958,7 +962,7 @@
      * implementation+feature combination, except that the value {@code -1}
      * always indicates failure.
      *
-     * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+     * @deprecated Deprecated in favor of the cleaner {@link unregisterNetworkCallback} API.
      * @removed
      */
     public int stopUsingNetworkFeature(int networkType, String feature) {
@@ -1236,8 +1240,9 @@
      * @param hostAddress the IP address of the host to which the route is desired
      * @return {@code true} on success, {@code false} on failure
      *
-     * @deprecated Deprecated in favor of the {@link #requestNetwork},
-     *             {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api.
+     * @deprecated Deprecated in favor of the
+     *             {@link #requestNetwork(NetworkRequest, NetworkCallback)},
+     *             {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
      * @removed
      */
     public boolean requestRouteToHost(int networkType, int hostAddress) {
@@ -1256,7 +1261,7 @@
      * @return {@code true} on success, {@code false} on failure
      * @hide
      * @deprecated Deprecated in favor of the {@link #requestNetwork} and
-     *             {@link #bindProcessToNetwork} api.
+     *             {@link #bindProcessToNetwork} API.
      * @removed
      */
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
@@ -2144,14 +2149,22 @@
         public static final int CANCELED     = 8;
 
         /**
-         * @hide
-         * Called whenever the framework connects to a network that it may use to
-         * satisfy this request
+         * Called when the framework connects to a new network to evaluate whether it satisfies this
+         * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
+         * callback. There is no guarantee that this new network will satisfy any requests, or that
+         * the network will stay connected for longer than the time necessary to evaluate it.
+         * <p>
+         * Most applications <b>should not</b> act on this callback, and should instead use
+         * {@link #onAvailable}. This callback is intended for use by applications that can assist
+         * the framework in properly evaluating the network &mdash; for example, an application that
+         * can automatically log in to a captive portal without user intervention.
+         *
+         * @param network The {@link Network} of the network that is being evaluated.
          */
         public void onPreCheck(Network network) {}
 
         /**
-         * Called when the framework connects and has declared new network ready for use.
+         * Called when the framework connects and has declared a new network ready for use.
          * This callback may be called more than once if the {@link Network} that is
          * satisfying the request changes.
          *
@@ -2251,116 +2264,82 @@
         @Override
         public void handleMessage(Message message) {
             Log.d(TAG, "CM callback handler got msg " + message.what);
+            NetworkRequest request = (NetworkRequest) getObject(message, NetworkRequest.class);
+            Network network = (Network) getObject(message, Network.class);
             switch (message.what) {
                 case CALLBACK_PRECHECK: {
-                    NetworkRequest request = (NetworkRequest)getObject(message,
-                            NetworkRequest.class);
-                    NetworkCallback callbacks = getCallbacks(request);
-                    if (callbacks != null) {
-                        callbacks.onPreCheck((Network)getObject(message, Network.class));
-                    } else {
-                        Log.e(TAG, "callback not found for PRECHECK message");
+                    NetworkCallback callback = getCallback(request, "PRECHECK");
+                    if (callback != null) {
+                        callback.onPreCheck(network);
                     }
                     break;
                 }
                 case CALLBACK_AVAILABLE: {
-                    NetworkRequest request = (NetworkRequest)getObject(message,
-                            NetworkRequest.class);
-                    NetworkCallback callbacks = getCallbacks(request);
-                    if (callbacks != null) {
-                        callbacks.onAvailable((Network)getObject(message, Network.class));
-                    } else {
-                        Log.e(TAG, "callback not found for AVAILABLE message");
+                    NetworkCallback callback = getCallback(request, "AVAILABLE");
+                    if (callback != null) {
+                        callback.onAvailable(network);
                     }
                     break;
                 }
                 case CALLBACK_LOSING: {
-                    NetworkRequest request = (NetworkRequest)getObject(message,
-                            NetworkRequest.class);
-                    NetworkCallback callbacks = getCallbacks(request);
-                    if (callbacks != null) {
-                        callbacks.onLosing((Network)getObject(message, Network.class),
-                                message.arg1);
-                    } else {
-                        Log.e(TAG, "callback not found for LOSING message");
+                    NetworkCallback callback = getCallback(request, "LOSING");
+                    if (callback != null) {
+                        callback.onLosing(network, message.arg1);
                     }
                     break;
                 }
                 case CALLBACK_LOST: {
-                    NetworkRequest request = (NetworkRequest)getObject(message,
-                            NetworkRequest.class);
-
-                    NetworkCallback callbacks = getCallbacks(request);
-                    if (callbacks != null) {
-                        callbacks.onLost((Network)getObject(message, Network.class));
-                    } else {
-                        Log.e(TAG, "callback not found for LOST message");
+                    NetworkCallback callback = getCallback(request, "LOST");
+                    if (callback != null) {
+                        callback.onLost(network);
                     }
                     break;
                 }
                 case CALLBACK_UNAVAIL: {
-                    NetworkRequest request = (NetworkRequest)getObject(message,
-                            NetworkRequest.class);
-                    NetworkCallback callbacks = null;
-                    synchronized(mCallbackMap) {
-                        callbacks = mCallbackMap.get(request);
-                    }
-                    if (callbacks != null) {
-                        callbacks.onUnavailable();
-                    } else {
-                        Log.e(TAG, "callback not found for UNAVAIL message");
+                    NetworkCallback callback = getCallback(request, "UNAVAIL");
+                    if (callback != null) {
+                        callback.onUnavailable();
                     }
                     break;
                 }
                 case CALLBACK_CAP_CHANGED: {
-                    NetworkRequest request = (NetworkRequest)getObject(message,
-                            NetworkRequest.class);
-                    NetworkCallback callbacks = getCallbacks(request);
-                    if (callbacks != null) {
-                        Network network = (Network)getObject(message, Network.class);
+                    NetworkCallback callback = getCallback(request, "CAP_CHANGED");
+                    if (callback != null) {
                         NetworkCapabilities cap = (NetworkCapabilities)getObject(message,
                                 NetworkCapabilities.class);
 
-                        callbacks.onCapabilitiesChanged(network, cap);
-                    } else {
-                        Log.e(TAG, "callback not found for CAP_CHANGED message");
+                        callback.onCapabilitiesChanged(network, cap);
                     }
                     break;
                 }
                 case CALLBACK_IP_CHANGED: {
-                    NetworkRequest request = (NetworkRequest)getObject(message,
-                            NetworkRequest.class);
-                    NetworkCallback callbacks = getCallbacks(request);
-                    if (callbacks != null) {
-                        Network network = (Network)getObject(message, Network.class);
+                    NetworkCallback callback = getCallback(request, "IP_CHANGED");
+                    if (callback != null) {
                         LinkProperties lp = (LinkProperties)getObject(message,
                                 LinkProperties.class);
 
-                        callbacks.onLinkPropertiesChanged(network, lp);
-                    } else {
-                        Log.e(TAG, "callback not found for IP_CHANGED message");
+                        callback.onLinkPropertiesChanged(network, lp);
                     }
                     break;
                 }
                 case CALLBACK_RELEASED: {
-                    NetworkRequest req = (NetworkRequest)getObject(message, NetworkRequest.class);
-                    NetworkCallback callbacks = null;
+                    NetworkCallback callback = null;
                     synchronized(mCallbackMap) {
-                        callbacks = mCallbackMap.remove(req);
+                        callback = mCallbackMap.remove(request);
                     }
-                    if (callbacks != null) {
+                    if (callback != null) {
                         synchronized(mRefCount) {
                             if (mRefCount.decrementAndGet() == 0) {
                                 getLooper().quit();
                             }
                         }
                     } else {
-                        Log.e(TAG, "callback not found for CANCELED message");
+                        Log.e(TAG, "callback not found for RELEASED message");
                     }
                     break;
                 }
                 case CALLBACK_EXIT: {
-                    Log.d(TAG, "Listener quiting");
+                    Log.d(TAG, "Listener quitting");
                     getLooper().quit();
                     break;
                 }
@@ -2374,10 +2353,16 @@
         private Object getObject(Message msg, Class c) {
             return msg.getData().getParcelable(c.getSimpleName());
         }
-        private NetworkCallback getCallbacks(NetworkRequest req) {
+
+        private NetworkCallback getCallback(NetworkRequest req, String name) {
+            NetworkCallback callback;
             synchronized(mCallbackMap) {
-                return mCallbackMap.get(req);
+                callback = mCallbackMap.get(req);
             }
+            if (callback == null) {
+                Log.e(TAG, "callback not found for " + name + " message");
+            }
+            return callback;
         }
     }
 
@@ -2601,10 +2586,10 @@
 
     /**
      * Unregisters callbacks about and possibly releases networks originating from
-     * {@link #requestNetwork} and {@link #registerNetworkCallback} calls.  If the
-     * given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
-     * any networks that had been connected to only to satisfy that request will be
-     * disconnected.
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and {@link #registerNetworkCallback}
+     * calls.  If the given {@code NetworkCallback} had previously been used with
+     * {@code #requestNetwork}, any networks that had been connected to only to satisfy that request
+     * will be disconnected.
      *
      * @param networkCallback The {@link NetworkCallback} used when making the request.
      */
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8c8bfab..ab70485 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -37,6 +37,11 @@
      * @hide
      */
     public NetworkCapabilities() {
+        clearAll();
+        mNetworkCapabilities =
+                (1 << NET_CAPABILITY_NOT_RESTRICTED) |
+                (1 << NET_CAPABILITY_TRUSTED) |
+                (1 << NET_CAPABILITY_NOT_VPN);
     }
 
     public NetworkCapabilities(NetworkCapabilities nc) {
@@ -50,11 +55,21 @@
     }
 
     /**
+     * Completely clears the contents of this object, removing even the capabilities that are set
+     * by default when the object is constructed.
+     * @hide
+     */
+    public void clearAll() {
+        mNetworkCapabilities = mTransportTypes = 0;
+        mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = 0;
+        mNetworkSpecifier = null;
+    }
+
+    /**
      * Represents the network's capabilities.  If any are specified they will be satisfied
      * by any Network that matches all of them.
      */
-    private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED) |
-            (1 << NET_CAPABILITY_TRUSTED) | (1 << NET_CAPABILITY_NOT_VPN);
+    private long mNetworkCapabilities;
 
     /**
      * Indicates this is a network that has the ability to reach the
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e3e16eb..8e0584a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.File;
@@ -242,6 +243,15 @@
         return DATA_DIRECTORY;
     }
 
+    /** {@hide} */
+    public static File getDataAppDirectory(String volumeUuid) {
+        if (TextUtils.isEmpty(volumeUuid)) {
+            return new File("/data/app");
+        } else {
+            return new File("/mnt/expand/" + volumeUuid + "/app");
+        }
+    }
+
     /**
      * Return the primary external storage directory. This directory may not
      * currently be accessible if it has been mounted by the user on their
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 6db46e9..85b22fb 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -246,6 +246,9 @@
             mSurface = null;
             mLayer = null;
 
+            // Make sure if/when new layer gets re-created, transform matrix will
+            // be re-applied.
+            mMatrixChanged = true;
             mHadSurface = true;
         }
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 87d5d9a..5017a38 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -360,7 +360,7 @@
     @Override
     boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
         return nCopyLayerInto(mNativeProxy,
-                layer.getDeferredLayerUpdater(), bitmap.getSkBitmap());
+                layer.getDeferredLayerUpdater(), bitmap);
     }
 
     @Override
@@ -531,7 +531,7 @@
 
     private static native long nCreateTextureLayer(long nativeProxy);
     private static native void nBuildLayer(long nativeProxy, long node);
-    private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
+    private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap);
     private static native void nPushLayerUpdate(long nativeProxy, long layer);
     private static native void nCancelLayerUpdate(long nativeProxy, long layer);
     private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 191662c..6d4e058 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -216,6 +216,8 @@
         if (mVelocityTracker == null) {
             return super.onTouchEvent(ev);
         }
+        // offset because the view is translated during swipe
+        ev.offsetLocation(mTranslationX, 0);
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_UP:
                 updateDismiss(ev);
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 6591d29..d0f7591 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -80,11 +80,12 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong bitmapHandle,
+static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jobject jbitmap,
                                       jint tileModeX, jint tileModeY)
 {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkShader* s = SkShader::CreateBitmapShader(*bitmap,
+    SkBitmap bitmap;
+    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+    SkShader* s = SkShader::CreateBitmapShader(bitmap,
                                         (SkShader::TileMode)tileModeX,
                                         (SkShader::TileMode)tileModeY);
 
@@ -249,7 +250,7 @@
 };
 
 static JNINativeMethod gBitmapShaderMethods[] = {
-    { "nativeCreate",     "(JII)J",  (void*)BitmapShader_constructor },
+    { "nativeCreate",     "(Landroid/graphics/Bitmap;II)J",  (void*)BitmapShader_constructor },
 };
 
 static JNINativeMethod gLinearGradientMethods[] = {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 5d08532..50a1069 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -40,22 +40,21 @@
 
 // Native wrapper constructor used by Canvas(Bitmap)
 static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) {
-    SkBitmap* bitmap = nullptr;
+    SkBitmap bitmap;
     if (jbitmap != NULL) {
-        bitmap = GraphicsJNI::getSkBitmapDeprecated(env, jbitmap);
+        GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
     }
-    return reinterpret_cast<jlong>(Canvas::create_canvas(
-            bitmap ? *bitmap : SkBitmap()));
+    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
 }
 
 // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
 // optionally copying canvas matrix & clip state.
 static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap) {
-    SkBitmap* bitmap = nullptr;
+    SkBitmap bitmap;
     if (jbitmap != NULL) {
-        bitmap = GraphicsJNI::getSkBitmapDeprecated(env, jbitmap);
+        GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
     }
-    get_canvas(canvasHandle)->setBitmap(bitmap ? *bitmap : SkBitmap());
+    get_canvas(canvasHandle)->setBitmap(bitmap);
 }
 
 static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 11b3805..4ccbb41 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -21,6 +21,7 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
+#include <GraphicsJNI.h>
 #include <ScopedPrimitiveArray.h>
 
 #include <EGL/egl.h>
@@ -347,10 +348,11 @@
 }
 
 static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) {
+        jlong proxyPtr, jlong layerPtr, jobject jbitmap) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
+    SkBitmap bitmap;
+    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
     return proxy->copyLayerInto(layer, bitmap);
 }
 
@@ -458,7 +460,7 @@
     { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
     { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer },
-    { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
+    { "nCopyLayerInto", "(JJLandroid/graphics/Bitmap;)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
     { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate },
     { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate },
     { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture },
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index dc364a12..0aa588b 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -402,12 +402,12 @@
     </family>
     <family>
         <fileset>
-            <file lang="zh-Hans">NotoSansHans-Regular.otf</file>
+            <file lang="zh-Hans">NotoSansSC-Regular.otf</file>
         </fileset>
     </family>
     <family>
         <fileset>
-            <file lang="zh-Hant">NotoSansHant-Regular.otf</file>
+            <file lang="zh-Hant">NotoSansTC-Regular.otf</file>
         </fileset>
     </family>
     <family>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 44ea1c9..f903aab 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -329,10 +329,10 @@
         <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
     </family>
     <family lang="zh-Hans">
-        <font weight="400" style="normal">NotoSansHans-Regular.otf</font>
+        <font weight="400" style="normal">NotoSansSC-Regular.otf</font>
     </family>
     <family lang="zh-Hant">
-        <font weight="400" style="normal">NotoSansHant-Regular.otf</font>
+        <font weight="400" style="normal">NotoSansTC-Regular.otf</font>
     </family>
     <family lang="ja">
         <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index f2f890e..bd74bc8 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -42,8 +42,7 @@
         mBitmap = bitmap;
         mTileX = tileX;
         mTileY = tileY;
-        final long b = bitmap.getSkBitmap();
-        init(nativeCreate(b, tileX.nativeInt, tileY.nativeInt));
+        init(nativeCreate(bitmap, tileX.nativeInt, tileY.nativeInt));
     }
 
     /**
@@ -56,6 +55,6 @@
         return copy;
     }
 
-    private static native long nativeCreate(long native_bitmap, int shaderTileModeX,
+    private static native long nativeCreate(Bitmap bitmap, int shaderTileModeX,
             int shaderTileModeY);
 }
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 9fe8e0c..7a1ecf7 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -81,10 +81,6 @@
      */
     protected int mScreenDensity = Bitmap.DENSITY_NONE;
 
-    // Used by native code
-    @SuppressWarnings("UnusedDeclaration")
-    private int mSurfaceFormat;
-
     // Maximum bitmap size as defined in Skia's native code
     // (see SkCanvas.cpp, SkDraw.cpp)
     private static final int MAXMIMUM_BITMAP_SIZE = 32766;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index cc87241..d15fa39 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -293,11 +293,11 @@
     return (void*) success;
 }
 
-bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
     SETUP_TASK(copyLayerInto);
     args->context = mContext;
     args->layer = layer;
-    args->bitmap = bitmap;
+    args->bitmap = &bitmap;
     return (bool) postAndWait(task);
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 29c6f08..cc475fa 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -83,7 +83,7 @@
 
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
     ANDROID_API void buildLayer(RenderNode* node);
-    ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+    ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap);
     ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void detachSurfaceTexture(DeferredLayerUpdater* layer);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index a33fa59..77adb39 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1953,21 +1953,16 @@
 
         TrackInfo(Parcel in) {
             mTrackType = in.readInt();
-            // TODO: parcel in the full MediaFormat
+            // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat
+            // even for audio/video tracks, meaning we only set the mime and language.
+            String mime = in.readString();
             String language = in.readString();
+            mFormat = MediaFormat.createSubtitleFormat(mime, language);
 
-            if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
-                mFormat = MediaFormat.createSubtitleFormat(
-                    MEDIA_MIMETYPE_TEXT_SUBRIP, language);
-            } else if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-                String mime = in.readString();
-                mFormat = MediaFormat.createSubtitleFormat(mime, language);
+            if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
                 mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
                 mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
                 mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
-            } else {
-                mFormat = new MediaFormat();
-                mFormat.setString(MediaFormat.KEY_LANGUAGE, language);
             }
         }
 
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index dc6760d..ecc87e7 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -199,6 +199,7 @@
     private final Object mAudioLock = new Object();
     private AudioTrack mAudioTrack = null;
     private List<AudioBuffer> mAudioBuffers = new LinkedList<AudioBuffer>();
+    // this is only used for paused/running decisions, so it is not affected by clock drift
     private float mPlaybackRate = 0.0f;
 
     private long mNativeContext;
@@ -459,36 +460,11 @@
      * @throws IllegalArgumentException if the settings are not supported.
      */
     public void setPlaybackSettings(@NonNull PlaybackSettings settings) {
-        float rate;
-        try {
-            rate = settings.getSpeed();
-
-            // rate is specified
-            if (mAudioTrack != null) {
-                try {
-                    if (rate == 0.0) {
-                        mAudioTrack.pause();
-                    } else {
-                        mAudioTrack.setPlaybackSettings(settings);
-                        mAudioTrack.play();
-                    }
-                } catch (IllegalStateException e) {
-                    throw e;
-                }
-            }
-
-            synchronized(mAudioLock) {
-                mPlaybackRate = rate;
-            }
-            if (mPlaybackRate != 0.0 && mAudioThread != null) {
-                postRenderAudio(0);
-            }
-            native_setPlaybackRate(mPlaybackRate);
-        } catch (IllegalStateException e) {
-            // rate is not specified; still, propagate settings to audio track
-            if (mAudioTrack != null) {
-                mAudioTrack.setPlaybackSettings(settings);
-            }
+        synchronized(mAudioLock) {
+            mPlaybackRate = native_setPlaybackSettings(settings);;
+        }
+        if (mPlaybackRate != 0.0 && mAudioThread != null) {
+            postRenderAudio(0);
         }
     }
 
@@ -501,18 +477,9 @@
      *     been initialized.
      */
     @NonNull
-    public PlaybackSettings getPlaybackSettings() {
-        if (mAudioTrack != null) {
-            return mAudioTrack.getPlaybackSettings();
-        } else {
-            PlaybackSettings settings = new PlaybackSettings();
-            settings.allowDefaults();
-            settings.setSpeed(mPlaybackRate);
-            return settings;
-        }
-    }
+    public native PlaybackSettings getPlaybackSettings();
 
-    private native final void native_setPlaybackRate(float rate);
+    private native float native_setPlaybackSettings(@NonNull PlaybackSettings settings);
 
     /**
      * Sets A/V sync mode.
@@ -523,7 +490,16 @@
      * initialized.
      * @throws IllegalArgumentException if settings are not supported.
      */
-    public native void setSyncSettings(@NonNull SyncSettings settings);
+    public void setSyncSettings(@NonNull SyncSettings settings) {
+        synchronized(mAudioLock) {
+            mPlaybackRate = native_setSyncSettings(settings);;
+        }
+        if (mPlaybackRate != 0.0 && mAudioThread != null) {
+            postRenderAudio(0);
+        }
+    }
+
+    private native float native_setSyncSettings(@NonNull SyncSettings settings);
 
     /**
      * Gets the A/V sync mode.
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 2c61779..5b55a61 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -174,6 +174,8 @@
     } else {  // Throw exception!
         if ( opStatus == (status_t) INVALID_OPERATION ) {
             jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        } else if ( opStatus == (status_t) BAD_VALUE ) {
+            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
             jniThrowException(env, "java/lang/SecurityException", NULL);
         } else if ( opStatus != (status_t) OK ) {
@@ -442,8 +444,33 @@
             pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode,
             pbs.audioStretchModeSet, pbs.audioRate.mStretchMode);
 
-    // TODO: pass playback settings to mediaplayer when audiotrack supports it
-    process_media_player_call(env, thiz, mp->setPlaybackRate(pbs.audioRate.mSpeed), NULL, NULL);
+    AudioPlaybackRate rate;
+    status_t err = mp->getPlaybackSettings(&rate);
+    if (err == OK) {
+        bool updatedRate = false;
+        if (pbs.speedSet) {
+            rate.mSpeed = pbs.audioRate.mSpeed;
+            updatedRate = true;
+        }
+        if (pbs.pitchSet) {
+            rate.mPitch = pbs.audioRate.mPitch;
+            updatedRate = true;
+        }
+        if (pbs.audioFallbackModeSet) {
+            rate.mFallbackMode = pbs.audioRate.mFallbackMode;
+            updatedRate = true;
+        }
+        if (pbs.audioStretchModeSet) {
+            rate.mStretchMode = pbs.audioRate.mStretchMode;
+            updatedRate = true;
+        }
+        if (updatedRate) {
+            err = mp->setPlaybackSettings(rate);
+        }
+    }
+    process_media_player_call(
+            env, thiz, err,
+            "java/lang/IllegalStateException", "unexpected error");
 }
 
 static jobject
@@ -457,15 +484,9 @@
 
     PlaybackSettings pbs;
     AudioPlaybackRate &audioRate = pbs.audioRate;
-
-    audioRate.mSpeed = 1.0f;
-    audioRate.mPitch = 1.0f;
-    audioRate.mFallbackMode = AUDIO_TIMESTRETCH_FALLBACK_DEFAULT;
-    audioRate.mStretchMode = AUDIO_TIMESTRETCH_STRETCH_DEFAULT;
-
-    // TODO: get this from mediaplayer when audiotrack supports it
-    // process_media_player_call(
-    //        env, thiz, mp->getPlaybackSettings(&audioRate), NULL, NULL);
+    process_media_player_call(
+            env, thiz, mp->getPlaybackSettings(&audioRate),
+            "java/lang/IllegalStateException", "unexpected error");
     ALOGV("getPlaybackSettings: %f %f %d %d",
             audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
 
@@ -489,13 +510,35 @@
     SyncSettings scs;
     scs.fillFromJobject(env, gSyncSettingsFields, settings);
     ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
-            scs.syncSourceSet, scs.syncSource,
-            scs.audioAdjustModeSet, scs.audioAdjustMode,
-            scs.toleranceSet, scs.tolerance,
-            scs.frameRateSet, scs.frameRate);
+          scs.syncSourceSet, scs.sync.mSource,
+          scs.audioAdjustModeSet, scs.sync.mAudioAdjustMode,
+          scs.toleranceSet, scs.sync.mTolerance,
+          scs.frameRateSet, scs.frameRate);
 
-    // TODO: pass sync settings to mediaplayer when it supports it
-    // process_media_player_call(env, thiz, mp->setSyncSettings(scs), NULL, NULL);
+    AVSyncSettings avsync;
+    float videoFrameRate;
+    status_t err = mp->getSyncSettings(&avsync, &videoFrameRate);
+    if (err == OK) {
+        bool updatedSync = scs.frameRateSet;
+        if (scs.syncSourceSet) {
+            avsync.mSource = scs.sync.mSource;
+            updatedSync = true;
+        }
+        if (scs.audioAdjustModeSet) {
+            avsync.mAudioAdjustMode = scs.sync.mAudioAdjustMode;
+            updatedSync = true;
+        }
+        if (scs.toleranceSet) {
+            avsync.mTolerance = scs.sync.mTolerance;
+            updatedSync = true;
+        }
+        if (updatedSync) {
+            err = mp->setSyncSettings(avsync, scs.frameRateSet ? scs.frameRate : -1.f);
+        }
+    }
+    process_media_player_call(
+            env, thiz, err,
+            "java/lang/IllegalStateException", "unexpected error");
 }
 
 static jobject
@@ -508,21 +551,27 @@
     }
 
     SyncSettings scs;
-    scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
-    scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
-    scs.tolerance = 0.f;
-    scs.frameRate = 0.f;
+    scs.frameRate = -1.f;
+    process_media_player_call(
+            env, thiz, mp->getSyncSettings(&scs.sync, &scs.frameRate),
+            "java/lang/IllegalStateException", "unexpected error");
 
-    // TODO: get this from mediaplayer when it supports it
-    // process_media_player_call(
-    //        env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
     ALOGV("getSyncSettings: %d %d %f %f",
-            scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+            scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate);
+
+    // sanity check settings
+    if (scs.sync.mSource >= AVSYNC_SOURCE_MAX
+            || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
+            || scs.sync.mTolerance < 0.f
+            || scs.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) {
+        jniThrowException(env,  "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
 
     scs.syncSourceSet = true;
     scs.audioAdjustModeSet = true;
     scs.toleranceSet = true;
-    scs.frameRateSet = false;
+    scs.frameRateSet = scs.frameRate >= 0.f;
 
     return scs.asJobject(env, gSyncSettingsFields);
 }
diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp
index f192262..8ad4b71 100644
--- a/media/jni/android_media_MediaSync.cpp
+++ b/media/jni/android_media_MediaSync.cpp
@@ -21,6 +21,7 @@
 #include "android_media_MediaSync.h"
 
 #include "android_media_AudioTrack.h"
+#include "android_media_PlaybackSettings.h"
 #include "android_media_SyncSettings.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
@@ -29,6 +30,7 @@
 
 #include <gui/Surface.h>
 
+#include <media/AudioResamplerPublic.h>
 #include <media/AudioTrack.h>
 #include <media/stagefright/MediaClock.h>
 #include <media/stagefright/MediaSync.h>
@@ -47,6 +49,7 @@
 };
 
 static fields_t gFields;
+static PlaybackSettings::fields_t gPlaybackSettingsFields;
 static SyncSettings::fields_t gSyncSettingsFields;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -62,10 +65,8 @@
     return mSync->configureSurface(bufferProducer);
 }
 
-status_t JMediaSync::configureAudioTrack(
-        const sp<AudioTrack> &audioTrack,
-        int32_t nativeSampleRateInHz) {
-    return mSync->configureAudioTrack(audioTrack, nativeSampleRateInHz);
+status_t JMediaSync::configureAudioTrack(const sp<AudioTrack> &audioTrack) {
+    return mSync->configureAudioTrack(audioTrack);
 }
 
 status_t JMediaSync::createInputSurface(
@@ -73,14 +74,34 @@
     return mSync->createInputSurface(bufferProducer);
 }
 
-status_t JMediaSync::setPlaybackRate(float rate) {
-    return mSync->setPlaybackRate(rate);
-}
-
 sp<const MediaClock> JMediaSync::getMediaClock() {
     return mSync->getMediaClock();
 }
 
+status_t JMediaSync::setPlaybackSettings(const AudioPlaybackRate& rate) {
+    return mSync->setPlaybackSettings(rate);
+}
+
+void JMediaSync::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
+    mSync->getPlaybackSettings(rate);
+}
+
+status_t JMediaSync::setSyncSettings(const AVSyncSettings& syncSettings) {
+    return mSync->setSyncSettings(syncSettings);
+}
+
+void JMediaSync::getSyncSettings(AVSyncSettings* syncSettings /* nonnull */) {
+    mSync->getSyncSettings(syncSettings);
+}
+
+status_t JMediaSync::setVideoFrameRateHint(float rate) {
+    return mSync->setVideoFrameRateHint(rate);
+}
+
+float JMediaSync::getVideoFrameRate() {
+    return mSync->getVideoFrameRate();
+}
+
 status_t JMediaSync::updateQueuedAudioData(
         int sizeInBytes, int64_t presentationTimeUs) {
     return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs);
@@ -176,7 +197,7 @@
 }
 
 static void android_media_MediaSync_native_configureAudioTrack(
-        JNIEnv *env, jobject thiz, jobject jaudioTrack, jint nativeSampleRateInHz) {
+        JNIEnv *env, jobject thiz, jobject jaudioTrack) {
     ALOGV("android_media_MediaSync_configureAudioTrack");
 
     sp<JMediaSync> sync = getMediaSync(env, thiz);
@@ -194,7 +215,7 @@
         }
     }
 
-    status_t err = sync->configureAudioTrack(audioTrack, nativeSampleRateInHz);
+    status_t err = sync->configureAudioTrack(audioTrack);
 
     if (err == INVALID_OPERATION) {
         throwExceptionAsNecessary(
@@ -287,29 +308,132 @@
     return (jlong)playTimeUs;
 }
 
-static void
-android_media_MediaSync_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings)
-{
+static jfloat android_media_MediaSync_setPlaybackSettings(
+        JNIEnv *env, jobject thiz, jobject settings) {
     sp<JMediaSync> sync = getMediaSync(env, thiz);
     if (sync == NULL) {
         throwExceptionAsNecessary(env, INVALID_OPERATION);
-        return;
+        return (jfloat)0.f;
+    }
+
+    PlaybackSettings pbs;
+    pbs.fillFromJobject(env, gPlaybackSettingsFields, settings);
+    ALOGV("setPlaybackSettings: %d:%f %d:%f %d:%u %d:%u",
+            pbs.speedSet, pbs.audioRate.mSpeed,
+            pbs.pitchSet, pbs.audioRate.mPitch,
+            pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode,
+            pbs.audioStretchModeSet, pbs.audioRate.mStretchMode);
+
+    AudioPlaybackRate rate;
+    sync->getPlaybackSettings(&rate);
+    bool updatedRate = false;
+    if (pbs.speedSet) {
+        rate.mSpeed = pbs.audioRate.mSpeed;
+        updatedRate = true;
+    }
+    if (pbs.pitchSet) {
+        rate.mPitch = pbs.audioRate.mPitch;
+        updatedRate = true;
+    }
+    if (pbs.audioFallbackModeSet) {
+        rate.mFallbackMode = pbs.audioRate.mFallbackMode;
+        updatedRate = true;
+    }
+    if (pbs.audioStretchModeSet) {
+        rate.mStretchMode = pbs.audioRate.mStretchMode;
+        updatedRate = true;
+    }
+    if (updatedRate) {
+        status_t err = sync->setPlaybackSettings(rate);
+        if (err != OK) {
+            throwExceptionAsNecessary(env, err);
+            return (jfloat)0.f;
+        }
+    }
+
+    sp<const MediaClock> mediaClock = sync->getMediaClock();
+    if (mediaClock == NULL) {
+        return (jfloat)0.f;
+    }
+
+    return (jfloat)mediaClock->getPlaybackRate();
+}
+
+static jobject android_media_MediaSync_getPlaybackSettings(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaSync> sync = getMediaSync(env, thiz);
+    if (sync == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return NULL;
+    }
+
+    PlaybackSettings pbs;
+    AudioPlaybackRate &audioRate = pbs.audioRate;
+    sync->getPlaybackSettings(&audioRate);
+    ALOGV("getPlaybackSettings: %f %f %d %d",
+            audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
+
+    pbs.speedSet = true;
+    pbs.pitchSet = true;
+    pbs.audioFallbackModeSet = true;
+    pbs.audioStretchModeSet = true;
+
+    return pbs.asJobject(env, gPlaybackSettingsFields);
+}
+
+static jfloat android_media_MediaSync_setSyncSettings(
+        JNIEnv *env, jobject thiz, jobject settings) {
+    sp<JMediaSync> sync = getMediaSync(env, thiz);
+    if (sync == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return (jfloat)0.f;
     }
 
     SyncSettings scs;
     scs.fillFromJobject(env, gSyncSettingsFields, settings);
     ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f",
-            scs.syncSourceSet, scs.syncSource,
-            scs.audioAdjustModeSet, scs.audioAdjustMode,
-            scs.toleranceSet, scs.tolerance,
+            scs.syncSourceSet, scs.sync.mSource,
+            scs.audioAdjustModeSet, scs.sync.mAudioAdjustMode,
+            scs.toleranceSet, scs.sync.mTolerance,
             scs.frameRateSet, scs.frameRate);
 
-    // TODO: pass sync settings to mediasync when it supports it
+    AVSyncSettings avsync;
+    sync->getSyncSettings(&avsync);
+    bool updatedSync = false;
+    status_t err = OK;
+    if (scs.syncSourceSet) {
+        avsync.mSource = scs.sync.mSource;
+        updatedSync = true;
+    }
+    if (scs.audioAdjustModeSet) {
+        avsync.mAudioAdjustMode = scs.sync.mAudioAdjustMode;
+        updatedSync = true;
+    }
+    if (scs.toleranceSet) {
+        avsync.mTolerance = scs.sync.mTolerance;
+        updatedSync = true;
+    }
+    if (updatedSync) {
+        err = sync->setSyncSettings(avsync);
+    }
+
+    if (scs.frameRateSet && err == OK) {
+        err = sync->setVideoFrameRateHint(scs.frameRate);
+    }
+    if (err != OK) {
+        throwExceptionAsNecessary(env, err);
+        return (jfloat)0.f;
+    }
+
+    sp<const MediaClock> mediaClock = sync->getMediaClock();
+    if (mediaClock == NULL) {
+        return (jfloat)0.f;
+    }
+
+    return (jfloat)mediaClock->getPlaybackRate();
 }
 
-static jobject
-android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz)
-{
+static jobject android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz) {
     sp<JMediaSync> sync = getMediaSync(env, thiz);
     if (sync == NULL) {
         throwExceptionAsNecessary(env, INVALID_OPERATION);
@@ -317,21 +441,25 @@
     }
 
     SyncSettings scs;
-    scs.syncSource = 0; // SYNC_SOURCE_DEFAULT
-    scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT
-    scs.tolerance = 0.f;
-    scs.frameRate = 0.f;
+    sync->getSyncSettings(&scs.sync);
+    scs.frameRate = sync->getVideoFrameRate();
 
-    // TODO: get this from mediaplayer when it supports it
-    // process_media_player_call(
-    //        env, thiz, mp->getSyncSettings(&scs), NULL, NULL);
     ALOGV("getSyncSettings: %d %d %f %f",
-            scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate);
+            scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate);
+
+    // sanity check settings
+    if (scs.sync.mSource >= AVSYNC_SOURCE_MAX
+            || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
+            || scs.sync.mTolerance < 0.f
+            || scs.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return NULL;
+    }
 
     scs.syncSourceSet = true;
     scs.audioAdjustModeSet = true;
     scs.toleranceSet = true;
-    scs.frameRateSet = false;
+    scs.frameRateSet = scs.frameRate >= 0.f;
 
     return scs.asJobject(env, gSyncSettingsFields);
 }
@@ -359,6 +487,7 @@
     CHECK(gFields.mediaTimestampClockRateID != NULL);
 
     gSyncSettingsFields.init(env);
+    gPlaybackSettingsFields.init(env);
 }
 
 static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) {
@@ -367,21 +496,6 @@
     setMediaSync(env, thiz, sync);
 }
 
-static void android_media_MediaSync_native_setPlaybackRate(
-        JNIEnv *env, jobject thiz, jfloat rate) {
-    sp<JMediaSync> sync = getMediaSync(env, thiz);
-    if (sync == NULL) {
-        throwExceptionAsNecessary(env, INVALID_OPERATION);
-        return;
-    }
-
-    status_t err = sync->setPlaybackRate(rate);
-    if (err != NO_ERROR) {
-        throwExceptionAsNecessary(env, err);
-        return;
-    }
-}
-
 static void android_media_MediaSync_native_finalize(JNIEnv *env, jobject thiz) {
     android_media_MediaSync_release(env, thiz);
 }
@@ -416,11 +530,17 @@
 
     { "native_release", "()V", (void *)android_media_MediaSync_release },
 
-    { "native_setPlaybackRate", "(F)V", (void *)android_media_MediaSync_native_setPlaybackRate },
+    { "native_setPlaybackSettings", "(Landroid/media/PlaybackSettings;)F",
+      (void *)android_media_MediaSync_setPlaybackSettings },
 
-    { "setSyncSettings", "(Landroid/media/SyncSettings;)V", (void *)android_media_MediaSync_setSyncSettings},
+    { "getPlaybackSettings", "()Landroid/media/PlaybackSettings;",
+      (void *)android_media_MediaSync_getPlaybackSettings },
 
-    { "getSyncSettings", "()Landroid/media/SyncSettings;", (void *)android_media_MediaSync_getSyncSettings},
+    { "native_setSyncSettings", "(Landroid/media/SyncSettings;)F",
+      (void *)android_media_MediaSync_setSyncSettings },
+
+    { "getSyncSettings", "()Landroid/media/SyncSettings;",
+      (void *)android_media_MediaSync_getSyncSettings },
 
     { "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize },
 };
diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h
index cf81a72..80f5d63 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -18,11 +18,13 @@
 #define _ANDROID_MEDIA_MEDIASYNC_H_
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaSync.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
 namespace android {
 
+struct AudioPlaybackRate;
 class AudioTrack;
 struct IGraphicBufferProducer;
 struct MediaClock;
@@ -32,17 +34,21 @@
     JMediaSync();
 
     status_t configureSurface(const sp<IGraphicBufferProducer> &bufferProducer);
-    status_t configureAudioTrack(
-            const sp<AudioTrack> &audioTrack, int32_t nativeSampleRateInHz);
+    status_t configureAudioTrack(const sp<AudioTrack> &audioTrack);
 
     status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
 
     status_t updateQueuedAudioData(int sizeInBytes, int64_t presentationTimeUs);
 
-    status_t setPlaybackRate(float rate);
-
     status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
 
+    status_t setPlaybackSettings(const AudioPlaybackRate& rate);
+    void getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
+    status_t setSyncSettings(const AVSyncSettings& syncSettings);
+    void getSyncSettings(AVSyncSettings* syncSettings /* nonnull */);
+    status_t setVideoFrameRateHint(float rate);
+    float getVideoFrameRate();
+
     sp<const MediaClock> getMediaClock();
 
 protected:
diff --git a/media/jni/android_media_SyncSettings.cpp b/media/jni/android_media_SyncSettings.cpp
index 2f0605e..5da35e7 100644
--- a/media/jni/android_media_SyncSettings.cpp
+++ b/media/jni/android_media_SyncSettings.cpp
@@ -57,9 +57,9 @@
 }
 
 void SyncSettings::fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings) {
-    syncSource = env->GetIntField(settings, fields.sync_source);
-    audioAdjustMode = env->GetIntField(settings, fields.audio_adjust_mode);
-    tolerance = env->GetFloatField(settings, fields.tolerance);
+    sync.mSource = (AVSyncSource)env->GetIntField(settings, fields.sync_source);
+    sync.mAudioAdjustMode = (AVSyncAudioAdjustMode)env->GetIntField(settings, fields.audio_adjust_mode);
+    sync.mTolerance = env->GetFloatField(settings, fields.tolerance);
     frameRate = env->GetFloatField(settings, fields.frame_rate);
     int set = env->GetIntField(settings, fields.set);
 
@@ -74,9 +74,9 @@
     if (settings == NULL) {
         return NULL;
     }
-    env->SetIntField(settings, fields.sync_source, (jint)syncSource);
-    env->SetIntField(settings, fields.audio_adjust_mode, (jint)audioAdjustMode);
-    env->SetFloatField(settings, fields.tolerance, (jfloat)tolerance);
+    env->SetIntField(settings, fields.sync_source, (jint)sync.mSource);
+    env->SetIntField(settings, fields.audio_adjust_mode, (jint)sync.mAudioAdjustMode);
+    env->SetFloatField(settings, fields.tolerance, (jfloat)sync.mTolerance);
     env->SetFloatField(settings, fields.frame_rate, (jfloat)frameRate);
     env->SetIntField(
             settings, fields.set,
diff --git a/media/jni/android_media_SyncSettings.h b/media/jni/android_media_SyncSettings.h
index 586533f..23530db 100644
--- a/media/jni/android_media_SyncSettings.h
+++ b/media/jni/android_media_SyncSettings.h
@@ -19,13 +19,12 @@
 
 #include "jni.h"
 
+#include <media/stagefright/MediaSync.h>
+
 namespace android {
 
 struct SyncSettings {
-    // keep this here until it is implemented
-    int syncSource;
-    int audioAdjustMode;
-    float tolerance;
+    AVSyncSettings sync;
     float frameRate;
 
     bool syncSourceSet;
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f3a2912..00b8df2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -400,6 +400,8 @@
     <string name="accessibility_recents_all_items_dismissed">All recent applications dismissed.</string>
     <!-- Content description to tell the user an application has been launched from recents -->
     <string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
+    <!-- Content description of individual recents task. -->
+    <string name="accessibility_recents_task_header"><xliff:g id="app" example="Chrome">%1$s</xliff:g> <xliff:g id="activity_label" example="www.google.com">%2$s</xliff:g></string>
     <!-- Content description to tell the user a notification has been removed from the notification shade -->
     <string name="accessibility_notification_dismissed">Notification dismissed.</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index d60df9c..e3fb16a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -438,6 +438,33 @@
         return info.loadLabel(mPm).toString();
     }
 
+    /** Returns the application label */
+    public String getApplicationLabel(Intent baseIntent, int userId) {
+        if (mPm == null) return null;
+
+        // If we are mocking, then return a mock label
+        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+            return "Recent Task";
+        }
+
+        ResolveInfo ri = mPm.resolveActivityAsUser(baseIntent, 0, userId);
+        CharSequence label = (ri != null) ? ri.loadLabel(mPm) : null;
+        return (label != null) ? label.toString() : null;
+    }
+
+    /** Returns the content description for a given task */
+    public String getContentDescription(Intent baseIntent, int userId, String activityLabel,
+            Resources res) {
+        String applicationLabel = getApplicationLabel(baseIntent, userId);
+        if (applicationLabel == null) {
+            return getBadgedLabel(activityLabel, userId);
+        }
+        String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
+        return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
+                : res.getString(R.string.accessibility_recents_task_header,
+                        badgedApplicationLabel, activityLabel);
+    }
+
     /**
      * Returns the activity icon for the ActivityInfo for a user, badging if
      * necessary.
@@ -464,6 +491,16 @@
         return icon;
     }
 
+    /**
+     * Returns the given label for a user, badging if necessary.
+     */
+    public String getBadgedLabel(String label, int userId) {
+        if (userId != UserHandle.myUserId()) {
+            label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
+        }
+        return label;
+    }
+
     /** Returns the package name of the home activity. */
     public String getHomeActivityPackageName() {
         if (mPm == null) return null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 5d98dda..40cd211 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -130,6 +130,8 @@
             // Load the label, icon, and color
             String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,
                     mSystemServicesProxy, infoHandle);
+            String contentDescription = loader.getAndUpdateContentDescription(taskKey,
+                    activityLabel, mSystemServicesProxy, res);
             Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
                     mSystemServicesProxy, res, infoHandle, false);
             int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig);
@@ -148,9 +150,9 @@
 
             // Add the task to the stack
             Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID),
-                    t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, activityIcon,
-                    activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, icon,
-                    iconFilename);
+                    t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, contentDescription,
+                    activityIcon, activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled,
+                    icon, iconFilename);
             task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
             if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 3192fe6..b2aa2b6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -259,6 +259,7 @@
     DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
     StringLruCache mActivityLabelCache;
+    StringLruCache mContentDescriptionCache;
     TaskResourceLoadQueue mLoadQueue;
     TaskResourceLoader mLoader;
 
@@ -298,6 +299,7 @@
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
         mActivityLabelCache = new StringLruCache(100);
+        mContentDescriptionCache = new StringLruCache(100);
         mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
                 mDefaultThumbnail, mDefaultApplicationIcon);
     }
@@ -348,6 +350,24 @@
         return label;
     }
 
+    /** Returns the content description using as many cached values as we can. */
+    public String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel,
+            SystemServicesProxy ssp, Resources res) {
+        // Return the cached content description if it exists
+        String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
+        if (label != null) {
+            return label;
+        }
+        label = ssp.getContentDescription(taskKey.baseIntent, taskKey.userId, activityLabel, res);
+        if (label != null) {
+            mContentDescriptionCache.put(taskKey, label);
+        } else {
+            Log.w(TAG, "Missing content description for " + taskKey.baseIntent.getComponent()
+                    + " u=" + taskKey.userId);
+        }
+        return label;
+    }
+
     /** Returns the activity icon using as many cached values as we can. */
     public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
             ActivityManager.TaskDescription td, SystemServicesProxy ssp,
@@ -541,6 +561,7 @@
                 mApplicationIconCache.evictAll();
                 // The cache is small, only clear the label cache when we are critical
                 mActivityLabelCache.evictAll();
+                mContentDescriptionCache.evictAll();
                 break;
             default:
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 0cd55d7..c14adf4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -124,6 +124,7 @@
     public boolean isLaunchTarget;
     public Drawable applicationIcon;
     public Drawable activityIcon;
+    public String contentDescription;
     public String activityLabel;
     public int colorPrimary;
     public boolean useLightOnPrimaryColor;
@@ -140,8 +141,8 @@
     }
 
     public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor,
-                String activityTitle, Drawable activityIcon, int colorPrimary,
-                boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
+                String activityTitle, String contentDescription, Drawable activityIcon,
+                int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
                 String iconFilename) {
         boolean isInAffiliationGroup = (taskAffiliation != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
@@ -149,6 +150,7 @@
         this.taskAffiliation = taskAffiliation;
         this.taskAffiliationColor = taskAffiliationColor;
         this.activityLabel = activityTitle;
+        this.contentDescription = contentDescription;
         this.activityIcon = activityIcon;
         this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary;
         this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
@@ -166,6 +168,7 @@
         this.taskAffiliation = o.taskAffiliation;
         this.taskAffiliationColor = o.taskAffiliationColor;
         this.activityLabel = o.activityLabel;
+        this.contentDescription = o.contentDescription;
         this.activityIcon = o.activityIcon;
         this.colorPrimary = o.colorPrimary;
         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 60a91bf..f397bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -190,10 +190,12 @@
         } else if (t.applicationIcon != null) {
             mApplicationIcon.setImageDrawable(t.applicationIcon);
         }
-        mApplicationIcon.setContentDescription(t.activityLabel);
+        mApplicationIcon.setContentDescription(t.contentDescription);
         if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
             mActivityDescription.setText(t.activityLabel);
         }
+        mActivityDescription.setContentDescription(t.contentDescription);
+
         // Try and apply the system ui tint
         int existingBgColor = getBackgroundColor();
         if (existingBgColor != t.colorPrimary) {
@@ -207,7 +209,7 @@
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
-                t.activityLabel));
+                t.contentDescription));
         mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
         if (mConfig.multiStackEnabled) {
             updateResizeTaskBarIcon(t);
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
index 149c0be..5e28d3f 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
@@ -1288,7 +1288,7 @@
         } else {
             K = A.getType().getX();
         }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_ssyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, A.getID(mRS), B.getID(mRS), beta, C.getID(mRS), 0, 0, 0, 0);
+        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, A.getID(mRS), B.getID(mRS), beta, C.getID(mRS), 0, 0, 0, 0);
     }
     public void CSYR2K(@Uplo int Uplo, @Transpose int Trans, Float2 alpha, Allocation A, Allocation B, Float2 beta, Allocation C) {
         validateUplo(Uplo);
@@ -1299,7 +1299,7 @@
         } else {
             K = A.getType().getX();
         }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ssyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0);
+        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_csyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0);
     }
     public void ZSYR2K(@Uplo int Uplo, @Transpose int Trans, Double2 alpha, Allocation A, Allocation B, Double2 beta, Allocation C) {
         validateUplo(Uplo);
@@ -1310,7 +1310,7 @@
         } else {
             K = A.getType().getX();
         }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ssyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0);
+        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zsyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0);
     }
 
     static void validateTRMM(Element e, @Side int Side, @Transpose int TransA, Allocation A, Allocation B) {
@@ -1351,21 +1351,21 @@
         validateUplo(Uplo);
         validateDiag(Diag);
         validateTRMM(Element.F64(mRS), Side, TransA, A, B);
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_strmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                        alpha, A.getID(mRS), B.getID(mRS), 0.f, 0, 0, 0, 0, 0);
+        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
+                                        alpha, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0);
     }
     public void CTRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Float2 alpha, Allocation A, Allocation B) {
         validateUplo(Uplo);
         validateDiag(Diag);
         validateTRMM(Element.F32_2(mRS), Side, TransA, A, B);
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_strmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
+        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
                                          alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0);
     }
     public void ZTRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Double2 alpha, Allocation A, Allocation B) {
         validateUplo(Uplo);
         validateDiag(Diag);
         validateTRMM(Element.F64_2(mRS), Side, TransA, A, B);
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_strmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
+        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
                                    alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0);
     }
 
@@ -1409,21 +1409,21 @@
         validateUplo(Uplo);
         validateDiag(Diag);
         validateTRSM(Element.F64(mRS), Side, TransA, A, B);
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_strsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
+        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
                                         alpha, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0);
     }
     public void CTRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Float2 alpha, Allocation A, Allocation B) {
         validateUplo(Uplo);
         validateDiag(Diag);
         validateTRSM(Element.F32_2(mRS), Side, TransA, A, B);
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_strsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
+        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
                                          alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0);
     }
     public void ZTRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Double2 alpha, Allocation A, Allocation B) {
         validateUplo(Uplo);
         validateDiag(Diag);
         validateTRSM(Element.F64_2(mRS), Side, TransA, A, B);
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_strsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
+        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
                                    alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0);
     }
 
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index ef82bb7..1019faa 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -48,6 +48,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.util.Log;
 
 import java.io.FileDescriptor;
@@ -443,6 +444,16 @@
     /** Internal death rec list */
     Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();
 
+    @Override
+    public boolean isBleScanAlwaysAvailable() {
+        try {
+            return (Settings.Global.getInt(mContentResolver,
+                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0;
+        } catch (SettingNotFoundException e) {
+        }
+        return false;
+    }
+
     public int updateBleAppCount(IBinder token, boolean enable) {
         if (enable) {
             ClientDeathRecipient r = mBleApps.get(token);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 0f3b4e6..fb98d94 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -264,9 +264,9 @@
         return mInstaller.execute(builder.toString());
     }
 
-    public int moveUserDataDirs(String fromUuid, String toUuid, String packageName, int appId,
-            String seinfo) {
-        StringBuilder builder = new StringBuilder("mvuserdata");
+    public int moveCompleteApp(String fromUuid, String toUuid, String packageName,
+            String dataAppName, int appId, String seinfo) {
+        StringBuilder builder = new StringBuilder("mvcompleteapp");
         builder.append(' ');
         builder.append(escapeNull(fromUuid));
         builder.append(' ');
@@ -274,6 +274,8 @@
         builder.append(' ');
         builder.append(packageName);
         builder.append(' ');
+        builder.append(dataAppName);
+        builder.append(' ');
         builder.append(appId);
         builder.append(' ');
         builder.append(seinfo);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 11ab042..9ad594e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -171,6 +171,7 @@
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.LogPrinter;
+import android.util.MathUtils;
 import android.util.PrintStreamPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -241,6 +242,8 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
@@ -5049,8 +5052,7 @@
                             + " better than installed " + ps.versionCode);
 
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
-                            ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
-                            getAppDexInstructionSets(ps));
+                            ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                     synchronized (mInstallLock) {
                         args.cleanUpResourcesLI();
                     }
@@ -5116,8 +5118,7 @@
                             + " reverting from " + ps.codePathString + ": new version "
                             + pkg.mVersionCode + " better than installed " + ps.versionCode);
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
-                            ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
-                            getAppDexInstructionSets(ps));
+                            ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                     synchronized (mInstallLock) {
                         args.cleanUpResourcesLI();
                     }
@@ -8499,46 +8500,53 @@
         }
     };
 
-    static final void sendPackageBroadcast(String action, String pkg,
-            Bundle extras, String targetPkg, IIntentReceiver finishedReceiver,
-            int[] userIds) {
-        IActivityManager am = ActivityManagerNative.getDefault();
-        if (am != null) {
-            try {
-                if (userIds == null) {
-                    userIds = am.getRunningUserIds();
+    final void sendPackageBroadcast(final String action, final String pkg,
+            final Bundle extras, final String targetPkg, final IIntentReceiver finishedReceiver,
+            final int[] userIds) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    final IActivityManager am = ActivityManagerNative.getDefault();
+                    if (am == null) return;
+                    final int[] resolvedUserIds;
+                    if (userIds == null) {
+                        resolvedUserIds = am.getRunningUserIds();
+                    } else {
+                        resolvedUserIds = userIds;
+                    }
+                    for (int id : resolvedUserIds) {
+                        final Intent intent = new Intent(action,
+                                pkg != null ? Uri.fromParts("package", pkg, null) : null);
+                        if (extras != null) {
+                            intent.putExtras(extras);
+                        }
+                        if (targetPkg != null) {
+                            intent.setPackage(targetPkg);
+                        }
+                        // Modify the UID when posting to other users
+                        int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                        if (uid > 0 && UserHandle.getUserId(uid) != id) {
+                            uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
+                            intent.putExtra(Intent.EXTRA_UID, uid);
+                        }
+                        intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                        if (DEBUG_BROADCASTS) {
+                            RuntimeException here = new RuntimeException("here");
+                            here.fillInStackTrace();
+                            Slog.d(TAG, "Sending to user " + id + ": "
+                                    + intent.toShortString(false, true, false, false)
+                                    + " " + intent.getExtras(), here);
+                        }
+                        am.broadcastIntent(null, intent, null, finishedReceiver,
+                                0, null, null, null, android.app.AppOpsManager.OP_NONE,
+                                finishedReceiver != null, false, id);
+                    }
+                } catch (RemoteException ex) {
                 }
-                for (int id : userIds) {
-                    final Intent intent = new Intent(action,
-                            pkg != null ? Uri.fromParts("package", pkg, null) : null);
-                    if (extras != null) {
-                        intent.putExtras(extras);
-                    }
-                    if (targetPkg != null) {
-                        intent.setPackage(targetPkg);
-                    }
-                    // Modify the UID when posting to other users
-                    int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                    if (uid > 0 && UserHandle.getUserId(uid) != id) {
-                        uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
-                        intent.putExtra(Intent.EXTRA_UID, uid);
-                    }
-                    intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                    if (DEBUG_BROADCASTS) {
-                        RuntimeException here = new RuntimeException("here");
-                        here.fillInStackTrace();
-                        Slog.d(TAG, "Sending to user " + id + ": "
-                                + intent.toShortString(false, true, false, false)
-                                + " " + intent.getExtras(), here);
-                    }
-                    am.broadcastIntent(null, intent, null, finishedReceiver,
-                            0, null, null, null, android.app.AppOpsManager.OP_NONE,
-                            finishedReceiver != null, false, id);
-                }
-            } catch (RemoteException ex) {
             }
-        }
+        });
     }
 
     /**
@@ -8665,8 +8673,8 @@
         final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
-        msg.obj = new InstallParams(origin, observer, installFlags,
-                installerPackageName, null, verificationParams, user, packageAbiOverride);
+        msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
+                null, verificationParams, user, packageAbiOverride);
         mHandler.sendMessage(msg);
     }
 
@@ -8684,7 +8692,7 @@
         }
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
-        msg.obj = new InstallParams(origin, observer, params.installFlags,
+        msg.obj = new InstallParams(origin, null, observer, params.installFlags,
                 installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride);
         mHandler.sendMessage(msg);
     }
@@ -9517,8 +9525,30 @@
         }
     }
 
+    class MoveInfo {
+        final int moveId;
+        final String fromUuid;
+        final String toUuid;
+        final String packageName;
+        final String dataAppName;
+        final int appId;
+        final String seinfo;
+
+        public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName,
+                String dataAppName, int appId, String seinfo) {
+            this.moveId = moveId;
+            this.fromUuid = fromUuid;
+            this.toUuid = toUuid;
+            this.packageName = packageName;
+            this.dataAppName = dataAppName;
+            this.appId = appId;
+            this.seinfo = seinfo;
+        }
+    }
+
     class InstallParams extends HandlerParams {
         final OriginInfo origin;
+        final MoveInfo move;
         final IPackageInstallObserver2 observer;
         int installFlags;
         final String installerPackageName;
@@ -9528,11 +9558,12 @@
         private int mRet;
         final String packageAbiOverride;
 
-        InstallParams(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags,
-                String installerPackageName, String volumeUuid,
+        InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
+                int installFlags, String installerPackageName, String volumeUuid,
                 VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
             super(user);
             this.origin = origin;
+            this.move = move;
             this.observer = observer;
             this.installFlags = installFlags;
             this.installerPackageName = installerPackageName;
@@ -9911,7 +9942,9 @@
     }
 
     private InstallArgs createInstallArgs(InstallParams params) {
-        if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
+        if (params.move != null) {
+            return new MoveInstallArgs(params);
+        } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
             return new AsecInstallArgs(params);
         } else {
             return new FileInstallArgs(params);
@@ -9923,7 +9956,7 @@
      * when cleaning up old installs, or used as a move source.
      */
     private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
-            String resourcePath, String nativeLibraryRoot, String[] instructionSets) {
+            String resourcePath, String[] instructionSets) {
         final boolean isInAsec;
         if (installOnExternalAsec(installFlags)) {
             /* Apps on SD card are always in ASEC containers. */
@@ -9943,14 +9976,15 @@
             return new AsecInstallArgs(codePath, instructionSets,
                     installOnExternalAsec(installFlags), installForwardLocked(installFlags));
         } else {
-            return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot,
-                    instructionSets);
+            return new FileInstallArgs(codePath, resourcePath, instructionSets);
         }
     }
 
     static abstract class InstallArgs {
         /** @see InstallParams#origin */
         final OriginInfo origin;
+        /** @see InstallParams#move */
+        final MoveInfo move;
 
         final IPackageInstallObserver2 observer;
         // Always refers to PackageManager flags only
@@ -9966,10 +10000,12 @@
         // if we move dex files under the common app path.
         /* nullable */ String[] instructionSets;
 
-        InstallArgs(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags,
-                String installerPackageName, String volumeUuid, ManifestDigest manifestDigest,
-                UserHandle user, String[] instructionSets, String abiOverride) {
+        InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
+                int installFlags, String installerPackageName, String volumeUuid,
+                ManifestDigest manifestDigest, UserHandle user, String[] instructionSets,
+                String abiOverride) {
             this.origin = origin;
+            this.move = move;
             this.installFlags = installFlags;
             this.observer = observer;
             this.installerPackageName = installerPackageName;
@@ -9994,12 +10030,10 @@
         abstract String getCodePath();
         /** @see PackageSettingBase#resourcePathString */
         abstract String getResourcePath();
-        abstract String getLegacyNativeLibraryPath();
 
         // Need installer lock especially for dex file removal.
         abstract void cleanUpResourcesLI();
         abstract boolean doPostDeleteLI(boolean delete);
-        abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException;
 
         /**
          * Called before the source arguments are copied. This is used mostly
@@ -10060,7 +10094,6 @@
     class FileInstallArgs extends InstallArgs {
         private File codeFile;
         private File resourceFile;
-        private File legacyNativeLibraryPath;
 
         // Example topology:
         // /data/app/com.example/base.apk
@@ -10071,7 +10104,7 @@
 
         /** New install */
         FileInstallArgs(InstallParams params) {
-            super(params.origin, params.observer, params.installFlags,
+            super(params.origin, params.move, params.observer, params.installFlags,
                     params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride);
             if (isFwdLocked()) {
@@ -10080,21 +10113,11 @@
         }
 
         /** Existing install */
-        FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath,
-                String[] instructionSets) {
-            super(OriginInfo.fromNothing(), null, 0, null, null, null, null, instructionSets, null);
+        FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
+            super(OriginInfo.fromNothing(), null, null, 0, null, null, null, null, instructionSets,
+                    null);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
-            this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ?
-                    new File(legacyNativeLibraryPath) : null;
-        }
-
-        boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
-            final long sizeBytes = imcs.calculateInstalledSize(origin.file.getAbsolutePath(),
-                    isFwdLocked(), abiOverride);
-
-            final StorageManager storage = StorageManager.from(mContext);
-            return (sizeBytes <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
@@ -10166,46 +10189,46 @@
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
                 return false;
-            } else {
-                final File targetDir = codeFile.getParentFile();
-                final File beforeCodeFile = codeFile;
-                final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
-
-                Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
-                try {
-                    Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
-                } catch (ErrnoException e) {
-                    Slog.d(TAG, "Failed to rename", e);
-                    return false;
-                }
-
-                if (!SELinux.restoreconRecursive(afterCodeFile)) {
-                    Slog.d(TAG, "Failed to restorecon");
-                    return false;
-                }
-
-                // Reflect the rename internally
-                codeFile = afterCodeFile;
-                resourceFile = afterCodeFile;
-
-                // Reflect the rename in scanned details
-                pkg.codePath = afterCodeFile.getAbsolutePath();
-                pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
-                        pkg.baseCodePath);
-                pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
-                        pkg.splitCodePaths);
-
-                // Reflect the rename in app info
-                pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
-                pkg.applicationInfo.setCodePath(pkg.codePath);
-                pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
-                pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
-                pkg.applicationInfo.setResourcePath(pkg.codePath);
-                pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
-                pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
-
-                return true;
             }
+
+            final File targetDir = codeFile.getParentFile();
+            final File beforeCodeFile = codeFile;
+            final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
+
+            Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
+            try {
+                Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
+            } catch (ErrnoException e) {
+                Slog.d(TAG, "Failed to rename", e);
+                return false;
+            }
+
+            if (!SELinux.restoreconRecursive(afterCodeFile)) {
+                Slog.d(TAG, "Failed to restorecon");
+                return false;
+            }
+
+            // Reflect the rename internally
+            codeFile = afterCodeFile;
+            resourceFile = afterCodeFile;
+
+            // Reflect the rename in scanned details
+            pkg.codePath = afterCodeFile.getAbsolutePath();
+            pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+                    pkg.baseCodePath);
+            pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+                    pkg.splitCodePaths);
+
+            // Reflect the rename in app info
+            pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
+            pkg.applicationInfo.setCodePath(pkg.codePath);
+            pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
+            pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
+            pkg.applicationInfo.setResourcePath(pkg.codePath);
+            pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
+            pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+
+            return true;
         }
 
         int doPostInstall(int status, int uid) {
@@ -10225,11 +10248,6 @@
             return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
         }
 
-        @Override
-        String getLegacyNativeLibraryPath() {
-            return (legacyNativeLibraryPath != null) ? legacyNativeLibraryPath.getAbsolutePath() : null;
-        }
-
         private boolean cleanUp() {
             if (codeFile == null || !codeFile.exists()) {
                 return false;
@@ -10245,13 +10263,6 @@
                 resourceFile.delete();
             }
 
-            if (legacyNativeLibraryPath != null && !FileUtils.contains(codeFile, legacyNativeLibraryPath)) {
-                if (!FileUtils.deleteContents(legacyNativeLibraryPath)) {
-                    Slog.w(TAG, "Couldn't delete native library directory " + legacyNativeLibraryPath);
-                }
-                legacyNativeLibraryPath.delete();
-            }
-
             return true;
         }
 
@@ -10315,11 +10326,10 @@
         String cid;
         String packagePath;
         String resourcePath;
-        String legacyNativeLibraryDir;
 
         /** New install */
         AsecInstallArgs(InstallParams params) {
-            super(params.origin, params.observer, params.installFlags,
+            super(params.origin, params.move, params.observer, params.installFlags,
                     params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride);
         }
@@ -10327,7 +10337,7 @@
         /** Existing install */
         AsecInstallArgs(String fullCodePath, String[] instructionSets,
                         boolean isExternal, boolean isForwardLocked) {
-            super(OriginInfo.fromNothing(), null, (isExternal ? INSTALL_EXTERNAL : 0)
+            super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
                     instructionSets, null);
             // Hackily pretend we're still looking at a full code path
@@ -10344,7 +10354,7 @@
         }
 
         AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
-            super(OriginInfo.fromNothing(), null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
+            super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
                     | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
                     instructionSets, null);
             this.cid = cid;
@@ -10355,21 +10365,6 @@
             cid = mInstallerService.allocateExternalStageCidLegacy();
         }
 
-        boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
-            final long sizeBytes = imcs.calculateInstalledSize(packagePath, isFwdLocked(),
-                    abiOverride);
-
-            final File target;
-            if (isExternalAsec()) {
-                target = new UserEnvironment(UserHandle.USER_OWNER).getExternalStorageDirectory();
-            } else {
-                target = Environment.getDataDirectory();
-            }
-
-            final StorageManager storage = StorageManager.from(mContext);
-            return (sizeBytes <= storage.getStorageBytesUntilLow(target));
-        }
-
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
             if (origin.staged) {
                 Slog.d(TAG, origin.cid + " already staged; skipping copy");
@@ -10410,11 +10405,6 @@
             return resourcePath;
         }
 
-        @Override
-        String getLegacyNativeLibraryPath() {
-            return legacyNativeLibraryDir;
-        }
-
         int doPreInstall(int status) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 // Destroy container
@@ -10513,8 +10503,6 @@
                 packagePath = mountFile.getAbsolutePath();
                 resourcePath = packagePath;
             }
-
-            legacyNativeLibraryDir = new File(mountFile, LIB_DIR_NAME).getAbsolutePath();
         }
 
         int doPostInstall(int status, int uid) {
@@ -10576,8 +10564,6 @@
             removeDexFiles(allCodePaths, instructionSets);
         }
 
-
-
         String getPackageName() {
             return getAsecPackageName(cid);
         }
@@ -10626,6 +10612,108 @@
         }
     }
 
+    /**
+     * Logic to handle movement of existing installed applications.
+     */
+    class MoveInstallArgs extends InstallArgs {
+        private File codeFile;
+        private File resourceFile;
+
+        /** New install */
+        MoveInstallArgs(InstallParams params) {
+            super(params.origin, params.move, params.observer, params.installFlags,
+                    params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+                    params.getUser(), null /* instruction sets */, params.packageAbiOverride);
+        }
+
+        int copyApk(IMediaContainerService imcs, boolean temp) {
+            Slog.d(TAG, "Moving " + move.packageName + " from " + move.fromUuid + " to "
+                    + move.toUuid);
+            synchronized (mInstaller) {
+                if (mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
+                        move.dataAppName, move.appId, move.seinfo) != 0) {
+                    return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+                }
+            }
+
+            codeFile = new File(Environment.getDataAppDirectory(move.toUuid), move.dataAppName);
+            resourceFile = codeFile;
+            Slog.d(TAG, "codeFile after move is " + codeFile);
+
+            return PackageManager.INSTALL_SUCCEEDED;
+        }
+
+        int doPreInstall(int status) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                cleanUp();
+            }
+            return status;
+        }
+
+        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                cleanUp();
+                return false;
+            }
+
+            // Reflect the move in app info
+            pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
+            pkg.applicationInfo.setCodePath(pkg.codePath);
+            pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
+            pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
+            pkg.applicationInfo.setResourcePath(pkg.codePath);
+            pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
+            pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+
+            return true;
+        }
+
+        int doPostInstall(int status, int uid) {
+            if (status != PackageManager.INSTALL_SUCCEEDED) {
+                cleanUp();
+            }
+            return status;
+        }
+
+        @Override
+        String getCodePath() {
+            return (codeFile != null) ? codeFile.getAbsolutePath() : null;
+        }
+
+        @Override
+        String getResourcePath() {
+            return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
+        }
+
+        private boolean cleanUp() {
+            if (codeFile == null || !codeFile.exists()) {
+                return false;
+            }
+
+            if (codeFile.isDirectory()) {
+                mInstaller.rmPackageDir(codeFile.getAbsolutePath());
+            } else {
+                codeFile.delete();
+            }
+
+            if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
+                resourceFile.delete();
+            }
+
+            return true;
+        }
+
+        void cleanUpResourcesLI() {
+            cleanUp();
+        }
+
+        boolean doPostDeleteLI(boolean delete) {
+            // XXX err, shouldn't we respect the delete flag?
+            cleanUpResourcesLI();
+            return true;
+        }
+    }
+
     static String getAsecPackageName(String packageCid) {
         int idx = packageCid.lastIndexOf("-");
         if (idx == -1) {
@@ -11012,7 +11100,6 @@
                 res.removedInfo.args = createInstallArgsForExisting(0,
                         deletedPackage.applicationInfo.getCodePath(),
                         deletedPackage.applicationInfo.getResourcePath(),
-                        deletedPackage.applicationInfo.nativeLibraryRootDir,
                         getAppDexInstructionSets(deletedPackage.applicationInfo));
             } else {
                 res.removedInfo.args = null;
@@ -11320,8 +11407,10 @@
             return;
         }
 
-        // If app directory is not writable, dexopt will be called after the rename
-        if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+        if (args.move != null) {
+            // We did an in-place move, so dex is ready to roll
+            scanFlags |= SCAN_NO_DEX;
+        } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
             // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
             scanFlags |= SCAN_NO_DEX;
             // Run dexopt before old package gets removed, to minimize time when app is unavailable
@@ -11704,7 +11793,7 @@
         return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
     }
 
-    static class PackageRemovedInfo {
+    class PackageRemovedInfo {
         String removedPackage;
         int uid = -1;
         int removedAppId = -1;
@@ -11949,8 +12038,7 @@
         // Delete application code and resources
         if (deleteCodeAndResources && (outInfo != null)) {
             outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
-                    ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,
-                    getAppDexInstructionSets(ps));
+                    ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
             if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
         }
         return true;
@@ -13119,8 +13207,8 @@
         enforceCrossUserPermission(uid, userId, true, true, "stop package");
         // writer
         synchronized (mPackages) {
-            if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission,
-                    uid, userId)) {
+            if (mSettings.setPackageStoppedStateLPw(this, packageName, stopped,
+                    allowedByPermission, uid, userId)) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
@@ -14180,17 +14268,16 @@
     private void loadPrivatePackages(VolumeInfo vol) {
         final ArrayList<ApplicationInfo> loaded = new ArrayList<>();
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
+        synchronized (mInstallLock) {
         synchronized (mPackages) {
             final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
             for (PackageSetting ps : packages) {
-                synchronized (mInstallLock) {
-                    final PackageParser.Package pkg;
-                    try {
-                        pkg = scanPackageLI(ps.codePath, parseFlags, 0, 0, null);
-                        loaded.add(pkg.applicationInfo);
-                    } catch (PackageManagerException e) {
-                        Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
-                    }
+                final PackageParser.Package pkg;
+                try {
+                    pkg = scanPackageLI(ps.codePath, parseFlags, 0, 0, null);
+                    loaded.add(pkg.applicationInfo);
+                } catch (PackageManagerException e) {
+                    Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
                 }
             }
 
@@ -14198,6 +14285,7 @@
 
             mSettings.writeLPr();
         }
+        }
 
         Slog.d(TAG, "Loaded packages " + loaded);
         sendResourcesChangedBroadcast(true, false, loaded, null);
@@ -14205,24 +14293,25 @@
 
     private void unloadPrivatePackages(VolumeInfo vol) {
         final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
+        synchronized (mInstallLock) {
         synchronized (mPackages) {
             final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
             for (PackageSetting ps : packages) {
                 if (ps.pkg == null) continue;
-                synchronized (mInstallLock) {
-                    final ApplicationInfo info = ps.pkg.applicationInfo;
-                    final PackageRemovedInfo outInfo = new PackageRemovedInfo();
-                    if (deletePackageLI(ps.name, null, false, null, null,
-                            PackageManager.DELETE_KEEP_DATA, outInfo, false)) {
-                        unloaded.add(info);
-                    } else {
-                        Slog.w(TAG, "Failed to unload " + ps.codePath);
-                    }
+
+                final ApplicationInfo info = ps.pkg.applicationInfo;
+                final PackageRemovedInfo outInfo = new PackageRemovedInfo();
+                if (deletePackageLI(ps.name, null, false, null, null,
+                        PackageManager.DELETE_KEEP_DATA, outInfo, false)) {
+                    unloaded.add(info);
+                } else {
+                    Slog.w(TAG, "Failed to unload " + ps.codePath);
                 }
             }
 
             mSettings.writeLPr();
         }
+        }
 
         Slog.d(TAG, "Unloaded packages " + unloaded);
         sendResourcesChangedBroadcast(false, false, unloaded, null);
@@ -14255,6 +14344,7 @@
     private void movePackageInternal(final String packageName, final String volumeUuid,
             final int moveId) throws PackageManagerException {
         final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final PackageManager pm = mContext.getPackageManager();
 
         final boolean currentAsec;
@@ -14284,11 +14374,17 @@
                         "Package already moved to " + volumeUuid);
             }
 
+            final File probe = new File(pkg.codePath);
+            final File probeOat = new File(probe, "oat");
+            if (!probe.isDirectory() || !probeOat.isDirectory()) {
+                throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+                        "Move only supported for modern cluster style installs");
+            }
+
             if (ps.frozen) {
                 throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
                         "Failed to move already frozen package");
             }
-
             ps.frozen = true;
 
             currentAsec = pkg.applicationInfo.isForwardLocked()
@@ -14302,7 +14398,7 @@
             label = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo));
         }
 
-        // Now that we're guarded by frozen state, kill app during upgrade
+        // Now that we're guarded by frozen state, kill app during move
         killApplication(packageName, appId, "move pkg");
 
         final Bundle extras = new Bundle();
@@ -14311,16 +14407,18 @@
         mMoveCallbacks.notifyCreated(moveId, extras);
 
         int installFlags;
-        final boolean moveData;
+        final boolean moveCompleteApp;
+        final File measurePath;
 
         if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
             installFlags = INSTALL_INTERNAL;
-            moveData = !currentAsec;
+            moveCompleteApp = !currentAsec;
+            measurePath = Environment.getDataAppDirectory(volumeUuid);
         } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
             installFlags = INSTALL_EXTERNAL;
-            moveData = false;
+            moveCompleteApp = false;
+            measurePath = storage.getPrimaryPhysicalVolume().getPath();
         } else {
-            final StorageManager storage = mContext.getSystemService(StorageManager.class);
             final VolumeInfo volume = storage.findVolumeByUuid(volumeUuid);
             if (volume == null || volume.getType() != VolumeInfo.TYPE_PRIVATE
                     || !volume.isMountedWritable()) {
@@ -14332,26 +14430,38 @@
             Preconditions.checkState(!currentAsec);
 
             installFlags = INSTALL_INTERNAL;
-            moveData = true;
+            moveCompleteApp = true;
+            measurePath = Environment.getDataAppDirectory(volumeUuid);
         }
 
-        Slog.d(TAG, "Moving " + packageName + " from " + currentVolumeUuid + " to " + volumeUuid);
-        mMoveCallbacks.notifyStatusChanged(moveId, 10);
-
-        if (moveData) {
-            synchronized (mInstallLock) {
-                // TODO: split this into separate copy and delete operations
-                if (mInstaller.moveUserDataDirs(currentVolumeUuid, volumeUuid, packageName, appId,
-                        seinfo) != 0) {
-                    unfreezePackage(packageName);
-                    throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
-                            "Failed to move private data to " + volumeUuid);
-                }
+        final PackageStats stats = new PackageStats(null, -1);
+        synchronized (mInstaller) {
+            if (!getPackageSizeInfoLI(packageName, -1, stats)) {
+                unfreezePackage(packageName);
+                throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+                        "Failed to measure package size");
             }
         }
 
-        mMoveCallbacks.notifyStatusChanged(moveId, 50);
+        Slog.d(TAG, "Measured code size " + stats.codeSize + ", data size " + stats.dataSize);
 
+        final long startFreeBytes = measurePath.getFreeSpace();
+        final long sizeBytes;
+        if (moveCompleteApp) {
+            sizeBytes = stats.codeSize + stats.dataSize;
+        } else {
+            sizeBytes = stats.codeSize;
+        }
+
+        if (sizeBytes > storage.getStorageBytesUntilLow(measurePath)) {
+            unfreezePackage(packageName);
+            throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+                    "Not enough free space to move");
+        }
+
+        mMoveCallbacks.notifyStatusChanged(moveId, 10);
+
+        final CountDownLatch installedLatch = new CountDownLatch(1);
         final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
             @Override
             public void onUserActionRequired(Intent intent) throws RemoteException {
@@ -14364,6 +14474,8 @@
                 Slog.d(TAG, "Install result for move: "
                         + PackageManager.installStatusToString(returnCode, msg));
 
+                installedLatch.countDown();
+
                 // Regardless of success or failure of the move operation,
                 // always unfreeze the package
                 unfreezePackage(packageName);
@@ -14386,13 +14498,40 @@
             }
         };
 
-        // Treat a move like reinstalling an existing app, which ensures that we
-        // process everythign uniformly, like unpacking native libraries.
+        final MoveInfo move;
+        if (moveCompleteApp) {
+            // Kick off a thread to report progress estimates
+            new Thread() {
+                @Override
+                public void run() {
+                    while (true) {
+                        try {
+                            if (installedLatch.await(1, TimeUnit.SECONDS)) {
+                                break;
+                            }
+                        } catch (InterruptedException ignored) {
+                        }
+
+                        final long deltaFreeBytes = startFreeBytes - measurePath.getFreeSpace();
+                        final int progress = 10 + (int) MathUtils.constrain(
+                                ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
+                        mMoveCallbacks.notifyStatusChanged(moveId, progress);
+                    }
+                }
+            }.start();
+
+            final String dataAppName = codeFile.getName();
+            move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
+                    dataAppName, appId, seinfo);
+        } else {
+            move = null;
+        }
+
         installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
-        msg.obj = new InstallParams(origin, installObserver, installFlags,
+        msg.obj = new InstallParams(origin, move, installObserver, installFlags,
                 installerPackageName, volumeUuid, null, user, packageAbiOverride);
         mHandler.sendMessage(msg);
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f9c248d..7cf7c7c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3603,8 +3603,8 @@
         return pkg.getCurrentEnabledStateLPr(classNameStr, userId);
     }
 
-    boolean setPackageStoppedStateLPw(String packageName, boolean stopped,
-            boolean allowedByPermission, int uid, int userId) {
+    boolean setPackageStoppedStateLPw(PackageManagerService yucky, String packageName,
+            boolean stopped, boolean allowedByPermission, int uid, int userId) {
         int appId = UserHandle.getAppId(uid);
         final PackageSetting pkgSetting = mPackages.get(packageName);
         if (pkgSetting == null) {
@@ -3628,7 +3628,7 @@
             // pkgSetting.pkg.mSetStopped = stopped;
             if (pkgSetting.getNotLaunched(userId)) {
                 if (pkgSetting.installerPackageName != null) {
-                    PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
+                    yucky.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
                             pkgSetting.name, null,
                             pkgSetting.installerPackageName, null, new int[] {userId});
                 }
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 3d8905d..8f4fb51 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -64,7 +64,8 @@
 static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject,
         jobject graphicBuffer, jobject bitmapHandle) {
 
-    SkBitmap& bitmap = *GraphicsJNI::getSkBitmapDeprecated(env, bitmapHandle);
+    SkBitmap bitmap;
+    GraphicsJNI::getSkBitmap(env, bitmapHandle, &bitmap);
     SkAutoLockPixels alp(bitmap);
 
     // The goal of this method is to copy the bitmap into the GraphicBuffer
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f801d2d..c886c4c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4065,11 +4065,12 @@
         }
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         synchronized (this) {
-            if (mDeviceOwner != null) {
-                return mDeviceOwner.getDeviceOwnerName();
+            if (mDeviceOwner == null || !mDeviceOwner.hasDeviceOwner()) {
+                return null;
             }
+            String deviceOwnerPackage = mDeviceOwner.getDeviceOwnerPackageName();
+            return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_OWNER);
         }
-        return null;
     }
 
     // Returns the active device owner or null if there is no device owner.
@@ -4426,7 +4427,6 @@
         if (profileOwner == null) {
             return null;
         }
-
         DevicePolicyData policy = getUserData(userHandle);
         final int n = policy.mAdminList.size();
         for (int i = 0; i < n; i++) {
@@ -4444,13 +4444,37 @@
             return null;
         }
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
-
-        synchronized (this) {
-            if (mDeviceOwner != null) {
-                return mDeviceOwner.getProfileOwnerName(userHandle);
-            }
+        ComponentName profileOwner = getProfileOwner(userHandle);
+        if (profileOwner == null) {
+            return null;
         }
-        return null;
+        return getApplicationLabel(profileOwner.getPackageName(), userHandle);
+    }
+
+    /**
+     * Canonical name for a given package.
+     */
+    private String getApplicationLabel(String packageName, int userHandle) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            final Context userContext;
+            try {
+                UserHandle handle = new UserHandle(userHandle);
+                userContext = mContext.createPackageContextAsUser(packageName, 0, handle);
+            } catch (PackageManager.NameNotFoundException nnfe) {
+                Log.w(LOG_TAG, packageName + " is not installed for user " + userHandle, nnfe);
+                return null;
+            }
+            ApplicationInfo appInfo = userContext.getApplicationInfo();
+            CharSequence result = null;
+            if (appInfo != null) {
+                PackageManager pm = userContext.getPackageManager();
+                result = pm.getApplicationLabel(appInfo);
+            }
+            return result != null ? result.toString() : null;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d92c0c7..9273939 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -19,10 +19,12 @@
 import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 
 import java.lang.String;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -608,7 +610,7 @@
     private final List<String> mChildrenIds = new ArrayList<>();
     private final List<Call> mChildren = new ArrayList<>();
     private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
-    private final List<Callback> mCallbacks = new CopyOnWriteArrayList<>();
+    private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
     private final List<Call> mConferenceableCalls = new ArrayList<>();
     private final List<Call> mUnmodifiableConferenceableCalls =
             Collections.unmodifiableList(mConferenceableCalls);
@@ -850,7 +852,20 @@
      * @param callback A {@code Callback}.
      */
     public void registerCallback(Callback callback) {
-        mCallbacks.add(callback);
+        registerCallback(callback, new Handler());
+    }
+
+    /**
+     * Registers a callback to this {@code Call}.
+     *
+     * @param callback A {@code Callback}.
+     * @param handler A handler which command and status changes will be delivered to.
+     */
+    public void registerCallback(Callback callback, Handler handler) {
+        unregisterCallback(callback);
+        if (callback != null && handler != null) {
+            mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
+        }
     }
 
     /**
@@ -860,7 +875,12 @@
      */
     public void unregisterCallback(Callback callback) {
         if (callback != null) {
-            mCallbacks.remove(callback);
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                if (record.getCallback() == callback) {
+                    mCallbackRecords.remove(record);
+                    break;
+                }
+            }
         }
     }
 
@@ -1021,57 +1041,120 @@
         }
     }
 
-    private void fireStateChanged(int newState) {
-        for (Callback callback : mCallbacks) {
-            callback.onStateChanged(this, newState);
+    private void fireStateChanged(final int newState) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onStateChanged(call, newState);
+                }
+            });
         }
     }
 
-    private void fireParentChanged(Call newParent) {
-        for (Callback callback : mCallbacks) {
-            callback.onParentChanged(this, newParent);
+    private void fireParentChanged(final Call newParent) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onParentChanged(call, newParent);
+                }
+            });
         }
     }
 
-    private void fireChildrenChanged(List<Call> children) {
-        for (Callback callback : mCallbacks) {
-            callback.onChildrenChanged(this, children);
+    private void fireChildrenChanged(final List<Call> children) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onChildrenChanged(call, children);
+                }
+            });
         }
     }
 
-    private void fireDetailsChanged(Details details) {
-        for (Callback callback : mCallbacks) {
-            callback.onDetailsChanged(this, details);
+    private void fireDetailsChanged(final Details details) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onDetailsChanged(call, details);
+                }
+            });
         }
     }
 
-    private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
-        for (Callback callback : mCallbacks) {
-            callback.onCannedTextResponsesLoaded(this, cannedTextResponses);
+    private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
+                }
+            });
         }
     }
 
-    private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
-        for (Callback callback : mCallbacks) {
-            callback.onVideoCallChanged(this, videoCall);
+    private void fireVideoCallChanged(final InCallService.VideoCall videoCall) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onVideoCallChanged(call, videoCall);
+                }
+            });
         }
     }
 
-    private void firePostDialWait(String remainingPostDialSequence) {
-        for (Callback callback : mCallbacks) {
-            callback.onPostDialWait(this, remainingPostDialSequence);
+    private void firePostDialWait(final String remainingPostDialSequence) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onPostDialWait(call, remainingPostDialSequence);
+                }
+            });
         }
     }
 
     private void fireCallDestroyed() {
-        for (Callback callback : mCallbacks) {
-            callback.onCallDestroyed(this);
+        for (CallbackRecord<Callback> record: mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onCallDestroyed(call);
+                }
+            });
         }
     }
 
     private void fireConferenceableCallsChanged() {
-        for (Callback callback : mCallbacks) {
-            callback.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls);
+                }
+            });
         }
     }
 }
diff --git a/telecomm/java/android/telecom/CallbackRecord.java b/telecomm/java/android/telecom/CallbackRecord.java
new file mode 100644
index 0000000..1a81925
--- /dev/null
+++ b/telecomm/java/android/telecom/CallbackRecord.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.os.Handler;
+
+
+/**
+ * This class is used to associate a generic callback of type T with a handler to which commands and
+ * status updates will be delivered to.
+ *
+ * @hide
+ */
+class CallbackRecord<T> {
+    private final T mCallback;
+    private final Handler mHandler;
+
+    public CallbackRecord(T callback, Handler handler) {
+        mCallback = callback;
+        mHandler = handler;
+    }
+
+    public T getCallback() {
+        return mCallback;
+    }
+
+    public Handler getHandler() {
+        return mHandler;
+    }
+}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 2c8415a..3cb4e87 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -362,6 +362,9 @@
      */
     public static abstract class VideoCall {
 
+        /** @hide */
+        public abstract void destroy();
+
         /**
          * Registers a callback to receive commands and state changes for video calls.
          *
@@ -370,9 +373,17 @@
         public abstract void registerCallback(VideoCall.Callback callback);
 
         /**
+         * Registers a callback to receive commands and state changes for video calls.
+         *
+         * @param callback The video call callback.
+         * @param handler A handler which commands and status changes will be delivered to.
+         */
+        public abstract void registerCallback(VideoCall.Callback callback, Handler handler);
+
+        /**
          * Clears the video call listener set via {@link #registerCallback}.
          */
-        public abstract void unregisterCallback();
+        public abstract void unregisterCallback(VideoCall.Callback callback);
 
         /**
          * Sets the camera to be used for video recording in a video call.
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 3d9acda..4cdfd2e 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -125,7 +125,7 @@
 
         InCallService.VideoCall videoCall = call.getVideoCall();
         if (videoCall != null) {
-            videoCall.unregisterCallback();
+            videoCall.destroy();
         }
         fireCallRemoved(call);
     }
@@ -174,7 +174,7 @@
         for (Call call : mCalls) {
             InCallService.VideoCall videoCall = call.getVideoCall();
             if (videoCall != null) {
-                videoCall.unregisterCallback();
+                videoCall.destroy();
             }
             if (call.getState() != Call.STATE_DISCONNECTED) {
                 call.internalSetDisconnected();
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index fba3ee3..a76bf59 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.telecom.IConnectionService;
 
+import android.os.Handler;
 import android.os.RemoteException;
 
 import java.util.ArrayList;
@@ -49,7 +50,7 @@
     private final String mId;
     private final IConnectionService mConnectionService;
 
-    private final Set<Callback> mCallbacks = new CopyOnWriteArraySet<>();
+    private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>();
     private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
     private final List<RemoteConnection> mUnmodifiableChildConnections =
             Collections.unmodifiableList(mChildConnections);
@@ -77,13 +78,20 @@
         for (RemoteConnection connection : mChildConnections) {
             connection.setConference(null);
         }
-        for (Callback c : mCallbacks) {
-            c.onDestroyed(this);
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final RemoteConference conference = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onDestroyed(conference);
+                }
+            });
         }
     }
 
     /** {@hide} */
-    void setState(int newState) {
+    void setState(final int newState) {
         if (newState != Connection.STATE_ACTIVE &&
                 newState != Connection.STATE_HOLDING &&
                 newState != Connection.STATE_DISCONNECTED) {
@@ -93,42 +101,71 @@
         }
 
         if (mState != newState) {
-            int oldState = mState;
+            final int oldState = mState;
             mState = newState;
-            for (Callback c : mCallbacks) {
-                c.onStateChanged(this, oldState, newState);
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                final RemoteConference conference = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onStateChanged(conference, oldState, newState);
+                    }
+                });
             }
         }
     }
 
     /** {@hide} */
-    void addConnection(RemoteConnection connection) {
+    void addConnection(final RemoteConnection connection) {
         if (!mChildConnections.contains(connection)) {
             mChildConnections.add(connection);
             connection.setConference(this);
-            for (Callback c : mCallbacks) {
-                c.onConnectionAdded(this, connection);
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                final RemoteConference conference = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onConnectionAdded(conference, connection);
+                    }
+                });
             }
         }
     }
 
     /** {@hide} */
-    void removeConnection(RemoteConnection connection) {
+    void removeConnection(final RemoteConnection connection) {
         if (mChildConnections.contains(connection)) {
             mChildConnections.remove(connection);
             connection.setConference(null);
-            for (Callback c : mCallbacks) {
-                c.onConnectionRemoved(this, connection);
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                final RemoteConference conference = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onConnectionRemoved(conference, connection);
+                    }
+                });
             }
         }
     }
 
     /** {@hide} */
-    void setConnectionCapabilities(int connectionCapabilities) {
+    void setConnectionCapabilities(final int connectionCapabilities) {
         if (mConnectionCapabilities != connectionCapabilities) {
             mConnectionCapabilities = connectionCapabilities;
-            for (Callback c : mCallbacks) {
-                c.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                final RemoteConference conference = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onConnectionCapabilitiesChanged(
+                                conference, mConnectionCapabilities);
+                    }
+                });
             }
         }
     }
@@ -137,18 +174,33 @@
     void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
         mConferenceableConnections.clear();
         mConferenceableConnections.addAll(conferenceableConnections);
-        for (Callback c : mCallbacks) {
-            c.onConferenceableConnectionsChanged(this, mUnmodifiableConferenceableConnections);
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final RemoteConference conference = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onConferenceableConnectionsChanged(
+                            conference, mUnmodifiableConferenceableConnections);
+                }
+            });
         }
     }
 
     /** {@hide} */
-    void setDisconnected(DisconnectCause disconnectCause) {
+    void setDisconnected(final DisconnectCause disconnectCause) {
         if (mState != Connection.STATE_DISCONNECTED) {
             mDisconnectCause = disconnectCause;
             setState(Connection.STATE_DISCONNECTED);
-            for (Callback c : mCallbacks) {
-                c.onDisconnected(this, disconnectCause);
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                final RemoteConference conference = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onDisconnected(conference, disconnectCause);
+                    }
+                });
             }
         }
     }
@@ -239,10 +291,24 @@
     }
 
     public final void registerCallback(Callback callback) {
-        mCallbacks.add(callback);
+        registerCallback(callback, new Handler());
+    }
+
+    public final void registerCallback(Callback callback, Handler handler) {
+        unregisterCallback(callback);
+        if (callback != null && handler != null) {
+            mCallbackRecords.add(new CallbackRecord(callback, handler));
+        }
     }
 
     public final void unregisterCallback(Callback callback) {
-        mCallbacks.remove(callback);
+        if (callback != null) {
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                if (record.getCallback() == callback) {
+                    mCallbackRecords.remove(record);
+                    break;
+                }
+            }
+        }
     }
 }
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 4ecfd50..1493b20 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -21,6 +21,7 @@
 import com.android.internal.telecom.IVideoProvider;
 
 import android.net.Uri;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.Surface;
@@ -392,8 +393,8 @@
      * load factor before resizing, 1 means we only expect a single thread to
      * access the map so make only a single shard
      */
-    private final Set<Callback> mCallbacks = Collections.newSetFromMap(
-            new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
+    private final Set<CallbackRecord> mCallbackRecords = Collections.newSetFromMap(
+            new ConcurrentHashMap<CallbackRecord, Boolean>(8, 0.9f, 1));
     private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
     private final List<RemoteConnection> mUnmodifiableconferenceableConnections =
             Collections.unmodifiableList(mConferenceableConnections);
@@ -470,7 +471,20 @@
      * @param callback A {@code Callback}.
      */
     public void registerCallback(Callback callback) {
-        mCallbacks.add(callback);
+        registerCallback(callback, new Handler());
+    }
+
+    /**
+     * Adds a callback to this {@code RemoteConnection}.
+     *
+     * @param callback A {@code Callback}.
+     * @param handler A {@code Handler} which command and status changes will be delivered to.
+     */
+    public void registerCallback(Callback callback, Handler handler) {
+        unregisterCallback(callback);
+        if (callback != null && handler != null) {
+            mCallbackRecords.add(new CallbackRecord(callback, handler));
+        }
     }
 
     /**
@@ -480,7 +494,12 @@
      */
     public void unregisterCallback(Callback callback) {
         if (callback != null) {
-            mCallbacks.remove(callback);
+            for (CallbackRecord record : mCallbackRecords) {
+                if (record.getCallback() == callback) {
+                    mCallbackRecords.remove(record);
+                    break;
+                }
+            }
         }
     }
 
@@ -800,11 +819,18 @@
     /**
      * @hide
      */
-    void setState(int state) {
+    void setState(final int state) {
         if (mState != state) {
             mState = state;
-            for (Callback c: mCallbacks) {
-                c.onStateChanged(this, state);
+            for (CallbackRecord record: mCallbackRecords) {
+                final RemoteConnection connection = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onStateChanged(connection, state);
+                    }
+                });
             }
         }
     }
@@ -812,13 +838,20 @@
     /**
      * @hide
      */
-    void setDisconnected(DisconnectCause disconnectCause) {
+    void setDisconnected(final DisconnectCause disconnectCause) {
         if (mState != Connection.STATE_DISCONNECTED) {
             mState = Connection.STATE_DISCONNECTED;
             mDisconnectCause = disconnectCause;
 
-            for (Callback c : mCallbacks) {
-                c.onDisconnected(this, mDisconnectCause);
+            for (CallbackRecord record : mCallbackRecords) {
+                final RemoteConnection connection = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onDisconnected(connection, disconnectCause);
+                    }
+                });
             }
         }
     }
@@ -826,11 +859,18 @@
     /**
      * @hide
      */
-    void setRingbackRequested(boolean ringback) {
+    void setRingbackRequested(final boolean ringback) {
         if (mRingbackRequested != ringback) {
             mRingbackRequested = ringback;
-            for (Callback c : mCallbacks) {
-                c.onRingbackRequested(this, ringback);
+            for (CallbackRecord record : mCallbackRecords) {
+                final RemoteConnection connection = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onRingbackRequested(connection, ringback);
+                    }
+                });
             }
         }
     }
@@ -838,10 +878,17 @@
     /**
      * @hide
      */
-    void setConnectionCapabilities(int connectionCapabilities) {
+    void setConnectionCapabilities(final int connectionCapabilities) {
         mConnectionCapabilities = connectionCapabilities;
-        for (Callback c : mCallbacks) {
-            c.onConnectionCapabilitiesChanged(this, connectionCapabilities);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onConnectionCapabilitiesChanged(connection, connectionCapabilities);
+                }
+            });
         }
     }
 
@@ -849,17 +896,24 @@
      * @hide
      */
     void setDestroyed() {
-        if (!mCallbacks.isEmpty()) {
+        if (!mCallbackRecords.isEmpty()) {
             // Make sure that the callbacks are notified that the call is destroyed first.
             if (mState != Connection.STATE_DISCONNECTED) {
                 setDisconnected(
                         new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
             }
 
-            for (Callback c : mCallbacks) {
-                c.onDestroyed(this);
+            for (CallbackRecord record : mCallbackRecords) {
+                final RemoteConnection connection = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onDestroyed(connection);
+                    }
+                });
             }
-            mCallbacks.clear();
+            mCallbackRecords.clear();
 
             mConnected = false;
         }
@@ -868,90 +922,162 @@
     /**
      * @hide
      */
-    void setPostDialWait(String remainingDigits) {
-        for (Callback c : mCallbacks) {
-            c.onPostDialWait(this, remainingDigits);
+    void setPostDialWait(final String remainingDigits) {
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onPostDialWait(connection, remainingDigits);
+                }
+            });
         }
     }
 
     /**
      * @hide
      */
-    void onPostDialChar(char nextChar) {
-        for (Callback c : mCallbacks) {
-            c.onPostDialChar(this, nextChar);
+    void onPostDialChar(final char nextChar) {
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onPostDialWait(connection, String.valueOf(nextChar));
+                }
+            });
         }
     }
 
     /**
      * @hide
      */
-    void setVideoState(int videoState) {
+    void setVideoState(final int videoState) {
         mVideoState = videoState;
-        for (Callback c : mCallbacks) {
-            c.onVideoStateChanged(this, videoState);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onVideoStateChanged(connection, videoState);
+                }
+            });
         }
     }
 
     /**
      * @hide
      */
-    void setVideoProvider(VideoProvider videoProvider) {
+    void setVideoProvider(final VideoProvider videoProvider) {
         mVideoProvider = videoProvider;
-        for (Callback c : mCallbacks) {
-            c.onVideoProviderChanged(this, videoProvider);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onVideoProviderChanged(connection, videoProvider);
+                }
+            });
         }
     }
 
     /** @hide */
-    void setIsVoipAudioMode(boolean isVoip) {
+    void setIsVoipAudioMode(final boolean isVoip) {
         mIsVoipAudioMode = isVoip;
-        for (Callback c : mCallbacks) {
-            c.onVoipAudioChanged(this, isVoip);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onVoipAudioChanged(connection, isVoip);
+                }
+            });
         }
     }
 
     /** @hide */
-    void setStatusHints(StatusHints statusHints) {
+    void setStatusHints(final StatusHints statusHints) {
         mStatusHints = statusHints;
-        for (Callback c : mCallbacks) {
-            c.onStatusHintsChanged(this, statusHints);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onStatusHintsChanged(connection, statusHints);
+                }
+            });
         }
     }
 
     /** @hide */
-    void setAddress(Uri address, int presentation) {
+    void setAddress(final Uri address, final int presentation) {
         mAddress = address;
         mAddressPresentation = presentation;
-        for (Callback c : mCallbacks) {
-            c.onAddressChanged(this, address, presentation);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onAddressChanged(connection, address, presentation);
+                }
+            });
         }
     }
 
     /** @hide */
-    void setCallerDisplayName(String callerDisplayName, int presentation) {
+    void setCallerDisplayName(final String callerDisplayName, final int presentation) {
         mCallerDisplayName = callerDisplayName;
         mCallerDisplayNamePresentation = presentation;
-        for (Callback c : mCallbacks) {
-            c.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onCallerDisplayNameChanged(
+                            connection, callerDisplayName, presentation);
+                }
+            });
         }
     }
 
     /** @hide */
-    void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
+    void setConferenceableConnections(final List<RemoteConnection> conferenceableConnections) {
         mConferenceableConnections.clear();
         mConferenceableConnections.addAll(conferenceableConnections);
-        for (Callback c : mCallbacks) {
-            c.onConferenceableConnectionsChanged(this, mUnmodifiableconferenceableConnections);
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onConferenceableConnectionsChanged(
+                            connection, mUnmodifiableconferenceableConnections);
+                }
+            });
         }
     }
 
     /** @hide */
-    void setConference(RemoteConference conference) {
+    void setConference(final RemoteConference conference) {
         if (mConference != conference) {
             mConference = conference;
-            for (Callback c : mCallbacks) {
-                c.onConferenceChanged(this, conference);
+            for (CallbackRecord record : mCallbackRecords) {
+                final RemoteConnection connection = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onConferenceChanged(connection, conference);
+                    }
+                });
             }
         }
     }
@@ -968,4 +1094,22 @@
     public static RemoteConnection failure(DisconnectCause disconnectCause) {
         return new RemoteConnection(disconnectCause);
     }
+
+    private static final class CallbackRecord extends Callback {
+        private final Callback mCallback;
+        private final Handler mHandler;
+
+        public CallbackRecord(Callback callback, Handler handler) {
+            mCallback = callback;
+            mHandler = handler;
+        }
+
+        public Callback getCallback() {
+            return mCallback;
+        }
+
+        public Handler getHandler() {
+            return mHandler;
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index 3779d1a..7a82c1b 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -36,13 +36,6 @@
  * {@hide}
  */
 public class VideoCallImpl extends VideoCall {
-    private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
-    private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
-    private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
-    private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
-    private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
-    private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
-    private static final int MSG_CHANGE_VIDEO_QUALITY = 7;
 
     private final IVideoProvider mVideoProvider;
     private final VideoCallListenerBinder mBinder;
@@ -61,7 +54,7 @@
     private final class VideoCallListenerBinder extends IVideoCallback.Stub {
         @Override
         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
-            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST,
+            mHandler.obtainMessage(MessageHandler.MSG_RECEIVE_SESSION_MODIFY_REQUEST,
                     videoProfile).sendToTarget();
         }
 
@@ -72,12 +65,14 @@
             args.arg1 = status;
             args.arg2 = requestProfile;
             args.arg3 = responseProfile;
-            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
+            mHandler.obtainMessage(MessageHandler.MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args)
+                    .sendToTarget();
         }
 
         @Override
         public void handleCallSessionEvent(int event) {
-            mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, event).sendToTarget();
+            mHandler.obtainMessage(MessageHandler.MSG_HANDLE_CALL_SESSION_EVENT, event)
+                    .sendToTarget();
         }
 
         @Override
@@ -85,28 +80,42 @@
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = width;
             args.arg2 = height;
-            mHandler.obtainMessage(MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
+            mHandler.obtainMessage(MessageHandler.MSG_CHANGE_PEER_DIMENSIONS, args).sendToTarget();
         }
 
         @Override
         public void changeVideoQuality(int videoQuality) {
-            mHandler.obtainMessage(MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0).sendToTarget();
+            mHandler.obtainMessage(MessageHandler.MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0)
+                    .sendToTarget();
         }
 
         @Override
         public void changeCallDataUsage(long dataUsage) {
-            mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, dataUsage).sendToTarget();
+            mHandler.obtainMessage(MessageHandler.MSG_CHANGE_CALL_DATA_USAGE, dataUsage)
+                    .sendToTarget();
         }
 
         @Override
         public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
-            mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES,
+            mHandler.obtainMessage(MessageHandler.MSG_CHANGE_CAMERA_CAPABILITIES,
                     cameraCapabilities).sendToTarget();
         }
     }
 
     /** Default handler used to consolidate binder method calls onto a single thread. */
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+    private final class MessageHandler extends Handler {
+        private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
+        private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
+        private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
+        private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
+        private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
+        private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
+        private static final int MSG_CHANGE_VIDEO_QUALITY = 7;
+
+        public MessageHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             if (mCallback == null) {
@@ -160,7 +169,8 @@
         }
     };
 
-    /** {@hide} */
+    private Handler mHandler;
+
     VideoCallImpl(IVideoProvider videoProvider) throws RemoteException {
         mVideoProvider = videoProvider;
         mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
@@ -169,13 +179,31 @@
         mVideoProvider.addVideoCallback(mBinder);
     }
 
-    /** {@inheritDoc} */
-    public void registerCallback(VideoCall.Callback callback) {
-        mCallback = callback;
+    public void destroy() {
+        unregisterCallback(mCallback);
     }
 
     /** {@inheritDoc} */
-    public void unregisterCallback() {
+    public void registerCallback(VideoCall.Callback callback) {
+        registerCallback(callback, null);
+    }
+
+    /** {@inheritDoc} */
+    public void registerCallback(VideoCall.Callback callback, Handler handler) {
+        mCallback = callback;
+        if (handler == null) {
+            mHandler = new MessageHandler(Looper.getMainLooper());
+        } else {
+            mHandler = new MessageHandler(handler.getLooper());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void unregisterCallback(VideoCall.Callback callback) {
+        if (callback != mCallback) {
+            return;
+        }
+
         mCallback = null;
         try {
             mVideoProvider.removeVideoCallback(mBinder);
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index cdecb33..303a492 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1104,6 +1104,58 @@
                 || radioTechnology == RIL_RADIO_TECHNOLOGY_EHRPD;
     }
 
+    /** @hide */
+    public static boolean hasCdma(int radioTechnologyBitmask) {
+        int cdmaBitmask = (RIL_RADIO_TECHNOLOGY_IS95A
+                | RIL_RADIO_TECHNOLOGY_IS95B
+                | RIL_RADIO_TECHNOLOGY_1xRTT
+                | RIL_RADIO_TECHNOLOGY_EVDO_0
+                | RIL_RADIO_TECHNOLOGY_EVDO_A
+                | RIL_RADIO_TECHNOLOGY_EVDO_B
+                | RIL_RADIO_TECHNOLOGY_EHRPD);
+
+        return ((radioTechnologyBitmask & cdmaBitmask) != 0);
+    }
+
+    /** @hide */
+    public static boolean bitmaskHasTech(int bearerBitmask, int radioTech) {
+        if (bearerBitmask == 0) {
+            return true;
+        } else if (radioTech >= 1) {
+            return ((bearerBitmask & (1 << (radioTech - 1))) != 0);
+        }
+        return false;
+    }
+
+    /** @hide */
+    public static int getBitmaskForTech(int radioTech) {
+        if (radioTech >= 1) {
+            return (1 << (radioTech - 1));
+        }
+        return 0;
+    }
+
+    /** @hide */
+    public static int getBitmaskFromString(String bearerList) {
+        String[] bearers = bearerList.split("\\|");
+        int bearerBitmask = 0;
+        for (String bearer : bearers) {
+            int bearerInt = 0;
+            try {
+                bearerInt = Integer.parseInt(bearer.trim());
+            } catch (NumberFormatException nfe) {
+                return 0;
+            }
+
+            if (bearerInt == 0) {
+                return 0;
+            }
+
+            bearerBitmask |= getBitmaskForTech(bearerInt);
+        }
+        return bearerBitmask;
+    }
+
     /**
      * Returns a merged ServiceState consisting of the base SS with voice settings from the
      * voice SS. The voice SS is only used if it is IN_SERVICE (otherwise the base SS is returned).
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 7f1e977..4072302 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -164,10 +164,6 @@
                                 com.android.internal.R.styleable.Include_id, View.NO_ID);
                         final int visibility = a.getInt(
                                 com.android.internal.R.styleable.Include_visibility, -1);
-                        final boolean hasWidth = a.hasValue(
-                                com.android.internal.R.styleable.Include_layout_width);
-                        final boolean hasHeight = a.hasValue(
-                                com.android.internal.R.styleable.Include_layout_height);
                         a.recycle();
 
                         // We try to load the layout params set in the <include /> tag. If
@@ -179,19 +175,17 @@
                         // successfully loaded layout params from the <include /> tag,
                         // false means we need to rely on the included layout params.
                         ViewGroup.LayoutParams params = null;
-                        if (hasWidth && hasHeight) {
-                            try {
-                                // ---- START CHANGES
-                                sIsInInclude = true;
-                                // ---- END CHANGES
+                        try {
+                            // ---- START CHANGES
+                            sIsInInclude = true;
+                            // ---- END CHANGES
 
-                                params = group.generateLayoutParams(attrs);
+                            params = group.generateLayoutParams(attrs);
 
-                            } finally {
-                                // ---- START CHANGES
-                                sIsInInclude = false;
-                                // ---- END CHANGES
-                            }
+                        } finally {
+                            // ---- START CHANGES
+                            sIsInInclude = false;
+                            // ---- END CHANGES
                         }
                         if (params == null) {
                             params = group.generateLayoutParams(childAttrs);