Merge "NetworkStats always needs arrays." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index fd2afc5..cbf4d67 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28073,27 +28073,31 @@
     ctor public Conference(android.telecom.PhoneAccountHandle);
     method public final boolean addConnection(android.telecom.Connection);
     method public final void destroy();
+    method public final android.telecom.AudioState getAudioState();
     method public final int getCapabilities();
     method public final java.util.List<android.telecom.Connection> getConnections();
     method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method public final int getState();
+    method public void onAudioStateChanged(android.telecom.AudioState);
     method public void onDisconnect();
     method public void onHold();
     method public void onMerge();
+    method public void onPlayDtmfTone(char);
     method public void onSeparate(android.telecom.Connection);
+    method public void onStopDtmfTone();
     method public void onSwap();
     method public void onUnhold();
     method public final void removeConnection(android.telecom.Connection);
     method public final void setActive();
     method public final void setCapabilities(int);
-    method public final void setDisconnected(int, java.lang.String);
+    method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setOnHold();
   }
 
   public abstract class Connection {
     ctor public Connection();
     method public static android.telecom.Connection createCanceledConnection();
-    method public static android.telecom.Connection createFailedConnection(int, java.lang.String);
+    method public static android.telecom.Connection createFailedConnection(android.telecom.DisconnectCause);
     method public final void destroy();
     method public final android.net.Uri getAddress();
     method public final int getAddressPresentation();
@@ -28104,8 +28108,7 @@
     method public final int getCallerDisplayNamePresentation();
     method public final android.telecom.Conference getConference();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
-    method public final int getDisconnectCause();
-    method public final java.lang.String getDisconnectMessage();
+    method public final android.telecom.DisconnectCause getDisconnectCause();
     method public final int getState();
     method public final android.telecom.StatusHints getStatusHints();
     method public final boolean isRingbackRequested();
@@ -28130,7 +28133,7 @@
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConnectionService(android.telecom.ConnectionService);
     method public final void setDialing();
-    method public final void setDisconnected(int, java.lang.String);
+    method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setInitialized();
     method public final void setInitializing();
     method public final void setOnHold();
@@ -28173,6 +28176,30 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
   }
 
+  public final class DisconnectCause implements android.os.Parcelable {
+    ctor public DisconnectCause(int);
+    ctor public DisconnectCause(int, java.lang.String);
+    ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String, int);
+    method public int describeContents();
+    method public int getCode();
+    method public java.lang.CharSequence getDescription();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getReason();
+    method public int getTone();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUSY = 7; // 0x7
+    field public static final int CANCELED = 4; // 0x4
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int ERROR = 1; // 0x1
+    field public static final int LOCAL = 2; // 0x2
+    field public static final int MISSED = 5; // 0x5
+    field public static final int OTHER = 9; // 0x9
+    field public static final int REJECTED = 6; // 0x6
+    field public static final int REMOTE = 3; // 0x3
+    field public static final int RESTRICTED = 8; // 0x8
+    field public static final int UNKNOWN = 0; // 0x0
+  }
+
   public class GatewayInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.net.Uri getGatewayAddress();
@@ -28196,7 +28223,6 @@
     method public android.net.Uri getSubscriptionAddress();
     method public java.util.List<java.lang.String> getSupportedUriSchemes();
     method public boolean hasCapabilities(int);
-    method public boolean isEnabled();
     method public boolean supportsUriScheme(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
@@ -28248,12 +28274,14 @@
     method public void disconnect();
     method public final int getCallCapabilities();
     method public final java.util.List<android.telecom.RemoteConnection> getConnections();
-    method public int getDisconnectCause();
-    method public java.lang.String getDisconnectMessage();
+    method public android.telecom.DisconnectCause getDisconnectCause();
     method public final int getState();
     method public void hold();
+    method public void playDtmfTone(char);
     method public final void registerCallback(android.telecom.RemoteConference.Callback);
     method public void separate(android.telecom.RemoteConnection);
+    method public void setAudioState(android.telecom.AudioState);
+    method public void stopDtmfTone();
     method public void unhold();
     method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
   }
@@ -28264,7 +28292,7 @@
     method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onDestroyed(android.telecom.RemoteConference);
-    method public void onDisconnected(android.telecom.RemoteConference, int, java.lang.String);
+    method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
     method public void onStateChanged(android.telecom.RemoteConference, int, int);
   }
 
@@ -28279,10 +28307,7 @@
     method public int getCallerDisplayNamePresentation();
     method public android.telecom.RemoteConference getConference();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
-    method public int getDisconnectCauseCode();
-    method public java.lang.String getDisconnectCauseMessage();
-    method public int getFailureCode();
-    method public java.lang.String getFailureMessage();
+    method public android.telecom.DisconnectCause getDisconnectCause();
     method public int getState();
     method public android.telecom.StatusHints getStatusHints();
     method public void hold();
@@ -28306,7 +28331,7 @@
     method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference);
     method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
     method public void onDestroyed(android.telecom.RemoteConnection);
-    method public void onDisconnected(android.telecom.RemoteConnection, int, java.lang.String);
+    method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
     method public void onPostDialWait(android.telecom.RemoteConnection, java.lang.String);
     method public void onRingbackRequested(android.telecom.RemoteConnection, boolean);
     method public void onStateChanged(android.telecom.RemoteConnection, int);
@@ -28330,21 +28355,19 @@
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void cancelMissedCallsNotification();
     method public void clearAccounts();
+    method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
     method public android.telecom.PhoneAccountHandle getConnectionManager();
     method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
-    method public java.util.List<android.telecom.PhoneAccountHandle> getEnabledPhoneAccounts();
     method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
     method public boolean handleMmi(java.lang.String);
-    method public boolean hasMultipleEnabledAccounts();
+    method public boolean hasMultipleCallCapableAccounts();
     method public boolean isInCall();
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method public void showInCallScreen(boolean);
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
-    field public static final java.lang.String ACTION_PHONE_ACCOUNT_DISABLED = "android.telecom.action.PHONE_ACCOUNT_DISABLED";
-    field public static final java.lang.String ACTION_PHONE_ACCOUNT_ENABLED = "android.telecom.action.PHONE_ACCOUNT_ENABLED";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
@@ -28512,57 +28535,6 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public class DisconnectCause {
-    method public static java.lang.String toString(int);
-    field public static final int BUSY = 4; // 0x4
-    field public static final int CALL_BARRED = 20; // 0x14
-    field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
-    field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
-    field public static final int CDMA_CALL_LOST = 41; // 0x29
-    field public static final int CDMA_DROP = 27; // 0x1b
-    field public static final int CDMA_INTERCEPT = 28; // 0x1c
-    field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
-    field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
-    field public static final int CDMA_PREEMPTED = 33; // 0x21
-    field public static final int CDMA_REORDER = 29; // 0x1d
-    field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
-    field public static final int CDMA_SO_REJECT = 30; // 0x1e
-    field public static final int CONGESTION = 5; // 0x5
-    field public static final int CS_RESTRICTED = 22; // 0x16
-    field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
-    field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
-    field public static final int DIALED_MMI = 39; // 0x27
-    field public static final int EMERGENCY_ONLY = 37; // 0x25
-    field public static final int ERROR_UNSPECIFIED = 36; // 0x24
-    field public static final int FDN_BLOCKED = 21; // 0x15
-    field public static final int ICC_ERROR = 19; // 0x13
-    field public static final int INCOMING_MISSED = 1; // 0x1
-    field public static final int INCOMING_REJECTED = 16; // 0x10
-    field public static final int INVALID_CREDENTIALS = 10; // 0xa
-    field public static final int INVALID_NUMBER = 7; // 0x7
-    field public static final int LIMIT_EXCEEDED = 15; // 0xf
-    field public static final int LOCAL = 3; // 0x3
-    field public static final int LOST_SIGNAL = 14; // 0xe
-    field public static final int MAXIMUM_VALID_VALUE = 44; // 0x2c
-    field public static final int MINIMUM_VALID_VALUE = 0; // 0x0
-    field public static final int MMI = 6; // 0x6
-    field public static final int NORMAL = 2; // 0x2
-    field public static final int NOT_DISCONNECTED = 0; // 0x0
-    field public static final int NOT_VALID = -1; // 0xffffffff
-    field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
-    field public static final int NUMBER_UNREACHABLE = 8; // 0x8
-    field public static final int OUTGOING_CANCELED = 44; // 0x2c
-    field public static final int OUTGOING_FAILURE = 43; // 0x2b
-    field public static final int OUT_OF_NETWORK = 11; // 0xb
-    field public static final int OUT_OF_SERVICE = 18; // 0x12
-    field public static final int POWER_OFF = 17; // 0x11
-    field public static final int SERVER_ERROR = 12; // 0xc
-    field public static final int SERVER_UNREACHABLE = 9; // 0x9
-    field public static final int TIMED_OUT = 13; // 0xd
-    field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
-    field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
-  }
-
   public class IccOpenLogicalChannelResponse implements android.os.Parcelable {
     method public int describeContents();
     method public int getChannel();
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 6a5ecee..b8b2087 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -39,6 +39,7 @@
     }
 
     private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner";
+    private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
 
     private IDevicePolicyManager mDevicePolicyManager;
 
@@ -47,9 +48,13 @@
         out.println(
                 "usage: dpm [subcommand] [options]\n" +
                 "usage: dpm set-device-owner <COMPONENT>\n" +
+                "usage: dpm set-profile-owner <COMPONENT> <USER_ID>\n" +
                 "\n" +
                 "dpm set-device-owner: Sets the given component as active admin, and its\n" +
-                "  package as device owner.\n");
+                "  package as device owner.\n" +
+                "\n" +
+                "dpm set-profile-owner: Sets the given component as active admin and profile" +
+                "  owner for an existing user.\n");
     }
 
     @Override
@@ -64,24 +69,25 @@
         String command = nextArgRequired();
         switch (command) {
             case COMMAND_SET_DEVICE_OWNER:
-                runSetDeviceOwner(nextArgRequired());
+                runSetDeviceOwner();
+                break;
+            case COMMAND_SET_PROFILE_OWNER:
+                runSetProfileOwner();
                 break;
             default:
                 throw new IllegalArgumentException ("unknown command '" + command + "'");
         }
     }
 
-    private void runSetDeviceOwner(String argument) throws Exception {
-        ComponentName component = ComponentName.unflattenFromString(argument);
-        if (component == null) {
-            throw new IllegalArgumentException ("Invalid component " + argument);
-        }
-        mDevicePolicyManager.setActiveAdmin(component, true, UserHandle.USER_OWNER);
+    private void runSetDeviceOwner() throws RemoteException {
+        ComponentName component = parseComponentName(nextArgRequired());
+        mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, UserHandle.USER_OWNER);
 
         String packageName = component.getPackageName();
         try {
-            if (!mDevicePolicyManager.setDeviceOwner(packageName, null)) {
-                throw new Exception("Can't set package " + packageName + " as device owner.");
+            if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/)) {
+                throw new RuntimeException(
+                        "Can't set package " + packageName + " as device owner.");
             }
         } catch (Exception e) {
             // Need to remove the admin that we just added.
@@ -91,4 +97,39 @@
         System.out.println("Device owner set to package " + packageName);
         System.out.println("Active admin set to component " + component.toShortString());
     }
+
+    private void runSetProfileOwner() throws RemoteException {
+        ComponentName component = parseComponentName(nextArgRequired());
+        int userId = parseInt(nextArgRequired());
+        mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, userId);
+
+        try {
+            if (!mDevicePolicyManager.setProfileOwner(component, "" /*ownerName*/, userId)) {
+                throw new RuntimeException("Can't set component " + component.toShortString() +
+                        " as profile owner for user " + userId);
+            }
+        } catch (Exception e) {
+            // Need to remove the admin that we just added.
+            mDevicePolicyManager.removeActiveAdmin(component, userId);
+            throw e;
+        }
+        System.out.println("Active admin and profile owner set to " + component.toShortString() +
+                " for user " + userId);
+    }
+
+    private ComponentName parseComponentName(String component) {
+        ComponentName cn = ComponentName.unflattenFromString(component);
+        if (cn == null) {
+            throw new IllegalArgumentException ("Invalid component " + component);
+        }
+        return cn;
+    }
+
+    private int parseInt(String argument) {
+        try {
+            return Integer.parseInt(argument);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e);
+        }
+    }
 }
\ No newline at end of file
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 6d12133..5e9d8f7 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1190,10 +1190,6 @@
             if (userId < 0) {
                 info = mUm.createUser(name, flags);
             } else {
-                if (Process.myUid() != 0) {
-                    System.err.println("Error: not running as root.");
-                    return;
-                }
                 info = mUm.createProfileForUser(name, flags, userId);
             }
             if (info != null) {
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 80e5b91..b83198d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -120,6 +120,25 @@
      */
     public static final int EVENT_UNBLOCK_ADDRESS_FAMILY = BASE + 8;
 
+    /**
+     * Sent by ConnectivitySerice to the NetworkAgent to inform the agent of the
+     * networks status - whether we could use the network or could not, due to
+     * either a bad network configuration (no internet link) or captive portal.
+     *
+     * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
+     */
+    public static final int CMD_REPORT_NETWORK_STATUS = BASE + 9;
+
+    public static final int VALID_NETWORK = 1;
+    public static final int INVALID_NETWORK = 2;
+
+     /**
+     * Sent by the NetworkAgent to ConnectivityService to indicate this network was
+     * explicitly selected.  This should be sent before the NetworkInfo is marked
+     * CONNECTED so it can be given special treatment at that time.
+     */
+    public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 10;
+
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null);
@@ -181,6 +200,14 @@
                 log("Unhandled Message " + msg);
                 break;
             }
+            case CMD_REPORT_NETWORK_STATUS: {
+                if (VDBG) {
+                    log("CMD_REPORT_NETWORK_STATUS(" +
+                            (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)"));
+                }
+                networkStatus(msg.arg1);
+                break;
+            }
         }
     }
 
@@ -261,6 +288,15 @@
     }
 
     /**
+     * Called by the bearer to indicate this network was manually selected by the user.
+     * This should be called before the NetworkInfo is marked CONNECTED so that this
+     * Network can be given special treatment at that time.
+     */
+    public void explicitlySelected() {
+        queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, 0);
+    }
+
+    /**
      * Called when ConnectivityService has indicated they no longer want this network.
      * The parent factory should (previously) have received indication of the change
      * as well, either canceling NetworkRequests or altering their score such that this
@@ -268,6 +304,24 @@
      */
     abstract protected void unwanted();
 
+    /**
+     * Called when the system determines the usefulness of this network.
+     *
+     * Networks claiming internet connectivity will have their internet
+     * connectivity verified.
+     *
+     * Currently there are two possible values:
+     * {@code VALID_NETWORK} if the system is happy with the connection,
+     * {@code INVALID_NETWORK} if the system is not happy.
+     * TODO - add indications of captive portal-ness and related success/failure,
+     * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection
+     *
+     * This may be called multiple times as the network status changes and may
+     * generate false negatives if we lose ip connectivity before the link is torn down.
+     */
+    protected void networkStatus(int status) {
+    }
+
     protected void log(String s) {
         Log.d(LOG_TAG, "NetworkAgent: " + s);
     }
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 34f6cf4..5d2a43d 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -25,12 +25,32 @@
  * @hide
  */
 public class NetworkMisc implements Parcelable {
+
     /**
      * If the {@link Network} is a VPN, whether apps are allowed to bypass the VPN. This is set by
      * a {@link VpnService} and used by {@link ConnectivityService} when creating a VPN.
      */
     public boolean allowBypass;
 
+    /**
+     * Set if the network was manually/explicitly connected to by the user either from settings
+     * or a 3rd party app.  For example, turning on cell data is not explicit but tapping on a wifi
+     * ap in the wifi settings to trigger a connection is explicit.  A 3rd party app asking to
+     * connect to a particular access point is also explicit, though this may change in the future
+     * as we want apps to use the multinetwork apis.
+     */
+    public boolean explicitlySelected;
+
+    public NetworkMisc() {
+    }
+
+    public NetworkMisc(NetworkMisc nm) {
+        if (nm != null) {
+            allowBypass = nm.allowBypass;
+            explicitlySelected = nm.explicitlySelected;
+        }
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -39,6 +59,7 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(allowBypass ? 1 : 0);
+        out.writeInt(explicitlySelected ? 1 : 0);
     }
 
     public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -46,6 +67,7 @@
         public NetworkMisc createFromParcel(Parcel in) {
             NetworkMisc networkMisc = new NetworkMisc();
             networkMisc.allowBypass = in.readInt() != 0;
+            networkMisc.explicitlySelected = in.readInt() != 0;
             return networkMisc;
         }
 
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2bfe3b4..9e0719d 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -316,7 +316,6 @@
                     top ? 0 : mSystemWindowInsets.top,
                     right ? 0 : mSystemWindowInsets.right,
                     bottom ? 0 : mSystemWindowInsets.bottom);
-            result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
             return result;
         }
         return this;
@@ -336,7 +335,6 @@
             int right, int bottom) {
         final WindowInsets result = new WindowInsets(this);
         result.mSystemWindowInsets = new Rect(left, top, right, bottom);
-        result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
         return result;
     }
 
@@ -351,7 +349,6 @@
     public WindowInsets replaceSystemWindowInsets(Rect systemWindowInsets) {
         final WindowInsets result = new WindowInsets(this);
         result.mSystemWindowInsets = new Rect(systemWindowInsets);
-        result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
         return result;
     }
 
@@ -376,7 +373,6 @@
                     top ? 0 : mWindowDecorInsets.top,
                     right ? 0 : mWindowDecorInsets.right,
                     bottom ? 0 : mWindowDecorInsets.bottom);
-            result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets();
             return result;
         }
         return this;
@@ -388,7 +384,6 @@
     public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) {
         final WindowInsets result = new WindowInsets(this);
         result.mWindowDecorInsets = new Rect(left, top, right, bottom);
-        result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets();
         return result;
     }
 
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 334ff43..382a266 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -330,15 +330,30 @@
          */
         public final String mRawTypeface;
 
+        private final boolean mHasForegroundColor;
+        private final boolean mHasBackgroundColor;
+        private final boolean mHasEdgeType;
+        private final boolean mHasEdgeColor;
+        private final boolean mHasWindowColor;
+
+        /** Lazily-created typeface based on the raw typeface string. */
         private Typeface mParsedTypeface;
 
         private CaptionStyle(int foregroundColor, int backgroundColor, int edgeType, int edgeColor,
                 int windowColor, String rawTypeface) {
-            this.foregroundColor = foregroundColor;
-            this.backgroundColor = backgroundColor;
-            this.edgeType = edgeType;
-            this.edgeColor = edgeColor;
-            this.windowColor = windowColor;
+            mHasForegroundColor = foregroundColor != COLOR_UNSPECIFIED;
+            mHasBackgroundColor = backgroundColor != COLOR_UNSPECIFIED;
+            mHasEdgeType = edgeType != EDGE_TYPE_UNSPECIFIED;
+            mHasEdgeColor = edgeColor != COLOR_UNSPECIFIED;
+            mHasWindowColor = windowColor != COLOR_UNSPECIFIED;
+
+            // Always use valid colors, even when no override is specified, to
+            // ensure backwards compatibility with apps targeting KitKat MR2.
+            this.foregroundColor = mHasForegroundColor ? foregroundColor : Color.WHITE;
+            this.backgroundColor = mHasBackgroundColor ? backgroundColor : Color.BLACK;
+            this.edgeType = mHasEdgeType ? edgeType : EDGE_TYPE_NONE;
+            this.edgeColor = mHasEdgeColor ? edgeColor : Color.BLACK;
+            this.windowColor = mHasWindowColor ? windowColor : COLOR_NONE_OPAQUE;
 
             mRawTypeface = rawTypeface;
         }
@@ -375,7 +390,7 @@
          *         otherwise
          */
         public boolean hasBackgroundColor() {
-            return backgroundColor != COLOR_UNSPECIFIED;
+            return mHasBackgroundColor;
         }
 
         /**
@@ -384,7 +399,7 @@
          *         otherwise
          */
         public boolean hasForegroundColor() {
-            return foregroundColor != COLOR_UNSPECIFIED;
+            return mHasForegroundColor;
         }
 
         /**
@@ -393,7 +408,7 @@
          *         otherwise
          */
         public boolean hasEdgeType() {
-            return edgeType != EDGE_TYPE_UNSPECIFIED;
+            return mHasEdgeType;
         }
 
         /**
@@ -402,7 +417,7 @@
          *         otherwise
          */
         public boolean hasEdgeColor() {
-            return edgeColor != COLOR_UNSPECIFIED;
+            return mHasEdgeColor;
         }
 
         /**
@@ -411,7 +426,7 @@
          *         otherwise
          */
         public boolean hasWindowColor() {
-            return windowColor != COLOR_UNSPECIFIED;
+            return mHasWindowColor;
         }
 
         /**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3859e48..af53ec8 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3323,7 +3323,8 @@
         if (mLastY == Integer.MIN_VALUE) {
             rawDeltaY -= mMotionCorrection;
         }
-        if (dispatchNestedPreScroll(0, -rawDeltaY, mScrollConsumed, mScrollOffset)) {
+        if (dispatchNestedPreScroll(0, mLastY != Integer.MIN_VALUE ? mLastY - y : -rawDeltaY,
+                mScrollConsumed, mScrollOffset)) {
             rawDeltaY += mScrollConsumed[1];
             scrollOffsetCorrection = -mScrollOffset[1];
             scrollConsumedCorrection = mScrollConsumed[1];
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index d2f68d0..0460282 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -137,6 +137,11 @@
 
     private boolean mIs24HourMode;
     private boolean mShowHours;
+
+    /**
+     * When in 24-hour mode, indicates that the current hour is between
+     * 1 and 12 (inclusive).
+     */
     private boolean mIsOnInnerCircle;
 
     private int mXCenter;
@@ -513,49 +518,53 @@
         mListener = listener;
     }
 
+    /**
+     * Sets the current hour in 24-hour time.
+     *
+     * @param hour the current hour between 0 and 23 (inclusive)
+     */
     public void setCurrentHour(int hour) {
         final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR;
         mSelectionDegrees[HOURS] = degrees;
         mSelectionDegrees[HOURS_INNER] = degrees;
-        mAmOrPm = ((hour % 24) < 12) ? AM : PM;
+
+        // 0 is 12 AM (midnight) and 12 is 12 PM (noon).
+        mAmOrPm = (hour == 0 || (hour % 24) < 12) ? AM : PM;
+
         if (mIs24HourMode) {
-            mIsOnInnerCircle = (mAmOrPm == AM);
+            // Inner circle is 1 through 12.
+            mIsOnInnerCircle = hour >= 1 && hour <= 12;
         } else {
             mIsOnInnerCircle = false;
         }
+
         initData();
         updateLayoutData();
         invalidate();
     }
 
-    // Return hours in 0-23 range
+    /**
+     * Returns the current hour in 24-hour time.
+     *
+     * @return the current hour between 0 and 23 (inclusive)
+     */
     public int getCurrentHour() {
-        int hours =
-                mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS] / DEGREES_FOR_ONE_HOUR;
+        int hour = (mSelectionDegrees[mIsOnInnerCircle ?
+                HOURS_INNER : HOURS] / DEGREES_FOR_ONE_HOUR) % 12;
         if (mIs24HourMode) {
-            if (mIsOnInnerCircle) {
-                hours = hours % 12;
-                if (hours == 0) {
-                    hours = 12;
-                }
-            } else {
-                if (hours != 0) {
-                    hours += 12;
-                }
+            // Convert the 12-hour value into 24-hour time based on where the
+            // selector is positioned.
+            if (mIsOnInnerCircle && hour == 0) {
+                // Inner circle is 1 through 12.
+                hour = 12;
+            } else if (hour != 0) {
+                // Outer circle is 13 through 23 and 0.
+                hour += 12;
             }
-        } else {
-            hours = hours % 12;
-            if (hours == 0) {
-                if (mAmOrPm == PM) {
-                    hours = 12;
-                }
-            } else {
-                if (mAmOrPm == PM) {
-                    hours += 12;
-                }
-            }
+        } else if (mAmOrPm == PM) {
+            hour += 12;
         }
-        return hours;
+        return hour;
     }
 
     public void setCurrentMinute(int minute) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1509e80..a80d70a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8182,6 +8182,11 @@
      * @removed
      */
     public static ColorStateList getTextColors(Context context, TypedArray attrs) {
+        if (attrs == null) {
+            // Preserve behavior prior to removal of this API.
+            throw new NullPointerException();
+        }
+
         // It's not safe to use this method from apps. The parameter 'attrs'
         // must have been obtained using the TextView filter array which is not
         // available to the SDK. As such, we grab a default TypedArray with the
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index bf5e49b9..dd165ae 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -113,7 +113,7 @@
         }
 
         INotificationManager service = getService();
-        String pkg = mContext.getPackageName();
+        String pkg = mContext.getOpPackageName();
         TN tn = mTN;
         tn.mNextView = mNextView;
 
@@ -387,6 +387,7 @@
                 handleHide();
                 mView = mNextView;
                 Context context = mView.getContext().getApplicationContext();
+                String packageName = mView.getContext().getOpPackageName();
                 if (context == null) {
                     context = mView.getContext();
                 }
@@ -406,6 +407,7 @@
                 mParams.y = mY;
                 mParams.verticalMargin = mVerticalMargin;
                 mParams.horizontalMargin = mHorizontalMargin;
+                mParams.packageName = packageName;
                 if (mView.getParent() != null) {
                     if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
                     mWM.removeView(mView);
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
index 3bf3cd7..023f5cc 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
@@ -28,7 +28,7 @@
             android:fillColor="#00000000"
             android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
             android:strokeColor="?attr/colorControlActivated"
-            android:strokeLineCap="round"
+            android:strokeLineCap="square"
             android:strokeLineJoin="miter"
             android:strokeWidth="4"
             android:trimPathEnd="0"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
index 62f9225..e72097e 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
@@ -28,7 +28,7 @@
             android:fillColor="#00000000"
             android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
             android:strokeColor="?attr/colorControlActivated"
-            android:strokeLineCap="round"
+            android:strokeLineCap="square"
             android:strokeLineJoin="miter"
             android:strokeWidth="4"
             android:trimPathEnd="0"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
index 1352cfc..875e7a3 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
@@ -28,7 +28,7 @@
             android:fillColor="#00000000"
             android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
             android:strokeColor="?attr/colorControlActivated"
-            android:strokeLineCap="round"
+            android:strokeLineCap="square"
             android:strokeLineJoin="miter"
             android:strokeWidth="4"
             android:trimPathEnd="0"
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 5a7e168..cbaf54f 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -148,10 +148,10 @@
     <color name="battery_saver_mode_color">#fff4511e</color><!-- deep orange 600 -->
 
     <!-- Default user icon colors -->
-    <color name="user_icon_1">#ffe91e63</color><!-- pink 500 -->
+    <color name="user_icon_1">#ff00bcd4</color><!-- teal 500 -->
     <color name="user_icon_2">#ff3f51b5</color><!-- indigo 500 -->
     <color name="user_icon_3">#ff4285f4</color><!-- blue 500 -->
-    <color name="user_icon_4">#ff00bcd4</color><!-- teal 500 -->
+    <color name="user_icon_4">#ffe91e63</color><!-- pink 500 -->
     <color name="user_icon_5">#ff0f9d58</color><!-- green 500 -->
     <color name="user_icon_6">#ff8bc34a</color><!-- light green 500 -->
     <color name="user_icon_7">#ffff9800</color><!-- orange 500 -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 855c63a..e95d735 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -612,13 +612,13 @@
     <!-- Vibrator pattern for feedback when selecting an hour/minute tick of a Clock -->
     <integer-array name="config_clockTickVibePattern">
         <item>125</item>
-        <item>5</item>
+        <item>30</item>
     </integer-array>
 
     <!-- Vibrator pattern for feedback when selecting a day/month/year date of a Calendar -->
     <integer-array name="config_calendarDateVibePattern">
         <item>125</item>
-        <item>5</item>
+        <item>30</item>
     </integer-array>
 
     <!-- Vibrator pattern for feedback about booting with safe mode disabled -->
diff --git a/docs/html/distribute/googleplay/edu/start.jd b/docs/html/distribute/googleplay/edu/start.jd
index 067a15f..3c3a175 100644
--- a/docs/html/distribute/googleplay/edu/start.jd
+++ b/docs/html/distribute/googleplay/edu/start.jd
@@ -9,27 +9,23 @@
 <div id="qv-wrapper"><div id="qv">
 <h2>Steps to Join</h2>
 <ol>
-<li><a href="#register">Register for a Publisher Account</li>
+<li><a href="#register">Register for a Publisher Account</a></li>
 <li><a href="#prepare">Prepare Your Apps</a></li>
 <li><a href="#publish">Publish Your Apps</a></li>
 <li><a href="#related-resources">Related Resources</a></li>
 </ol>
 </div></div>
 <p>
-  If you've got great Android apps for education and want to reach even more teachers
+  If you've got great Android and Chrome apps for education and want to reach even more teachers
   and students, you can join the <strong>Google Play for Education</strong>
   program in a few simple steps. You do everything using the familiar tools and
-  processes in Google Play.
+  processes you currently use to manage your Android or Chrome apps.
 </p>
 
 <p>
 Note that Google Play for Education is currently available to <strong>K-12 schools in the United
 States</strong> only.</p>
 
-<p>If you have an educational Chrome app instead of an Android app, you can learn more about
-Chrome Apps in Google Play for Education at <a href=
-"https://developers.google.com/edu">developers.google.com/edu</a>.
-</p>
 
 <img src="{@docRoot}images/gpfe-start-0.jpg" style=
   "border:1px solid #ddd;padding:0px" width="760" height="403">
@@ -43,23 +39,20 @@
 </div>
 
 <p>
-  If you’re new to Google Play, review the information on <a href=
+  To publish an Android app in Google Play for Education, review the information on <a href=
   "{@docRoot}distribute/googleplay/start.html">getting started</a> with
   publishing on Google Play. You’ll gain access to the <a href=
   "{@docRoot}distribute/googleplay/developer-console.html">Developer
   Console</a>, where you’ll manage your details, apps, and payments.
 </p>
 
+<p>To publish a Chrome app in Google Play for Education, you'll need a
+<a href="https://developer.chrome.com/webstore/publish">Chrome Web Store account</a>.</p>
+
 <div class="headerLine">
   <h2 id="prepare">
     Prepare Your Apps
   </h2>
-
-
-</div>
-
-<div class="figure-right">
-  <img src="{@docRoot}images/gp-edu-process.png">
 </div>
 
 <p>
@@ -77,13 +70,13 @@
   To prepare for a launch on Google Play for Education, start by reviewing the
   guidelines for educational apps in Google Play and the policies that apply to
   your apps. See the <a href=
-  "{@docRoot}distribute/essentials/gpfe-guidelines.html">Education
+  "https://developers.google.com/edu/guidelines">Education
   Guidelines</a> for details.
 </p>
 
 <p>
   Also, make sure that you're familiar with the policies that your app must
-  comply with, including <a href=
+  comply with. For Android, they include <a href=
   "http://play.google.com/about/developer-content-policy.html">content
   policies</a>, the <a href=
   "http://play.google.com/about/developer-distribution-agreement.html">Developer
@@ -91,6 +84,14 @@
   "https://play.google.com/about/developer-distribution-agreement-addendum.html">
   Google Play for Education Addendum</a>.
 </p>
+<p>For Chrome, they include <a href=
+  "https://developer.chrome.com/webstore/program_policies">content
+  policies</a>, the <a href=
+  "https://developer.chrome.com/webstore/terms">Developer
+  Distribution Agreement</a>, and <a href=
+  "https://developers.google.com/edu/chrome-d-d-a-addendum">
+  Google Play for Education Addendum</a>.
+</p>
 
 <h3>
   Design and develop a great app for education
@@ -105,7 +106,7 @@
 
 <p>
   Assess your app against the criteria listed in the <a href=
-  "{@docRoot}distribute/essentials/gpfe-guidelines.html">Education
+  "https://developers.google.com/edu/guidelines">Education
   Guidelines</a> and plan on supporting them to the greatest extent possible.
   In some cases you might need to modify the app’s features or UI to support
   classroom requirements. It's a good idea to identify any changes early in
@@ -113,7 +114,7 @@
 </p>
 
 <p>
-  With Google Play for Education, optimizing your apps for tablets is crucial.
+  With Google Play for Education, optimizing your Android apps for tablets is crucial.
   A variety of resources are available to help you understand what you need to
   do — a good starting point is the <a href=
   "{@docRoot}distribute/essentials/quality/tablets.html">Tablet App Quality</a>
@@ -132,6 +133,11 @@
 </p>
 
 <p>
+For Chrome apps, optimizing your apps for mouse, keyboard, and touch is just as important.
+Some students will use touch as their primary input method, and some will have Chromebooks
+without touch. Make sure your app works for all students.
+</p>
+<p>
   Comprehensive testing and quality assurance are key aspects of delivering
   great apps for teachers and students. Make sure you set up a <a href=
   "{@docRoot}distribute/essentials/gpfe-guidelines.html#test-environment">proper
@@ -160,15 +166,13 @@
     apps.
   </li>
 
-  <li>Publish your apps in the Developer Console as normal, but opt-in to
+  <li>Publish your apps in the Developer Console (for Android apps) or the Chrome Web Store
+  (for Chrome apps) as normal, but opt-in to
   Google Play for Education.
   </li>
 </ul>
 
-<h3 id="opt-in">
-  Opt-in to Google Play for Education and publish
-</h3>
-
+<h3>Opt-in to Google Play for Education and publish Android apps</h3>
 <p>
   Once you've built your release-ready APK upload it to the Developer Console,
   create your store listing, and set distribution options. If you aren't
@@ -231,7 +235,7 @@
   will be shown to educators when they are browsing your app.
   </li>
 
-  <li>Click <strong>Save</strong>f to save your Pricing and Distribution
+  <li>Click <strong>Save</strong> to save your Pricing and Distribution
   changes.
   </li>
 </ol>
@@ -247,58 +251,55 @@
 </div>
 
 <p>
-  Once you save changes and publish your app, the app will be submitted to our
-  third-party educator network for review and approval. If the app is already
-  published, it will be submitted for review as soon as you opt-in and save
-  your changes.
+  Once you save changes and publish your app, the app will be available on Google Play for
+  Education. We may submit your app to our
+  third-party educator network for additional review and to be featured and badged as
+  Educator-approved.
 </p>
 
 <p class="note">
   <strong>Note</strong>: Google Play for Education is part of Google Play. When
-  you publish an app that's opted-in to Google Play for Education, the app
-  becomes available to users in Google Play right away. After the app is
-  <a href="{@docRoot}distribute/essentials/gpfe-guidelines.html#e-value">review
-  and approval</a>, it then becomes available to educators in Google Play for
-  Education.
+  you publish an Android app that's opted-in to Google Play for Education, the app
+  becomes available to users in Google Play.
 </p>
 
-<h3>
-  Track your review and approval
-</h3>
-
+<h3>Opt-in to Google Play for Education and publish Chrome apps</h3>
 <p>
-  As soon as you opt-in to Google Play for Education and publish, your apps are
-  queued for review by our third-party educator network. The review and
-  approval process can take four weeks or more. You'll receive notification by
-  email (to your developer account address) when the review is complete, with a
-  summary of the review results.
+Once you've uploaded your app or extension to the Developer Dashboard, create your store listing
+and set distribution options.
 </p>
-
 <p>
-  At any time, you can check the review and approval status of your app in the
-  Developer Console, under "Google Play for Education" in the app's Pricing and
-  Distribution page. There are three approval states:
+When your apps are ready to publish, you opt-in to Google Play for Education directly from the
+<a href="https://chrome.google.com/webstore/developer/dashboard">Developer Dashboard</a>.
+Opt-in means that you want your apps to be made available to educators
+through Google Play for Education, including review, classification, and approval by our
+third-party educator network.
+</p>
+<p>
+Opt-in also confirms that your app complies with
+<a href="https://developer.chrome.com/webstore/program_policies">Chrome Web Store Program Policies</a>
+and the <a href="https://developer.chrome.com/webstore/terms">Terms of Service</a>, including a
+<a href="https://developers.google.com/edu/chrome-d-d-a-addendum">Google Play for Education Addendum</a>.
+If you are not familiar with these
+policy documents or the Addendum, make sure to read them before opting-in.
 </p>
 
-<ul>
-  <li>
-    <em>Pending</em> &mdash; Your app was sent for review and the review isn't
-    yet complete.
-  </li>
+<p>Here's how to opt-in to Google Play for Education for a specific app or extension:</p>
+<ol>
+<li>In the Developer Dashboard, click <b>Edit</b> on the app you want to opt-in.</li>
+<li>Scroll down to <b>Google Play for Education</b> and review the additional
+Developer Terms and Conditions.</li>
+<li>Select the checkbox to opt-in to include this item in Google Play for Education.</li>
+<li>Click <b>Publish Changes</b> to save your changes and submit your application.
+Once you save changes and publish your app, the app will be available on Google Play for Education.
+We may submit your app to our third-party educator network for additional review, and to be
+featured and badged as Educator-approved.</li>
+<p class="note"><b>Note:</b>
+When you publish an app that's opted-in to Google Play for Education,
+the app becomes available to users in the Chrome Web Store.
+</p>
+</ol>
 
-  <li>
-    <em>Approved</em> &mdash; Your app was reviewed and approved. The app will
-    be made available directly to educators through Google Play for Education.
-    Once your app is approved, you can update it at your convenience without
-    needing another full review.
-  </li>
-
-  <li>
-    <em>Not approved</em> &mdash; Your app was reviewed and not approved. Check
-    the notification email send for information about why the app wasn’t
-    approved. You can address any issues and opt-in again for another review.
-  </li>
-</ul>
 <div class="headerLine">
 <h2 id="related-resources">Related Resources</h2>
 </div>
diff --git a/docs/html/google/play/billing/billing_subscriptions.jd b/docs/html/google/play/billing/billing_subscriptions.jd
index 2b78ab3..b9b77df 100644
--- a/docs/html/google/play/billing/billing_subscriptions.jd
+++ b/docs/html/google/play/billing/billing_subscriptions.jd
@@ -11,25 +11,32 @@
 <div id="qv">
   <h2>Quickview</h2>
   <ul>
-     <li>Users purchase your subscriptions from inside your apps, rather than 
-directly from Google Play.</li>
-     <li>Subscriptions let you sell products with automated, recurring billing
-(monthly or annual).</li>
-     <li>You can offer a configurable trial period for any subscription.</li>
-
+    <li>Subscriptions let you sell products with automated, recurring billing
+        at a variety of intervals.</li>
+    <li>You can offer a configurable trial period for monthly and
+        annual subscriptions.</li>
+    <li>You can manage subscriptions through the Developer Console, or by using
+        the
+        <a href="https://developers.google.com/android-publisher/">Google Play
+        Developer API</a>.</li>
+    <li>Users purchase your subscriptions from inside your apps, rather than
+        directly from Google Play.</li>
+    <li>You can defer billing for a particular user's subscription, to manage
+        accounts or offer rewards.</li>
   </ul>
   <h2>In this document</h2>
   <ol>
-    <li><a href="#overview">Overview</a></li>
+    <li><a href="#overview">Overview of Subscriptions</a></li>
     <li><a href="#administering">Configuring Subscriptions Items</a></li>
-    <li><a href="#cancellation">Cancellation</a></li>
-    <li><a href="#payment">Payment Processing</a></li>
+    <li><a href="#cancellation">Subscription Cancellation</a></li>
+    <li><a href="#payment">Payment Processing and Policies</a></li>
     <li><a href="#strategies">Purchase Verification Strategies</a></li>
+    <li><a href="#play-dev-api">Google Play Developer API</a></li>
   </ol>
   <h2>See also</h2>
   <ol>
     <li><a href="{@docRoot}google/play/billing/billing_integrate.html#Subs">Implementing Subscriptions (V3)</a></li>
-    <li><a href="https://developers.google.com/android-publisher/v1_1/">Google Play Android Developer API</a></li>
+    <li><a href="https://developers.google.com/android-publisher/">Google Play Developer API</a></li>
   </ol>
 </div>
 </div>
@@ -44,14 +51,15 @@
 <h2 id="overview">Overview of Subscriptions</h2>
 <p>A <em>subscription</em> is a product type offered in In-app Billing that 
 lets you sell content, services, or features to users from inside your app with 
-recurring monthly or annual billing. You can sell subscriptions to almost any 
+recurring, automated billing at the interval you specify. You can sell subscriptions to almost
+any
 type of digital content, from any type of app or game.</p>
 
 <p>As with other in-app products, you configure and publish subscriptions using
 the Developer Console and then sell them from inside apps installed on 
 Android devices. In the Developer console, you create subscription
 products and add them to a product list, then set a price and optional trial
-period for each, choose a billing interval (monthly or annual), and then 
+period for each, choose a billing interval, and then
 publish. For more information about using the Developer Console, see 
 <a href="#administering">Configuring Subscription Items</a>.</p>
 
@@ -63,17 +71,17 @@
 
 <img src="{@docRoot}images/in-app-billing/v3/billing_subscription_v3.png" style="float:right; border:4px solid ddd;">
 
-<p>After users have purchase subscriptions, they can view the subscriptions and 
+<p>After users have purchased subscriptions, they can view the subscriptions and 
 cancel them from the <strong>My Apps</strong> screen in the Play Store app or 
 from the app's product details page in the Play Store app. For more information 
 about handling user cancellations, see <a href="#cancellation">Subscription Cancellation</a>.</p>
 
-<p>In adddition to client-side API calls, you can use the server-side API for 
+<p>In addition to client-side API calls, you can use the server-side API for 
 In-app Billing to provide subscription purchasers with extended access to 
 content (for example, from your web site or another service).
 The server-side API lets you validate the status of a subscription when users
 sign into your other services. For more information about the API, see <a
-href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Purchase Status API</a>. </p>
+href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play Developer API</a>. </p>
 
 <p>You can also build on your existing external subscriber base from inside your
 Android apps.</p>
@@ -102,8 +110,10 @@
 subscriptions, see the <a href="{@docRoot}google/play/billing/versions.html#Subs">Version Notes</a>.</p>
 
 <h2 id="administering">Configuring Subscription Items</h2>
-<p>To create and manage subscriptions, use the Developer Console to set up a 
-product list for the app then configure these attributes for each subscription 
+
+<p>To create and manage subscriptions, you can use the Developer Console to set
+up a
+product list for the app, then configure these attributes for each subscription 
 product:</p>
 
 <ul>
@@ -113,8 +123,8 @@
 <li>Language: The default language for displaying the subscription</li>
 <li>Title: The title of the subscription product</li>
 <li>Description: Details that tell the user about the subscription</li>
-<li>Price: USD price of subscription per recurrence</li>
-<li>Recurrence: monthly or yearly</li>
+<li>Price: Default price of subscription per recurrence</li>
+<li>Recurrence: Interval of billing recurrence</li>
 <li>Additional currency pricing (can be auto-filled)</li>
 </ul>
 
@@ -122,6 +132,10 @@
 see <a href="{@docRoot}google/play/billing/billing_admin.html">Administering
 In-app Billing</a>.</p>
 
+<p>You can also create and manage subscriptions using the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">
+Google Play Developer API</a>.</p>
+
 <h3 id="pricing">Subscription pricing</h3>
 
 <p>When you create a subscription in the Developer Console, you can set a price
@@ -139,20 +153,30 @@
 <h3 id="user-billing">User billing</h3>
 
 <p>In the Developer Console, you can configure subscription products with 
-automated recurring billing at either of two intervals:</p>
+automated recurring billing at your choice of intervals:</p>
 
 <ul>
   <li>Monthly &mdash; Google Play bills the customer’s Google Wallet account at
   the time of purchase and monthly subsequent to the purchase date (exact billing
-  intervals can vary slightly over time)</li>
+  intervals can vary slightly over time).</li>
   <li>Annually &mdash; Google Play bills the customer's Google Wallet account at
   the time of purchase and again on the same date in subsequent years.</li>
+
+  <li>Seasonal &mdash; Google Play bills the customer's Google Wallet account at
+  the beginning of each "season" (you specify the season beginning and end
+  dates). This
+  is intended for annual purchases of seasonal content (such as sports-related
+  content). The subscription runs through the end of the season, and restarts
+  the next year at the start of the season.</li>
+
 </ul>
 
 <p>Billing continues indefinitely at the interval and price specified for the
 subscription. At each subscription renewal, Google Play charges the user account
-automatically, then notifies the user of the charges afterward by email. Billing
-cycles will always match subscription cycles, based on the purchase date.</p>
+automatically, then notifies the user of the charges afterward by email. For
+monthly and annual subscriptions, billing cycles will always match subscription
+cycles, based on the purchase date. (Seasonal subscriptions are charged
+annually, on the first day of the season.)</p>
 
 <p>Over the life of a subscription, the form of payment billed remains the same
 &mdash; Google Play always bills the same form of payment (such as credit card
@@ -164,7 +188,7 @@
 API. Your apps can store the token locally or pass it to your backend servers, 
 which can then use it to validate or cancel the subscription remotely using the
 <a
-href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Purchase Status API</a>.</p>
+href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play Developer API</a>.</p>
 
 <p>If a recurring payment fails (for example, because the customer’s credit
 card has become invalid), the subscription does not renew. How your app is 
@@ -182,18 +206,57 @@
 billing errors that may occur. Your backend servers can use the server-side API 
 to query and update your records and follow up with customers directly, if needed.</p>
 
+<h3 id="deferred-billing">Deferred Billing</h3>
+
+<p>Using the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google
+Play Developer API</a>, you can defer the next billing date for a
+subscriber. The user continues to be subscribed to the content, and has full
+access to it, but is not charged during the deferral period. This allows you
+to do things like:</p>
+
+<ul>
+  <li>Give users free access as part of a bundle or a special offer (for
+    example, giving free access to web content to users who subscribe to a
+    print magazine)</li>
+  <li>Give free access to customers as a goodwill gesture</li>
+</ul>
+
+<p>The longest you can defer billing is for one year per call. Of course, you
+can call the API again before the year is up to defer billing further.</p>
+
+<p>For example, Darcy has a monthly subscription to online content for the
+<em>Fishing Gentleman</em> app. He is normally
+billed £1.25 on the first of each month.
+On March 10, he participates in an online survey for the app publisher. The
+publisher rewards him by deferring his next payment until June 1. Darcy is not
+charged on April 1 or May 1, but still has access to the content as normal. On
+June 1, he is charged his normal £1.25 subscription fee.</p>
+
+<p class="note"><strong>Note:</strong> The API always defers the billing date
+by a whole number of days. If you request a deferral period that includes a
+fractional number of days, the API rounds the period up to the next full day.
+For example, if a user's subscription is set to renew on 15 June 2015 at
+14:00:00 UTC, and you use the API to defer the renewal date to 15 August 2015 at
+02:00:00 UTC, the API will round up to the next full day and set the renewal
+date to 15 August 2015 14:00:00 UTC.</p>
+
+<p>You can also offer free trials to new subscribers, as described in
+<a href="#trials">Free trials</a>.</p>
+
 <h3 id="trials">Free trials</h3>
 
 <p>In the Developer Console, you can set up a free trial period that lets users
 try your subscription content before buying it. The trial period runs for the 
 period of time that you set and then automatically converts to a full 
 subscription managed according to the subscription's billing interval and 
-price.</p>
+price. Free trials are supported for monthly and annual subscriptions only, and are not supported for seasonal subscriptions.</p>
 
 <p>To take advantage of a free trial, a user must "purchase" the full
 subscription through the standard In-app Billing flow, providing a valid form of
 payment to use for billing and completing the normal purchase transaction.
-However, the user is not charged any money, since the initial period corresponds
+However, the user is not charged any money, because the initial period
+corresponds
 to the free trial. Instead, Google Play records a transaction of $0.00 and the
 subscription is marked as purchased for the duration of the trial period or
 until cancellation. When the transaction is complete, Google Play notifies users
@@ -220,8 +283,10 @@
 period per subscription product.</p>
 
 <h3 id="publishing">Subscription publishing</h3>
+
 <p>When you have finished configuring your subscription product details in the
-Developer Console, you can publish the subscription in the app product list.</p>
+Developer Console or via the API,
+you can publish the subscription in the app product list.</p>
 
 <p>In the product list, you can add subscriptions, in-app products, or both. You
 can add multiple subscriptions that give access to different content or
@@ -263,10 +328,13 @@
 
 <p class="caution"><strong>Important:</strong> In all cases, you must continue
 to offer the content that your subscribers have purchased through their
-subscriptions, for as long any users are able to access it. That is, you must
-not remove any subscriber’s content while any user still has an active
+subscriptions, as long any user is able to access it. That is, you must
+not remove any content while any user still has an active
 subscription to it, even if that subscription will terminate at the end of the
-current billing cycle. Removing content that a subscriber is entitled to access
+current billing cycle. Alternatively, you can use the <a href="#refunds">refund
+and revoke</a> API to revoke each subscriber's subscription (one by one) and
+refund their subscription payments.
+Removing content that any subscriber is entitled to access
 will result in penalties. Please see the <a
 href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504">policies document</a> for more information. </p>
 
@@ -280,19 +348,26 @@
 screen of the Play Store app. If the user chooses to cancel the uninstallation, 
 the app and subscriptions remain as they were.</p>
 
-<h3 id="refunds">Refunds</h3>
+<h3 id="refunds">Refunding and revoking subscriptions</h3>
 
-<p>With subscriptions, Google Play does not provide a refund window, so users 
-will need to contact you directly to request a refund.
+<p>With subscriptions, Google Play does not provide a refund window, so users
+will need to request a refund. They can request a refund from the <strong>My
+Orders</strong> page in the Play Store, or by contacting you directly.</p>
 
-<p>If you receive requests for refunds, you can use the server-side API to
-cancel the subscription or verify that it is already cancelled. However, keep in
-mind that Google Play considers cancelled subscriptions valid until the end of
-their current billing cycles, so even if you grant a refund and cancel the
-subscription, the user will still have access to the content.
+<p>If you receive requests for refunds, you can use the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play
+Developer API</a> or the Merchant Center to cancel the subscription, verify that it
+is already cancelled, or refund the user's payment without cancelling it. You
+can also use the
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google
+Play Developer API</a> to <em>refund and revoke</em> a
+user's subscription. If you refund and revoke a subscription, the user's
+subscription is immediately cancelled, and the user's most recent subscription
+payment is refunded. (If you want to refund more than the most recent payment,
+you can process additional refunds through the Merchant Center.)</p>
 
-<p class="caution"><strong>Important:</strong> Partial refunds for canceled
-subscriptions are not available at this time.</p>
+<p class="caution"><strong>Important:</strong> Partial refunds are not available
+at this time.</p>
 
 <h2 id="payment">Payment Processing and Policies</h2>
 
@@ -317,9 +392,9 @@
 each recurring transaction by appending an integer as follows: </p>
 
 <p><span style="color:#777"><code style="color:#777">12999556515565155651.5565135565155651</code> (base order number)</span><br />
-<code>12999556515565155651.5565135565155651..0</code> (initial purchase orderID)<br />
-<code>12999556515565155651.5565135565155651..1</code> (first recurrence orderID)<br />
-<code>12999556515565155651.5565135565155651..2</code> (second recurrence orderID)<br />
+<code>12999556515565155651.5565135565155651..0</code> (first recurrence orderID)<br />
+<code>12999556515565155651.5565135565155651..1</code> (second recurrence orderID)<br />
+<code>12999556515565155651.5565135565155651..2</code> (third recurrence orderID)<br />
 ...<br /></p>
 
 <p>Google Play provides the order number as the value of the 
@@ -334,19 +409,28 @@
 
 <p>To verify a purchase, the app passes the purchase token and other details up
 to your backend servers, which verifies them directly with Google Play using the
-Purchase Status API.  If the backend server determines that the purchase is
+Google Play Developer API.  If the backend server determines that the purchase is
 valid, it notifies the app and grants access to the content.</p>
 
 <p>Keep in mind that users will want the ability to use your app at any time,
 including when there may be no network connection available. Make sure that your
 approach to purchase verification accounts for the offline use-case.</p>
 
-<h2 id="play-dev-api">Google Play Android Developer API</h2>
+<h2 id="play-dev-api">Google Play Developer API</h2>
 
-<p>Google Play offers an HTTP-based API that lets you remotely query the
-validity of a specific subscription at any time or cancel a subscription. The
-API is designed to be used from your backend servers as a way of securely
+<p>Google Play offers an HTTP-based API that lets you perform such tasks as:</p>
+  <ul>
+    <li>Remotely query the validity of a specific subscription at any time</li>
+    <li>Cancel a subscription</li>
+    <li>Defer a subscription's next billing date</li>
+    <li>Refund a subscription payment without cancelling the subscription</li>
+    <li>Refund and revoke a subscription</li>
+  </ul>
+
+<p>The API is designed to be used from your backend servers as a way of securely
 managing subscriptions, as well as extending and integrating subscriptions with
 other services.</p>
 
-<p>For complete information, see <a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Purchase Status API</a>.</p>
+<p>For complete information, see
+<a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google Play
+Developer API</a>.</p>
diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd
index 875271fe..e1326d7 100644
--- a/docs/html/google/play/billing/index.jd
+++ b/docs/html/google/play/billing/index.jd
@@ -14,14 +14,27 @@
 <div class="sidebox">
   <h2><strong>New in In-App Billing</strong></h2>
   <ul>
+  <li><strong>Seasonal subscriptions</strong>&mdash;You can now set up a
+    recurring <a href="billing_subscriptions.html#user-billing">seasonal
+    subscription</a> that starts and ends on the same date each year (for
+    example, a sports subscription that starts every September 1 and ends every
+    April 10).</li>
+  <li><strong>Deferred subscription billing</strong>&mdash;You can
+    <a href="billing_subscriptions.html#deferred-billing">defer</a> a
+    subscriber's next billing date until the date you choose. The user still has
+    access to the content but is not charged during the deferral period.</li>
   <li><strong>Google Play Developer API</strong>&mdash;The
     <a href="{@docRoot}google/play/billing/gp-purchase-status-api.html">Google
     Play Developer API</a> allows you to perform a number of publishing and
     app-management tasks. It includes the functionality previously known as the
     <em>Purchase Status API.</em> </li>
+  <li><strong>Refund/Revoke subscription</strong>&mdash;You can use the
+    Google Play Developer API to <a href="billing_subscriptions.html#refunds">refund
+    and revoke</a> a user's subscription. If you do this, the user's
+    subscription ends
+    immediately, and his or her most recent subscription payment is
+    refunded.</li>
   <li><strong>In-app Billing Version 3</strong>&mdash;The <a href="{@docRoot}google/play/billing/api.html">latest version</a> of In-app Billing features a synchronous API that is easier to implement and lets you manage in-app products and subscriptions more effectively.</li>
-  <li><strong>Subscriptions now supported in Version 3</strong>&mdash;You can query and launch purchase flows for subscription items using the V3 API.</li>
-  <li><strong>Free trials</strong>&mdash;You can now offer users a configurable <a href="/google/play/billing/v2/billing_subscriptions.html#trials">free trial period</a> for your in-app subscriptions. You can set up trials with a simple change in the Developer Console&mdash;no change to your app code is needed.</li>
  </ul>
 </div>
 </div>
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index a8b8b16..06353c0 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -327,8 +327,10 @@
         // None
         "",
         // Matrix
+        "    fragColor.rgb /= (fragColor.a + 0.0019);\n" // un-premultiply
         "    fragColor *= colorMatrix;\n"
-        "    fragColor += colorMatrixVector;\n",
+        "    fragColor += colorMatrixVector;\n"
+        "    fragColor.rgb *= (fragColor.a + 0.0019);\n", // re-premultiply
         // PorterDuff
         "    fragColor = blendColors(colorBlend, fragColor);\n"
 };
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 6d4bb4a..3681637 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -70,6 +70,7 @@
     // TODO: move to a method on android:Paint
     static inline bool paintWillNotDraw(const SkPaint& paint) {
         return paint.getAlpha() == 0
+                && !paint.getColorFilter()
                 && getXfermode(paint.getXfermode()) != SkXfermode::kClear_Mode;
     }
 
@@ -77,6 +78,7 @@
     static inline bool paintWillNotDrawText(const SkPaint& paint) {
         return paint.getAlpha() == 0
                 && paint.getLooper() == NULL
+                && !paint.getColorFilter()
                 && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
     }
 // ----------------------------------------------------------------------------
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 40d45ea..71a05ab 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1351,6 +1351,16 @@
             streamType = AudioSystem.STREAM_NOTIFICATION;
         }
 
+        // If Hdmi-CEC system audio mode is on, show volume bar
+        // only when TV receives volume notification from Audio Receiver.
+        if (mHdmiTvClient != null && streamType == AudioSystem.STREAM_MUSIC) {
+            synchronized (mHdmiTvClient) {
+                if (mHdmiSystemAudioSupported &&
+                        ((flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0)) {
+                    flags &= ~AudioManager.FLAG_SHOW_UI;
+                }
+            }
+        }
         mVolumeController.postVolumeChanged(streamType, flags);
 
         if ((flags & AudioManager.FLAG_FIXED_VOLUME) == 0) {
diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java
index 713f147..4661226 100644
--- a/media/java/android/media/ToneGenerator.java
+++ b/media/java/android/media/ToneGenerator.java
@@ -34,6 +34,12 @@
      * the enum in ToneGenerator C++ class.     */
 
     /**
+     * Default value for an unknown or unspecified tone.
+     * @hide
+     */
+    public static final int TONE_UNKNOWN = -1;
+
+    /**
      * DTMF tone for key 0: 1336Hz, 941Hz, continuous</p>
      *
      * @see #ToneGenerator(int, int)
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index 14c41da..0bec217 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -526,7 +526,7 @@
                         callback.run();
                     }
                 }
-            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
         }
 
         public void close(final Runnable callback) {
@@ -552,7 +552,7 @@
                         callback.run();
                     }
                 }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
         }
 
         public void destroy() {
@@ -571,7 +571,7 @@
                     mPageContentCache.invalidate();
                     mPageContentCache.clear();
                 }
-            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
         }
 
         public void startPreload(int firstShownPage, int lastShownPage, RenderSpec renderSpec) {
@@ -687,7 +687,7 @@
             // Oh well, we will have work to do...
             renderTask = new RenderPageTask(pageIndex, renderSpec, callback);
             mPageToRenderTaskMap.put(pageIndex, renderTask);
-            renderTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+            renderTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
         }
 
         public void cancelRendering(int pageIndex) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 369c453..84bbc27 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -275,7 +275,6 @@
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         View page = mLayoutInflater.inflate(R.layout.preview_page, parent, false);
         ViewHolder holder = new MyViewHolder(page);
-        holder.setIsRecyclable(true);
         return holder;
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 693e5ac..d169319 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -312,6 +312,14 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+        if (mState != STATE_INITIALIZING && mCurrentPrinter != null) {
+            mPrinterRegistry.setTrackedPrinter(mCurrentPrinter.getId());
+        }
+    }
+
+    @Override
     public void onPause() {
         PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
 
@@ -347,6 +355,7 @@
         }
 
         mPrinterAvailabilityDetector.cancel();
+        mPrinterRegistry.setTrackedPrinter(null);
 
         super.onPause();
     }
@@ -816,6 +825,9 @@
     }
 
     private void ensureProgressUiShown() {
+        if (isFinishing()) {
+            return;
+        }
         if (mUiState != UI_STATE_PROGRESS) {
             mUiState = UI_STATE_PROGRESS;
             mPrintPreviewController.setUiShown(false);
@@ -825,6 +837,9 @@
     }
 
     private void ensurePreviewUiShown() {
+        if (isFinishing()) {
+            return;
+        }
         if (mUiState != UI_STATE_PREVIEW) {
             mUiState = UI_STATE_PREVIEW;
             mPrintPreviewController.setUiShown(true);
@@ -833,6 +848,9 @@
     }
 
     private void ensureErrorUiShown(CharSequence message, int action) {
+        if (isFinishing()) {
+            return;
+        }
         if (mUiState != UI_STATE_ERROR) {
             mUiState = UI_STATE_ERROR;
             mPrintPreviewController.setUiShown(false);
@@ -935,7 +953,7 @@
             // When the update is done we update the print preview.
             mProgressMessageController.post();
             return true;
-        } else  {
+        } else if (!willUpdate) {
             // Update preview.
             updatePrintPreviewController(false);
         }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
index b999866..0d45352 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
@@ -77,6 +77,7 @@
         mRecyclerView = (RecyclerView) activity.findViewById(R.id.preview_content);
         mRecyclerView.setLayoutManager(mLayoutManger);
         mRecyclerView.setAdapter(mPageAdapter);
+        mRecyclerView.setItemViewCacheSize(0);
         mPreloadController = new PreloadController(mRecyclerView);
         mRecyclerView.setOnScrollListener(mPreloadController);
 
@@ -348,8 +349,7 @@
 
         public void startPreloadContent() {
             PageAdapter pageAdapter = (PageAdapter) mRecyclerView.getAdapter();
-
-            if (pageAdapter.isOpened()) {
+            if (pageAdapter != null && pageAdapter.isOpened()) {
                 PageRange shownPages = computeShownPages();
                 if (shownPages != null) {
                     pageAdapter.startPreloadContent(shownPages);
@@ -359,8 +359,7 @@
 
         public void stopPreloadContent() {
             PageAdapter pageAdapter = (PageAdapter) mRecyclerView.getAdapter();
-
-            if (pageAdapter.isOpened()) {
+            if (pageAdapter != null && pageAdapter.isOpened()) {
                 pageAdapter.stopPreloadContent();
             }
         }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
index 1000117..b792789 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
@@ -52,12 +52,12 @@
 
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mContentRequested = false;
         requestPageContentIfNeeded();
     }
 
     @Override
     public void onPageContentAvailable(BitmapDrawable content) {
-        assert (getBackground() != content);
         setBackground(content);
     }
 
@@ -70,7 +70,7 @@
         final boolean providerChanged = (mProvider == null)
                 ? provider != null : !mProvider.equals(provider);
         final boolean loadingDrawableChanged = (mEmptyState == null)
-                ? mEmptyState != null : !mEmptyState.equals(emptyState);
+                ? emptyState != null : !mEmptyState.equals(emptyState);
         final boolean mediaSizeChanged = (mMediaSize == null)
                 ? mediaSize != null : !mMediaSize.equals(mediaSize);
         final boolean marginsChanged = (mMinMargins == null)
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b94a258..3c2a776 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -199,7 +199,7 @@
                   android:excludeFromRecents="true"
                   android:stateNotNeeded="true"
                   android:resumeWhilePausing="true"
-                  android:theme="@style/RecentsTheme">
+                  android:theme="@style/config_recents_activity_theme">
             <intent-filter>
                 <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
             </intent-filter>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a8c95c1..9654da9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -35,6 +35,10 @@
     <!-- The number of app icons we keep in memory -->
     <integer name="config_recents_max_icon_count">20</integer>
 
+
+    <!-- The theme to use for RecentsActivity. -->
+    <item type="style" name="config_recents_activity_theme">@style/RecentsTheme.Wallpaper</item>
+
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 0456c82..46e7587 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -20,14 +20,9 @@
         <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
     </style>
 
-    <!-- Alternate Recents theme -->
     <style name="RecentsTheme" parent="@android:style/Theme">
         <!-- NoTitle -->
         <item name="android:windowNoTitle">true</item>
-        <!-- Wallpaper -->
-        <item name="android:windowBackground">@color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
         <!-- Misc -->
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:navigationBarColor">@android:color/transparent</item>
@@ -36,6 +31,20 @@
         <item name="android:ambientShadowAlpha">0.35</item>
     </style>
 
+
+    <!-- Alternate Recents theme -->
+    <style name="RecentsTheme.Wallpaper">
+        <!-- Wallpaper -->
+        <item name="android:windowBackground">@color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+    </style>
+
+    <!-- Performance optimized alternate Recents theme (no wallpaper) -->
+    <style name="RecentsTheme.NoWallpaper">
+        <item name="android:windowBackground">@android:color/black</item>
+    </style>
+
     <!-- Animations for a non-full-screen window or activity. -->
     <style name="Animation.RecentsActivity" parent="@android:style/Animation.Activity">
         <item name="android:activityOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item>
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index 6d30bce..66e1e15 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -32,6 +32,7 @@
 public class SegmentedButtons extends LinearLayout {
     private static final Typeface MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL);
     private static final Typeface BOLD = Typeface.create("sans-serif", Typeface.BOLD);
+    private static final int LABEL_RES_KEY = R.id.label;
 
     private final Context mContext;
     private final LayoutInflater mInflater;
@@ -69,6 +70,7 @@
 
     public void addButton(int labelResId, Object value) {
         final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+        b.setTag(LABEL_RES_KEY, labelResId);
         b.setText(labelResId);
         final LayoutParams lp = (LayoutParams) b.getLayoutParams();
         if (getChildCount() == 0) {
@@ -80,6 +82,14 @@
         b.setOnClickListener(mClick);
     }
 
+    public void updateLocale() {
+        for (int i = 0; i < getChildCount(); i++) {
+            final Button b = (Button) getChildAt(i);
+            final int labelResId = (Integer) b.getTag(LABEL_RES_KEY);
+            b.setText(labelResId);
+        }
+    }
+
     private void fireOnSelected() {
         if (mCallback != null) {
             mCallback.onSelected(mSelectedValue);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index f829994..2b541d3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -120,7 +120,10 @@
         super.onFinishInflate();
 
         mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
-        addZenButtons();
+        mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
+        mZenButtons.addButton(R.string.interruption_level_priority,
+                Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
         mZenButtons.setCallback(mZenButtonsCallback);
 
         mZenSubhead = findViewById(R.id.zen_subhead);
@@ -170,13 +173,6 @@
         setExpanded(false);
     }
 
-    private void addZenButtons() {
-        mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
-        mZenButtons.addButton(R.string.interruption_level_priority,
-                Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
-    }
-
     public void setHidden(boolean hidden) {
         if (mHidden == hidden) return;
         mHidden = hidden;
@@ -238,8 +234,7 @@
     }
 
     public void updateLocale() {
-        mZenButtons.removeAllViews();
-        addZenButtons();
+        mZenButtons.updateLocale();
     }
 
     private void setExitCondition(Condition exitCondition) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 55d8c09..dfec307 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1925,6 +1925,15 @@
                     }
                     break;
                 }
+                case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai == null) {
+                        loge("EVENT_SET_EXPLICITLY_SELECTED from unknown NetworkAgent");
+                        break;
+                    }
+                    nai.networkMisc.explicitlySelected = true;
+                    break;
+                }
                 case NetworkMonitor.EVENT_NETWORK_TESTED: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
                     if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
@@ -1935,6 +1944,11 @@
                             rematchNetworkAndRequests(nai);
                         }
                         updateInetCondition(nai, valid);
+                        // Let the NetworkAgent know the state of its network
+                        nai.asyncChannel.sendMessage(
+                                android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+                                (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+                                0, null);
                     }
                     break;
                 }
@@ -2517,6 +2531,7 @@
             nai = mNetworkForNetId.get(network.netId);
         }
         if (nai == null) return;
+        if (DBG) log("reportBadNetwork(" + nai.name() + ") by " + uid);
         synchronized (nai) {
             if (isNetworkBlocked(nai, uid)) return;
 
@@ -4244,7 +4259,7 @@
         NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
             new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
             new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
-            networkMisc);
+            new NetworkMisc(networkMisc));
         synchronized (this) {
             nai.networkMonitor.systemReady = mSystemReady;
         }
@@ -4546,8 +4561,10 @@
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
             if (newNetwork == currentNetwork) {
-                if (DBG) log("Network " + newNetwork.name() + " was already satisfying" +
-                              " request " + nri.request.requestId + ". No change.");
+                if (DBG) {
+                    log("Network " + newNetwork.name() + " was already satisfying" +
+                            " request " + nri.request.requestId + ". No change.");
+                }
                 keep = true;
                 continue;
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 95d33e7..6d8e105 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3360,7 +3360,7 @@
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
                     mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
-                            null, null, null, null, 0, 0, 0, null, 0, null, false, null, null,
+                            null, null, null, null, 0, 0, 0, null, 0, 0, 0, null, false, null, null,
                             null);
                 }
             }
@@ -3759,8 +3759,8 @@
             final long origId = Binder.clearCallingIdentity();
             int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
                     r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null,
-                    resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0,
-                    options, false, null, null, null);
+                    resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage,
+                    -1, r.launchedFromUid, 0, options, false, null, null, null);
             Binder.restoreCallingIdentity(origId);
 
             r.finishing = wasFinishing;
@@ -8482,7 +8482,7 @@
 
     void moveTaskToFrontLocked(int taskId, int flags, Bundle options) {
         if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
-                Binder.getCallingUid(), "Task to front")) {
+                Binder.getCallingUid(), -1, -1, "Task to front")) {
             ActivityOptions.abort(options);
             return;
         }
@@ -8524,7 +8524,7 @@
                 ActivityStack stack = tr.stack;
                 if (stack.mResumedActivity != null && stack.mResumedActivity.task == tr) {
                     if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
-                            Binder.getCallingUid(), "Task to back")) {
+                            Binder.getCallingUid(), -1, -1, "Task to back")) {
                         return;
                     }
                 }
@@ -8576,7 +8576,7 @@
 
         synchronized(this) {
             if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
-                    Binder.getCallingUid(), "Task backwards")) {
+                    Binder.getCallingUid(), -1, -1, "Task backwards")) {
                 return;
             }
             final long origId = Binder.clearCallingIdentity();
@@ -10120,20 +10120,31 @@
         }
     }
     
-    boolean checkAppSwitchAllowedLocked(int callingPid, int callingUid,
-            String name) {
+    boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
+            int callingPid, int callingUid, String name) {
         if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
             return true;
         }
 
-        final int perm = checkComponentPermission(
-                android.Manifest.permission.STOP_APP_SWITCHES, callingPid,
-                callingUid, -1, true);
+        int perm = checkComponentPermission(
+                android.Manifest.permission.STOP_APP_SWITCHES, sourcePid,
+                sourceUid, -1, true);
         if (perm == PackageManager.PERMISSION_GRANTED) {
             return true;
         }
-        
-        Slog.w(TAG, name + " request from " + callingUid + " stopped");
+
+        // If the actual IPC caller is different from the logical source, then
+        // also see if they are allowed to control app switches.
+        if (callingUid != -1 && callingUid != sourceUid) {
+            perm = checkComponentPermission(
+                    android.Manifest.permission.STOP_APP_SWITCHES, callingPid,
+                    callingUid, -1, true);
+            if (perm == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+        }
+
+        Slog.w(TAG, name + " request from " + sourceUid + " stopped");
         return false;
     }
     
@@ -18944,13 +18955,7 @@
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                 }
                 if (tr.getRootActivity() != null) {
-                    long origId = Binder.clearCallingIdentity();
-                    try {
-                        moveTaskToFrontLocked(tr.taskId, 0, null);
-                        return;
-                    } finally {
-                        Binder.restoreCallingIdentity(origId);
-                    }
+                    moveTaskToFrontLocked(tr.taskId, 0, null);
                 }
             }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e224ac0..a75fc5a 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -550,10 +550,10 @@
 
             if (DEBUG_TASKS) Slog.d(TAG, "Comparing existing cls="
                     + taskIntent.getComponent().flattenToShortString()
-                    + "/aff=" + r.task.affinity + " to new cls="
+                    + "/aff=" + r.task.rootAffinity + " to new cls="
                     + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
-            if (!isDocument && !taskIsDocument && task.affinity != null) {
-                if (task.affinity.equals(target.taskAffinity)) {
+            if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
+                if (task.rootAffinity.equals(target.taskAffinity)) {
                     if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
                     return r;
                 }
@@ -2949,7 +2949,7 @@
                     int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
                             null, aInfo, null, null, parent.appToken, null,
                             0, -1, parent.launchedFromUid, parent.launchedFromPackage,
-                            0, null, true, null, null, null);
+                            -1, parent.launchedFromUid, 0, null, true, null, null, null);
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
                 } catch (RemoteException e) {
                     foundParentInTask = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7b6a202..4940493 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -785,8 +785,8 @@
 
     void startHomeActivity(Intent intent, ActivityInfo aInfo) {
         moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
-        startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
-                null, false, null, null, null);
+        startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null,
+                0, 0, 0, null, false, null, null, null);
     }
 
     final int startActivityMayWait(IApplicationThread caller, int callingUid,
@@ -810,12 +810,14 @@
 
         ActivityContainer container = (ActivityContainer)iContainer;
         synchronized (mService) {
+            final int realCallingPid = Binder.getCallingPid();
+            final int realCallingUid = Binder.getCallingUid();
             int callingPid;
             if (callingUid >= 0) {
                 callingPid = -1;
             } else if (caller == null) {
-                callingPid = Binder.getCallingPid();
-                callingUid = Binder.getCallingUid();
+                callingPid = realCallingPid;
+                callingUid = realCallingUid;
             } else {
                 callingPid = callingUid = -1;
             }
@@ -841,11 +843,11 @@
                     if (mService.mHeavyWeightProcess != null &&
                             (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
                             !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
-                        int realCallingUid = callingUid;
+                        int appCallingUid = callingUid;
                         if (caller != null) {
                             ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
                             if (callerApp != null) {
-                                realCallingUid = callerApp.info.uid;
+                                appCallingUid = callerApp.info.uid;
                             } else {
                                 Slog.w(TAG, "Unable to find app for caller " + caller
                                       + " (pid=" + callingPid + ") when starting: "
@@ -857,7 +859,7 @@
 
                         IIntentSender target = mService.getIntentSenderLocked(
                                 ActivityManager.INTENT_SENDER_ACTIVITY, "android",
-                                realCallingUid, userId, null, null, 0, new Intent[] { intent },
+                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
                                 new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
 
@@ -903,7 +905,8 @@
 
             int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                     voiceSession, voiceInteractor, resultTo, resultWho,
-                    requestCode, callingPid, callingUid, callingPackage, startFlags, options,
+                    requestCode, callingPid, callingUid, callingPackage,
+                    realCallingPid, realCallingUid, startFlags, options,
                     componentSpecified, null, container, inTask);
 
             Binder.restoreCallingIdentity(origId);
@@ -1017,7 +1020,8 @@
                         theseOptions = null;
                     }
                     int res = startActivityLocked(caller, intent, resolvedTypes[i],
-                            aInfo, null, null, resultTo, null, -1, callingPid, callingUid, callingPackage,
+                            aInfo, null, null, resultTo, null, -1, callingPid, callingUid,
+                            callingPackage, callingPid, callingUid,
                             0, theseOptions, componentSpecified, outActivity, null, null);
                     if (res < 0) {
                         return res;
@@ -1248,7 +1252,8 @@
             Intent intent, String resolvedType, ActivityInfo aInfo,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode,
-            int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
+            int callingPid, int callingUid, String callingPackage,
+            int realCallingPid, int realCallingUid, int startFlags, Bundle options,
             boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
             TaskRecord inTask) {
         int err = ActivityManager.START_SUCCESS;
@@ -1293,8 +1298,7 @@
 
         final int launchFlags = intent.getFlags();
 
-        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
-                && sourceRecord != null) {
+        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
             // Transfer the result target from the source activity to the new
             // one being started, including any failures.
             if (requestCode >= 0) {
@@ -1344,8 +1348,8 @@
             if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                     && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
                 try {
-                    if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
-                            intent, resolvedType)) {
+                    if (!AppGlobals.getPackageManager().activitySupportsIntent(
+                            intent.getComponent(), intent, resolvedType)) {
                         err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
                     }
                 } catch (RemoteException e) {
@@ -1439,7 +1443,8 @@
         final ActivityStack stack = getFocusedStack();
         if (voiceSession == null && (stack.mResumedActivity == null
                 || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
-            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
+                    realCallingPid, realCallingUid, "Activity start")) {
                 PendingActivityLaunch pal =
                         new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
                 mPendingActivityLaunches.add(pal);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index b21af48..df1772a 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -51,6 +51,12 @@
      * task being launched a chance to load its resources without this occupying IO bandwidth. */
     private static final long PRE_TASK_DELAY_MS = 3000;
 
+    /** The maximum number of entries to keep in the queue before draining it automatically. */
+    private static final int MAX_WRITE_QUEUE_LENGTH = 6;
+
+    /** Special value for mWriteTime to mean don't wait, just write */
+    private static final long FLUSH_QUEUE = -1;
+
     private static final String RECENTS_FILENAME = "_task";
     private static final String TASKS_DIRNAME = "recent_tasks";
     private static final String TASK_EXTENSION = ".xml";
@@ -120,6 +126,31 @@
         mLazyTaskWriterThread.start();
     }
 
+    private void removeThumbnails(TaskRecord task) {
+        final String taskString = Integer.toString(task.taskId);
+        for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
+            final WriteQueueItem item = mWriteQueue.get(queueNdx);
+            if (item instanceof ImageWriteQueueItem &&
+                    ((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
+                if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
+                        " from write queue");
+                mWriteQueue.remove(queueNdx);
+            }
+        }
+    }
+
+    private void yieldIfQueueTooDeep() {
+        boolean stall = false;
+        synchronized (this) {
+            if (mNextWriteTime == FLUSH_QUEUE) {
+                stall = true;
+            }
+        }
+        if (stall) {
+            Thread.yield();
+        }
+    }
+
     void wakeup(TaskRecord task, boolean flush) {
         synchronized (this) {
             if (task != null) {
@@ -128,6 +159,10 @@
                     final WriteQueueItem item = mWriteQueue.get(queueNdx);
                     if (item instanceof TaskWriteQueueItem &&
                             ((TaskWriteQueueItem) item).mTask == task) {
+                        if (!task.inRecents) {
+                            // This task is being removed.
+                            removeThumbnails(task);
+                        }
                         break;
                     }
                 }
@@ -138,15 +173,18 @@
                 // Dummy.
                 mWriteQueue.add(new WriteQueueItem());
             }
-            if (flush) {
-                mNextWriteTime = -1;
+            if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+                mNextWriteTime = FLUSH_QUEUE;
             } else if (mNextWriteTime == 0) {
                 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
             }
             if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
-                    + mNextWriteTime + " Callers=" + Debug.getCallers(4));
+                    + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
+                    + " Callers=" + Debug.getCallers(4));
             notifyAll();
         }
+
+        yieldIfQueueTooDeep();
     }
 
     void saveImage(Bitmap image, String filename) {
@@ -166,7 +204,9 @@
             if (queueNdx < 0) {
                 mWriteQueue.add(new ImageWriteQueueItem(filename, image));
             }
-            if (mNextWriteTime == 0) {
+            if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+                mNextWriteTime = FLUSH_QUEUE;
+            } else if (mNextWriteTime == 0) {
                 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
             }
             if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
@@ -174,6 +214,8 @@
                     mNextWriteTime + " Callers=" + Debug.getCallers(4));
             notifyAll();
         }
+
+        yieldIfQueueTooDeep();
     }
 
     Bitmap getThumbnail(String filename) {
@@ -425,7 +467,7 @@
                 // If mNextWriteTime, then don't delay between each call to saveToXml().
                 final WriteQueueItem item;
                 synchronized (TaskPersister.this) {
-                    if (mNextWriteTime >= 0) {
+                    if (mNextWriteTime != FLUSH_QUEUE) {
                         // The next write we don't have to wait so long.
                         mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
                         if (DEBUG) Slog.d(TAG, "Next write time may be in " +
@@ -439,13 +481,14 @@
                             TaskPersister.this.wait();
                         } catch (InterruptedException e) {
                         }
-                        // Invariant: mNextWriteTime is either -1 or PRE_WRITE_DELAY_MS from now.
+                        // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+                        // from now.
                     }
                     item = mWriteQueue.remove(0);
 
                     long now = SystemClock.uptimeMillis();
                     if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
-                            mNextWriteTime);
+                            mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
                     while (now < mNextWriteTime) {
                         try {
                             if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
@@ -484,7 +527,7 @@
                     TaskRecord task = ((TaskWriteQueueItem) item).mTask;
                     if (DEBUG) Slog.d(TAG, "Writing task=" + task);
                     synchronized (mService) {
-                        if (mService.mRecentTasks.contains(task)) {
+                        if (task.inRecents) {
                             // Still there.
                             try {
                                 if (DEBUG) Slog.d(TAG, "Saving task=" + task);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 071db97..4de7367 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -58,6 +58,7 @@
     private static final String ATTR_ORIGACTIVITY = "orig_activity";
     private static final String TAG_ACTIVITY = "activity";
     private static final String ATTR_AFFINITY = "affinity";
+    private static final String ATTR_ROOT_AFFINITY = "root_affinity";
     private static final String ATTR_ROOTHASRESET = "root_has_reset";
     private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
     private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
@@ -84,7 +85,8 @@
     static final boolean IGNORE_RETURN_TO_RECENTS = true;
 
     final int taskId;       // Unique identifier for this task.
-    String affinity;        // The affinity name for this task, or null.
+    String affinity;        // The affinity name for this task, or null; may change identity.
+    String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
     final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
     final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
     Intent intent;          // The original intent that started the task.
@@ -208,9 +210,9 @@
     }
 
     TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
-            String _affinity, ComponentName _realActivity, ComponentName _origActivity,
-            boolean _rootWasReset, boolean _autoRemoveRecents, boolean _askedCompatMode,
-            int _taskType, int _userId, int _effectiveUid,
+            String _affinity, String _rootAffinity, ComponentName _realActivity,
+            ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents,
+            boolean _askedCompatMode, int _taskType, int _userId, int _effectiveUid,
             String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime,
             long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
             ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
@@ -224,6 +226,7 @@
         intent = _intent;
         affinityIntent = _affinityIntent;
         affinity = _affinity;
+        rootAffinity = _affinity;
         voiceSession = null;
         voiceInteractor = null;
         realActivity = _realActivity;
@@ -279,6 +282,12 @@
         }
 
         affinity = info.taskAffinity;
+        if (intent == null) {
+            // If this task already has an intent associated with it, don't set the root
+            // affinity -- we don't want it changing after initially set, but the initially
+            // set value may be null.
+            rootAffinity = affinity;
+        }
         effectiveUid = info.applicationInfo.uid;
         stringName = null;
 
@@ -840,8 +849,17 @@
         if (origActivity != null) {
             out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
         }
+        // Write affinity, and root affinity if it is different from affinity.
+        // We use the special string "@" for a null root affinity, so we can identify
+        // later whether we were given a root affinity or should just make it the
+        // same as the affinity.
         if (affinity != null) {
             out.attribute(null, ATTR_AFFINITY, affinity);
+            if (!affinity.equals(rootAffinity)) {
+                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+            }
+        } else if (rootAffinity != null) {
+            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
         }
         out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
         out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
@@ -901,6 +919,8 @@
         ComponentName realActivity = null;
         ComponentName origActivity = null;
         String affinity = null;
+        String rootAffinity = null;
+        boolean hasRootAffinity = false;
         boolean rootHasReset = false;
         boolean autoRemoveRecents = false;
         boolean askedCompatMode = false;
@@ -935,6 +955,9 @@
                 origActivity = ComponentName.unflattenFromString(attrValue);
             } else if (ATTR_AFFINITY.equals(attrName)) {
                 affinity = attrValue;
+            } else if (ATTR_ROOT_AFFINITY.equals(attrName)) {
+                rootAffinity = attrValue;
+                hasRootAffinity = true;
             } else if (ATTR_ROOTHASRESET.equals(attrName)) {
                 rootHasReset = Boolean.valueOf(attrValue);
             } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
@@ -1007,6 +1030,12 @@
                     createLastTaskDescriptionIconFilename(taskId, lastActiveTime)));
         }
 
+        if (!hasRootAffinity) {
+            rootAffinity = affinity;
+        } else if ("@".equals(rootAffinity)) {
+            rootAffinity = null;
+        }
+
         if (effectiveUid <= 0) {
             Intent checkIntent = intent != null ? intent : affinityIntent;
             effectiveUid = 0;
@@ -1028,7 +1057,7 @@
         }
 
         final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
-                affinityIntent, affinity, realActivity, origActivity, rootHasReset,
+                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
                 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
@@ -1047,8 +1076,13 @@
                 pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
                 pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
                 pw.print(" mCallingPackage="); pw.println(mCallingPackage);
-        if (affinity != null) {
-            pw.print(prefix); pw.print("affinity="); pw.println(affinity);
+        if (affinity != null || rootAffinity != null) {
+            pw.print(prefix); pw.print("affinity="); pw.print(affinity);
+            if (affinity == null || !affinity.equals(rootAffinity)) {
+                pw.print(" root="); pw.println(rootAffinity);
+            } else {
+                pw.println();
+            }
         }
         if (voiceSession != null || voiceInteractor != null) {
             pw.print(prefix); pw.print("VOICE: session=0x");
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 957d705..15ffc0d 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -53,6 +53,9 @@
     // Penalty applied to scores of Networks that have not been validated.
     private static final int UNVALIDATED_SCORE_PENALTY = 40;
 
+    // Score for explicitly connected network.
+    private static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
+
     // The list of NetworkRequests being satisfied by this Network.
     public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
     public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
@@ -95,9 +98,10 @@
         int score = currentScore;
 
         if (!validated) score -= UNVALIDATED_SCORE_PENALTY;
-
         if (score < 0) score = 0;
 
+        if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
+
         return score;
     }
 
@@ -110,7 +114,8 @@
                 network + "}  lp{" +
                 linkProperties + "}  nc{" +
                 networkCapabilities + "}  Score{" + getCurrentScore() + "} " +
-                "validated{" + validated + "} created{" + created + "} }";
+                "validated{" + validated + "} created{" + created + "} " +
+                "explicitlySelected{" + networkMisc.explicitlySelected + "} }";
     }
 
     public String name() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 9e33205..fab064c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -260,7 +260,7 @@
         addState(mUserPromptedState, mDefaultState);
         addState(mCaptivePortalState, mDefaultState);
         addState(mLingeringState, mDefaultState);
-        setInitialState(mOfflineState);
+        setInitialState(mDefaultState);
 
         mServer = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.CAPTIVE_PORTAL_SERVER);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9a34047..4d9b4e9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -798,7 +798,7 @@
             // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
             // volume change notification back to hdmi control service.
             audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
-                    AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
+                    AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/MhlConstants.java b/services/core/java/com/android/server/hdmi/MhlConstants.java
deleted file mode 100644
index fe479f3..0000000
--- a/services/core/java/com/android/server/hdmi/MhlConstants.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.android.server.hdmi;
-
-/**
- * Defines constants related to MHL protocol internal implementation.
- */
-final class MhlConstants {
-    // --------------------------------------------------
-    // MHL sub command message types.
-    static final int MSG_MSGE  = 0x02;
-    static final int MSG_RCP   = 0x10;
-    static final int MSG_RCPK  = 0x11;
-    static final int MSG_RCPE  = 0x12;
-    static final int MSG_RAP   = 0x20;
-    static final int MSG_RAPK  = 0x21;
-
-    // MHL RAP messages.
-    static final int RAP_ACTION_POLL = 0x00;
-    static final int RAP_ACTION_CONTENT_ON = 0x10;
-    static final int RAP_ACTION_CONTENT_OFF = 0x11;
-
-    // MHL RAPK messages.
-    static final int RAPK_NO_ERROR = 0x00;
-    static final int RAPK_UNRECOGNIZED_ACTION = 0x01;
-    static final int RAPK_UNSUPPORTED_ACTION = 0x02;
-    static final int RAPK_RESPONDER_BUSY = 0x03;
-
-    static final int INVALID_ADOPTER_ID = -1;
-    static final int INVALID_DEVICE_ID = -1;
-
-    static final int CBUS_MODE_OCBUS = 1;
-    static final int CBUS_MODE_ECBUS_S = 2;
-    static final int CBUS_MODE_ECBUS_D = 3;
-
-    // MHL RCPE messages
-    static final int RCPE_NO_ERROR = 0x00;
-    static final int RCPE_INEFFECTIVE_KEYCODE = 0x01;
-    static final int RCPE_RESPONDER_BUSY = 0x02;
-
-    private MhlConstants() { /* cannot be instantiated */ }
-}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d87fc2e..79c9955 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4311,7 +4311,7 @@
                  * grantPermissions will assume the package update is trying to
                  * expand its permissions.
                  */
-                grantPermissionsLPw(pkg, true);
+                grantPermissionsLPw(pkg, true, pkg.packageName);
                 mSettings.disableSystemPackageLPw(pkg.packageName);
             }
         }
@@ -6696,17 +6696,19 @@
         if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
             for (PackageParser.Package pkg : mPackages.values()) {
                 if (pkg != pkgInfo) {
-                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
+                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
+                            changingPkg);
                 }
             }
         }
         
         if (pkgInfo != null) {
-            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0);
+            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
         }
     }
 
-    private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
+    private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
+            String packageOfInterest) {
         final PackageSetting ps = (PackageSetting) pkg.mExtras;
         if (ps == null) {
             return;
@@ -6740,8 +6742,10 @@
             }
 
             if (bp == null || bp.packageSetting == null) {
-                Slog.w(TAG, "Unknown permission " + name
-                        + " in package " + pkg.packageName);
+                if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+                    Slog.w(TAG, "Unknown permission " + name
+                            + " in package " + pkg.packageName);
+                }
                 continue;
             }
 
@@ -6806,9 +6810,11 @@
                         gp.gids = appendInts(gp.gids, bp.gids);
                     }
                 } else {
-                    Slog.w(TAG, "Not granting permission " + perm
-                            + " to package " + pkg.packageName
-                            + " because it was previously installed without");
+                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+                        Slog.w(TAG, "Not granting permission " + perm
+                                + " to package " + pkg.packageName
+                                + " because it was previously installed without");
+                    }
                 }
             } else {
                 if (gp.grantedPermissions.remove(perm)) {
@@ -6822,11 +6828,13 @@
                 } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
                     // Don't print warning for app op permissions, since it is fine for them
                     // not to be granted, there is a UI for the user to decide.
-                    Slog.w(TAG, "Not granting permission " + perm
-                            + " to package " + pkg.packageName
-                            + " (protectionLevel=" + bp.protectionLevel
-                            + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
-                            + ")");
+                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+                        Slog.w(TAG, "Not granting permission " + perm
+                                + " to package " + pkg.packageName
+                                + " (protectionLevel=" + bp.protectionLevel
+                                + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+                                + ")");
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5bfc834..dc355c4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -323,9 +323,10 @@
                 notifyInputAddedLocked(userState, inputId);
             } else if (updatedPackages != null) {
                 // Notify the package updates
-                TvInputState inputState = inputMap.get(inputId);
+                ComponentName component = inputMap.get(inputId).info.getComponent();
                 for (String updatedPackage : updatedPackages) {
-                    if (inputState.info.getComponent().getPackageName().equals(updatedPackage)) {
+                    if (component.getPackageName().equals(updatedPackage)) {
+                        updateServiceConnectionLocked(component, userId);
                         notifyInputUpdatedLocked(userState, inputId);
                         break;
                     }
@@ -553,18 +554,6 @@
         updateServiceConnectionLocked(serviceState.component, userId);
     }
 
-    private ClientState createClientStateLocked(IBinder clientToken, int userId) {
-        UserState userState = getUserStateLocked(userId);
-        ClientState clientState = new ClientState(clientToken, userId);
-        try {
-            clientToken.linkToDeath(clientState, 0);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "client process has already died", e);
-        }
-        userState.clientStateMap.put(clientToken, clientState);
-        return clientState;
-    }
-
     private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
             int userId) {
         UserState userState = getUserStateLocked(userId);
@@ -1926,56 +1915,67 @@
     }
 
     private final class SessionCallback extends ITvInputSessionCallback.Stub {
-        private final SessionState sessionState;
+        private final SessionState mSessionState;
         private final InputChannel[] mChannels;
 
         SessionCallback(SessionState sessionState, InputChannel[] channels) {
-            this.sessionState = sessionState;
+            mSessionState = sessionState;
             mChannels = channels;
         }
 
         @Override
         public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
             if (DEBUG) {
-                Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.info.getId() + ")");
+                Slog.d(TAG, "onSessionCreated(inputId=" + mSessionState.info.getId() + ")");
             }
             synchronized (mLock) {
-                sessionState.session = session;
-                sessionState.hardwareSessionToken = harewareSessionToken;
-                if (session == null) {
-                    removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
-                    sendSessionTokenToClientLocked(sessionState.client,
-                            sessionState.info.getId(), null, null, sessionState.seq);
+                mSessionState.session = session;
+                mSessionState.hardwareSessionToken = harewareSessionToken;
+                if (session != null && addSessionTokenToClientStateLocked(session)) {
+                    sendSessionTokenToClientLocked(mSessionState.client,
+                            mSessionState.info.getId(), mSessionState.sessionToken, mChannels[0],
+                            mSessionState.seq);
                 } else {
-                    try {
-                        session.asBinder().linkToDeath(sessionState, 0);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "session process has already died", e);
-                    }
-
-                    IBinder clientToken = sessionState.client.asBinder();
-                    UserState userState = getUserStateLocked(sessionState.userId);
-                    ClientState clientState = userState.clientStateMap.get(clientToken);
-                    if (clientState == null) {
-                        clientState = createClientStateLocked(clientToken, sessionState.userId);
-                    }
-                    clientState.sessionTokens.add(sessionState.sessionToken);
-
-                    sendSessionTokenToClientLocked(sessionState.client,
-                            sessionState.info.getId(), sessionState.sessionToken, mChannels[0],
-                            sessionState.seq);
+                    removeSessionStateLocked(mSessionState.sessionToken, mSessionState.userId);
+                    sendSessionTokenToClientLocked(mSessionState.client,
+                            mSessionState.info.getId(), null, null, mSessionState.seq);
                 }
                 mChannels[0].dispose();
             }
         }
 
+        private boolean addSessionTokenToClientStateLocked(ITvInputSession session) {
+            try {
+                session.asBinder().linkToDeath(mSessionState, 0);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "session process has already died", e);
+                return false;
+            }
+
+            IBinder clientToken = mSessionState.client.asBinder();
+            UserState userState = getUserStateLocked(mSessionState.userId);
+            ClientState clientState = userState.clientStateMap.get(clientToken);
+            if (clientState == null) {
+                clientState = new ClientState(clientToken, mSessionState.userId);
+                try {
+                    clientToken.linkToDeath(clientState, 0);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "client process has already died", e);
+                    return false;
+                }
+                userState.clientStateMap.put(clientToken, clientState);
+            }
+            clientState.sessionTokens.add(mSessionState.sessionToken);
+            return true;
+        }
+
         @Override
         public void onChannelRetuned(Uri channelUri) {
             synchronized (mLock) {
                 if (DEBUG) {
                     Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
@@ -1983,7 +1983,7 @@
                     // that, how we can protect the watch log from malicious tv inputs should
                     // be addressed. e.g. add a field which represents where the channel change
                     // originated from.
-                    sessionState.client.onChannelRetuned(channelUri, sessionState.seq);
+                    mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onChannelRetuned", e);
                 }
@@ -1996,11 +1996,11 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onTracksChanged(" + tracks + ")");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onTracksChanged(tracks, sessionState.seq);
+                    mSessionState.client.onTracksChanged(tracks, mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onTracksChanged", e);
                 }
@@ -2013,11 +2013,11 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onTrackSelected(type, trackId, sessionState.seq);
+                    mSessionState.client.onTrackSelected(type, trackId, mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onTrackSelected", e);
                 }
@@ -2030,11 +2030,11 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onVideoAvailable()");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onVideoAvailable(sessionState.seq);
+                    mSessionState.client.onVideoAvailable(mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onVideoAvailable", e);
                 }
@@ -2047,11 +2047,11 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onVideoUnavailable(reason, sessionState.seq);
+                    mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onVideoUnavailable", e);
                 }
@@ -2064,11 +2064,11 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onContentAllowed()");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onContentAllowed(sessionState.seq);
+                    mSessionState.client.onContentAllowed(mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onContentAllowed", e);
                 }
@@ -2081,11 +2081,11 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onContentBlocked()");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onContentBlocked(rating, sessionState.seq);
+                    mSessionState.client.onContentBlocked(rating, mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onContentBlocked", e);
                 }
@@ -2099,11 +2099,12 @@
                     Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
                             + ", right=" + right + ", bottom=" + bottom + ",)");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onLayoutSurface(left, top, right, bottom, sessionState.seq);
+                    mSessionState.client.onLayoutSurface(left, top, right, bottom,
+                            mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onLayoutSurface", e);
                 }
@@ -2116,12 +2117,11 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
                 }
-                if (sessionState.session == null || sessionState.client == null) {
+                if (mSessionState.session == null || mSessionState.client == null) {
                     return;
                 }
                 try {
-                    sessionState.client.onSessionEvent(eventType, eventArgs,
-                            sessionState.seq);
+                    mSessionState.client.onSessionEvent(eventType, eventArgs, mSessionState.seq);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onSessionEvent", e);
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d4bcd5c..0c51160 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -328,6 +328,7 @@
                     for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
                         AppWindowToken wtoken = tokens.get(tokenNdx);
                         if (wtoken.mDeferRemoval) {
+                            stack.mExitingAppTokens.remove(wtoken);
                             wtoken.mDeferRemoval = false;
                             mService.removeAppFromTaskLocked(wtoken);
                         }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 1d33b3b..1a6c52f 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -19,7 +19,6 @@
 import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Bundle;
-import android.telephony.DisconnectCause;
 
 import java.lang.String;
 import java.util.ArrayList;
@@ -89,8 +88,7 @@
         private final PhoneAccountHandle mAccountHandle;
         private final int mCallCapabilities;
         private final int mCallProperties;
-        private final int mDisconnectCauseCode;
-        private final String mDisconnectCauseMessage;
+        private final DisconnectCause mDisconnectCause;
         private final long mConnectTimeMillis;
         private final GatewayInfo mGatewayInfo;
         private final int mVideoState;
@@ -154,18 +152,10 @@
 
         /**
          * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
-         * as a code chosen from among those declared in {@link DisconnectCause}.
+         * by {@link android.telecomm.DisconnectCause}.
          */
-        public int getDisconnectCauseCode() {
-            return mDisconnectCauseCode;
-        }
-
-        /**
-         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, an optional reason for
-         * disconnection expressed as a free text message.
-         */
-        public String getDisconnectCauseMessage() {
-            return mDisconnectCauseMessage;
+        public DisconnectCause getDisconnectCause() {
+            return mDisconnectCause;
         }
 
         /**
@@ -219,8 +209,7 @@
                         Objects.equals(mAccountHandle, d.mAccountHandle) &&
                         Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
                         Objects.equals(mCallProperties, d.mCallProperties) &&
-                        Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
-                        Objects.equals(mDisconnectCauseMessage, d.mDisconnectCauseMessage) &&
+                        Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
                         Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
                         Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
                         Objects.equals(mVideoState, d.mVideoState) &&
@@ -240,8 +229,7 @@
                     Objects.hashCode(mAccountHandle) +
                     Objects.hashCode(mCallCapabilities) +
                     Objects.hashCode(mCallProperties) +
-                    Objects.hashCode(mDisconnectCauseCode) +
-                    Objects.hashCode(mDisconnectCauseMessage) +
+                    Objects.hashCode(mDisconnectCause) +
                     Objects.hashCode(mConnectTimeMillis) +
                     Objects.hashCode(mGatewayInfo) +
                     Objects.hashCode(mVideoState) +
@@ -258,8 +246,7 @@
                 PhoneAccountHandle accountHandle,
                 int capabilities,
                 int properties,
-                int disconnectCauseCode,
-                String disconnectCauseMessage,
+                DisconnectCause disconnectCause,
                 long connectTimeMillis,
                 GatewayInfo gatewayInfo,
                 int videoState,
@@ -272,8 +259,7 @@
             mAccountHandle = accountHandle;
             mCallCapabilities = capabilities;
             mCallProperties = properties;
-            mDisconnectCauseCode = disconnectCauseCode;
-            mDisconnectCauseMessage = disconnectCauseMessage;
+            mDisconnectCause = disconnectCause;
             mConnectTimeMillis = connectTimeMillis;
             mGatewayInfo = gatewayInfo;
             mVideoState = videoState;
@@ -654,8 +640,7 @@
                 parcelableCall.getAccountHandle(),
                 parcelableCall.getCapabilities(),
                 parcelableCall.getProperties(),
-                parcelableCall.getDisconnectCauseCode(),
-                parcelableCall.getDisconnectCauseMsg(),
+                parcelableCall.getDisconnectCause(),
                 parcelableCall.getConnectTimeMillis(),
                 parcelableCall.getGatewayInfo(),
                 parcelableCall.getVideoState(),
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index ca85446..9b350c1 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,8 +16,6 @@
 
 package android.telecom;
 
-import android.telephony.DisconnectCause;
-
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -32,7 +30,7 @@
     /** @hide */
     public abstract static class Listener {
         public void onStateChanged(Conference conference, int oldState, int newState) {}
-        public void onDisconnected(Conference conference, int cause, String message) {}
+        public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {}
         public void onConnectionAdded(Conference conference, Connection connection) {}
         public void onConnectionRemoved(Conference conference, Connection connection) {}
         public void onDestroyed(Conference conference) {}
@@ -45,8 +43,9 @@
             Collections.unmodifiableList(mChildConnections);
 
     private PhoneAccountHandle mPhoneAccount;
+    private AudioState mAudioState;
     private int mState = Connection.STATE_NEW;
-    private int mDisconnectCause = DisconnectCause.NOT_VALID;
+    private DisconnectCause mDisconnectCause;
     private int mCapabilities;
     private String mDisconnectMessage;
 
@@ -96,6 +95,15 @@
     }
 
     /**
+     * @return The audio state of the conference, describing how its audio is currently
+     *         being routed by the system. This is {@code null} if this Conference
+     *         does not directly know about its audio state.
+     */
+    public final AudioState getAudioState() {
+        return mAudioState;
+    }
+
+    /**
      * Invoked when the Conference and all it's {@link Connection}s should be disconnected.
      */
     public void onDisconnect() {}
@@ -130,6 +138,25 @@
     public void onSwap() {}
 
     /**
+     * Notifies this conference of a request to play a DTMF tone.
+     *
+     * @param c A DTMF character.
+     */
+    public void onPlayDtmfTone(char c) {}
+
+    /**
+     * Notifies this conference of a request to stop any currently playing DTMF tones.
+     */
+    public void onStopDtmfTone() {}
+
+    /**
+     * Notifies this conference that the {@link #getAudioState()} property has a new value.
+     *
+     * @param state The new call audio state.
+     */
+    public void onAudioStateChanged(AudioState state) {}
+
+    /**
      * Sets state to be on hold.
      */
     public final void setOnHold() {
@@ -146,16 +173,14 @@
     /**
      * Sets state to disconnected.
      *
-     * @param cause The reason for the disconnection, any of
-     *         {@link android.telephony.DisconnectCause}.
-     * @param message Optional call-service-provided message about the disconnect.
+     * @param disconnectCause The reason for the disconnection, as described by
+     *     {@link android.telecom.DisconnectCause}.
      */
-    public final void setDisconnected(int cause, String message) {
-        mDisconnectCause = cause;
-        mDisconnectMessage = message;
+    public final void setDisconnected(DisconnectCause disconnectCause) {
+        mDisconnectCause = disconnectCause;;
         setState(Connection.STATE_DISCONNECTED);
         for (Listener l : mListeners) {
-            l.onDisconnected(this, mDisconnectCause, mDisconnectMessage);
+            l.onDisconnected(this, mDisconnectCause);
         }
     }
 
@@ -222,7 +247,7 @@
         // If not yet disconnected, set the conference call as disconnected first.
         if (mState != Connection.STATE_DISCONNECTED) {
             Log.d(this, "setting to disconnected");
-            setDisconnected(DisconnectCause.LOCAL, null);
+            setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
         }
 
         // ...and notify.
@@ -255,6 +280,18 @@
         return this;
     }
 
+    /**
+     * Inform this Conference that the state of its audio output has been changed externally.
+     *
+     * @param state The new audio state.
+     * @hide
+     */
+    final void setAudioState(AudioState state) {
+        Log.d(this, "setAudioState %s", state);
+        mAudioState = state;
+        onAudioStateChanged(state);
+    }
+
     private void setState(int newState) {
         if (newState != Connection.STATE_ACTIVE &&
                 newState != Connection.STATE_HOLDING &&
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 5f63af3..7979e44 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -24,7 +24,6 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
-import android.telephony.DisconnectCause;
 import android.view.Surface;
 
 import java.util.ArrayList;
@@ -71,7 +70,7 @@
         public void onCallerDisplayNameChanged(
                 Connection c, String callerDisplayName, int presentation) {}
         public void onVideoStateChanged(Connection c, int videoState) {}
-        public void onDisconnected(Connection c, int cause, String message) {}
+        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
         public void onPostDialWait(Connection c, String remaining) {}
         public void onRingbackRequested(Connection c, boolean ringback) {}
         public void onDestroyed(Connection c) {}
@@ -473,8 +472,7 @@
     private boolean mAudioModeIsVoip;
     private StatusHints mStatusHints;
     private int mVideoState;
-    private int mDisconnectCause;
-    private String mDisconnectMessage;
+    private DisconnectCause mDisconnectCause;
     private Conference mConference;
     private ConnectionService mConnectionService;
 
@@ -604,18 +602,11 @@
     /**
      * @return The {@link DisconnectCause} for this connection.
      */
-    public final int getDisconnectCause() {
+    public final DisconnectCause getDisconnectCause() {
         return mDisconnectCause;
     }
 
     /**
-     * @return The disconnect message for this connection.
-     */
-    public final String getDisconnectMessage() {
-        return mDisconnectMessage;
-    }
-
-    /**
      * Inform this Connection that the state of its audio output has been changed externally.
      *
      * @param state The new audio state.
@@ -774,17 +765,15 @@
     /**
      * Sets state to disconnected.
      *
-     * @param cause The reason for the disconnection, any of
+     * @param disconnectCause The reason for the disconnection, as specified by
      *         {@link DisconnectCause}.
-     * @param message Optional call-service-provided message about the disconnect.
      */
-    public final void setDisconnected(int cause, String message) {
-        mDisconnectCause = cause;
-        mDisconnectMessage = message;
+    public final void setDisconnected(DisconnectCause disconnectCause) {
+        mDisconnectCause = disconnectCause;
         setState(STATE_DISCONNECTED);
-        Log.d(this, "Disconnected with cause %d message %s", cause, message);
+        Log.d(this, "Disconnected with cause %s", disconnectCause);
         for (Listener l : mListeners) {
-            l.onDisconnected(this, cause, message);
+            l.onDisconnected(this, disconnectCause);
         }
     }
 
@@ -1071,26 +1060,24 @@
     }
 
     private static class FailureSignalingConnection extends Connection {
-        public FailureSignalingConnection(int cause, String message) {
-            setDisconnected(cause, message);
+        public FailureSignalingConnection(DisconnectCause disconnectCause) {
+            setDisconnected(disconnectCause);
         }
     }
 
     /**
      * Return a {@code Connection} which represents a failed connection attempt. The returned
-     * {@code Connection} will have a {@link #getDisconnectCause()} and
-     * {@link #getDisconnectMessage()} as specified, and a {@link #getState()} of
-     * {@link #STATE_DISCONNECTED}.
+     * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
+     * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
      * <p>
      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
      * so users of this method need not maintain a reference to its return value to destroy it.
      *
-     * @param cause The disconnect cause, ({@see DisconnectCause}).
-     * @param message A reason for why the connection failed (not intended to be shown to the user).
+     * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
      * @return A {@code Connection} which indicates failure.
      */
-    public static Connection createFailedConnection(int cause, String message) {
-        return new FailureSignalingConnection(cause, message);
+    public static Connection createFailedConnection(DisconnectCause disconnectCause) {
+        return new FailureSignalingConnection(disconnectCause);
     }
 
     /**
@@ -1105,7 +1092,7 @@
      * @return A {@code Connection} which indicates that the underlying call should be canceled.
      */
     public static Connection createCanceledConnection() {
-        return new FailureSignalingConnection(DisconnectCause.OUTGOING_CANCELED, null);
+        return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
     }
 
     private final void  fireOnConferenceableConnectionsChanged() {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index cc80e22..3e18bac 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -25,7 +25,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.telephony.DisconnectCause;
 
 import com.android.internal.os.SomeArgs;
 import com.android.internal.telecom.IConnectionService;
@@ -344,9 +343,9 @@
         }
 
         @Override
-        public void onDisconnected(Conference conference, int cause, String message) {
+        public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {
             String id = mIdByConference.get(conference);
-            mAdapter.setDisconnected(id, cause, message);
+            mAdapter.setDisconnected(id, disconnectCause);
         }
 
         @Override
@@ -399,10 +398,10 @@
         }
 
         @Override
-        public void onDisconnected(Connection c, int cause, String message) {
+        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
             String id = mIdByConnection.get(c);
-            Log.d(this, "Adapter set disconnected %d %s", cause, message);
-            mAdapter.setDisconnected(id, cause, message);
+            Log.d(this, "Adapter set disconnected %s", disconnectCause);
+            mAdapter.setDisconnected(id, disconnectCause);
         }
 
         @Override
@@ -522,7 +521,8 @@
                 : onCreateOutgoingConnection(callManagerAccount, request);
         Log.d(this, "createConnection, connection: %s", connection);
         if (connection == null) {
-            connection = Connection.createFailedConnection(DisconnectCause.OUTGOING_FAILURE, null);
+            connection = Connection.createFailedConnection(
+                    new DisconnectCause(DisconnectCause.ERROR));
         }
 
         if (connection.getState() != Connection.STATE_DISCONNECTED) {
@@ -555,7 +555,6 @@
                         connection.getAudioModeIsVoip(),
                         connection.getStatusHints(),
                         connection.getDisconnectCause(),
-                        connection.getDisconnectMessage(),
                         createConnectionIdList(connection.getConferenceableConnections())));
     }
 
@@ -608,17 +607,29 @@
 
     private void onAudioStateChanged(String callId, AudioState audioState) {
         Log.d(this, "onAudioStateChanged %s %s", callId, audioState);
-        findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+        } else {
+            findConferenceForAction(callId, "onAudioStateChanged").setAudioState(audioState);
+        }
     }
 
     private void playDtmfTone(String callId, char digit) {
         Log.d(this, "playDtmfTone %s %c", callId, digit);
-        findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+        } else {
+            findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+        }
     }
 
     private void stopDtmfTone(String callId) {
         Log.d(this, "stopDtmfTone %s", callId);
-        findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
+        } else {
+            findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();
+        }
     }
 
     private void conference(String callId1, String callId2) {
@@ -836,7 +847,7 @@
      *         making the connection.
      * @param request Details about the outgoing call.
      * @return The {@code Connection} object to satisfy this call, or the result of an invocation
-     *         of {@link Connection#createFailedConnection(int, String)} to not handle the call.
+     *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
      */
     public Connection onCreateOutgoingConnection(
             PhoneAccountHandle connectionManagerPhoneAccount,
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index f6bcdc6..c676172 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -134,14 +134,13 @@
      * Sets a call's state to disconnected.
      *
      * @param callId The unique ID of the call whose state is changing to disconnected.
-     * @param disconnectCause The reason for the disconnection, any of
-     *            {@link android.telephony.DisconnectCause}.
-     * @param disconnectMessage Optional call-service-provided message about the disconnect.
+     * @param disconnectCause The reason for the disconnection, as described by
+     *            {@link android.telecomm.DisconnectCause}.
      */
-    void setDisconnected(String callId, int disconnectCause, String disconnectMessage) {
+    void setDisconnected(String callId, DisconnectCause disconnectCause) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setDisconnected(callId, disconnectCause, disconnectMessage);
+                adapter.setDisconnected(callId, disconnectCause);
             } catch (RemoteException e) {
             }
         }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index ffbbc8a..217dbc3 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -96,8 +96,7 @@
                 case MSG_SET_DISCONNECTED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setDisconnected(
-                                (String) args.arg1, args.argi1, (String) args.arg2);
+                        mDelegate.setDisconnected((String) args.arg1, (DisconnectCause) args.arg2);
                     } finally {
                         args.recycle();
                     }
@@ -234,11 +233,10 @@
 
         @Override
         public void setDisconnected(
-                String connectionId, int disconnectCause, String disconnectMessage) {
+                String connectionId, DisconnectCause disconnectCause) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
-            args.arg2 = disconnectMessage;
-            args.argi1 = disconnectCause;
+            args.arg2 = disconnectCause;
             mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
         }
 
diff --git a/telecomm/java/android/telecom/DisconnectCause.aidl b/telecomm/java/android/telecom/DisconnectCause.aidl
new file mode 100644
index 0000000..26b8652
--- /dev/null
+++ b/telecomm/java/android/telecom/DisconnectCause.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable DisconnectCause;
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
new file mode 100644
index 0000000..cae115d
--- /dev/null
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.media.ToneGenerator;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Describes the cause of a disconnected call. This always includes a code describing the generic
+ * cause of the disconnect. Optionally, it may include a localized label and/or localized description
+ * to display to the user which is provided by the {@link ConnectionService}. It also may contain a
+ * reason for the the disconnect, which is intended for logging and not for display to the user.
+ */
+public final class DisconnectCause implements Parcelable {
+
+    /** Disconnected because of an unknown or unspecified reason. */
+    public static final int UNKNOWN = 0;
+    /** Disconnected because there was an error, such as a problem with the network. */
+    public static final int ERROR = 1;
+    /** Disconnected because of a local user-initiated action, such as hanging up. */
+    public static final int LOCAL = 2;
+    /**
+     * Disconnected because of a remote user-initiated action, such as the other party hanging up
+     * up.
+     */
+    public static final int REMOTE = 3;
+    /** Disconnected because it has been canceled. */
+    public static final int CANCELED = 4;
+    /** Disconnected because there was no response to an incoming call. */
+    public static final int MISSED = 5;
+    /** Disconnected because the user rejected an incoming call. */
+    public static final int REJECTED = 6;
+    /** Disconnected because the other party was busy. */
+    public static final int BUSY = 7;
+    /**
+     * Disconnected because of a restriction on placing the call, such as dialing in airplane
+     * mode.
+     */
+    public static final int RESTRICTED = 8;
+    /** Disconnected for reason not described by other disconnect codes. */
+    public static final int OTHER = 9;
+
+    private int mDisconnectCode;
+    private CharSequence mDisconnectLabel;
+    private CharSequence mDisconnectDescription;
+    private String mDisconnectReason;
+    private int mToneToPlay;
+
+    /**
+     * Creates a new DisconnectCause.
+     *
+     * @param code The code for the disconnect cause.
+     */
+    public DisconnectCause(int code) {
+        this(code, null, null, null, ToneGenerator.TONE_UNKNOWN);
+    }
+
+    /**
+     * Creates a new DisconnectCause.
+     *
+     * @param code The code for the disconnect cause.
+     * @param reason The reason for the disconnect.
+     */
+    public DisconnectCause(int code, String reason) {
+        this(code, null, null, reason, ToneGenerator.TONE_UNKNOWN);
+    }
+
+    /**
+     * Creates a new DisconnectCause.
+     *
+     * @param code The code for the disconnect cause.
+     * @param label The localized label to show to the user to explain the disconnect.
+     * @param description The localized description to show to the user to explain the disconnect.
+     * @param reason The reason for the disconnect.
+     * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
+     */
+    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+            int toneToPlay) {
+        mDisconnectCode = code;
+        mDisconnectLabel = label;
+        mDisconnectDescription = description;
+        mDisconnectReason = reason;
+        mToneToPlay = toneToPlay;
+    }
+
+    /**
+     * Returns the code for the reason for this disconnect.
+     *
+     * @return The disconnect code.
+     */
+    public int getCode() {
+        return mDisconnectCode;
+    }
+
+    /**
+     * Returns a short label which explains the reason for the disconnect cause and is for display
+     * in the user interface. The {@link ConnectionService } is responsible for providing and
+     * localizing this label. If there is no string provided, returns null.
+     *
+     * @return The disconnect label.
+     */
+    public CharSequence getLabel() {
+        return mDisconnectLabel;
+    }
+
+    /**
+     * Returns a description which explains the reason for the disconnect cause and is for display
+     * in the user interface. The {@link ConnectionService } is responsible for providing and
+     * localizing this message. If there is no string provided, returns null.
+     *
+     * @return The disconnect description.
+     */
+    public CharSequence getDescription() {
+        return mDisconnectDescription;
+    }
+
+    /**
+     * Returns an explanation of the reason for the disconnect. This is not intended for display to
+     * the user and is used mainly for logging.
+     *
+     * @return The disconnect reason.
+     */
+    public String getReason() {
+        return mDisconnectReason;
+    }
+
+    /**
+     * Returns the tone to play when disconnected.
+     *
+     * @return the tone as defined in {@link ToneGenerator} to play when disconnected.
+     */
+    public int getTone() {
+        return mToneToPlay;
+    }
+
+    public static final Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() {
+        @Override
+        public DisconnectCause createFromParcel(Parcel source) {
+            int code = source.readInt();
+            CharSequence label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            String reason = source.readString();
+            int tone = source.readInt();
+            return new DisconnectCause(code, label, description, reason, tone);
+        }
+
+        @Override
+        public DisconnectCause[] newArray(int size) {
+            return new DisconnectCause[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel destination, int flags) {
+        destination.writeInt(mDisconnectCode);
+        TextUtils.writeToParcel(mDisconnectLabel, destination, flags);
+        TextUtils.writeToParcel(mDisconnectDescription, destination, flags);
+        destination.writeString(mDisconnectReason);
+        destination.writeInt(mToneToPlay);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mDisconnectCode)
+                + Objects.hashCode(mDisconnectLabel)
+                + Objects.hashCode(mDisconnectDescription)
+                + Objects.hashCode(mDisconnectReason)
+                + Objects.hashCode(mToneToPlay);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof DisconnectCause) {
+            DisconnectCause d = (DisconnectCause) o;
+            return Objects.equals(mDisconnectCode, d.getCode())
+                    && Objects.equals(mDisconnectLabel, d.getLabel())
+                    && Objects.equals(mDisconnectDescription, d.getDescription())
+                    && Objects.equals(mDisconnectReason, d.getReason())
+                    && Objects.equals(mToneToPlay, d.getTone());
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        String code = "";
+        switch (getCode()) {
+            case ERROR:
+                code = "ERROR";
+                break;
+            case LOCAL:
+                code = "LOCAL";
+                break;
+            case REMOTE:
+                code = "REMOTE";
+                break;
+            case MISSED:
+                code = "MISSED";
+                break;
+            case REJECTED:
+                code = "REJECTED";
+                break;
+            case BUSY:
+                code = "BUSY";
+                break;
+            case RESTRICTED:
+                code = "RESTRICTED";
+                break;
+            case OTHER:
+                code = "OTHER";
+                break;
+            case UNKNOWN:
+            default:
+                code = "UNKNOWN";
+        }
+        String label = mDisconnectLabel == null ? "" : mDisconnectLabel.toString();
+        String description = mDisconnectDescription == null
+                ? "" : mDisconnectDescription.toString();
+        String reason = mDisconnectReason == null ? "" : mDisconnectReason;
+        return "DisconnectCause [ Code: (" + code + ")"
+                + " Label: (" + label + ")"
+                + " Description: (" + description + ")"
+                + " Reason: (" + reason + ")"
+                + " Tone: (" + mToneToPlay + ") ]";
+    }
+}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 838c7cf..c5c3d11 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -21,7 +21,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.telephony.DisconnectCause;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -36,8 +35,7 @@
 public final class ParcelableCall implements Parcelable {
     private final String mId;
     private final int mState;
-    private final int mDisconnectCauseCode;
-    private final String mDisconnectCauseMsg;
+    private final DisconnectCause mDisconnectCause;
     private final List<String> mCannedSmsResponses;
     private final int mCapabilities;
     private final int mProperties;
@@ -60,8 +58,7 @@
     public ParcelableCall(
             String id,
             int state,
-            int disconnectCauseCode,
-            String disconnectCauseMsg,
+            DisconnectCause disconnectCause,
             List<String> cannedSmsResponses,
             int capabilities,
             int properties,
@@ -81,8 +78,7 @@
             Bundle extras) {
         mId = id;
         mState = state;
-        mDisconnectCauseCode = disconnectCauseCode;
-        mDisconnectCauseMsg = disconnectCauseMsg;
+        mDisconnectCause = disconnectCause;
         mCannedSmsResponses = cannedSmsResponses;
         mCapabilities = capabilities;
         mProperties = properties;
@@ -113,19 +109,11 @@
     }
 
     /**
-     * Reason for disconnection, values are defined in {@link DisconnectCause}. Valid when call
-     * state is {@link CallState#DISCONNECTED}.
+     * Reason for disconnection, as described by {@link android.telecomm.DisconnectCause}. Valid
+     * when call state is {@link CallState#DISCONNECTED}.
      */
-    public int getDisconnectCauseCode() {
-        return mDisconnectCauseCode;
-    }
-
-    /**
-     * Further optional textual information about the reason for disconnection. Valid when call
-     * state is {@link CallState#DISCONNECTED}.
-     */
-    public String getDisconnectCauseMsg() {
-        return mDisconnectCauseMsg;
+    public DisconnectCause getDisconnectCause() {
+        return mDisconnectCause;
     }
 
     /**
@@ -252,8 +240,7 @@
             ClassLoader classLoader = ParcelableCall.class.getClassLoader();
             String id = source.readString();
             int state = source.readInt();
-            int disconnectCauseCode = source.readInt();
-            String disconnectCauseMsg = source.readString();
+            DisconnectCause disconnectCause = source.readParcelable(classLoader);
             List<String> cannedSmsResponses = new ArrayList<>();
             source.readList(cannedSmsResponses, classLoader);
             int capabilities = source.readInt();
@@ -275,11 +262,27 @@
             List<String> conferenceableCallIds = new ArrayList<>();
             source.readList(conferenceableCallIds, classLoader);
             Bundle extras = source.readParcelable(classLoader);
-            return new ParcelableCall(id, state, disconnectCauseCode, disconnectCauseMsg,
-                    cannedSmsResponses, capabilities, properties, connectTimeMillis, handle,
-                    handlePresentation, callerDisplayName, callerDisplayNamePresentation,
-                    gatewayInfo, accountHandle, videoCallProvider, parentCallId, childCallIds,
-                    statusHints, videoState, conferenceableCallIds, extras);
+            return new ParcelableCall(
+                    id,
+                    state,
+                    disconnectCause,
+                    cannedSmsResponses,
+                    capabilities,
+                    properties,
+                    connectTimeMillis,
+                    handle,
+                    handlePresentation,
+                    callerDisplayName,
+                    callerDisplayNamePresentation,
+                    gatewayInfo,
+                    accountHandle,
+                    videoCallProvider,
+                    parentCallId,
+                    childCallIds,
+                    statusHints,
+                    videoState,
+                    conferenceableCallIds,
+                    extras);
         }
 
         @Override
@@ -299,8 +302,7 @@
     public void writeToParcel(Parcel destination, int flags) {
         destination.writeString(mId);
         destination.writeInt(mState);
-        destination.writeInt(mDisconnectCauseCode);
-        destination.writeString(mDisconnectCauseMsg);
+        destination.writeParcelable(mDisconnectCause, 0);
         destination.writeList(mCannedSmsResponses);
         destination.writeInt(mCapabilities);
         destination.writeInt(mProperties);
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index 63393b2..9004448 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -44,8 +44,7 @@
     private final boolean mRingbackRequested;
     private final boolean mIsVoipAudioMode;
     private final StatusHints mStatusHints;
-    private final int mDisconnectCause;
-    private final String mDisconnectMessage;
+    private final DisconnectCause mDisconnectCause;
     private final List<String> mConferenceableConnectionIds;
 
     /** @hide */
@@ -62,8 +61,7 @@
             boolean ringbackRequested,
             boolean isVoipAudioMode,
             StatusHints statusHints,
-            int disconnectCause,
-            String disconnectMessage,
+            DisconnectCause disconnectCause,
             List<String> conferenceableConnectionIds) {
         mPhoneAccount = phoneAccount;
         mState = state;
@@ -78,7 +76,6 @@
         mIsVoipAudioMode = isVoipAudioMode;
         mStatusHints = statusHints;
         mDisconnectCause = disconnectCause;
-        mDisconnectMessage = disconnectMessage;
         this.mConferenceableConnectionIds = conferenceableConnectionIds;
     }
 
@@ -131,14 +128,10 @@
         return mStatusHints;
     }
 
-    public final int getDisconnectCause() {
+    public final DisconnectCause getDisconnectCause() {
         return mDisconnectCause;
     }
 
-    public final String getDisconnectMessage() {
-        return mDisconnectMessage;
-    }
-
     public final List<String> getConferenceableConnectionIds() {
         return mConferenceableConnectionIds;
     }
@@ -174,8 +167,7 @@
             boolean ringbackRequested = source.readByte() == 1;
             boolean audioModeIsVoip = source.readByte() == 1;
             StatusHints statusHints = source.readParcelable(classLoader);
-            int disconnectCauseCode = source.readInt();
-            String disconnectCauseMessage = source.readString();
+            DisconnectCause disconnectCause = source.readParcelable(classLoader);
             List<String> conferenceableConnectionIds = new ArrayList<>();
             source.readStringList(conferenceableConnectionIds);
 
@@ -192,8 +184,7 @@
                     ringbackRequested,
                     audioModeIsVoip,
                     statusHints,
-                    disconnectCauseCode,
-                    disconnectCauseMessage,
+                    disconnectCause,
                     conferenceableConnectionIds);
         }
 
@@ -225,8 +216,7 @@
         destination.writeByte((byte) (mRingbackRequested ? 1 : 0));
         destination.writeByte((byte) (mIsVoipAudioMode ? 1 : 0));
         destination.writeParcelable(mStatusHints, 0);
-        destination.writeInt(mDisconnectCause);
-        destination.writeString(mDisconnectMessage);
+        destination.writeParcelable(mDisconnectCause, 0);
         destination.writeStringList(mConferenceableConnectionIds);
     }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 0c233eb..4b059b2 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -91,17 +91,6 @@
     public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 0x10;
 
     /**
-     * Flag indicating that this {@code PhoneAccount} is always enabled and cannot be disabled by
-     * the user.
-     * This capability is reserved for important {@code PhoneAccount}s such as the emergency calling
-     * only {@code PhoneAccount}.
-     * <p>
-     * See {@link #getCapabilities}
-     * @hide
-     */
-    public static final int CAPABILITY_ALWAYS_ENABLED = 0x20;
-
-    /**
      * URI scheme for telephone number URIs.
      */
     public static final String SCHEME_TEL = "tel";
@@ -124,7 +113,6 @@
     private final CharSequence mLabel;
     private final CharSequence mShortDescription;
     private final List<String> mSupportedUriSchemes;
-    private final boolean mIsEnabled;
 
     public static class Builder {
         private PhoneAccountHandle mAccountHandle;
@@ -135,7 +123,6 @@
         private CharSequence mLabel;
         private CharSequence mShortDescription;
         private List<String> mSupportedUriSchemes = new ArrayList<String>();
-        private boolean mIsEnabled = false;
 
         public Builder(PhoneAccountHandle accountHandle, CharSequence label) {
             this.mAccountHandle = accountHandle;
@@ -157,7 +144,6 @@
             mLabel = phoneAccount.getLabel();
             mShortDescription = phoneAccount.getShortDescription();
             mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
-            mIsEnabled = phoneAccount.isEnabled();
         }
 
         public Builder setAddress(Uri value) {
@@ -217,19 +203,6 @@
         }
 
         /**
-         * Specifies whether the {@link PhoneAccount} is enabled or not.  {@link PhoneAccount}s are
-         * by default not enabled.
-         *
-         * @param value {@code True} if the {@link PhoneAccount} is enabled.
-         * @return The Builder.
-         * @hide
-         */
-        public Builder setEnabled(boolean value) {
-            this.mIsEnabled = value;
-            return this;
-        }
-
-        /**
          * Creates an instance of a {@link PhoneAccount} based on the current builder settings.
          *
          * @return The {@link PhoneAccount}.
@@ -248,8 +221,7 @@
                     mIconResId,
                     mLabel,
                     mShortDescription,
-                    mSupportedUriSchemes,
-                    mIsEnabled);
+                    mSupportedUriSchemes);
         }
     }
 
@@ -261,8 +233,7 @@
             int iconResId,
             CharSequence label,
             CharSequence shortDescription,
-            List<String> supportedUriSchemes,
-            boolean enabled) {
+            List<String> supportedUriSchemes) {
         mAccountHandle = account;
         mAddress = address;
         mSubscriptionAddress = subscriptionAddress;
@@ -271,7 +242,6 @@
         mLabel = label;
         mShortDescription = shortDescription;
         mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
-        mIsEnabled = enabled;
     }
 
     public static Builder builder(
@@ -392,15 +362,6 @@
     }
 
     /**
-     * Determines whether this {@code PhoneAccount} is enabled.
-     *
-     * @return {@code True} if this {@code PhoneAccount} is enabled..
-     */
-    public boolean isEnabled() {
-        return mIsEnabled;
-    }
-
-    /**
      * The icon resource ID for the icon of this {@code PhoneAccount}.
      *
      * @return A resource ID.
@@ -455,7 +416,6 @@
         out.writeCharSequence(mLabel);
         out.writeCharSequence(mShortDescription);
         out.writeList(mSupportedUriSchemes);
-        out.writeInt(mIsEnabled ? 1 : 0);
     }
 
     public static final Creator<PhoneAccount> CREATOR
@@ -485,6 +445,5 @@
         List<String> supportedUriSchemes = new ArrayList<>();
         in.readList(supportedUriSchemes, classLoader);
         mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
-        mIsEnabled = in.readInt() == 1;
     }
 }
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index 996e091..f931bc5 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -19,7 +19,6 @@
 import com.android.internal.telecom.IConnectionService;
 
 import android.os.RemoteException;
-import android.telephony.DisconnectCause;
 
 import java.util.Collections;
 import java.util.List;
@@ -34,7 +33,7 @@
 
     public abstract static class Callback {
         public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
-        public void onDisconnected(RemoteConference conference, int cause, String message) {}
+        public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
         public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
         public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
         public void onCapabilitiesChanged(RemoteConference conference, int capabilities) {}
@@ -50,9 +49,8 @@
             Collections.unmodifiableList(mChildConnections);
 
     private int mState = Connection.STATE_NEW;
-    private int mDisconnectCause = DisconnectCause.NOT_VALID;
+    private DisconnectCause mDisconnectCause;
     private int mCallCapabilities;
-    private String mDisconnectMessage;
 
     /** {@hide} */
     RemoteConference(String id, IConnectionService connectionService) {
@@ -127,13 +125,12 @@
     }
 
     /** {@hide} */
-    void setDisconnected(int cause, String message) {
+    void setDisconnected(DisconnectCause disconnectCause) {
         if (mState != Connection.STATE_DISCONNECTED) {
-            mDisconnectCause = cause;
-            mDisconnectMessage = message;
+            mDisconnectCause = disconnectCause;
             setState(Connection.STATE_DISCONNECTED);
             for (Callback c : mCallbacks) {
-                c.onDisconnected(this, cause, message);
+                c.onDisconnected(this, disconnectCause);
             }
         }
     }
@@ -180,12 +177,29 @@
         }
     }
 
-    public int getDisconnectCause() {
+    public DisconnectCause getDisconnectCause() {
         return mDisconnectCause;
     }
 
-    public String getDisconnectMessage() {
-        return mDisconnectMessage;
+    public void playDtmfTone(char digit) {
+        try {
+            mConnectionService.playDtmfTone(mId, digit);
+        } catch (RemoteException e) {
+        }
+    }
+
+    public void stopDtmfTone() {
+        try {
+            mConnectionService.stopDtmfTone(mId);
+        } catch (RemoteException e) {
+        }
+    }
+
+    public void setAudioState(AudioState state) {
+        try {
+            mConnectionService.onAudioStateChanged(mId, state);
+        } catch (RemoteException e) {
+        }
     }
 
     public final void registerCallback(Callback callback) {
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index bf699b3..9a094df 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -23,7 +23,6 @@
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.DisconnectCause;
 import android.view.Surface;
 
 import java.util.ArrayList;
@@ -55,15 +54,12 @@
          * Invoked when this {@code RemoteConnection} is disconnected.
          *
          * @param connection The {@code RemoteConnection} invoking this method.
-         * @param disconnectCauseCode The failure code ({@see DisconnectCause}) associated with this
-         *         failed connection.
-         * @param disconnectCauseMessage The reason for the connection failure. This will not be
-         *         displayed to the user.
+         * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
+         *     connection.
          */
         public void onDisconnected(
                 RemoteConnection connection,
-                int disconnectCauseCode,
-                String disconnectCauseMessage) {}
+                DisconnectCause disconnectCause) {}
 
         /**
          * Invoked when this {@code RemoteConnection} is requesting ringback. See
@@ -383,8 +379,7 @@
             Collections.unmodifiableList(mConferenceableConnections);
 
     private int mState = Connection.STATE_NEW;
-    private int mDisconnectCauseCode = DisconnectCause.NOT_VALID;
-    private String mDisconnectCauseMessage;
+    private DisconnectCause mDisconnectCause;
     private boolean mRingbackRequested;
     private boolean mConnected;
     private int mCallCapabilities;
@@ -396,8 +391,6 @@
     private int mAddressPresentation;
     private String mCallerDisplayName;
     private int mCallerDisplayNamePresentation;
-    private int mFailureCode;
-    private String mFailureMessage;
     private RemoteConference mConference;
 
     /**
@@ -418,15 +411,14 @@
      * "real" purpose will almost certainly fail. Callers should note the failure and act
      * accordingly (moving on to another RemoteConnection, for example)
      *
-     * @param failureCode
-     * @param failureMessage
+     * @param disconnectCause The reason for the failed connection.
+     * @hide
      */
-    RemoteConnection(int failureCode, String failureMessage) {
+    RemoteConnection(DisconnectCause disconnectCause) {
         this("NULL", null, null);
         mConnected = false;
         mState = Connection.STATE_DISCONNECTED;
-        mFailureCode = DisconnectCause.OUTGOING_FAILURE;
-        mFailureMessage = failureMessage + " original code = " + failureCode;
+        mDisconnectCause = disconnectCause;
     }
 
     /**
@@ -463,16 +455,8 @@
      * disconnect cause expressed as a code chosen from among those declared in
      * {@link DisconnectCause}.
      */
-    public int getDisconnectCauseCode() {
-        return mDisconnectCauseCode;
-    }
-
-    /**
-     * @return For a {@link Connection#STATE_DISCONNECTED} {@code RemoteConnection}, an optional
-     * reason for disconnection expressed as a free text message.
-     */
-    public String getDisconnectCauseMessage() {
-        return mDisconnectCauseMessage;
+    public DisconnectCause getDisconnectCause() {
+        return mDisconnectCause;
     }
 
     /**
@@ -547,21 +531,6 @@
     }
 
     /**
-     * @return The failure code ({@see DisconnectCause}) associated with this failed
-     * {@code RemoteConnection}.
-     */
-    public int getFailureCode() {
-        return mFailureCode;
-    }
-
-    /**
-     * @return The reason for the connection failure. This will not be displayed to the user.
-     */
-    public String getFailureMessage() {
-        return mFailureMessage;
-    }
-
-    /**
      * @return Whether the {@code RemoteConnection} is requesting that the framework play a
      * ringback tone on its behalf.
      */
@@ -779,14 +748,13 @@
     /**
      * @hide
      */
-    void setDisconnected(int cause, String message) {
+    void setDisconnected(DisconnectCause disconnectCause) {
         if (mState != Connection.STATE_DISCONNECTED) {
             mState = Connection.STATE_DISCONNECTED;
-            mDisconnectCauseCode = cause;
-            mDisconnectCauseMessage = message;
+            mDisconnectCause = disconnectCause;
 
             for (Callback c : mCallbacks) {
-                c.onDisconnected(this, cause, message);
+                c.onDisconnected(this, mDisconnectCause);
             }
         }
     }
@@ -820,7 +788,8 @@
         if (!mCallbacks.isEmpty()) {
             // Make sure that the callbacks are notified that the call is destroyed first.
             if (mState != Connection.STATE_DISCONNECTED) {
-                setDisconnected(DisconnectCause.ERROR_UNSPECIFIED, "Connection destroyed.");
+                setDisconnected(
+                        new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
             }
 
             for (Callback c : mCallbacks) {
@@ -923,7 +892,7 @@
      *
      * @hide
      */
-    public static RemoteConnection failure(int failureCode, String failureMessage) {
-        return new RemoteConnection(failureCode, failureMessage);
+    public static RemoteConnection failure(DisconnectCause disconnectCause) {
+        return new RemoteConnection(disconnectCause);
     }
 }
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index bfd7c51..03b38c2 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -20,7 +20,6 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
-import android.telephony.DisconnectCause;
 
 import com.android.internal.telecom.IConnectionService;
 import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -107,14 +106,13 @@
         }
 
         @Override
-        public void setDisconnected(String callId, int disconnectCause,
-                String disconnectMessage) {
+        public void setDisconnected(String callId, DisconnectCause disconnectCause) {
             if (mConnectionById.containsKey(callId)) {
                 findConnectionForAction(callId, "setDisconnected")
-                        .setDisconnected(disconnectCause, disconnectMessage);
+                        .setDisconnected(disconnectCause);
             } else {
                 findConferenceForAction(callId, "setDisconnected")
-                        .setDisconnected(disconnectCause, disconnectMessage);
+                        .setDisconnected(disconnectCause);
             }
         }
 
@@ -351,8 +349,8 @@
             });
             return connection;
         } catch (RemoteException e) {
-            return RemoteConnection
-                    .failure(DisconnectCause.ERROR_UNSPECIFIED, e.toString());
+            return RemoteConnection.failure(
+                    new DisconnectCause(DisconnectCause.ERROR, e.toString()));
         }
     }
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4d438ed..a91d92f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -71,24 +71,6 @@
             "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
 
     /**
-     * The {@link android.content.Intent} action used to inform a
-     * {@link android.telecom.ConnectionService} that one of its {@link PhoneAccount}s has been
-     * enabled.  The {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} extra is used to indicate
-     * which {@link PhoneAccount} has been enabled.
-     */
-    public static final String ACTION_PHONE_ACCOUNT_ENABLED =
-            "android.telecom.action.PHONE_ACCOUNT_ENABLED";
-
-    /**
-     * The {@link android.content.Intent} action used to inform a
-     * {@link android.telecom.ConnectionService} that one of its {@link PhoneAccount}s has been
-     * disabled.  The {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} extra is used to indicate
-     * which {@link PhoneAccount} has been disabled.
-     */
-    public static final String ACTION_PHONE_ACCOUNT_DISABLED =
-            "android.telecom.action.PHONE_ACCOUNT_DISABLED";
-
-    /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
      * determines whether the speakerphone should be automatically turned on for an outgoing call.
      */
@@ -325,14 +307,14 @@
     /**
      * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
      * calls with a specified URI scheme. This {@code PhoneAccount} will always be a member of the
-     * list which is returned from calling {@link #getEnabledPhoneAccounts()}.
+     * list which is returned from calling {@link #getCallCapablePhoneAccounts()}.
      * <p>
      * Apps must be prepared for this method to return {@code null}, indicating that there currently
      * exists no user-chosen default {@code PhoneAccount}. In this case, apps wishing to initiate a
      * phone call must either create their {@link android.content.Intent#ACTION_CALL} or
      * {@link android.content.Intent#ACTION_DIAL} {@code Intent} with no
      * {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}, or present the user with an affordance to
-     * select one of the elements of {@link #getEnabledPhoneAccounts()}.
+     * select one of the elements of {@link #getCallCapablePhoneAccounts()}.
      * <p>
      * An {@link android.content.Intent#ACTION_CALL} or {@link android.content.Intent#ACTION_DIAL}
      * {@code Intent} with no {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} is valid, and
@@ -355,7 +337,7 @@
     /**
      * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
      * calls. This {@code PhoneAccount} will always be a member of the list which is returned from
-     * calling {@link #getEnabledPhoneAccounts()}
+     * calling {@link #getCallCapablePhoneAccounts()}
      *
      * Apps must be prepared for this method to return {@code null}, indicating that there currently
      * exists no user-chosen default {@code PhoneAccount}.
@@ -389,19 +371,19 @@
     }
 
     /**
-     * Return a list of enabled {@link PhoneAccountHandle}s which can be used to make and receive
-     * phone calls.
+     * Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
+     * calls.
      *
      * @see #EXTRA_PHONE_ACCOUNT_HANDLE
      * @return A list of {@code PhoneAccountHandle} objects.
      */
-    public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
+    public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
         try {
             if (isServiceConnected()) {
-                return getTelecomService().getEnabledPhoneAccounts();
+                return getTelecomService().getCallCapablePhoneAccounts();
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecomService#getEnabledPhoneAccounts", e);
+            Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts", e);
         }
         return new ArrayList<>();
     }
@@ -467,8 +449,8 @@
     }
 
     /**
-     * Returns a list of the enabled {@link PhoneAccountHandle}s which can be used to make and
-     * receive phone calls which support the specified URI scheme.
+     * Returns a list of the {@link PhoneAccountHandle}s which can be used to make and receive phone
+     * calls which support the specified URI scheme.
      * <P>
      * For example, invoking with {@code "tel"} will find all {@link PhoneAccountHandle}s which
      * support telephone calls (e.g. URIs such as {@code tel:555-555-1212}).  Invoking with
@@ -490,13 +472,14 @@
     }
 
     /**
-     * Determine whether the device has more than one account registered and enabled.
+     * Determine whether the device has more than one account registered that can make and receive
+     * phone calls.
      *
-     * @return {@code true} if the device has more than one account registered and enabled and
-     * {@code false} otherwise.
+     * @return {@code true} if the device has more than one account registered and {@code false}
+     * otherwise.
      */
-    public boolean hasMultipleEnabledAccounts() {
-        return getEnabledPhoneAccounts().size() > 1;
+    public boolean hasMultipleCallCapableAccounts() {
+        return getCallCapablePhoneAccounts().size() > 1;
     }
 
     /**
@@ -518,9 +501,9 @@
     }
 
     /**
-     * Returns a count of enabled and disabled {@link PhoneAccount}s.
+     * Returns a count of all {@link PhoneAccount}s.
      *
-     * @return The count of enabled and disabled {@link PhoneAccount}s.
+     * @return The count of {@link PhoneAccount}s.
      * @hide
      */
     @SystemApi
@@ -572,24 +555,6 @@
     }
 
     /**
-     * Enables or disables a {@link PhoneAccount}.
-     *
-     * @param account The {@link PhoneAccountHandle} to enable or disable.
-     * @param isEnabled {@code True} if the phone account should be enabled.
-     * @hide
-     */
-    @SystemApi
-    public void setPhoneAccountEnabled(PhoneAccountHandle account, boolean isEnabled) {
-        try {
-            if (isServiceConnected()) {
-                getTelecomService().setPhoneAccountEnabled(account, isEnabled);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecomService#setPhoneAccountEnabled", e);
-        }
-    }
-
-    /**
      * Register a {@link PhoneAccount} for use by the system.
      *
      * @param account The complete {@link PhoneAccount}.
@@ -797,9 +762,8 @@
     /**
      * Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
      * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
-     * with {@link #registerPhoneAccount} and subsequently enabled by the user within the phone's
-     * settings. Once invoked, this method will cause the system to bind to the
-     * {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
+     * with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
+     * to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
      * additional information about the call (See
      * {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
      *
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 8f3506d..5daa568 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -19,6 +19,7 @@
 import android.app.PendingIntent;
 import android.net.Uri;
 import android.telecom.ConnectionRequest;
+import android.telecom.DisconnectCause;
 import android.telecom.ParcelableConnection;
 import android.telecom.ParcelableConference;
 import android.telecom.StatusHints;
@@ -45,7 +46,7 @@
 
     void setDialing(String callId);
 
-    void setDisconnected(String callId, int disconnectCause, String disconnectMessage);
+    void setDisconnected(String callId, in DisconnectCause disconnectCause);
 
     void setOnHold(String callId);
 
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 4875cc3..77a80fe 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -50,9 +50,9 @@
     void setUserSelectedOutgoingPhoneAccount(in PhoneAccountHandle account);
 
     /**
-     * @see TelecomServiceImpl#getEnabledPhoneAccounts
+     * @see TelecomServiceImpl#getCallCapablePhoneAccounts
      */
-    List<PhoneAccountHandle> getEnabledPhoneAccounts();
+    List<PhoneAccountHandle> getCallCapablePhoneAccounts();
 
     /**
      * @see TelecomManager#getPhoneAccountsSupportingScheme
@@ -95,11 +95,6 @@
     List<PhoneAccountHandle> getSimCallManagers();
 
     /**
-     * @see TelecomServiceImpl#setPhoneAccountEnabled
-     */
-    void setPhoneAccountEnabled(in PhoneAccountHandle account, in boolean isEnabled);
-
-    /**
      * @see TelecomServiceImpl#registerPhoneAccount
      */
     void registerPhoneAccount(in PhoneAccount metadata);
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 6366c91..6fe10dc 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -18,6 +18,7 @@
 
 /**
  * Contains disconnect call causes generated by the framework and the RIL.
+ * @hide
  */
 public class DisconnectCause {
 
@@ -262,7 +263,7 @@
         case OUTGOING_CANCELED:
             return "OUTGOING_CANCELED";
         default:
-            return "INVALID";
+            return "INVALID: " + cause;
         }
     }
 }