Merge "Add TtsSpan class."
diff --git a/Android.mk b/Android.mk
index 0c5051c..6859678 100644
--- a/Android.mk
+++ b/Android.mk
@@ -153,6 +153,7 @@
 	core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \
 	core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl \
 	core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl \
+	core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \
 	core/java/android/hardware/input/IInputManager.aidl \
 	core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
 	core/java/android/hardware/location/IFusedLocationHardware.aidl \
@@ -328,12 +329,12 @@
 	media/java/android/media/tv/ITvInputServiceCallback.aidl \
 	media/java/android/media/tv/ITvInputSession.aidl \
 	media/java/android/media/tv/ITvInputSessionCallback.aidl \
-	telecomm/java/com/android/internal/telecomm/ICallService.aidl \
-	telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl \
+	telecomm/java/com/android/internal/telecomm/IConnectionService.aidl \
+	telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
 	telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 20e48f9..2c9ea54 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -209,6 +209,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework2_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/telecomm/java/com/android/internal/telecomm)
 
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 2915e36..8890d6d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -347,6 +347,9 @@
     field public static final int bufferType = 16843086; // 0x101014e
     field public static final int button = 16843015; // 0x1010107
     field public static final int buttonBarButtonStyle = 16843567; // 0x101032f
+    field public static final int buttonBarNegativeButtonStyle = 16843917; // 0x101048d
+    field public static final int buttonBarNeutralButtonStyle = 16843916; // 0x101048c
+    field public static final int buttonBarPositiveButtonStyle = 16843915; // 0x101048b
     field public static final int buttonBarStyle = 16843566; // 0x101032e
     field public static final int buttonStyle = 16842824; // 0x1010048
     field public static final int buttonStyleInset = 16842826; // 0x101004a
@@ -2384,6 +2387,7 @@
     field public static final int Widget_Material_ButtonBar = 16974439; // 0x1030267
     field public static final int Widget_Material_ButtonBar_AlertDialog = 16974440; // 0x1030268
     field public static final int Widget_Material_Button_Borderless = 16974434; // 0x1030262
+    field public static final int Widget_Material_Button_Borderless_Colored = 16974556; // 0x10302dc
     field public static final int Widget_Material_Button_Borderless_Small = 16974435; // 0x1030263
     field public static final int Widget_Material_Button_Inset = 16974436; // 0x1030264
     field public static final int Widget_Material_Button_Small = 16974437; // 0x1030265
@@ -2417,6 +2421,7 @@
     field public static final int Widget_Material_Light_ButtonBar = 16974500; // 0x10302a4
     field public static final int Widget_Material_Light_ButtonBar_AlertDialog = 16974501; // 0x10302a5
     field public static final int Widget_Material_Light_Button_Borderless = 16974495; // 0x103029f
+    field public static final int Widget_Material_Light_Button_Borderless_Colored = 16974557; // 0x10302dd
     field public static final int Widget_Material_Light_Button_Borderless_Small = 16974496; // 0x10302a0
     field public static final int Widget_Material_Light_Button_Inset = 16974497; // 0x10302a1
     field public static final int Widget_Material_Light_Button_Small = 16974498; // 0x10302a2
@@ -8972,6 +8977,7 @@
 
   public class TypedArray {
     method public boolean getBoolean(int, boolean);
+    method public int getChangingConfigurations();
     method public int getColor(int, int);
     method public android.content.res.ColorStateList getColorStateList(int);
     method public float getDimension(int, float);
@@ -16092,6 +16098,7 @@
 
   public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session();
+    method public void dispatchChannelRetuned(android.net.Uri);
     method public android.view.View onCreateOverlayView();
     method public boolean onGenericMotionEvent(android.view.MotionEvent);
     method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -16129,6 +16136,7 @@
 
   public static abstract class TvView.TvInputListener {
     ctor public TvView.TvInputListener();
+    method public void onChannelRetuned(java.lang.String, android.net.Uri);
     method public void onError(java.lang.String, int);
   }
 
@@ -27530,21 +27538,6 @@
     field public static final int VoWIFI = 2; // 0x2
   }
 
-  public final class CallInfo implements android.os.Parcelable {
-    ctor public CallInfo(java.lang.String, android.telecomm.CallState, android.net.Uri);
-    method public int describeContents();
-    method public android.telecomm.PhoneAccount getAccount();
-    method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
-    method public android.os.Bundle getExtras();
-    method public android.telecomm.GatewayInfo getGatewayInfo();
-    method public android.net.Uri getHandle();
-    method public java.lang.String getId();
-    method public android.net.Uri getOriginalHandle();
-    method public android.telecomm.CallState getState();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
   public final class CallNumberPresentation extends java.lang.Enum {
     method public static android.telecomm.CallNumberPresentation valueOf(java.lang.String);
     method public static final android.telecomm.CallNumberPresentation[] values();
@@ -27554,15 +27547,9 @@
     enum_constant public static final android.telecomm.CallNumberPresentation UNKNOWN;
   }
 
-  public abstract class CallService extends android.app.Service {
-    ctor public CallService();
-    method public final android.os.IBinder getBinder();
-    method public final android.os.IBinder onBind(android.content.Intent);
-  }
-
   public final class CallServiceDescriptor implements android.os.Parcelable {
     method public int describeContents();
-    method public java.lang.String getCallServiceId();
+    method public java.lang.String getConnectionServiceId();
     method public int getNetworkType();
     method public android.content.ComponentName getServiceComponent();
     method public static android.telecomm.CallServiceDescriptor.Builder newBuilder(android.content.Context);
@@ -27575,7 +27562,7 @@
 
   public static class CallServiceDescriptor.Builder {
     method public android.telecomm.CallServiceDescriptor build();
-    method public android.telecomm.CallServiceDescriptor.Builder setCallService(java.lang.Class<? extends android.telecomm.CallService>);
+    method public android.telecomm.CallServiceDescriptor.Builder setConnectionService(java.lang.Class<? extends android.telecomm.ConnectionService>);
     method public android.telecomm.CallServiceDescriptor.Builder setNetworkType(int);
   }
 
@@ -27648,7 +27635,6 @@
     method protected void onAbort();
     method protected void onAnswer();
     method protected void onChildrenChanged(java.util.List<android.telecomm.Connection>);
-    method protected void onConference();
     method protected void onDisconnect();
     method protected void onHold();
     method protected void onPhoneAccountClicked();
@@ -27699,12 +27685,13 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract class ConnectionService extends android.telecomm.CallService {
+  public abstract class ConnectionService extends android.app.Service {
     ctor public ConnectionService();
     method public final void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.RemoteConnection>);
     method public final java.util.Collection<android.telecomm.Connection> getAllConnections();
     method public final void lookupRemoteAccounts(android.net.Uri, android.telecomm.SimpleResponse<android.net.Uri, java.util.List<android.telecomm.PhoneAccount>>);
     method public final void maybeRespondToAccountLookup();
+    method public final android.os.IBinder onBind(android.content.Intent);
     method protected void onConnectionAdded(android.telecomm.Connection);
     method protected void onConnectionRemoved(android.telecomm.Connection);
     method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
@@ -27822,6 +27809,7 @@
     method public void disconnect();
     method public int getDisconnectCause();
     method public java.lang.String getDisconnectMessage();
+    method public int getFeatures();
     method public int getState();
     method public void hold();
     method public void playDtmf(char);
@@ -27836,6 +27824,7 @@
   public static abstract interface RemoteConnection.Listener {
     method public abstract void onDestroyed(android.telecomm.RemoteConnection);
     method public abstract void onDisconnected(android.telecomm.RemoteConnection, int, java.lang.String);
+    method public abstract void onFeaturesChanged(android.telecomm.RemoteConnection, int);
     method public abstract void onPostDialWait(android.telecomm.RemoteConnection, java.lang.String);
     method public abstract void onRequestingRingback(android.telecomm.RemoteConnection, boolean);
     method public abstract void onStateChanged(android.telecomm.RemoteConnection, int);
@@ -27853,14 +27842,15 @@
 
   public final class TelecommConstants {
     ctor public TelecommConstants();
-    field public static final java.lang.String ACTION_CALL_SERVICE;
     field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
+    field public static final java.lang.String ACTION_CONNECTION_SERVICE;
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
     field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
+    field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO = "android.intent.extra.START_CALL_WITH_VIDEO";
@@ -28326,6 +28316,7 @@
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
+    method public int hasCarrierPrivileges();
     method public boolean hasIccCard();
     method public boolean isNetworkRoaming();
     method public void listen(android.telephony.PhoneStateListener, int);
@@ -28334,6 +28325,10 @@
     field public static final int CALL_STATE_IDLE = 0; // 0x0
     field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
     field public static final int CALL_STATE_RINGING = 1; // 0x1
+    field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
+    field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
+    field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
+    field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
     field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
     field public static final int DATA_ACTIVITY_IN = 1; // 0x1
     field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
@@ -34275,6 +34270,7 @@
     method public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo getCollectionInfo();
     method public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo getCollectionItemInfo();
     method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getError();
     method public android.os.Bundle getExtras();
     method public int getInputType();
     method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
@@ -34333,6 +34329,7 @@
     method public void setDismissable(boolean);
     method public void setEditable(boolean);
     method public void setEnabled(boolean);
+    method public void setError(java.lang.CharSequence);
     method public void setFocusable(boolean);
     method public void setFocused(boolean);
     method public void setInputType(int);
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3dc024e..e659b2b 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -108,7 +108,7 @@
         return NO_INIT;
     SkBitmap bitmap;
     SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
-            &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode);
+            &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode);
     asset->close();
     delete asset;
 
@@ -127,20 +127,20 @@
     glGenTextures(1, &texture->name);
     glBindTexture(GL_TEXTURE_2D, texture->name);
 
-    switch (bitmap.config()) {
-        case SkBitmap::kA8_Config:
+    switch (bitmap.colorType()) {
+        case kAlpha_8_SkColorType:
             glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
                     GL_UNSIGNED_BYTE, p);
             break;
-        case SkBitmap::kARGB_4444_Config:
+        case kARGB_4444_SkColorType:
             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
                     GL_UNSIGNED_SHORT_4_4_4_4, p);
             break;
-        case SkBitmap::kARGB_8888_Config:
+        case kN32_SkColorType:
             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, p);
             break;
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
                     GL_UNSIGNED_SHORT_5_6_5, p);
             break;
@@ -166,7 +166,7 @@
     if (codec) {
         codec->setDitherImage(false);
         codec->decode(&stream, &bitmap,
-                SkBitmap::kARGB_8888_Config,
+                kN32_SkColorType,
                 SkImageDecoder::kDecodePixels_Mode);
         delete codec;
     }
@@ -190,8 +190,8 @@
     if (tw < w) tw <<= 1;
     if (th < h) th <<= 1;
 
-    switch (bitmap.config()) {
-        case SkBitmap::kARGB_8888_Config:
+    switch (bitmap.colorType()) {
+        case kN32_SkColorType:
             if (tw != w || th != h) {
                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE, 0);
@@ -203,7 +203,7 @@
             }
             break;
 
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             if (tw != w || th != h) {
                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
                         GL_UNSIGNED_SHORT_5_6_5, 0);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 228d82e..2914a61 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5202,6 +5202,22 @@
     }
 
     /**
+     * Indication of whether this is the highest level activity in this task. Can be used to
+     * determine whether an activity launched by this activity was placed in the same task or
+     * another task.
+     *
+     * @return true if this is the topmost, non-finishing activity in its task.
+     * @hide
+     */
+    public boolean isTopOfTask() {
+        try {
+            return ActivityManagerNative.getDefault().isTopOfTask(mToken);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a
      * fullscreen opaque Activity.
      * <p>
@@ -5284,6 +5300,14 @@
         }
     }
 
+    /** @hide */
+    public void onNewActivityOptions(ActivityOptions options) {
+        mActivityTransitionState.setEnterActivityOptions(this, options);
+        if (!mStopped) {
+            mActivityTransitionState.enterReady(this);
+        }
+    }
+
     /**
      * Retrieve the ActivityOptions passed in from the launching activity or passed back
      * from an activity launched by this activity in its call to {@link
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 6e45868..3cf8f9a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -677,6 +677,12 @@
         public int userId;
 
         /**
+         * The first time this task was active.
+         * @hide
+         */
+        public long firstActiveTime;
+
+        /**
          * The last time this task was active.
          * @hide
          */
@@ -719,6 +725,7 @@
             }
             dest.writeInt(stackId);
             dest.writeInt(userId);
+            dest.writeLong(firstActiveTime);
             dest.writeLong(lastActiveTime);
         }
 
@@ -732,6 +739,7 @@
                     TaskDescription.CREATOR.createFromParcel(source) : null;
             stackId = source.readInt();
             userId = source.readInt();
+            firstActiveTime = source.readLong();
             lastActiveTime = source.readLong();
         }
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1b2b08f..ce36fd8 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1546,6 +1546,15 @@
             return true;
         }
 
+        case IS_TOP_OF_TASK_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            final boolean isTopOfTask = isTopOfTask(token);
+            reply.writeNoException();
+            reply.writeInt(isTopOfTask ? 1 : 0);
+            return true;
+        }
+
         case CONVERT_FROM_TRANSLUCENT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -2129,7 +2138,7 @@
             return true;
         }
 
-        case START_LOCK_TASK_BY_CURRENT: {
+        case START_LOCK_TASK_BY_CURRENT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             startLockTaskModeOnCurrent();
             reply.writeNoException();
@@ -2143,7 +2152,7 @@
             return true;
         }
 
-        case STOP_LOCK_TASK_BY_CURRENT: {
+        case STOP_LOCK_TASK_BY_CURRENT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             stopLockTaskModeOnCurrent();
             reply.writeNoException();
@@ -4182,6 +4191,19 @@
         return res;
     }
 
+    public boolean isTopOfTask(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(IS_TOP_OF_TASK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() == 1;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     public boolean isTopActivityImmersive()
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -4930,7 +4952,7 @@
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(START_LOCK_TASK_BY_CURRENT, data, reply, 0);
+        mRemote.transact(START_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -4952,7 +4974,7 @@
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(STOP_LOCK_TASK_BY_CURRENT, data, reply, 0);
+        mRemote.transact(STOP_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 422d88c..7b48e1d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -77,6 +77,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LogPrinter;
+import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SuperNotCalledException;
@@ -1113,6 +1114,11 @@
             sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
         }
 
+        public void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options) {
+            sendMessage(H.ON_NEW_ACTIVITY_OPTIONS,
+                    new Pair<IBinder, ActivityOptions>(token, options));
+        }
+
         public void setProcessState(int state) {
             updateProcessState(state, true);
         }
@@ -1196,6 +1202,7 @@
         public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
         public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
         public static final int INSTALL_PROVIDER        = 145;
+        public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1245,6 +1252,7 @@
                     case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
                     case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
                     case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
+                    case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
                 }
             }
             return Integer.toString(code);
@@ -1459,6 +1467,10 @@
                 case INSTALL_PROVIDER:
                     handleInstallProvider((ProviderInfo) msg.obj);
                     break;
+                case ON_NEW_ACTIVITY_OPTIONS:
+                    Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
+                    onNewActivityOptions(pair.first, pair.second);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
         }
@@ -2435,6 +2447,13 @@
         }
     }
 
+    public void onNewActivityOptions(IBinder token, ActivityOptions options) {
+        ActivityClientRecord r = mActivities.get(token);
+        if (r != null) {
+            r.activity.onNewActivityOptions(options);
+        }
+    }
+
     public void handleInstallProvider(ProviderInfo info) {
         installContentProviders(mInitialApplication, Lists.newArrayList(info));
     }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 5998d7a..6dead08 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -611,6 +611,16 @@
             return true;
         }
 
+        case SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            IBinder token = data.readStrongBinder();
+            ActivityOptions options = new ActivityOptions(data.readBundle());
+            scheduleOnNewActivityOptions(token, options);
+            reply.writeNoException();
+            return true;
+        }
+
         case SET_PROCESS_STATE_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -1251,7 +1261,20 @@
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         data.writeInt(timeout ? 1 : 0);
-        mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
+    @Override
+    public void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(token);
+        data.writeBundle(options == null ? null : options.toBundle());
+        mRemote.transact(SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
         data.recycle();
     }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 931903b..6e4192b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -302,10 +302,10 @@
     
     public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
             throws RemoteException;
-    
+
     public void overridePendingTransition(IBinder token, String packageName,
             int enterAnim, int exitAnim) throws RemoteException;
-    
+
     public boolean isUserAMonkey() throws RemoteException;
 
     public void setUserIsMonkey(boolean monkey) throws RemoteException;
@@ -320,12 +320,13 @@
     public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
     public boolean isImmersive(IBinder token) throws RemoteException;
     public boolean isTopActivityImmersive() throws RemoteException;
-    
+    public boolean isTopOfTask(IBinder token) throws RemoteException;
+
     public void crashApplication(int uid, int initialPid, String packageName,
             String message) throws RemoteException;
 
     public String getProviderMimeType(Uri uri, int userId) throws RemoteException;
-    
+
     public IBinder newUriPermissionOwner(String name) throws RemoteException;
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
             Uri uri, int mode, int userId) throws RemoteException;
@@ -748,7 +749,8 @@
     int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
     int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219;
     int GET_APP_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+220;
-    int START_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+221;
-    int STOP_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+222;
+    int START_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+221;
+    int STOP_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
     int FINISH_VOICE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+223;
+    int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index d0df7c3..d3c4854 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -140,6 +140,8 @@
             throws RemoteException;
     void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
             throws RemoteException;
+    void scheduleOnNewActivityOptions(IBinder token, ActivityOptions options)
+            throws RemoteException;
     void setProcessState(int state) throws RemoteException;
     void scheduleInstallProvider(ProviderInfo provider) throws RemoteException;
     void updateTimePrefs(boolean is24Hour) throws RemoteException;
@@ -176,7 +178,7 @@
     int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
     int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
     int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
-
+    int SCHEDULE_ON_NEW_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
     int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
     int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
     int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 8cf8c25..abd042b 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -16,9 +16,11 @@
 
 package android.app;
 
+import android.app.UiModeManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
@@ -110,11 +112,14 @@
                 (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         View view = inflater.inflate(R.layout.time_picker_dialog, null);
         setView(view);
-        setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
         mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
 
         // Initialize state
-        mTimePicker.setLegacyMode(false /* will show new UI */);
+        UiModeManager uiModeManager =
+                (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
+        if (uiModeManager.getCurrentModeType() != Configuration.UI_MODE_TYPE_TELEVISION) {
+            mTimePicker.setLegacyMode(false /* will show new UI */);
+        }
         mTimePicker.setShowDoneButton(true);
         mTimePicker.setDismissCallback(new TimePicker.TimePickerDismissCallback() {
             @Override
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index bb90fd7..514b26e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -410,10 +410,14 @@
      */
     public int largestWidthLimitDp = 0;
 
+    /** {@hide} */
+    public String scanSourceDir;
+    /** {@hide} */
+    public String scanPublicSourceDir;
+
     /**
      * Full path to the base APK for this application.
      */
-    // TODO: verify that nobody is doing codePath comparisons against this
     public String sourceDir;
 
     /**
@@ -779,11 +783,25 @@
             return true;
         }
     }
-    
+
     /**
      * @hide
      */
     @Override protected ApplicationInfo getApplicationInfo() {
         return this;
     }
+
+    /** {@hide} */ public void setCodePath(String codePath) { scanSourceDir = codePath; }
+    /** {@hide} */ public void setBaseCodePath(String baseCodePath) { sourceDir = baseCodePath; }
+    /** {@hide} */ public void setSplitCodePaths(String[] splitCodePaths) { splitSourceDirs = splitCodePaths; }
+    /** {@hide} */ public void setResourcePath(String resourcePath) { scanPublicSourceDir = resourcePath; }
+    /** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; }
+    /** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
+
+    /** {@hide} */ public String getCodePath() { return scanSourceDir; }
+    /** {@hide} */ public String getBaseCodePath() { return sourceDir; }
+    /** {@hide} */ public String[] getSplitCodePaths() { return splitSourceDirs; }
+    /** {@hide} */ public String getResourcePath() { return scanPublicSourceDir; }
+    /** {@hide} */ public String getBaseResourcePath() { return publicSourceDir; }
+    /** {@hide} */ public String[] getSplitResourcePaths() { return splitSourceDirs; }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bb47124..7651aef 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -719,7 +719,12 @@
      * <p>
      * Note that this <em>does not</em> perform signature verification; that
      * must be done separately in {@link #collectCertificates(Package, int)}.
+     *
+     * @deprecated external callers should move to
+     *             {@link #parsePackage(File, int)}. Eventually this method will
+     *             be marked private.
      */
+    @Deprecated
     public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
         final Package pkg = parseBaseApk(apkFile, flags);
         if (pkg == null) {
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index d2146ac..645f7df 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -927,6 +927,30 @@
         return attrs;
     }
 
+    /**
+     * Return a mask of the configuration parameters for which the values in
+     * this typed array may change.
+     *
+     * @return Returns a mask of the changing configuration parameters, as
+     *         defined by {@link android.content.pm.ActivityInfo}.
+     * @see android.content.pm.ActivityInfo
+     */
+    public int getChangingConfigurations() {
+        int changingConfig = 0;
+
+        final int[] data = mData;
+        final int N = length();
+        for (int i = 0; i < N; i++) {
+            final int index = i * AssetManager.STYLE_NUM_ENTRIES;
+            final int type = data[index + AssetManager.STYLE_TYPE];
+            if (type == TypedValue.TYPE_NULL) {
+                continue;
+            }
+            changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS];
+        }
+        return changingConfig;
+    }
+
     private boolean getValueAt(int index, TypedValue outValue) {
         final int[] data = mData;
         final int type = data[index+AssetManager.STYLE_TYPE];
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 2e59eee..e55ee67 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -533,6 +533,20 @@
             handler = checkHandler(handler);
         }
 
+        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+        for (CaptureRequest request : requestList) {
+            if (request.getTargets().isEmpty()) {
+                throw new IllegalArgumentException(
+                        "Each request must have at least one Surface target");
+            }
+
+            for (Surface surface : request.getTargets()) {
+                if (surface == null) {
+                    throw new IllegalArgumentException("Null Surface targets are not allowed");
+                }
+            }
+        }
+
         try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
             int requestId;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index d7b1a36..3ef538c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -154,16 +154,6 @@
     private static void mapCharacteristicsFromParameters(CameraMetadataNative m,
             Camera.Parameters p) {
         /*
-         * info.supportedHardwareLevel
-         */
-        m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
-
-        /*
-         * scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
-         */
-        mapScalerStreamConfigs(m, p);
-
-        /*
          * control.ae*
          */
         mapControlAe(m, p);
@@ -205,6 +195,17 @@
          * sync.*
          */
         mapSync(m, p);
+
+        /*
+         * info.supportedHardwareLevel
+         */
+        m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
+
+        /*
+         * scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
+         */
+        mapScalerStreamConfigs(m, p);
+
     }
 
     private static void mapScalerStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 3345967..17dda08 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -176,8 +176,23 @@
         Range<Integer> aeFpsRange = request.get(CONTROL_AE_TARGET_FPS_RANGE);
         if (aeFpsRange != null) {
             int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
-            params.setPreviewFpsRange(legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX],
-                    legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX]);
+
+            // TODO - Should we enforce that all HAL1 devices must include (30, 30) FPS range?
+            boolean supported = false;
+            for(int[] range : params.getSupportedPreviewFpsRange()) {
+                if (legacyFps[0] == range[0] && legacyFps[1] == range[1]) {
+                    supported = true;
+                    break;
+                }
+            }
+            if (supported) {
+                params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+                params.setRecordingHint(false);
+            } else {
+                Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
+                params.setRecordingHint(true);
+            }
         }
 
         /*
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index a9f9355..e6d84c5 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -78,8 +78,8 @@
     private volatile RequestHolder mInFlightPreview;
     private volatile RequestHolder mInFlightJpeg;
 
-    private final List<Surface> mPreviewOutputs = new ArrayList<Surface>();
-    private final List<Surface> mCallbackOutputs = new ArrayList<Surface>();
+    private final List<Surface> mPreviewOutputs = new ArrayList<>();
+    private final List<Surface> mCallbackOutputs = new ArrayList<>();
     private GLThreadManager mGLThreadManager;
     private SurfaceTexture mPreviewTexture;
     private Camera.Parameters mParams;
@@ -274,18 +274,6 @@
         mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
                 mIntermediateBufferSize.getHeight());
         mCamera.setPreviewTexture(mPreviewTexture);
-        Camera.Parameters params = mCamera.getParameters();
-        List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
-        int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
-        if (DEBUG) {
-            Log.d(TAG, "doPreviewCapture - Selected range [" +
-                    bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
-                    bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
-        }
-        params.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
-                bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
-        params.setRecordingHint(true);
-        mCamera.setParameters(params);
 
         startPreview();
     }
@@ -322,6 +310,18 @@
             }
         }
         mParams = mCamera.getParameters();
+
+        List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
+        int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
+        if (DEBUG) {
+            Log.d(TAG, "doPreviewCapture - Selected range [" +
+                    bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
+                    bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
+        }
+        mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+        mParams.setRecordingHint(true);
+
         if (mPreviewOutputs.size() > 0) {
             List<Size> outputSizes = new ArrayList<>(outputs.size());
             for (Surface s : mPreviewOutputs) {
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
new file mode 100644
index 0000000..7bdcca2
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -0,0 +1,51 @@
+package android.hardware.hdmi;
+
+import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Parent for classes of various HDMI-CEC device type used to access
+ * {@link HdmiControlService}. Contains methods and data used in common.
+ *
+ * @hide
+ */
+public abstract class HdmiClient {
+    private static final String TAG = "HdmiClient";
+
+    protected final IHdmiControlService mService;
+
+    protected abstract int getDeviceType();
+
+    public HdmiClient(IHdmiControlService service) {
+        mService = service;
+    }
+
+    public void sendVendorCommand(int targetAddress, byte[] params, boolean hasVendorId) {
+        try {
+            mService.sendVendorCommand(getDeviceType(), targetAddress, params, hasVendorId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to send vendor command: ", e);
+        }
+    }
+
+    public void addVendorCommandListener(VendorCommandListener listener) {
+        try {
+            mService.addVendorCommandListener(getListenerWrapper(listener), getDeviceType());
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to add vendor command listener: ", e);
+        }
+    }
+
+    private static IHdmiVendorCommandListener getListenerWrapper(
+            final VendorCommandListener listener) {
+        return new IHdmiVendorCommandListener.Stub() {
+            @Override
+            public void onReceived(int srcAddress, byte[] params, boolean hasVendorId) {
+                listener.onReceived(srcAddress, params, hasVendorId);
+            }
+        };
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 80ec6f8..56fc1d6 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -17,6 +17,8 @@
 package android.hardware.hdmi;
 
 import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 
@@ -36,6 +38,22 @@
 public final class HdmiControlManager {
     @Nullable private final IHdmiControlService mService;
 
+    /**
+     * Broadcast Action: Display OSD message.
+     * <p>Send when the service has a message to display on screen for events
+     * that need user's attention such as ARC status change.
+     * <p>Always contains the extra fields {@link #EXTRA_MESSAGE}.
+     * <p>Requires {@link android.Manifest.permission#HDMI_CEC} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
+
+    /**
+     * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of
+     * the message to display on screen.
+     */
+    public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
+
     public static final int POWER_STATUS_UNKNOWN = -1;
     public static final int POWER_STATUS_ON = 0;
     public static final int POWER_STATUS_STANDBY = 1;
@@ -130,6 +148,21 @@
     }
 
     /**
+     * Listener used to get vendor-specific commands.
+     */
+    public interface VendorCommandListener {
+        /**
+         * Called when a vendor command is received.
+         *
+         * @param srcAddress source logical address
+         * @param params vendor-specific parameters
+         * @param hasVendorId {@code true} if the command is &lt;Vendor Command
+         *        With ID&gt;. The first 3 bytes of params is vendor id.
+         */
+        void onReceived(int srcAddress, byte[] params, boolean hasVendorId);
+    }
+
+    /**
      * Adds a listener to get informed of {@link HdmiHotplugEvent}.
      *
      * <p>To stop getting the notification,
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index d7e9a9a..74cdc4e 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -18,7 +18,6 @@
 
 import android.annotation.SystemApi;
 import android.os.RemoteException;
-
 import android.util.Log;
 
 /**
@@ -30,11 +29,9 @@
  * @hide
  */
 @SystemApi
-public final class HdmiPlaybackClient {
+public final class HdmiPlaybackClient extends HdmiClient {
     private static final String TAG = "HdmiPlaybackClient";
 
-    private final IHdmiControlService mService;
-
     /**
      * Listener used by the client to get the result of one touch play operation.
      */
@@ -66,7 +63,7 @@
     }
 
     HdmiPlaybackClient(IHdmiControlService service) {
-        mService = service;
+        super(service);
     }
 
     /**
@@ -85,6 +82,10 @@
         }
     }
 
+    public int getDeviceType() {
+        return HdmiCecDeviceInfo.DEVICE_PLAYBACK;
+    }
+
     /**
      * Get the status of display device connected through HDMI bus.
      *
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 6f65efc..1285ccb 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -16,6 +16,8 @@
 package android.hardware.hdmi;
 
 import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -27,7 +29,7 @@
  * @hide
  */
 @SystemApi
-public final class HdmiTvClient {
+public final class HdmiTvClient extends HdmiClient {
     private static final String TAG = "HdmiTvClient";
 
     // Definitions used for setOption(). These should be in sync with the definition
@@ -79,10 +81,8 @@
     public static final int DISABLED = 0;
     public static final int ENABLED = 1;
 
-    private final IHdmiControlService mService;
-
     HdmiTvClient(IHdmiControlService service) {
-        mService = service;
+        super(service);
     }
 
     // Factory method for HdmiTvClient.
@@ -91,6 +91,10 @@
         return new HdmiTvClient(service);
     }
 
+    public int getDeviceType() {
+        return HdmiCecDeviceInfo.DEVICE_TV;
+    }
+
     /**
      * Callback interface used to get the result of {@link #deviceSelect}.
      */
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 3f0f2ae..3a477fb 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -23,6 +23,7 @@
 import android.hardware.hdmi.IHdmiHotplugEventListener;
 import android.hardware.hdmi.IHdmiInputChangeListener;
 import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
 
 import java.util.List;
 
@@ -57,4 +58,7 @@
     oneway void setSystemAudioMute(boolean mute);
     void setInputChangeListener(IHdmiInputChangeListener listener);
     List<HdmiCecDeviceInfo> getInputDevices();
+    void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
+            boolean hasVendorId);
+    void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
 }
diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
new file mode 100644
index 0000000..55cc925
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.hardware.hdmi;
+
+/**
+ * Callback interface definition for HDMI client to get the vendor-specific
+ * commands.
+ *
+ * @hide
+ */
+oneway interface IHdmiVendorCommandListener {
+    void onReceived(int logicalAddress, in byte[] operands, boolean hasVendorId);
+}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 4c34d46..fe47f5b 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -371,6 +371,8 @@
      * attacks.
      */
     public static boolean contains(File dir, File file) {
+        if (file == null) return false;
+
         String dirPath = dir.getAbsolutePath();
         String filePath = file.getAbsolutePath();
 
@@ -418,16 +420,27 @@
     }
 
     public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
+        if (path == null) return null;
         final File result = rewriteAfterRename(beforeDir, afterDir, new File(path));
         return (result != null) ? result.getAbsolutePath() : null;
     }
 
+    public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) {
+        if (paths == null) return null;
+        final String[] result = new String[paths.length];
+        for (int i = 0; i < paths.length; i++) {
+            result[i] = rewriteAfterRename(beforeDir, afterDir, paths[i]);
+        }
+        return result;
+    }
+
     /**
      * Given a path under the "before" directory, rewrite it to live under the
      * "after" directory. For example, {@code /before/foo/bar.txt} would become
      * {@code /after/foo/bar.txt}.
      */
     public static File rewriteAfterRename(File beforeDir, File afterDir, File file) {
+        if (file == null) return null;
         if (contains(beforeDir, file)) {
             final String splice = file.getAbsolutePath().substring(
                     beforeDir.getAbsolutePath().length());
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index 71d12c6..84aa427 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -28,9 +28,15 @@
  * {@hide}
  */
 public class SELinux {
-
     private static final String TAG = "SELinux";
 
+    /** Keep in sync with ./external/libselinux/include/selinux/android.h */
+    private static final int SELINUX_ANDROID_RESTORECON_NOCHANGE = 1;
+    private static final int SELINUX_ANDROID_RESTORECON_VERBOSE = 2;
+    private static final int SELINUX_ANDROID_RESTORECON_RECURSE = 4;
+    private static final int SELINUX_ANDROID_RESTORECON_FORCE = 8;
+    private static final int SELINUX_ANDROID_RESTORECON_DATADATA = 16;
+
     /**
      * Determine whether SELinux is disabled or enabled.
      * @return a boolean indicating whether SELinux is enabled.
@@ -136,7 +142,7 @@
      */
     public static boolean restorecon(String pathname) throws NullPointerException {
         if (pathname == null) { throw new NullPointerException(); }
-        return native_restorecon(pathname);
+        return native_restorecon(pathname, 0);
     }
 
     /**
@@ -149,7 +155,7 @@
      * @param pathname The pathname of the file to be relabeled.
      * @return a boolean indicating whether the relabeling succeeded.
      */
-    private static native boolean native_restorecon(String pathname);
+    private static native boolean native_restorecon(String pathname, int flags);
 
     /**
      * Restores a file to its default SELinux security context.
@@ -164,10 +170,10 @@
      */
     public static boolean restorecon(File file) throws NullPointerException {
         try {
-            return native_restorecon(file.getCanonicalPath());
+            return native_restorecon(file.getCanonicalPath(), 0);
         } catch (IOException e) {
             Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
-                   file.getPath(), e);
+                    file.getPath(), e);
             return false;
         }
     }
@@ -180,14 +186,13 @@
      *
      * @return a boolean indicating whether the relabeling succeeded.
      */
-    public static boolean restoreconTree(File dir) {
-        final File[] files = dir.listFiles();
-        boolean success = true;
-        if (files != null) {
-            for (File file : files) {
-                success &= restorecon(file);
-            }
+    public static boolean restoreconRecursive(File file) {
+        try {
+            return native_restorecon(file.getCanonicalPath(), SELINUX_ANDROID_RESTORECON_RECURSE);
+        } catch (IOException e) {
+            Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
+                    file.getPath(), e);
+            return false;
         }
-        return success;
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 01e1f8a..206b75d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6190,18 +6190,21 @@
                 "lock_screen_show_notifications";
 
         /**
-         * Defines global zen mode.  ZEN_MODE_OFF or ZEN_MODE_ON.
+         * Defines global zen mode.  ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+         * or ZEN_MODE_NO_INTERRUPTIONS.
          *
          * @hide
          */
         public static final String ZEN_MODE = "zen_mode";
 
         /** @hide */ public static final int ZEN_MODE_OFF = 0;
-        /** @hide */ public static final int ZEN_MODE_ON = 1;
+        /** @hide */ public static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+        /** @hide */ public static final int ZEN_MODE_NO_INTERRUPTIONS = 2;
 
         /** @hide */ public static String zenModeToString(int mode) {
-            if (mode == ZEN_MODE_OFF) return "ZEN_MODE_OFF";
-            return "ZEN_MODE_ON";
+            if (mode == ZEN_MODE_IMPORTANT_INTERRUPTIONS) return "ZEN_MODE_IMPORTANT_INTERRUPTIONS";
+            if (mode == ZEN_MODE_NO_INTERRUPTIONS) return "ZEN_MODE_NO_INTERRUPTIONS";
+            return "ZEN_MODE_OFF";
         }
 
         /**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 68a3d30..8fc3c5a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -30,6 +30,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Objects;
 
 /**
@@ -42,12 +43,18 @@
 
     public static final String SLEEP_MODE_NIGHTS = "nights";
     public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
+    public static final String SLEEP_MODE_DAYS_PREFIX = "days:";
 
     public static final int SOURCE_ANYONE = 0;
     public static final int SOURCE_CONTACT = 1;
     public static final int SOURCE_STAR = 2;
     public static final int MAX_SOURCE = SOURCE_STAR;
 
+    public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
+            Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
+    public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
+            Calendar.WEDNESDAY, Calendar.THURSDAY };
+
     private static final int XML_VERSION = 1;
     private static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
@@ -198,8 +205,39 @@
     public boolean isValid() {
         return isValidHour(sleepStartHour) && isValidMinute(sleepStartMinute)
                 && isValidHour(sleepEndHour) && isValidMinute(sleepEndMinute)
-                && (sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
-                    || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS));
+                && isValidSleepMode(sleepMode);
+    }
+
+    public static boolean isValidSleepMode(String sleepMode) {
+        return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
+                || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null;
+    }
+
+    public static int[] tryParseDays(String sleepMode) {
+        if (sleepMode == null) return null;
+        sleepMode = sleepMode.trim();
+        if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS;
+        if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS;
+        if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null;
+        if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null;
+        final String[] tokens = sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()).split(",");
+        if (tokens.length == 0) return null;
+        final int[] rt = new int[tokens.length];
+        for (int i = 0; i < tokens.length; i++) {
+            final int day = tryParseInt(tokens[i], -1);
+            if (day == -1) return null;
+            rt[i] = day;
+        }
+        return rt;
+    }
+
+    private static int tryParseInt(String value, int defValue) {
+        if (TextUtils.isEmpty(value)) return defValue;
+        try {
+            return Integer.valueOf(value);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
     }
 
     public static ZenModeConfig readXml(XmlPullParser parser)
@@ -209,7 +247,7 @@
         String tag = parser.getName();
         if (!ZEN_TAG.equals(tag)) return null;
         final ZenModeConfig rt = new ZenModeConfig();
-        final int version = Integer.parseInt(parser.getAttributeValue(null, ZEN_ATT_VERSION));
+        final int version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
         final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>();
         final ArrayList<Uri> conditionIds = new ArrayList<Uri>();
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -232,8 +270,7 @@
                     }
                 } else if (SLEEP_TAG.equals(tag)) {
                     final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
-                    rt.sleepMode = (SLEEP_MODE_NIGHTS.equals(mode)
-                            || SLEEP_MODE_WEEKNIGHTS.equals(mode)) ? mode : null;
+                    rt.sleepMode = isValidSleepMode(mode)? mode : null;
                     final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
                     final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
                     final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
@@ -312,8 +349,7 @@
 
     private static int safeInt(XmlPullParser parser, String att, int defValue) {
         final String val = parser.getAttributeValue(null, att);
-        if (TextUtils.isEmpty(val)) return defValue;
-        return Integer.valueOf(val);
+        return tryParseInt(val, defValue);
     }
 
     private static ComponentName safeComponentName(XmlPullParser parser, String att) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 57d1beb..fa4564e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -127,6 +127,8 @@
     @Override
     void destroyHardwareResources(View view) {
         destroyResources(view);
+        // mRootNode belongs to us and not a view, so we need to destroy it
+        mRootNode.destroyDisplayListData();
         nDestroyHardwareResources(mNativeProxy);
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3b2e1d1..706fb1c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13625,9 +13625,7 @@
      * @hide
      */
     protected void destroyHardwareResources() {
-        // Intentionally empty. RenderNode's lifecycle is now fully managed
-        // by the hardware renderer.
-        // However some subclasses (eg, WebView, TextureView) still need this signal
+        resetDisplayList();
     }
 
     /**
@@ -14996,6 +14994,7 @@
         if (mBackgroundSizeChanged) {
             background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
             mBackgroundSizeChanged = false;
+            invalidateOutline();
         }
 
         // Attempt to use a display list if requested.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 66cc3f6..1bf4639 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -555,6 +555,7 @@
     private CharSequence mPackageName;
     private CharSequence mClassName;
     private CharSequence mText;
+    private CharSequence mError;
     private CharSequence mContentDescription;
     private String mViewIdResourceName;
 
@@ -1923,6 +1924,32 @@
     }
 
     /**
+     * Sets the error text of this node.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param error The error text.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setError(CharSequence error) {
+        enforceNotSealed();
+        mError = error;
+    }
+
+    /**
+     * Gets the error text of this node.
+     *
+     * @return The error text.
+     */
+    public CharSequence getError() {
+        return mError;
+    }
+
+    /**
      * Gets the content description of this node.
      *
      * @return The content description.
@@ -2449,6 +2476,7 @@
         parcel.writeCharSequence(mPackageName);
         parcel.writeCharSequence(mClassName);
         parcel.writeCharSequence(mText);
+        parcel.writeCharSequence(mError);
         parcel.writeCharSequence(mContentDescription);
         parcel.writeString(mViewIdResourceName);
 
@@ -2519,6 +2547,7 @@
         mPackageName = other.mPackageName;
         mClassName = other.mClassName;
         mText = other.mText;
+        mError = other.mError;
         mContentDescription = other.mContentDescription;
         mViewIdResourceName = other.mViewIdResourceName;
 
@@ -2614,6 +2643,7 @@
         mPackageName = parcel.readCharSequence();
         mClassName = parcel.readCharSequence();
         mText = parcel.readCharSequence();
+        mError = parcel.readCharSequence();
         mContentDescription = parcel.readCharSequence();
         mViewIdResourceName = parcel.readString();
 
@@ -2675,6 +2705,7 @@
         mPackageName = null;
         mClassName = null;
         mText = null;
+        mError = null;
         mContentDescription = null;
         mViewIdResourceName = null;
         if (mActions != null) {
@@ -2879,6 +2910,7 @@
         builder.append("; packageName: ").append(mPackageName);
         builder.append("; className: ").append(mClassName);
         builder.append("; text: ").append(mText);
+        builder.append("; error: ").append(mError);
         builder.append("; contentDescription: ").append(mContentDescription);
         builder.append("; viewIdResName: ").append(mViewIdResourceName);
 
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 8f8876b..3a1f6ed 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3326,8 +3326,8 @@
         if (mLastY == Integer.MIN_VALUE) {
             rawDeltaY -= mMotionCorrection;
         }
-        if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
-            rawDeltaY -= mScrollConsumed[1];
+        if (dispatchNestedPreScroll(0, -rawDeltaY, mScrollConsumed, mScrollOffset)) {
+            rawDeltaY += mScrollConsumed[1];
             scrollOffsetCorrection -= mScrollOffset[1];
             scrollConsumedCorrection -= mScrollConsumed[1];
             if (vtev != null) {
@@ -3853,7 +3853,8 @@
                     // Since we can potentially overfling more than we can overscroll, don't
                     // allow the weird behavior where you can scroll to a boundary then
                     // fling further.
-                    if (Math.abs(initialVelocity) > mMinimumVelocity &&
+                    boolean flingVelocity = Math.abs(initialVelocity) > mMinimumVelocity;
+                    if (flingVelocity &&
                             !((mFirstPosition == 0 &&
                                     firstChildTop == contentTop - mOverscrollDistance) ||
                               (mFirstPosition + childCount == mItemCount &&
@@ -3864,6 +3865,7 @@
                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
 
                         mFlingRunnable.start(-initialVelocity);
+                        dispatchNestedFling(0, -initialVelocity, true);
                     } else {
                         mTouchMode = TOUCH_MODE_REST;
                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
@@ -3873,6 +3875,9 @@
                         if (mPositionScroller != null) {
                             mPositionScroller.stop();
                         }
+                        if (flingVelocity) {
+                            dispatchNestedFling(0, -initialVelocity, false);
+                        }
                     }
                 }
             } else {
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 90fec23..3758d86 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -330,7 +330,7 @@
 
         canvas.clipRect(mBounds);
         canvas.translate(translateX, 0);
-        canvas.drawArc(mArcRect, 45, 90, true, mPaint);
+        canvas.drawArc(mArcRect, 45, 90, false, mPaint);
         canvas.restoreToCount(count);
 
         boolean oneLastFrame = false;
diff --git a/core/java/android/widget/LegacyTimePickerDelegate.java b/core/java/android/widget/LegacyTimePickerDelegate.java
index 1634d5f..97f46ea 100644
--- a/core/java/android/widget/LegacyTimePickerDelegate.java
+++ b/core/java/android/widget/LegacyTimePickerDelegate.java
@@ -75,6 +75,11 @@
     // accommodates these two cases to be backwards compatible.
     private final Button mAmPmButton;
 
+    // May be null if layout has no done button
+    private final View mDoneButton;
+    private boolean mShowDoneButton;
+    private TimePicker.TimePickerDismissCallback mDismissCallback;
+
     private final String[] mAmPmStrings;
 
     private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
@@ -218,6 +223,19 @@
             }
         }
 
+        mDoneButton = delegator.findViewById(R.id.done_button);
+        if (mDoneButton != null) {
+            mDoneButton.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mDismissCallback != null) {
+                        mDismissCallback.dismiss(mDelegator, false, getCurrentHour(),
+                                                 getCurrentMinute());
+                    }
+                }
+            });
+        }
+
         getHourFormatData();
 
         // update controls to initial state
@@ -242,6 +260,9 @@
         if (mDelegator.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             mDelegator.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         }
+
+        mShowDoneButton = false;
+        updateDoneButton();
     }
 
     private void getHourFormatData() {
@@ -408,12 +429,23 @@
 
     @Override
     public void setShowDoneButton(boolean showDoneButton) {
-        // Nothing to do
+        mShowDoneButton = showDoneButton;
+        updateDoneButton();
+    }
+
+    private boolean isShowDoneButton() {
+        return mShowDoneButton;
+    }
+
+    private void updateDoneButton() {
+        if (mDoneButton != null) {
+            mDoneButton.setVisibility(mShowDoneButton ? View.VISIBLE : View.GONE);
+        }
     }
 
     @Override
     public void setDismissCallback(TimePicker.TimePickerDismissCallback callback) {
-        // Nothing to do
+        mDismissCallback = callback;
     }
 
     @Override
@@ -428,7 +460,8 @@
 
     @Override
     public Parcelable onSaveInstanceState(Parcelable superState) {
-        return new SavedState(superState, getCurrentHour(), getCurrentMinute());
+        return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
+                isShowDoneButton());
     }
 
     @Override
@@ -436,6 +469,7 @@
         SavedState ss = (SavedState) state;
         setCurrentHour(ss.getHour());
         setCurrentMinute(ss.getMinute());
+        setShowDoneButton(ss.isShowDoneButton());
     }
 
     @Override
@@ -596,16 +630,20 @@
 
         private final int mMinute;
 
-        private SavedState(Parcelable superState, int hour, int minute) {
+        private final boolean mShowDoneButton;
+
+        private SavedState(Parcelable superState, int hour, int minute, boolean showDoneButton) {
             super(superState);
             mHour = hour;
             mMinute = minute;
+            mShowDoneButton = showDoneButton;
         }
 
         private SavedState(Parcel in) {
             super(in);
             mHour = in.readInt();
             mMinute = in.readInt();
+            mShowDoneButton = (in.readInt() == 1);
         }
 
         public int getHour() {
@@ -616,11 +654,16 @@
             return mMinute;
         }
 
+        public boolean isShowDoneButton() {
+            return mShowDoneButton;
+        }
+
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             super.writeToParcel(dest, flags);
             dest.writeInt(mHour);
             dest.writeInt(mMinute);
+            dest.writeInt(mShowDoneButton ? 1 : 0);
         }
 
         @SuppressWarnings({"unused", "hiding"})
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 755bb48..ee17b78 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -478,6 +478,11 @@
     private int mLastHandledDownDpadKeyCode = -1;
 
     /**
+     * If true then the selector wheel is hidden until the picker has focus.
+     */
+    private boolean mHideWheelUntilFocused;
+
+    /**
      * Interface to listen for changes of the current value.
      */
     public interface OnValueChangeListener {
@@ -598,6 +603,9 @@
 
         mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
 
+        mHideWheelUntilFocused = attributesArray.getBoolean(
+            R.styleable.NumberPicker_hideWheelUntilFocused, false);
+
         mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
 
         mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
@@ -974,8 +982,8 @@
                 }
                 switch (event.getAction()) {
                     case KeyEvent.ACTION_DOWN:
-                        if (mWrapSelectorWheel || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
-                                ? getValue() < getMaxValue() : getValue() > getMinValue()) {
+                        if (mWrapSelectorWheel || ((keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
+                                ? getValue() < getMaxValue() : getValue() > getMinValue())) {
                             requestFocus();
                             mLastHandledDownDpadKeyCode = keyCode;
                             removeAllCallbacks();
@@ -1497,11 +1505,12 @@
             super.onDraw(canvas);
             return;
         }
+        final boolean showSelectorWheel = mHideWheelUntilFocused ? hasFocus() : true;
         float x = (mRight - mLeft) / 2;
         float y = mCurrentScrollOffset;
 
         // draw the virtual buttons pressed state if needed
-        if (mVirtualButtonPressedDrawable != null
+        if (showSelectorWheel && mVirtualButtonPressedDrawable != null
                 && mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
             if (mDecrementVirtualButtonPressed) {
                 mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
@@ -1526,14 +1535,15 @@
             // item. Otherwise, if the user starts editing the text via the
             // IME he may see a dimmed version of the old value intermixed
             // with the new one.
-            if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
+            if ((showSelectorWheel && i != SELECTOR_MIDDLE_ITEM_INDEX) ||
+                (i == SELECTOR_MIDDLE_ITEM_INDEX && mInputText.getVisibility() != VISIBLE)) {
                 canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
             }
             y += mSelectorElementHeight;
         }
 
         // draw the selection dividers
-        if (mSelectionDivider != null) {
+        if (showSelectorWheel && mSelectionDivider != null) {
             // draw the top divider
             int topOfTopDivider = mTopSelectionDividerTop;
             int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2381a78..dd58ba6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8240,6 +8240,7 @@
 
             if (mEditor.mError != null) {
                 info.setContentInvalid(true);
+                info.setError(mEditor.mError);
             }
         }
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 52f611a..b8fcff6 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -201,7 +201,7 @@
     int sampleSize = 1;
 
     SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
-    SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+    SkColorType prefColorType = kN32_SkColorType;
 
     bool doDither = true;
     bool isMutable = false;
@@ -223,7 +223,7 @@
         env->SetObjectField(options, gOptions_mimeFieldID, 0);
 
         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
-        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+        prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
         isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
         doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
         preferQualityOverSpeed = env->GetBooleanField(options,
@@ -305,7 +305,7 @@
     }
 
     SkBitmap decodingBitmap;
-    if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
+    if (!decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)) {
         return nullObjectReturn("decoder->decode returned false");
     }
 
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index ead2de3..91efc8c 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -60,7 +60,7 @@
     }
 
     bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
-                      SkBitmap::Config pref, int sampleSize) {
+                      SkColorType pref, int sampleSize) {
         fDecoder->setSampleSize(sampleSize);
         return fDecoder->decodeSubset(bitmap, rect, pref);
     }
@@ -175,7 +175,7 @@
     jobject tileBitmap = NULL;
     SkImageDecoder *decoder = brd->getDecoder();
     int sampleSize = 1;
-    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+    SkColorType prefColorType = kUnknown_SkColorType;
     bool doDither = true;
     bool preferQualityOverSpeed = false;
     bool requireUnpremultiplied = false;
@@ -188,7 +188,7 @@
         env->SetObjectField(options, gOptions_mimeFieldID, 0);
 
         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
-        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+        prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
         doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
         preferQualityOverSpeed = env->GetBooleanField(options,
                 gOptions_preferQualityOverSpeedFieldID);
@@ -226,7 +226,7 @@
         adb.reset(bitmap);
     }
 
-    if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+    if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) {
         return nullObjectReturn("decoder->decodeRegion returned false");
     }
 
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 2bd7a28..7a186a2 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -301,18 +301,17 @@
     return b;
 }
 
-SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env,
-                                                    jobject jconfig) {
+SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
     SkASSERT(env);
     if (NULL == jconfig) {
-        return SkBitmap::kNo_Config;
+        return kUnknown_SkColorType;
     }
     SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
     int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
     if (c < 0 || c >= SkBitmap::kConfigCount) {
-        c = SkBitmap::kNo_Config;
+        c = kUnknown_SkColorType;
     }
-    return static_cast<SkBitmap::Config>(c);
+    return SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(c));
 }
 
 SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 0ad7fd8..6d82ceb 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -58,10 +58,10 @@
     // ref to its SkRasterizer* (or NULL).
     static SkRasterizer* refNativeRasterizer(jlong rasterizerHandle);
 
-    /** Return the corresponding native config from the java Config enum,
-        or kNo_Config if the java object is null.
+    /** Return the corresponding native colorType from the java Config enum,
+        or kUnknown_SkColorType if the java object is null.
     */
-    static SkBitmap::Config getNativeBitmapConfig(JNIEnv*, jobject jconfig);
+    static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig);
 
     /** Create a java Bitmap object given the native bitmap (required) and optional
         storage array (may be null).
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 26405b5..ffa569e 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -404,7 +404,7 @@
  * Returns: boolean: (true) file label successfully restored, (false) otherwise
  * Exceptions: none
  */
-static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr) {
+static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr, jint flags) {
     if (isSELinuxDisabled) {
         return true;
     }
@@ -415,7 +415,7 @@
         return false;
     }
 
-    int ret = selinux_android_restorecon(pathname.c_str(), 0);
+    int ret = selinux_android_restorecon(pathname.c_str(), flags);
     ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
     return (ret == 0);
 }
@@ -434,7 +434,7 @@
     { "getPidContext"            , "(I)Ljava/lang/String;"                        , (void*)getPidCon        },
     { "isSELinuxEnforced"        , "()Z"                                          , (void*)isSELinuxEnforced},
     { "isSELinuxEnabled"         , "()Z"                                          , (void*)isSELinuxEnabled },
-    { "native_restorecon"        , "(Ljava/lang/String;)Z"                        , (void*)native_restorecon},
+    { "native_restorecon"        , "(Ljava/lang/String;I)Z"                       , (void*)native_restorecon},
     { "setBooleanValue"          , "(Ljava/lang/String;Z)Z"                       , (void*)setBooleanValue  },
     { "setFileContext"           , "(Ljava/lang/String;Ljava/lang/String;)Z"      , (void*)setFileCon       },
     { "setFSCreateContext"       , "(Ljava/lang/String;)Z"                        , (void*)setFSCreateCon   },
diff --git a/core/res/res/anim/disabled_anim_material.xml b/core/res/res/anim/disabled_anim_material.xml
new file mode 100644
index 0000000..6a7731e
--- /dev/null
+++ b/core/res/res/anim/disabled_anim_material.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false">
+        <set>
+            <objectAnimator android:propertyName="alpha"
+                android:duration="@integer/disabled_alpha_animation_duration"
+                android:valueTo="?attr/disabledAlpha"
+                android:valueType="floatType"/>
+        </set>
+    </item>
+    <item>
+        <set>
+            <objectAnimator android:propertyName="alpha"
+                android:duration="@integer/disabled_alpha_animation_duration"
+                android:valueTo="1"
+                android:valueType="floatType"/>
+        </set>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
index 927b7b2..93acc3f 100644
--- a/core/res/res/layout/alert_dialog_material.xml
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -83,7 +83,7 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:minHeight="64dp">
-        <FrameLayout android:id="@+android:id/custom"
+        <FrameLayout android:id="@+id/custom"
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
     </FrameLayout>
@@ -101,7 +101,7 @@
             android:layout_height="wrap_content"
             android:layoutDirection="locale">
             <Button android:id="@+id/button3"
-                style="?attr/buttonBarButtonStyle"
+                style="?attr/buttonBarNeutralButtonStyle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
@@ -112,13 +112,13 @@
                 android:layout_weight="1"
                 android:visibility="invisible" />
             <Button android:id="@+id/button2"
-                style="?attr/buttonBarButtonStyle"
+                style="?attr/buttonBarNegativeButtonStyle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
                 android:minHeight="@dimen/alert_dialog_button_bar_height" />
             <Button android:id="@+id/button1"
-                style="?attr/buttonBarButtonStyle"
+                style="?attr/buttonBarPositiveButtonStyle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxLines="2"
diff --git a/core/res/res/layout/time_picker_legacy_leanback.xml b/core/res/res/layout/time_picker_legacy_leanback.xml
new file mode 100644
index 0000000..397f733
--- /dev/null
+++ b/core/res/res/layout/time_picker_legacy_leanback.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/timePickerLayout"
+    android:orientation="horizontal"
+    android:layout_gravity="center_horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:paddingStart="8dip"
+    android:paddingEnd="8dip">
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingStart="8dip"
+        android:paddingEnd="8dip"
+        android:layoutDirection="ltr">
+
+        <!-- hour -->
+        <NumberPicker
+            android:id="@+id/hour"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+        <!-- divider -->
+        <TextView
+            android:id="@+id/divider"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="6dip"
+            android:layout_marginEnd="6dip"
+            android:layout_gravity="center_vertical"
+            android:importantForAccessibility="no"
+            />
+
+        <!-- minute -->
+        <NumberPicker
+            android:id="@+id/minute"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+    </LinearLayout>
+
+    <!-- AM / PM -->
+    <NumberPicker
+        android:id="@+id/amPm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dip"
+        android:layout_marginBottom="16dip"
+        android:layout_marginStart="8dip"
+        android:layout_marginEnd="8dip"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        />
+
+    <!-- Width fixed here because TextView doesn't set MEASURED_STATE_TOO_SMALL -->
+    <Button
+        android:id="@+id/done_button"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:text="@string/done_label"
+        android:textSize="@dimen/timepicker_done_label_size"
+        style="?android:attr/buttonBarButtonStyle" />
+
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 44125b0..4b708a7 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -894,6 +894,15 @@
         <!-- Style for buttons within button bars -->
         <attr name="buttonBarButtonStyle" format="reference" />
 
+        <!-- Style for the "positive" buttons within button bars -->
+        <attr name="buttonBarPositiveButtonStyle" format="reference" />
+
+        <!-- Style for the "negative" buttons within button bars -->
+        <attr name="buttonBarNegativeButtonStyle" format="reference" />
+
+        <!-- Style for the "neutral" buttons within button bars -->
+        <attr name="buttonBarNeutralButtonStyle" format="reference" />
+
         <!-- Style for the search query widget. -->
         <attr name="searchViewStyle" format="reference" />
 
@@ -4411,6 +4420,8 @@
         <attr name="internalLayout" />
         <!-- @hide The drawable for pressed virtual (increment/decrement) buttons. -->
         <attr name="virtualButtonPressedDrawable" format="reference"/>
+        <!-- @hide If true then the selector wheel is hidden until the picker has focus. -->
+        <attr name="hideWheelUntilFocused" format="boolean"/>
     </declare-styleable>
 
     <declare-styleable name="TimePicker">
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index fd61f73..0343cfa 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -21,4 +21,5 @@
     <integer name="kg_glowpad_rotation_offset">0</integer>
     <integer name="button_pressed_animation_duration">100</integer>
     <integer name="button_pressed_animation_delay">100</integer>
+    <integer name="disabled_alpha_animation_duration">100</integer>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8e93353..99c3450 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2228,6 +2228,9 @@
   <public type="attr" name="suggestionRowLayout" />
   <public type="attr" name="queryBackground" />
   <public type="attr" name="submitBackground" />
+  <public type="attr" name="buttonBarPositiveButtonStyle" />
+  <public type="attr" name="buttonBarNeutralButtonStyle" />
+  <public type="attr" name="buttonBarNegativeButtonStyle" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
@@ -2470,6 +2473,9 @@
   <public type="style" name="TextAppearance.Material.Menu" />
   <public type="style" name="TextAppearance.Material.Button" />
 
+  <public type="style" name="Widget.Material.Button.Borderless.Colored" />
+  <public type="style" name="Widget.Material.Light.Button.Borderless.Colored" />
+
   <public-padding type="interpolator" name="l_resource_pad" end="0x010c0010" />
 
   <!-- An interpolator which accelerates fast but decelerates slowly. -->
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index 72d0379..6f9a88e 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -22,4 +22,12 @@
     <style name="AlertDialog.Leanback.Light">
     </style>
 
+    <style name="Widget.Leanback.TimePicker" parent="Widget.Material.TimePicker">
+        <item name="legacyLayout">@layout/time_picker_legacy_leanback</item>
+    </style>
+
+    <style name="Widget.Leanback.NumberPicker" parent="Widget.Material.NumberPicker">
+        <item name="hideWheelUntilFocused">true</item>
+    </style>
+
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index ea67f46..c1eb999 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -415,6 +415,12 @@
         <item name="stateListAnimator">@null</item>
     </style>
 
+    <!-- Colored borderless ink button -->
+    <style name="Widget.Material.Button.Borderless.Colored">
+        <item name="textColor">?attr/colorAccent</item>
+        <item name="stateListAnimator">@anim/disabled_anim_material</item>
+    </style>
+
     <!-- Small borderless ink button -->
     <style name="Widget.Material.Button.Borderless.Small">
         <item name="minHeight">48dip</item>
@@ -837,6 +843,7 @@
     <style name="Widget.Material.Light.Button" parent="Widget.Material.Button"/>
     <style name="Widget.Material.Light.Button.Small" parent="Widget.Material.Button.Small"/>
     <style name="Widget.Material.Light.Button.Borderless" parent="Widget.Material.Button.Borderless"/>
+    <style name="Widget.Material.Light.Button.Borderless.Colored" parent="Widget.Material.Button.Borderless.Colored"/>
     <style name="Widget.Material.Light.Button.Borderless.Small" parent="Widget.Material.Button.Borderless.Small"/>
     <style name="Widget.Material.Light.Button.Inset" parent="Widget.Material.Button.Inset"/>
     <style name="Widget.Material.Light.Button.Toggle" parent="Widget.Material.Button.Toggle" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 8edfc8f..b1bf123 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -371,6 +371,9 @@
         <item name="dividerHorizontal">@drawable/divider_vertical_dark</item>
         <item name="buttonBarStyle">@style/ButtonBar</item>
         <item name="buttonBarButtonStyle">?attr/buttonStyle</item>
+        <item name="buttonBarPositiveButtonStyle">?attr/buttonBarButtonStyle</item>
+        <item name="buttonBarNegativeButtonStyle">?attr/buttonBarButtonStyle</item>
+        <item name="buttonBarNeutralButtonStyle">?attr/buttonBarButtonStyle</item>
         <item name="segmentedButtonStyle">@style/SegmentedButton</item>
 
         <!-- SearchView attributes -->
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index a571b98..6f6385b 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -19,6 +19,7 @@
       <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
       <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
       <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
+      <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
     </style>
 
     <style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert">
@@ -26,6 +27,7 @@
       <item name="textColorPrimary">@color/primary_text_leanback_light</item>
       <item name="textColorSecondary">@color/secondary_text_leanback_light</item>
       <item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
+      <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
     </style>
 
     <style name="Theme.Leanback.Dialog.TimePicker" parent="Theme.Material.Dialog.BaseTimePicker">
@@ -33,6 +35,8 @@
       <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
       <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
       <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
+      <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
+      <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
     </style>
 
     <style name="Theme.Leanback.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker">
@@ -40,6 +44,8 @@
       <item name="textColorPrimary">@color/primary_text_leanback_light</item>
       <item name="textColorSecondary">@color/secondary_text_leanback_light</item>
       <item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
+      <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
+      <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
     </style>
 
     <style name="Theme.Leanback.Light.Dialog" parent="Theme.Material.Light.Dialog">
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 8646524..472177f 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -332,6 +332,7 @@
         <item name="dividerHorizontal">?attr/listDivider</item>
         <item name="buttonBarStyle">@style/Widget.Material.ButtonBar</item>
         <item name="buttonBarButtonStyle">@style/Widget.Material.Button.Borderless</item>
+        <item name="buttonBarPositiveButtonStyle">@style/Widget.Material.Button.Borderless.Colored</item>
         <item name="segmentedButtonStyle">@style/Widget.Material.SegmentedButton</item>
 
         <!-- SearchView attributes -->
@@ -681,6 +682,7 @@
         <item name="dividerHorizontal">?attr/listDivider</item>
         <item name="buttonBarStyle">@style/Widget.Material.Light.ButtonBar</item>
         <item name="buttonBarButtonStyle">@style/Widget.Material.Light.Button.Borderless</item>
+        <item name="buttonBarPositiveButtonStyle">@style/Widget.Material.Light.Button.Borderless.Colored</item>
         <item name="segmentedButtonStyle">@style/Widget.Material.Light.SegmentedButton</item>
 
         <!-- SearchView attributes -->
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 6437e07..86a45c5 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -56,7 +56,6 @@
 
 droidsans_fallback_src := DroidSansFallbackFull.ttf
 
-ifneq ($(EXTENDED_FONT_FOOTPRINT),true)
 include $(CLEAR_VARS)
 LOCAL_MODULE := MTLmr3m.ttf
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
@@ -65,7 +64,6 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
 include $(BUILD_PREBUILT)
 extra_font_files += MTLmr3m.ttf
-endif  # !EXTENDED_FONT_FOOTPRINT
 
 endif  # SMALLER_FONT_FOOTPRINT
 
diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs
index b4028bd..b770135 100644
--- a/docs/html/google/google_toc.cs
+++ b/docs/html/google/google_toc.cs
@@ -192,6 +192,12 @@
   </li>
 
   <li class="nav-section">
+  <div class="nav-section-header empty"><a href="<?cs var:toroot ?>google/gcs/index.html">
+        <span class="en">Google Cloud Save</span></a>
+      </div>
+  </li>
+
+  <li class="nav-section">
     <div class="nav-section-header"><a href="<?cs var:toroot ?>google/play/dist.html">
       <span class="en">Google Play Distribution</span></a>
     </div>
diff --git a/docs/html/sdk/installing/studio-build.jd b/docs/html/sdk/installing/studio-build.jd
index 2a616a0..29ba12d 100644
--- a/docs/html/sdk/installing/studio-build.jd
+++ b/docs/html/sdk/installing/studio-build.jd
@@ -882,11 +882,11 @@
     buildTypes { ... }
     productFlavors {
         demo {
-            packageName "com.buildsystemexample.app.demo"
+            applicationId "com.buildsystemexample.app.demo"
             versionName "1.0-demo"
         }
         full {
-            packageName "com.buildsystemexample.app.full"
+            applicationId "com.buildsystemexample.app.full"
             versionName "1.0-full"
         }
     }
@@ -896,8 +896,9 @@
 
 <p>The product flavor definitions support the same properties as the <code>defaultConfig</code>
 element. The base configuration for all flavors is specified in <code>defaultConfig</code>, and each
-flavor can override any value. The build file above assigns a different package name to each flavor:
-since each flavor definition creates a different app, they each need a distinct package name.</p>
+flavor can override any value. The build file above uses the <code>applicationId</code> property
+to assign a different package name to each flavor: since each flavor definition creates a
+different app, they each need a distinct package name.</p>
 
 <p class="note"><strong>Note:</strong> To distribute your app using
 <a href="{@docRoot}google/play/publishing/multiple-apks.html">Multiple APK Support</a> in
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e080375..83a8ed5 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -31,7 +31,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.ColorDrawable.ColorState;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.Shader;
@@ -712,9 +711,11 @@
         final Resources r = a.getResources();
         final BitmapState state = mBitmapState;
 
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
-        final int[] themeAttrs = a.extractThemeAttrs();
-        state.mThemeAttrs = themeAttrs;
+        state.mThemeAttrs = a.extractThemeAttrs();
 
         final int srcResId = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
         if (srcResId != 0) {
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 3716182..4f050e0 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -45,7 +45,6 @@
 
     @ViewDebug.ExportedProperty(deepExport = true, prefix = "state_")
     private ColorState mColorState;
-    private ColorStateList mTint;
     private PorterDuffColorFilter mTintFilter;
 
     private boolean mMutated;
@@ -191,7 +190,7 @@
 
     @Override
     public boolean isStateful() {
-        return mTint != null && mTint.isStateful();
+        return mColorState.mTint != null && mColorState.mTint.isStateful();
     }
 
     @Override
@@ -215,60 +214,42 @@
         super.inflate(r, parser, attrs, theme);
 
         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable);
-        inflateStateFromTypedArray(a);
+        updateStateFromTypedArray(a);
         a.recycle();
     }
 
     /**
-     * Initializes the constant state from the values in the typed array.
-     */
-    private void inflateStateFromTypedArray(TypedArray a) {
-        final ColorState state = mColorState;
-
-        // Extract the theme attributes, if any.
-        final int[] themeAttrs = a.extractThemeAttrs();
-        state.mThemeAttrs = themeAttrs;
-
-        if (themeAttrs == null || themeAttrs[R.styleable.ColorDrawable_color] == 0) {
-            final int color = a.getColor(R.styleable.ColorDrawable_color, 0);
-            state.mBaseColor = color;
-            state.mUseColor = color;
-        }
-    }
-
-    @Override
-    public void applyTheme(Theme t) {
-        super.applyTheme(t);
-
-        final ColorState state = mColorState;
-        if (state == null) {
-            throw new RuntimeException("Can't apply theme to <color> with no constant state");
-        }
-
-        final int[] themeAttrs = state.mThemeAttrs;
-        if (themeAttrs != null) {
-            final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.ColorDrawable);
-            updateStateFromTypedArray(a);
-            a.recycle();
-        }
-    }
-
-    /**
      * Updates the constant state from the values in the typed array.
      */
     private void updateStateFromTypedArray(TypedArray a) {
         final ColorState state = mColorState;
 
-        if (a.hasValue(R.styleable.ColorDrawable_color)) {
-            final int color = a.getColor(R.styleable.ColorDrawable_color, 0);
-            state.mBaseColor = color;
-            state.mUseColor = color;
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
+        // Extract the theme attributes, if any.
+        state.mThemeAttrs = a.extractThemeAttrs();
+
+        state.mBaseColor = a.getColor(R.styleable.ColorDrawable_color, state.mBaseColor);
+        state.mUseColor = state.mBaseColor;
+    }
+
+    @Override
+    public void applyTheme(Theme t) {
+        super.applyTheme(t);
+
+        final ColorState state = mColorState;
+        if (state == null || state.mThemeAttrs == null) {
+            return;
         }
+
+        final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ColorDrawable);
+        updateStateFromTypedArray(a);
+        a.recycle();
     }
 
     @Override
     public ConstantState getConstantState() {
-        mColorState.mChangingConfigurations = getChangingConfigurations();
         return mColorState;
     }
 
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index c2759ec..b050a02 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -238,9 +238,9 @@
      * may change, requiring that it be re-created.
      *
      * @param configs A mask of the changing configuration parameters, as
-     * defined by {@link android.content.res.Configuration}.
+     * defined by {@link android.content.pm.ActivityInfo}.
      *
-     * @see android.content.res.Configuration
+     * @see android.content.pm.ActivityInfo
      */
     public void setChangingConfigurations(int configs) {
         mChangingConfigurations = configs;
@@ -255,9 +255,9 @@
      * drawables they hold.
      *
      * @return Returns a mask of the changing configuration parameters, as
-     * defined by {@link android.content.res.Configuration}.
+     * defined by {@link android.content.pm.ActivityInfo}.
      *
-     * @see android.content.res.Configuration
+     * @see android.content.pm.ActivityInfo
      */
     public int getChangingConfigurations() {
         return mChangingConfigurations;
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 0ccce93..6e9b776 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1011,6 +1011,9 @@
     private void updateStateFromTypedArray(TypedArray a) {
         final GradientState state = mGradientState;
 
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         state.mThemeAttrs = a.extractThemeAttrs();
 
@@ -1152,6 +1155,9 @@
     private void updateGradientDrawablePadding(TypedArray a) {
         final GradientState st = mGradientState;
 
+        // Account for any configuration changes.
+        st.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         st.mAttrPadding = a.extractThemeAttrs();
 
@@ -1170,6 +1176,9 @@
     private void updateDrawableCorners(TypedArray a) {
         final GradientState st = mGradientState;
 
+        // Account for any configuration changes.
+        st.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         st.mAttrCorners = a.extractThemeAttrs();
 
@@ -1201,6 +1210,10 @@
     private void updateGradientDrawableStroke(TypedArray a) {
         final GradientState st = mGradientState;
 
+        // Account for any configuration changes.
+        st.mChangingConfigurations |= a.getChangingConfigurations();
+
+        // Extract the theme attributes, if any.
         st.mAttrStroke = a.extractThemeAttrs();
 
         // We have an explicit stroke defined, so the default stroke width
@@ -1227,7 +1240,13 @@
     }
 
     private void updateGradientDrawableSolid(TypedArray a) {
-        mGradientState.mAttrSolid = a.extractThemeAttrs();
+        final GradientState st = mGradientState;
+
+        // Account for any configuration changes.
+        st.mChangingConfigurations |= a.getChangingConfigurations();
+
+        // Extract the theme attributes, if any.
+        st.mAttrSolid = a.extractThemeAttrs();
 
         final ColorStateList colorStateList = a.getColorStateList(
                 R.styleable.GradientDrawableSolid_color);
@@ -1240,6 +1259,9 @@
             throws XmlPullParserException {
         final GradientState st = mGradientState;
 
+        // Account for any configuration changes.
+        st.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         st.mAttrGradient = a.extractThemeAttrs();
 
@@ -1351,6 +1373,9 @@
     private void updateGradientDrawableSize(TypedArray a) {
         final GradientState st = mGradientState;
 
+        // Account for any configuration changes.
+        st.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         st.mAttrSize = a.extractThemeAttrs();
 
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index e91ffb1..d214a47 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -16,6 +16,8 @@
 
 package android.graphics.drawable;
 
+import com.android.internal.R;
+
 import android.annotation.NonNull;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -48,9 +50,11 @@
  * @attr ref android.R.styleable#InsetDrawable_insetBottom
  */
 public class InsetDrawable extends Drawable implements Drawable.Callback {
-    // Most of this is copied from ScaleDrawable.
-    private InsetState mInsetState;
+    private static final String LOG_TAG = "InsetDrawable";
+
     private final Rect mTmpRect = new Rect();
+
+    private InsetState mInsetState;
     private boolean mMutated;
 
     /*package*/ InsetDrawable() {
@@ -79,59 +83,81 @@
     @Override
     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
-        int type;
-
-        TypedArray a = r.obtainAttributes(attrs,
-                com.android.internal.R.styleable.InsetDrawable);
-
-        super.inflateWithAttributes(r, parser, a,
-                com.android.internal.R.styleable.InsetDrawable_visible);
-
-        int drawableRes = a.getResourceId(com.android.internal.R.styleable.
-                                    InsetDrawable_drawable, 0);
-
-        int inLeft = a.getDimensionPixelOffset(com.android.internal.R.styleable.
-                                    InsetDrawable_insetLeft, 0);
-        int inTop = a.getDimensionPixelOffset(com.android.internal.R.styleable.
-                                    InsetDrawable_insetTop, 0);
-        int inRight = a.getDimensionPixelOffset(com.android.internal.R.styleable.
-                                    InsetDrawable_insetRight, 0);
-        int inBottom = a.getDimensionPixelOffset(com.android.internal.R.styleable.
-                                    InsetDrawable_insetBottom, 0);
-
+        final TypedArray a = r.obtainAttributes(attrs, R.styleable.InsetDrawable);
+        super.inflateWithAttributes(r, parser, a, R.styleable.InsetDrawable_visible);
+        updateStateFromTypedArray(a);
         a.recycle();
 
-        Drawable dr;
-        if (drawableRes != 0) {
-            dr = r.getDrawable(drawableRes);
-        } else {
+        // Load inner XML elements.
+        if (mInsetState.mDrawable == null) {
+            int type;
             while ((type=parser.next()) == XmlPullParser.TEXT) {
             }
             if (type != XmlPullParser.START_TAG) {
                 throw new XmlPullParserException(
                         parser.getPositionDescription()
-                        + ": <inset> tag requires a 'drawable' attribute or "
-                        + "child tag defining a drawable");
+                                + ": <inset> tag requires a 'drawable' attribute or "
+                                + "child tag defining a drawable");
             }
-            dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
-        }
-
-        if (dr == null) {
-            Log.w("drawable", "No drawable specified for <inset>");
-        }
-
-        mInsetState.mDrawable = dr;
-        mInsetState.mInsetLeft = inLeft;
-        mInsetState.mInsetRight = inRight;
-        mInsetState.mInsetTop = inTop;
-        mInsetState.mInsetBottom = inBottom;
-
-        if (dr != null) {
+            final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+            mInsetState.mDrawable = dr;
             dr.setCallback(this);
         }
+
+        // Verify state.
+        if (mInsetState.mDrawable == null) {
+            Log.w(LOG_TAG, "No drawable specified for <inset>");
+        }
     }
 
-    // overrides from Drawable.Callback
+    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
+        final InsetState state = mInsetState;
+
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
+        // Extract the theme attributes, if any.
+        state.mThemeAttrs = a.extractThemeAttrs();
+
+        final Drawable dr = a.getDrawable(R.styleable.InsetDrawable_drawable);
+        if (dr != null) {
+            state.mDrawable = dr;
+            dr.setCallback(this);
+        }
+
+        state.mInsetLeft = a.getDimensionPixelOffset(
+                R.styleable.InsetDrawable_insetLeft, state.mInsetLeft);
+        state.mInsetTop = a.getDimensionPixelOffset(
+                R.styleable.InsetDrawable_insetTop, state.mInsetTop);
+        state.mInsetRight = a.getDimensionPixelOffset(
+                R.styleable.InsetDrawable_insetRight, state.mInsetRight);
+        state.mInsetBottom = a.getDimensionPixelOffset(
+                R.styleable.InsetDrawable_insetBottom, state.mInsetBottom);
+    }
+
+    @Override
+    public void applyTheme(Theme t) {
+        super.applyTheme(t);
+
+        final InsetState state = mInsetState;
+        if (state == null || state.mThemeAttrs == null) {
+            return;
+        }
+
+        final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.InsetDrawable);
+        try {
+            updateStateFromTypedArray(a);
+        } catch (XmlPullParserException e) {
+            throw new RuntimeException(e);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    @Override
+    public boolean canApplyTheme() {
+        return mInsetState != null && mInsetState.mThemeAttrs != null;
+    }
 
     @Override
     public void invalidateDrawable(Drawable who) {
@@ -157,8 +183,6 @@
         }
     }
 
-    // overrides from Drawable
-
     @Override
     public void draw(Canvas canvas) {
         mInsetState.mDrawable.draw(canvas);
@@ -316,9 +340,11 @@
     }
 
     final static class InsetState extends ConstantState {
-        Drawable mDrawable;
+        int[] mThemeAttrs;
         int mChangingConfigurations;
 
+        Drawable mDrawable;
+
         int mInsetLeft;
         int mInsetTop;
         int mInsetRight;
@@ -329,6 +355,8 @@
 
         InsetState(InsetState orig, InsetDrawable owner, Resources res) {
             if (orig != null) {
+                mThemeAttrs = orig.mThemeAttrs;
+                mChangingConfigurations = orig.mChangingConfigurations;
                 if (res != null) {
                     mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
                 } else {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 2f22392..fa68bc5 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -155,9 +155,11 @@
     private void updateStateFromTypedArray(TypedArray a) {
         final LayerState state = mLayerState;
 
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
-        final int[] themeAttrs = a.extractThemeAttrs();
-        state.mThemeAttrs = themeAttrs;
+        state.mThemeAttrs = a.extractThemeAttrs();
 
         mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
 
@@ -172,7 +174,8 @@
      */
     private void inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
-        TypedArray a;
+        final LayerState state = mLayerState;
+
         final int innerDepth = parser.getDepth() + 1;
         int type;
         int depth;
@@ -186,28 +189,12 @@
                 continue;
             }
 
-            a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
-
-            final int[] themeAttrs = a.extractThemeAttrs();
-            final int left = a.getDimensionPixelOffset(
-                    R.styleable.LayerDrawableItem_left, 0);
-            final int top = a.getDimensionPixelOffset(
-                    R.styleable.LayerDrawableItem_top, 0);
-            final int right = a.getDimensionPixelOffset(
-                    R.styleable.LayerDrawableItem_right, 0);
-            final int bottom = a.getDimensionPixelOffset(
-                    R.styleable.LayerDrawableItem_bottom, 0);
-            final int drawableRes = a.getResourceId(
-                    R.styleable.LayerDrawableItem_drawable, 0);
-            final int id = a.getResourceId(
-                    R.styleable.LayerDrawableItem_id, View.NO_ID);
-
+            final ChildDrawable layer = new ChildDrawable();
+            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
+            updateLayerFromTypedArray(layer, a);
             a.recycle();
 
-            final Drawable dr;
-            if (drawableRes != 0) {
-                dr = r.getDrawable(drawableRes, theme);
-            } else {
+            if (layer.mDrawable == null) {
                 while ((type = parser.next()) == XmlPullParser.TEXT) {
                 }
                 if (type != XmlPullParser.START_TAG) {
@@ -215,10 +202,41 @@
                             + ": <item> tag requires a 'drawable' attribute or "
                             + "child tag defining a drawable");
                 }
-                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+                layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
             }
 
-            addLayer(dr, themeAttrs, id, left, top, right, bottom);
+            if (layer.mDrawable != null) {
+                state.mChildrenChangingConfigurations |=
+                        layer.mDrawable.getChangingConfigurations();
+                layer.mDrawable.setCallback(this);
+            }
+
+            addLayer(layer);
+        }
+    }
+
+    private void updateLayerFromTypedArray(ChildDrawable layer, TypedArray a) {
+        final LayerState state = mLayerState;
+
+        // Account for any configuration changes.
+        state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
+
+        // Extract the theme attributes, if any.
+        layer.mThemeAttrs = a.extractThemeAttrs();
+
+        layer.mInsetL = a.getDimensionPixelOffset(
+                R.styleable.LayerDrawableItem_left, layer.mInsetL);
+        layer.mInsetT = a.getDimensionPixelOffset(
+                R.styleable.LayerDrawableItem_top, layer.mInsetT);
+        layer.mInsetR = a.getDimensionPixelOffset(
+                R.styleable.LayerDrawableItem_right, layer.mInsetR);
+        layer.mInsetB = a.getDimensionPixelOffset(
+                R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
+        layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
+
+        final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
+        if (dr != null) {
+            layer.mDrawable = dr;
         }
     }
 
@@ -231,19 +249,23 @@
             return;
         }
 
-        final int[] themeAttrs = state.mThemeAttrs;
-        if (themeAttrs != null) {
-            final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.LayerDrawable);
+        if (state.mThemeAttrs != null) {
+            final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.LayerDrawable);
             updateStateFromTypedArray(a);
             a.recycle();
         }
 
-        // TODO: Update layer positions from cached typed arrays.
-
         final ChildDrawable[] array = mLayerState.mChildren;
         final int N = mLayerState.mNum;
         for (int i = 0; i < N; i++) {
             final ChildDrawable layer = array[i];
+            if (layer.mThemeAttrs != null) {
+                final TypedArray a = t.resolveAttributes(layer.mThemeAttrs,
+                        R.styleable.LayerDrawableItem);
+                updateLayerFromTypedArray(layer, a);
+                a.recycle();
+            }
+
             final Drawable d = layer.mDrawable;
             if (d.canApplyTheme()) {
                 d.applyTheme(t);
@@ -297,19 +319,7 @@
         return false;
     }
 
-    /**
-     * Add a new layer to this drawable. The new layer is identified by an id.
-     *
-     * @param layer The drawable to add as a layer.
-     * @param themeAttrs Theme attributes extracted from the layer.
-     * @param id The id of the new layer.
-     * @param left The left padding of the new layer.
-     * @param top The top padding of the new layer.
-     * @param right The right padding of the new layer.
-     * @param bottom The bottom padding of the new layer.
-     */
-    void addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
-            int bottom) {
+    void addLayer(ChildDrawable layer) {
         final LayerState st = mLayerState;
         final int N = st.mChildren != null ? st.mChildren.length : 0;
         final int i = st.mNum;
@@ -322,10 +332,25 @@
             st.mChildren = nu;
         }
 
-        mLayerState.mChildrenChangingConfigurations |= layer.getChangingConfigurations();
+        st.mChildren[i] = layer;
+        st.mNum++;
+        st.invalidateCache();
+    }
 
+    /**
+     * Add a new layer to this drawable. The new layer is identified by an id.
+     *
+     * @param layer The drawable to add as a layer.
+     * @param themeAttrs Theme attributes extracted from the layer.
+     * @param id The id of the new layer.
+     * @param left The left padding of the new layer.
+     * @param top The top padding of the new layer.
+     * @param right The right padding of the new layer.
+     * @param bottom The bottom padding of the new layer.
+     */
+    ChildDrawable addLayer(Drawable layer, int[] themeAttrs, int id, int left, int top, int right,
+            int bottom) {
         final ChildDrawable childDrawable = new ChildDrawable();
-        st.mChildren[i] = childDrawable;
         childDrawable.mId = id;
         childDrawable.mThemeAttrs = themeAttrs;
         childDrawable.mDrawable = layer;
@@ -334,10 +359,13 @@
         childDrawable.mInsetT = top;
         childDrawable.mInsetR = right;
         childDrawable.mInsetB = bottom;
-        st.mNum++;
-        st.invalidateCache();
 
+        addLayer(childDrawable);
+
+        mLayerState.mChildrenChangingConfigurations |= layer.getChangingConfigurations();
         layer.setCallback(this);
+
+        return childDrawable;
     }
 
     /**
@@ -566,13 +594,24 @@
     }
 
     /**
-     * Builds an Outline from the first child Drawable, if present.
+     * Populates <code>outline</code> with the first available layer outline.
+     * Returns <code>true</code> if an outline is available, <code>false</code>
+     * otherwise.
+     *
+     * @param outline Outline in which to place the first available layer outline
+     * @return <code>true</code> if an outline is available
      */
     @Override
     public boolean getOutline(@NonNull Outline outline) {
-        if (mLayerState.mNum < 1) return false;
-        final Drawable firstChild = mLayerState.mChildren[0].mDrawable;
-        return firstChild.getOutline(outline);
+        final LayerState state = mLayerState;
+        final ChildDrawable[] children = state.mChildren;
+        final int N = state.mNum;
+        for (int i = 0; i < N; i++) {
+            if (children[i].mDrawable.getOutline(outline)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override
@@ -893,7 +932,7 @@
         public Drawable mDrawable;
         public int[] mThemeAttrs;
         public int mInsetL, mInsetT, mInsetR, mInsetB;
-        public int mId;
+        public int mId = View.NO_ID;
 
         ChildDrawable() {
             // Default empty constructor.
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 6642bdd..24bbf7c 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -382,9 +382,11 @@
         final Resources r = a.getResources();
         final NinePatchState state = mNinePatchState;
 
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
-        final int[] themeAttrs = a.extractThemeAttrs();
-        state.mThemeAttrs = themeAttrs;
+        state.mThemeAttrs = a.extractThemeAttrs();
 
         state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, state.mDither);
 
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index be8df59..87f5989 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -25,6 +25,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
@@ -357,6 +358,9 @@
     private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
         final RippleState state = mState;
 
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         state.mTouchThemeAttrs = a.extractThemeAttrs();
 
@@ -497,8 +501,29 @@
         }
     }
 
+    /**
+     * Populates <code>outline</code> with the first available layer outline,
+     * excluding the mask layer. Returns <code>true</code> if an outline is
+     * available, <code>false</code> otherwise.
+     *
+     * @param outline Outline in which to place the first available layer outline
+     * @return <code>true</code> if an outline is available
+     */
     @Override
-    public void draw(Canvas canvas) {
+    public boolean getOutline(@NonNull Outline outline) {
+        final LayerState state = mLayerState;
+        final ChildDrawable[] children = state.mChildren;
+        final int N = state.mNum;
+        for (int i = 0; i < N; i++) {
+            if (children[i].mId != R.id.mask && children[i].mDrawable.getOutline(outline)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
         final boolean isProjected = isProjected();
         final boolean hasMask = mMask != null;
         final boolean drawNonMaskContent = mLayerState.mNum > (hasMask ? 1 : 0);
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 86765dd..9802529 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -422,6 +422,9 @@
         final ShapeState state = mShapeState;
         final Paint paint = state.mPaint;
 
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         state.mThemeAttrs = a.extractThemeAttrs();
 
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index b37204d..150f210 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -44,7 +44,6 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Stack;
 
 /**
@@ -308,6 +307,9 @@
     private void updateStateFromTypedArray(TypedArray a) {
         final VectorDrawableState state = mVectorState;
 
+        // Account for any configuration changes.
+        state.mChangingConfigurations |= a.getChangingConfigurations();
+
         // Extract the theme attributes, if any.
         state.mThemeAttrs = a.extractThemeAttrs();
 
@@ -322,8 +324,9 @@
         }
     }
 
-    private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
-            throws XmlPullParserException, IOException {
+    private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
+            Theme theme) throws XmlPullParserException, IOException {
+        final VectorDrawableState state = mVectorState;
         final VPathRenderer pathRenderer = new VPathRenderer();
 
         boolean noSizeTag = true;
@@ -349,12 +352,15 @@
                         mVGTargetsMap.put(path.getPathName(), path);
                     }
                     noPathTag = false;
+                    state.mChangingConfigurations |= path.mChangingConfigurations;
                 } else if (SHAPE_SIZE.equals(tagName)) {
                     pathRenderer.parseSize(res, attrs);
                     noSizeTag = false;
+                    state.mChangingConfigurations |= pathRenderer.mChangingConfigurations;
                 } else if (SHAPE_VIEWPORT.equals(tagName)) {
                     pathRenderer.parseViewport(res, attrs);
                     noViewportTag = false;
+                    state.mChangingConfigurations |= pathRenderer.mChangingConfigurations;
                 } else if (SHAPE_GROUP.equals(tagName)) {
                     VGroup newChildGroup = new VGroup();
                     newChildGroup.inflate(res, attrs, theme);
@@ -363,6 +369,7 @@
                     if (newChildGroup.getGroupName() != null) {
                         mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup);
                     }
+                    state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
                 }
             } else if (eventType == XmlPullParser.END_TAG) {
                 final String tagName = parser.getName();
@@ -483,6 +490,8 @@
         private ColorFilter mColorFilter;
         private PathMeasure mPathMeasure;
 
+        private int mChangingConfigurations;
+
         private float mBaseWidth = 0;
         private float mBaseHeight = 0;
         private float mViewportWidth = 0;
@@ -509,6 +518,7 @@
             mBaseHeight = copy.mBaseHeight;
             mViewportWidth = copy.mViewportHeight;
             mViewportHeight = copy.mViewportHeight;
+            mChangingConfigurations = copy.mChangingConfigurations;
         }
 
         public boolean canApplyTheme() {
@@ -609,7 +619,8 @@
 
             mFinalPathMatrix.set(vGroup.mStackedMatrix);
             mFinalPathMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
-            mFinalPathMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
+            mFinalPathMatrix.postTranslate(
+                    w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
 
             ArrayList<VPath> paths = vGroup.getPaths();
             for (int i = 0; i < paths.size(); i++) {
@@ -687,8 +698,14 @@
         private void parseViewport(Resources r, AttributeSet attrs)
                 throws XmlPullParserException {
             final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableViewport);
-            mViewportWidth = a.getFloat(R.styleable.VectorDrawableViewport_viewportWidth, mViewportWidth);
-            mViewportHeight = a.getFloat(R.styleable.VectorDrawableViewport_viewportHeight, mViewportHeight);
+
+            // Account for any configuration changes.
+            mChangingConfigurations |= a.getChangingConfigurations();
+
+            mViewportWidth = a.getFloat(
+                    R.styleable.VectorDrawableViewport_viewportWidth, mViewportWidth);
+            mViewportHeight = a.getFloat(
+                    R.styleable.VectorDrawableViewport_viewportHeight, mViewportHeight);
 
             if (mViewportWidth <= 0) {
                 throw new XmlPullParserException(a.getPositionDescription() +
@@ -704,6 +721,10 @@
         private void parseSize(Resources r, AttributeSet attrs)
                 throws XmlPullParserException  {
             final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableSize);
+
+            // Account for any configuration changes.
+            mChangingConfigurations |= a.getChangingConfigurations();
+
             mBaseWidth = a.getDimension(R.styleable.VectorDrawableSize_width, mBaseWidth);
             mBaseHeight = a.getDimension(R.styleable.VectorDrawableSize_height, mBaseHeight);
 
@@ -739,6 +760,7 @@
         // parents' local matrices with the current one.
         private final Matrix mStackedMatrix = new Matrix();
 
+        private int mChangingConfigurations;
         private int[] mThemeAttrs;
 
         private String mGroupName = null;
@@ -847,13 +869,19 @@
             return mThemeAttrs != null;
         }
 
-        public void applyTheme(Theme t) {
-            if (mThemeAttrs == null) {
-                return;
-            }
+        public void inflate(Resources res, AttributeSet attrs, Theme theme) {
+            final TypedArray a = obtainAttributes(res, theme, attrs,
+                    R.styleable.VectorDrawableGroup);
+            updateStateFromTypedArray(a);
+            a.recycle();
+        }
 
-            final TypedArray a = t.resolveAttributes(
-                    mThemeAttrs, R.styleable.VectorDrawablePath);
+        private void updateStateFromTypedArray(TypedArray a) {
+            // Account for any configuration changes.
+            mChangingConfigurations |= a.getChangingConfigurations();
+
+            // Extract the theme attributes, if any.
+            mThemeAttrs = a.extractThemeAttrs();
 
             mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate);
             mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX);
@@ -863,58 +891,22 @@
             mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
             mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
             mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
-            updateLocalMatrix();
-            if (a.hasValue(R.styleable.VectorDrawableGroup_name)) {
-                mGroupName = a.getString(R.styleable.VectorDrawableGroup_name);
+
+            final String groupName = a.getString(R.styleable.VectorDrawableGroup_name);
+            if (groupName != null) {
+                mGroupName = groupName;
             }
-            a.recycle();
+
+            updateLocalMatrix();
         }
 
-        public void inflate(Resources res, AttributeSet attrs, Theme theme) {
-            final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawableGroup);
-            final int[] themeAttrs = a.extractThemeAttrs();
-
-            mThemeAttrs = themeAttrs;
-            // NOTE: The set of attributes loaded here MUST match the
-            // set of attributes loaded in applyTheme.
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_rotation] == 0) {
-                mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate);
+        public void applyTheme(Theme t) {
+            if (mThemeAttrs == null) {
+                return;
             }
 
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotX] == 0) {
-                mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotY] == 0) {
-                mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleX] == 0) {
-                mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleY] == 0) {
-                mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateX] == 0) {
-                mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateY] == 0) {
-                mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) {
-                mGroupName = a.getString(R.styleable.VectorDrawableGroup_name);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) {
-                mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
-            }
-
-            updateLocalMatrix();
+            final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
+            updateStateFromTypedArray(a);
             a.recycle();
         }
 
@@ -939,6 +931,7 @@
     }
 
     private static class VPath {
+        private int mChangingConfigurations;
         private int[] mThemeAttrs;
 
         int mStrokeColor = 0;
@@ -1096,113 +1089,29 @@
             mTrimPathOffset = trimPathOffset;
         }
 
-        public void inflate(Resources r, AttributeSet attrs, Theme theme) {
-            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath);
-            final int[] themeAttrs = a.extractThemeAttrs();
-            mThemeAttrs = themeAttrs;
-
-            // NOTE: The set of attributes loaded here MUST match the
-            // set of attributes loaded in applyTheme.
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_clipToPath] == 0) {
-                mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_name] == 0) {
-                mPathName = a.getString(R.styleable.VectorDrawablePath_name);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pathData] == 0) {
-                mNode = PathParser.createNodesFromPathData(a.getString(
-                        R.styleable.VectorDrawablePath_pathData));
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fill] == 0) {
-                mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fillOpacity] == 0) {
-                mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity);
-            }
-
-            if (themeAttrs == null
-                    || themeAttrs[R.styleable.VectorDrawablePath_strokeLineCap] == 0) {
-                mStrokeLineCap = getStrokeLineCap(
-                        a.getInt(R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
-            }
-
-            if (themeAttrs == null
-                    || themeAttrs[R.styleable.VectorDrawablePath_strokeLineJoin] == 0) {
-                mStrokeLineJoin = getStrokeLineJoin(
-                        a.getInt(R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin);
-            }
-
-            if (themeAttrs == null
-                    || themeAttrs[R.styleable.VectorDrawablePath_strokeMiterLimit] == 0) {
-                mStrokeMiterlimit = a.getFloat(
-                        R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_stroke] == 0) {
-                mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor);
-            }
-
-            if (themeAttrs == null
-                    || themeAttrs[R.styleable.VectorDrawablePath_strokeOpacity] == 0) {
-                mStrokeOpacity = a.getFloat(
-                        R.styleable.VectorDrawablePath_strokeOpacity, mStrokeOpacity);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_strokeWidth] == 0) {
-                mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth);
-            }
-
-            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_trimPathEnd] == 0) {
-                mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd);
-            }
-
-            if (themeAttrs == null
-                    || themeAttrs[R.styleable.VectorDrawablePath_trimPathOffset] == 0) {
-                mTrimPathOffset = a.getFloat(
-                        R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
-            }
-
-            if (themeAttrs == null
-                    || themeAttrs[R.styleable.VectorDrawablePath_trimPathStart] == 0) {
-                mTrimPathStart = a.getFloat(
-                        R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
-            }
-
-            updateColorAlphas();
-
-            a.recycle();
-        }
-
         public boolean canApplyTheme() {
             return mThemeAttrs != null;
         }
 
-        public void applyTheme(Theme t) {
-            if (mThemeAttrs == null) {
-                return;
-            }
+        public void inflate(Resources r, AttributeSet attrs, Theme theme) {
+            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath);
+            updateStateFromTypedArray(a);
+            a.recycle();
+        }
 
-            final TypedArray a = t.resolveAttributes(
-                    mThemeAttrs, R.styleable.VectorDrawablePath);
+        private void updateStateFromTypedArray(TypedArray a) {
+            // Account for any configuration changes.
+            mChangingConfigurations |= a.getChangingConfigurations();
+
+            // Extract the theme attributes, if any.
+            mThemeAttrs = a.extractThemeAttrs();
 
             mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip);
-
-            if (a.hasValue(R.styleable.VectorDrawablePath_name)) {
-                mPathName = a.getString(R.styleable.VectorDrawablePath_name);
-            }
-
-            if (a.hasValue(R.styleable.VectorDrawablePath_pathData)) {
-                mNode = PathParser.createNodesFromPathData(a.getString(
-                        R.styleable.VectorDrawablePath_pathData));
-            }
-
+            mPathName = a.getString(R.styleable.VectorDrawablePath_name);
+            mNode = PathParser.createNodesFromPathData(a.getString(
+                    R.styleable.VectorDrawablePath_pathData));
             mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor);
             mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity);
-
             mStrokeLineCap = getStrokeLineCap(a.getInt(
                     R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
             mStrokeLineJoin = getStrokeLineJoin(a.getInt(
@@ -1213,7 +1122,6 @@
             mStrokeOpacity = a.getFloat(
                     R.styleable.VectorDrawablePath_strokeOpacity, mStrokeOpacity);
             mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth);
-
             mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd);
             mTrimPathOffset = a.getFloat(
                     R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
@@ -1221,6 +1129,15 @@
                     R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
 
             updateColorAlphas();
+        }
+
+        public void applyTheme(Theme t) {
+            if (mThemeAttrs == null) {
+                return;
+            }
+
+            final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
+            updateStateFromTypedArray(a);
             a.recycle();
         }
 
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 5e7f899..024bdfd 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -30,7 +30,7 @@
             , mType(kOutlineType_None)
             , mRadius(0) {}
 
-    void setRoundRect(int left, int top, int right, int bottom, int radius) {
+    void setRoundRect(int left, int top, int right, int bottom, float radius) {
         mType = kOutlineType_RoundRect;
         mBounds.set(left, top, right, bottom);
         mRadius = radius;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 89105ea..3cf625f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -211,7 +211,8 @@
         // This will also release the hardware layer if we have one as
         // isRenderable() will return false, thus causing pushLayerUpdate
         // to recycle the hardware layer
-        setStagingDisplayList(NULL);
+        LOG_ALWAYS_FATAL_IF(mStagingDisplayListData || (mDisplayListData && !mNeedsDisplayListDataSync),
+                "View.destroyHardwareResources wasn't called!");
         break;
     }
 
@@ -260,7 +261,11 @@
         mNeedsDisplayListDataSync = false;
         // Do a push pass on the old tree to handle freeing DisplayListData
         // that are no longer used
-        TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING, info);
+        TreeInfo::TraversalMode mode = TreeInfo::MODE_MAYBE_DETACHING;
+        if (CC_UNLIKELY(info.mode == TreeInfo::MODE_DESTROY_RESOURCES)) {
+            mode = TreeInfo::MODE_DESTROY_RESOURCES;
+        }
+        TreeInfo oldTreeInfo(mode, info);
         prepareSubTree(oldTreeInfo, mDisplayListData);
         delete mDisplayListData;
         mDisplayListData = mStagingDisplayListData;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 153ee31..f262390 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -109,12 +109,12 @@
     private static final String TAG = "AudioService";
 
     /** Debug remote control client/display feature */
-    protected static final boolean DEBUG_RC = false;
+    protected static final boolean DEBUG_RC = Log.isLoggable(TAG + ".RC", Log.DEBUG);
     /** Debug volumes */
-    protected static final boolean DEBUG_VOL = false;
+    protected static final boolean DEBUG_VOL = Log.isLoggable(TAG + ".VOL", Log.DEBUG);
 
     /** debug calls to media session apis */
-    private static final boolean DEBUG_SESSIONS = true;
+    private static final boolean DEBUG_SESSIONS = Log.isLoggable(TAG + ".SESSIONS", Log.DEBUG);
 
     /** Allow volume changes to set ringer mode to silent? */
     private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;
@@ -841,7 +841,8 @@
     /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage) {
-        if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
+        if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType
+                + ", flags=" + flags);
         int streamType;
         if (mVolumeControlStream != -1) {
             streamType = mVolumeControlStream;
@@ -871,7 +872,8 @@
         if (mUseFixedVolume) {
             return;
         }
-        if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
+        if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction
+                + ", flags="+flags);
 
         ensureValidDirection(direction);
         ensureValidStreamType(streamType);
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 011da35..2854007 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.media.tv.ITvInputSession;
+import android.net.Uri;
 import android.os.Bundle;
 import android.view.InputChannel;
 
@@ -34,4 +35,5 @@
     void onVideoStreamChanged(int width, int height, boolean interlaced, int seq);
     void onAudioStreamChanged(int channelCount, int seq);
     void onClosedCaptionStreamChanged(boolean hasClosedCaption, int seq);
+    void onChannelRetuned(in Uri channelUri, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 00f2922..5a57ccd 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -17,6 +17,7 @@
 package android.media.tv;
 
 import android.media.tv.ITvInputSession;
+import android.net.Uri;
 import android.os.Bundle;
 
 /**
@@ -30,4 +31,5 @@
     void onVideoStreamChanged(int width, int height, boolean interlaced);
     void onAudioStreamChanged(int channelCount);
     void onClosedCaptionStreamChanged(boolean hasClosedCaption);
+    void onChannelRetuned(in Uri channelUri);
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index daa7009..834ce64 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -125,6 +125,16 @@
         }
 
         /**
+         * This is called when the channel of this session is changed by the underlying TV input
+         * with out any {@link TvInputManager.Session#tune(Uri)} request.
+         *
+         * @param session A {@link TvInputManager.Session} associated with this callback
+         * @param channelUri The URI of a channel.
+         */
+        public void onChannelRetuned(Session session, Uri channelUri) {
+        }
+
+        /**
          * This is called when a custom event has been sent from this session.
          *
          * @param session A {@link TvInputManager.Session} associated with this callback
@@ -194,6 +204,15 @@
             });
         }
 
+        public void postChannelRetuned(final Uri channelUri) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onChannelRetuned(mSession, channelUri);
+                }
+            });
+        }
+
         public void postSessionEvent(final String eventType, final Bundle eventArgs) {
             mHandler.post(new Runnable() {
                 @Override
@@ -318,6 +337,18 @@
             }
 
             @Override
+            public void onChannelRetuned(Uri channelUri, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postChannelRetuned(channelUri);
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 0f4a930..1e512cd 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -286,6 +286,25 @@
         }
 
         /**
+         * Notifies the channel of the session is retuned by TV input.
+         *
+         * @param channelUri The URI of a channel.
+         */
+        public void dispatchChannelRetuned(final Uri channelUri) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "dispatchChannelRetuned");
+                        mSessionCallback.onChannelRetuned(channelUri);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in dispatchChannelRetuned");
+                    }
+                }
+            });
+        }
+
+        /**
          * Called when the session is released.
          */
         public abstract void onRelease();
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index a913e59c..664a215 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -456,6 +456,16 @@
         }
 
         /**
+         * This is invoked when the channel of this TvView is changed by the underlying TV input
+         * with out any {@link TvView#tune(String, Uri)} request.
+         *
+         * @param inputId The ID of the TV input bound to this view.
+         * @param channelUri The URI of a channel.
+         */
+        public void onChannelRetuned(String inputId, Uri channelUri) {
+        }
+
+        /**
          * This is invoked when a custom event from the bound TV input is sent to this view.
          *
          * @param eventType The type of the event.
@@ -562,6 +572,16 @@
         }
 
         @Override
+        public void onChannelRetuned(Session session, Uri channelUri) {
+            if (DEBUG) {
+                Log.d(TAG, "onChannelChangedByTvInput(" + channelUri + ")");
+            }
+            if (mListener != null) {
+                mListener.onChannelRetuned(mInputId, channelUri);
+            }
+        }
+
+        @Override
         public void onSessionEvent(TvInputManager.Session session, String eventType,
                 Bundle eventArgs) {
             if (mListener != null) {
diff --git a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
index 7537e35..de6294d 100644
--- a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
+++ b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
@@ -52,11 +52,11 @@
 int testDecodeBounds(SkImageDecoder* decoder, SkStream* stream,
         SkBitmap* bitmap) {
     int64_t startTime = getNowUs();
-    SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+    SkColorType prefColorType = kN32_SkColorType;
     SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodeBounds_Mode;
 
     // Decode the input stream and then use the bitmap.
-    if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
+    if (!decoder->decode(stream, bitmap, prefColorType, decodeMode)) {
         return nullObjectReturn("decoder->decode returned false");
     } else {
         int64_t delay = getNowUs() - startTime;
@@ -69,11 +69,11 @@
 int testDecodePixels(SkImageDecoder* decoder, SkStream* stream,
         SkBitmap* bitmap) {
     int64_t startTime = getNowUs();
-    SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+    SkColorType prefColorType = kN32_SkColorType;
     SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
 
     // Decode the input stream and then use the bitmap.
-    if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
+    if (!decoder->decode(stream, bitmap, prefColorType, decodeMode)) {
         return nullObjectReturn("decoder->decode returned false");
     } else {
         int64_t delay = getNowUs() - startTime;
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 7a21b92..67ed97c 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -499,19 +499,26 @@
 
     private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
             throws IOException, RemoteException {
-        // TODO: extend to support copying all split APKs
+        copyFile(pkg.baseCodePath, "base.apk", target);
         if (!ArrayUtils.isEmpty(pkg.splitNames)) {
-            throw new UnsupportedOperationException("Copying split APKs not yet supported");
+            for (int i = 0; i < pkg.splitNames.length; i++) {
+                copyFile(pkg.splitCodePaths[i], "split_" + pkg.splitNames[i] + ".apk", target);
+            }
         }
 
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+
+    private void copyFile(String sourcePath, String targetName,
+            IParcelFileDescriptorFactory target) throws IOException, RemoteException {
+        Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
         InputStream in = null;
         OutputStream out = null;
         try {
-            in = new FileInputStream(pkg.baseCodePath);
+            in = new FileInputStream(sourcePath);
             out = new ParcelFileDescriptor.AutoCloseOutputStream(
-                    target.open(null, ParcelFileDescriptor.MODE_READ_WRITE));
+                    target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
             Streams.copy(in, out);
-            return PackageManager.INSTALL_SUCCEEDED;
         } finally {
             IoUtils.closeQuietly(out);
             IoUtils.closeQuietly(in);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index 737e49b..276f795 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -67,7 +67,7 @@
     // Whether the volume keys should be handled by keyguard. If true, then
     // they will be handled here for specific media types such as music, otherwise
     // the audio service will bring up the volume dialog.
-    private static final boolean KEYGUARD_MANAGES_VOLUME = true;
+    private static final boolean KEYGUARD_MANAGES_VOLUME = false;
     public static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "KeyguardViewBase";
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 158227f..e6c6f9f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -18,7 +18,9 @@
 
 import java.io.FileNotFoundException;
 import java.security.SecureRandom;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -112,6 +114,9 @@
     static final HashSet<String> sSecureGlobalKeys;
     static final HashSet<String> sSystemGlobalKeys;
 
+    // Settings that cannot be modified if associated user restrictions are enabled.
+    static final Map<String, String> sRestrictedKeys;
+
     private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
 
     static {
@@ -125,6 +130,18 @@
         // These must match Settings.System.MOVED_TO_GLOBAL
         sSystemGlobalKeys = new HashSet<String>();
         Settings.System.getNonLegacyMovedKeys(sSystemGlobalKeys);
+
+        sRestrictedKeys = new HashMap<String, String>();
+        sRestrictedKeys.put(Settings.Secure.LOCATION_MODE, UserManager.DISALLOW_SHARE_LOCATION);
+        sRestrictedKeys.put(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                UserManager.DISALLOW_SHARE_LOCATION);
+        sRestrictedKeys.put(Settings.Global.INSTALL_NON_MARKET_APPS,
+                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+        sRestrictedKeys.put(Settings.Global.ADB_ENABLED, UserManager.DISALLOW_DEBUGGING_FEATURES);
+        sRestrictedKeys.put(Settings.Global.PACKAGE_VERIFIER_ENABLE,
+                UserManager.ENSURE_VERIFY_APPS);
+        sRestrictedKeys.put(Settings.Global.PREFERRED_NETWORK_MODE,
+                UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
     }
 
     private boolean settingMovedToGlobal(final String name) {
@@ -285,6 +302,15 @@
         }
     }
 
+    private void checkUserRestrictions(String setting) {
+        String userRestriction = sRestrictedKeys.get(setting);
+        if (!TextUtils.isEmpty(userRestriction)
+            && mUserManager.hasUserRestriction(userRestriction)) {
+            throw new SecurityException(
+                    "Permission denial: user is restricted from changing this setting.");
+        }
+    }
+
     // FileObserver for external modifications to the database file.
     // Note that this is for platform developers only with
     // userdebug/eng builds who should be able to tinker with the
@@ -783,6 +809,7 @@
         try {
             int numValues = values.length;
             for (int i = 0; i < numValues; i++) {
+                checkUserRestrictions(values[i].getAsString(Settings.Secure.NAME));
                 if (db.insert(args.table, null, values[i]) < 0) return 0;
                 SettingsCache.populate(cache, values[i]);
                 if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + values[i]);
@@ -913,6 +940,8 @@
         // Check write permissions only after determining which table the insert will touch
         checkWritePermissions(args);
 
+        checkUserRestrictions(name);
+
         // The global table is stored under the owner, always
         if (TABLE_GLOBAL.equals(args.table)) {
             desiredUserHandle = UserHandle.USER_OWNER;
@@ -987,6 +1016,7 @@
             callingUser = UserHandle.USER_OWNER;
         }
         checkWritePermissions(args);
+        checkUserRestrictions(initialValues.getAsString(Settings.Secure.NAME));
 
         final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
         mutationCount.incrementAndGet();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ad27e41..23a4722 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -189,8 +189,6 @@
             </intent-filter>
         </activity>
 
-        <service android:name=".recents.RecentsService" />
-
         <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbConfirmActivity"
             android:exported="true"
diff --git a/packages/SystemUI/res/color/segmented_button_text_selector.xml b/packages/SystemUI/res/color/segmented_button_text_selector.xml
new file mode 100644
index 0000000..13c6169
--- /dev/null
+++ b/packages/SystemUI/res/color/segmented_button_text_selector.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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. ?android:attr/colorControlHighlight"
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_selected="true" android:color="@android:color/white"/>
+    <item android:color="@color/segmented_button_text_inactive"/>
+
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_minus.xml b/packages/SystemUI/res/drawable/ic_qs_minus.xml
index 376cc28..7b76e0f 100644
--- a/packages/SystemUI/res/drawable/ic_qs_minus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_minus.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="24dp"
+        android:height="24dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M7.0,11.0l0.0,2.0l10.0,0.0l0.0,-2.0L7.0,11.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/>
+        android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0L14.0,26.0l0.0,-4.0l20.0,0.0L34.0,26.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_plus.xml b/packages/SystemUI/res/drawable/ic_qs_plus.xml
index a315f7f..4b9f506 100644
--- a/packages/SystemUI/res/drawable/ic_qs_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_plus.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="24dp"
+        android:height="24dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M13.0,7.0l-2.0,0.0l0.0,4.0L7.0,11.0l0.0,2.0l4.0,0.0l0.0,4.0l2.0,0.0l0.0,-4.0l4.0,0.0l0.0,-2.0l-4.0,0.0L13.0,7.0zM12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.0c-4.4,0.0 -8.0,-3.6 -8.0,-8.0s3.6,-8.0 8.0,-8.0c4.4,0.0 8.0,3.6 8.0,8.0S16.4,20.0 12.0,20.0z"/>
+        android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0l-8.0,0.0l0.0,8.0l-4.0,0.0l0.0,-8.0l-8.0,0.0l0.0,-4.0l8.0,0.0l0.0,-8.0l4.0,0.0l0.0,8.0l8.0,0.0L34.0,26.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_silent.xml b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
index b5837f6..46b94e8 100644
--- a/packages/SystemUI/res/drawable/ic_ringer_silent.xml
+++ b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
@@ -23,6 +23,6 @@
         android:viewportHeight="24.0"/>
 
     <path
-        android:fill="#FFFFFFFF"
+        android:fill="@color/qs_subhead"
         android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
deleted file mode 100644
index 477c36b..0000000
--- a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="32dp"
-        android:height="32dp"/>
-
-    <viewport
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0"/>
-
-    <path
-        android:fill="#4DFFFFFF"
-        android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
deleted file mode 100644
index 0a43a7b..0000000
--- a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="32dp"
-        android:height="32dp"/>
-
-    <viewport
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml
index 5daec20..1f46502 100644
--- a/packages/SystemUI/res/drawable/notification_header_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_header_bg.xml
@@ -15,15 +15,7 @@
   ~ limitations under the License
   -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true">
-        <shape>
-            <solid android:color="@color/background_color_1_press" />
-        </shape>
-    </item>
-    <item>
-        <shape>
-            <solid android:color="@color/background_color_1" />
-        </shape>
-    </item>
-</selector>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight" >
+    <item android:drawable="@color/system_secondary_color"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml b/packages/SystemUI/res/drawable/qs_subhead_caret.xml
similarity index 65%
rename from packages/SystemUI/res/drawable/ic_qs_zen_on.xml
rename to packages/SystemUI/res/drawable/qs_subhead_caret.xml
index 4887b32..f140bd0 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
+++ b/packages/SystemUI/res/drawable/qs_subhead_caret.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="24.0dp"
+        android:height="24.0dp"/>
 
     <viewport
         android:viewportWidth="48.0"
         android:viewportHeight="48.0"/>
 
     <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
+        android:fill="@color/qs_subhead"
+        android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/segmented_button.xml b/packages/SystemUI/res/drawable/segmented_button.xml
new file mode 100644
index 0000000..9669985
--- /dev/null
+++ b/packages/SystemUI/res/drawable/segmented_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/segmented_button_selected" />
+            <corners android:radius="@dimen/segmented_button_radius" />
+        </shape>
+    </item>
+    <item>
+        <ripple android:color="@color/segmented_button_selected" />
+    </item>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/segmented_buttons.xml b/packages/SystemUI/res/drawable/segmented_buttons.xml
new file mode 100644
index 0000000..bfbac97
--- /dev/null
+++ b/packages/SystemUI/res/drawable/segmented_buttons.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+    <solid android:color="@color/segmented_button_background" />
+    <corners android:radius="@dimen/segmented_button_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
deleted file mode 100644
index 5992470..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_ringer_zen.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="18dp"
-        android:height="18dp"/>
-
-    <viewport
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml b/packages/SystemUI/res/drawable/stat_sys_zen_important.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/ic_qs_zen_on.xml
copy to packages/SystemUI/res/drawable/stat_sys_zen_important.xml
index 4887b32..54a9b1b 100644
--- a/packages/SystemUI/res/drawable/ic_qs_zen_on.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_zen_important.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="20dp"
+        android:height="20dp"/>
 
     <viewport
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0"/>
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
+        android:pathData="M12.0,17.273l6.1800003,3.7269993 -1.6350002,-7.0290003 5.455,-4.7269993 -7.191,-0.6170006 -2.809,-6.627 -2.809,6.627 -7.191,0.6170006 5.455,4.7269993 -1.6349998,7.0290003z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout/segmented_button.xml b/packages/SystemUI/res/layout/segmented_button.xml
new file mode 100644
index 0000000..ef78220
--- /dev/null
+++ b/packages/SystemUI/res/layout/segmented_button.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_marginStart="@dimen/segmented_button_spacing"
+    android:layout_weight="1"
+    android:textColor="@color/segmented_button_text_selector"
+    android:background="@drawable/segmented_button"
+    android:textAppearance="@style/TextAppearance.QS.SegmentedButton"
+    android:minHeight="0dp"
+    android:padding="12dp" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 70589b7..7d674f8 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -26,72 +26,68 @@
     android:paddingEnd="@dimen/notification_side_padding"
     android:baselineAligned="false"
     android:elevation="4dp"
+    android:background="@drawable/notification_header_bg"
+    android:clickable="true"
+    android:focusable="true"
     >
 
-    <View
-        android:id="@+id/background"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@drawable/notification_header_bg"
-        android:clickable="true"
-        />
-
     <View android:id="@+id/header_spacer"
         android:layout_height="8dp"
         android:layout_width="0dp" />
 
-    <TextView
-        android:id="@+id/header_emergency_calls_only"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:layout_below="@id/header_spacer"
-        android:paddingTop="12dp"
-        android:paddingStart="16dp"
-        android:paddingEnd="16dp"
-        android:visibility="gone"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
-        android:text="@*android:string/emergency_calls_only" />
-
     <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
         android:layout_width="40dp"
         android:layout_height="@dimen/status_bar_header_height"
         android:layout_alignParentEnd="true"
-        android:background="@null"
+        android:background="@drawable/ripple_drawable"
         android:scaleType="centerInside"
-        android:padding="8dp" />
+        android:padding="8dp"/>
 
     <ImageButton android:id="@+id/settings_button"
         style="@android:style/Widget.Material.Button.Borderless"
         android:layout_toStartOf="@id/multi_user_switch"
         android:layout_width="48dp"
         android:layout_height="@dimen/status_bar_header_height"
-        android:layout_marginStart="8dp"
+        android:layout_marginRight="4dp"
+        android:background="@drawable/ripple_drawable"
         android:src="@drawable/ic_settings_24dp"
         android:contentDescription="@string/accessibility_desc_quick_settings"/>
 
-    <FrameLayout android:id="@+id/system_icons_super_container"
+    <LinearLayout android:id="@+id/system_icons_super_container"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/status_bar_header_height"
         android:layout_toStartOf="@id/multi_user_switch"
         android:layout_alignWithParentIfMissing="true"
         android:layout_marginStart="16dp"
+        android:background="@drawable/ripple_drawable"
         android:paddingEnd="2dp">
         <FrameLayout android:id="@+id/system_icons_container"
             android:layout_width="wrap_content"
             android:layout_height="@dimen/status_bar_height"
             android:layout_gravity="center_vertical"/>
-    </FrameLayout>
+        <TextView android:id="@+id/battery_level"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="8dp"
+            android:paddingEnd="4dp"
+            android:textColor="#ffffff"
+            android:textSize="12sp"/>
+    </LinearLayout>
 
     <TextView
-        android:id="@+id/header_charging_info"
-        android:layout_width="wrap_content"
+        android:id="@+id/header_emergency_calls_only"
         android:layout_height="wrap_content"
-        android:layout_toEndOf="@id/system_icons_super_container"
+        android:layout_width="wrap_content"
         android:layout_below="@id/header_spacer"
+        android:layout_alignParentStart="true"
+        android:layout_toStartOf="@id/system_icons_super_container"
         android:paddingTop="12dp"
+        android:paddingStart="16dp"
         android:paddingEnd="16dp"
-        android:paddingStart="4dp"
-        style="@style/TextAppearance.StatusBar.Expanded.ChargingInfo"/>
+        android:visibility="gone"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
+        android:text="@*android:string/emergency_calls_only" />
 
     <RelativeLayout
         android:id="@+id/datetime"
@@ -103,7 +99,7 @@
         android:paddingBottom="16dp"
         android:paddingStart="16dp"
         android:paddingEnd="16dp"
-        android:background="@drawable/ic_notify_button_bg"
+        android:background="?android:attr/selectableItemBackground"
         android:enabled="false"
         >
         <com.android.systemui.statusbar.policy.Clock
@@ -128,29 +124,27 @@
         android:layout_width="match_parent"
         android:layout_height="@dimen/status_bar_header_height_keyguard"
         android:layout_marginLeft="16dp"
-        android:layout_toStartOf="@id/system_icons_container"
+        android:layout_toStartOf="@id/system_icons_super_container"
         android:gravity="center_vertical"
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="#ffffff"
         android:singleLine="true" />
 
-
-        <include
-            android:id="@+id/qs_detail_header"
-            layout="@layout/qs_detail_header"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            />
-        <include
-            android:id="@+id/brightness_container"
-            layout="@layout/quick_settings_brightness_dialog"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            />
-
+    <include
+        android:id="@+id/qs_detail_header"
+        layout="@layout/qs_detail_header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        />
+    <include
+        android:id="@+id/brightness_container"
+        layout="@layout/quick_settings_brightness_dialog"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        />
 
     <TextView
         android:id="@+id/header_debug_info"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 5afa967..7bd0aea 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -15,11 +15,11 @@
      limitations under the License.
 -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:layout_width="@dimen/volume_panel_width"
     android:layout_height="wrap_content"
     android:background="@drawable/qs_panel_background"
     android:translationZ="@dimen/volume_panel_z"
-    android:layout_margin="@dimen/volume_panel_z">
+    android:layout_marginBottom="@dimen/volume_panel_z">
 
         <include layout="@layout/volume_panel" />
 
diff --git a/packages/SystemUI/res/layout/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
index 046862f..b377a06 100644
--- a/packages/SystemUI/res/layout/volume_panel.xml
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -14,55 +14,23 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/visible_panel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal" >
+    android:orientation="vertical" >
 
     <FrameLayout
         android:id="@+id/slider_panel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="64dip"
-        android:layout_toLeftOf="@+id/expand_button_divider" />
+        android:clipChildren="false" />
 
-    <ImageView
-        android:id="@+id/expand_button_divider"
-        android:layout_width="wrap_content"
-        android:layout_height="32dip"
-        android:layout_gravity="top"
-        android:layout_marginBottom="16dip"
-        android:layout_marginTop="16dip"
-        android:layout_toLeftOf="@+id/expand_button"
-        android:scaleType="fitXY"
-        android:src="?android:attr/dividerVertical" />
-
-    <ImageView
-        android:id="@+id/expand_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_gravity="top"
-        style="@style/BorderlessButton.Tiny"
-        android:padding="16dip" />
-
-    <ImageView
-        android:id="@+id/zen_panel_divider"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@+id/slider_panel"
-        android:layout_marginLeft="16dip"
-        android:layout_marginRight="16dip"
-        android:scaleType="fitXY"
-        android:src="?android:attr/dividerHorizontal" />
-
-    <ViewStub
+     <ViewStub
         android:id="@+id/zen_panel_stub"
-        android:layout_below="@+id/zen_panel_divider"
-        android:inflatedId="@+id/zen_panel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:inflatedId="@+id/zen_panel"
         android:layout="@layout/zen_mode_panel" />
 
-</RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
index 6e5ab47..d91e2ab 100644
--- a/packages/SystemUI/res/layout/volume_panel_item.xml
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+     Copyright (C) 2011 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.
@@ -15,33 +16,29 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="80dip"
-        android:orientation="horizontal"
-        android:layout_marginTop="8dip"
-        android:layout_marginBottom="8dip"
-        android:gravity="start|center_vertical">
+    android:layout_width="match_parent"
+    android:clipChildren="false"
+    android:gravity="start|center_vertical"
+    android:orientation="horizontal" >
 
     <ImageView
-            android:id="@+id/stream_icon"
-            style="@style/BorderlessButton.Tiny"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:padding="16dip"
-            android:contentDescription="@null" />
-    <FrameLayout
-            android:id="@+id/seekbar_container"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1">
-        <SeekBar
-                android:id="@+id/seekbar"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="16dip"
-                android:paddingBottom="16dip"
-                android:paddingStart="11dip"
-                android:paddingEnd="11dip"
-                android:layout_marginEnd="16dip" />
-    </FrameLayout>
-</LinearLayout>
+        android:id="@+id/stream_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@null"
+        android:paddingBottom="12dip"
+        android:paddingLeft="16dip"
+        android:paddingRight="16dip"
+        android:paddingTop="12dip" />
+
+    <SeekBar
+        android:id="@+id/seekbar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingBottom="0dip"
+        android:paddingEnd="17dip"
+        android:paddingStart="0dip"
+        android:paddingTop="0dip" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
index 6ab8cf3..ad0d2dc 100644
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -16,22 +16,23 @@
 -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginLeft="@dimen/zen_mode_condition_detail_button_padding"
-    android:layout_marginRight="@dimen/zen_mode_condition_detail_button_padding" >
+    android:layout_height="@dimen/qs_detail_item_height"
+    android:layout_marginStart="@dimen/zen_mode_condition_detail_button_padding"
+    android:layout_marginEnd="@dimen/zen_mode_condition_detail_button_padding" >
 
     <RadioButton
         android:id="@android:id/checkbox"
         android:layout_width="40dp"
         android:layout_marginStart="2dp"
-        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_marginEnd="1dp"
+        android:layout_height="match_parent"
         android:layout_alignParentStart="true"
         android:gravity="center" />
 
     <TextView
         android:id="@android:id/title"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_height="match_parent"
         android:layout_toEndOf="@android:id/checkbox"
         android:layout_toStartOf="@android:id/button1"
         android:ellipsize="end"
@@ -42,24 +43,24 @@
     <ImageView
         android:id="@android:id/button1"
         style="@style/QSBorderlessButton"
-        android:layout_width="@dimen/zen_mode_condition_height"
-        android:layout_height="@dimen/zen_mode_condition_height"
-        android:layout_alignParentEnd="true"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         android:layout_centerVertical="true"
-        android:layout_marginEnd="@dimen/zen_mode_condition_height"
+        android:layout_marginEnd="@dimen/zen_mode_condition_detail_button_padding"
+        android:scaleType="center"
+        android:layout_toStartOf="@android:id/button2"
         android:contentDescription="@string/accessibility_quick_settings_less_time"
-        android:padding="@dimen/zen_mode_condition_detail_button_padding"
         android:src="@drawable/ic_qs_minus" />
 
     <ImageView
         android:id="@android:id/button2"
         style="@style/QSBorderlessButton"
-        android:layout_width="@dimen/zen_mode_condition_height"
-        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         android:layout_alignParentEnd="true"
+        android:scaleType="center"
         android:layout_centerVertical="true"
         android:contentDescription="@string/accessibility_quick_settings_more_time"
-        android:padding="@dimen/zen_mode_condition_detail_button_padding"
         android:src="@drawable/ic_qs_plus" />
 
 </RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 0420cbc..9d959fa 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -19,36 +19,75 @@
     android:id="@+id/zen_mode_panel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@color/system_primary_color"
+    android:background="@drawable/qs_panel_background"
+    android:clipChildren="false"
     android:orientation="vertical"
-    android:paddingTop="@dimen/qs_panel_padding" >
+    android:paddingBottom="@dimen/qs_panel_padding" >
 
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentStart="true"
-        android:layout_marginBottom="8dp"
-        android:layout_marginStart="@dimen/qs_panel_padding"
-        android:layout_marginEnd="@dimen/qs_panel_padding"
-        android:text="@string/zen_mode_title"
-        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
-
-    <LinearLayout
-        android:id="@android:id/content"
+    <com.android.systemui.volume.SegmentedButtons
+        android:id="@+id/zen_buttons"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical" />
+        android:layout_marginLeft="@dimen/qs_panel_padding"
+        android:layout_marginRight="@dimen/qs_panel_padding"
+        android:layout_marginTop="8dp"
+        android:background="@drawable/segmented_buttons"
+        android:clipChildren="false" />
+
+    <FrameLayout
+        android:id="@+id/zen_subhead"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="@dimen/qs_panel_padding"
+        android:paddingRight="@dimen/qs_panel_padding"
+        android:paddingTop="@dimen/qs_panel_padding" >
+
+        <TextView
+            android:id="@+id/zen_subhead_collapsed"
+            android:layout_width="wrap_content"
+            android:layout_height="32dp"
+            android:background="@drawable/btn_borderless_rect"
+            android:clickable="true"
+            android:drawableEnd="@drawable/qs_subhead_caret"
+            android:gravity="center_vertical"
+            android:textAppearance="@style/TextAppearance.QS.Subhead" />
+
+        <TextView
+            android:id="@+id/zen_subhead_expanded"
+            android:layout_width="wrap_content"
+            android:layout_height="32dp"
+            android:clickable="true"
+            android:gravity="center_vertical"
+            android:textAppearance="@style/TextAppearance.QS.Subhead" />
+
+        <ImageView
+            android:id="@+id/zen_more_settings"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_gravity="end"
+            android:background="@drawable/btn_borderless_rect"
+            android:clickable="true"
+            android:contentDescription="@null"
+            android:scaleType="center"
+            android:src="@drawable/ic_settings_24dp" />
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/zen_conditions"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingTop="3dp" />
 
     <TextView
-        android:id="@android:id/button2"
-        style="@style/QSBorderlessButton"
-        android:layout_width="wrap_content"
+        android:id="@+id/zen_alarm_warning"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignParentEnd="true"
-        android:layout_marginEnd="4dp"
-        android:layout_gravity="end"
-        android:text="@string/quick_settings_more_settings"
-        android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+        android:paddingBottom="8dp"
+        android:paddingLeft="@dimen/qs_panel_padding"
+        android:paddingRight="@dimen/qs_panel_padding"
+        android:paddingTop="8dp"
+        android:text="@string/zen_alarm_warning"
+        android:textAppearance="@style/TextAppearance.QS.Warning" />
 
 </com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a93768b..69a22fc 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -40,6 +40,7 @@
     <color name="system_error_color">#fff0592b</color>
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
     <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
+    <color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
     <color name="status_bar_clock_color">#FFFFFFFF</color>
 
     <!-- Tint color for the content on the notification overflow card. -->
@@ -79,10 +80,9 @@
     <color name="notification_ripple_tinted_color">#30ffffff</color>
 
     <!-- The color of the circle around the primary user in the user switcher -->
-    <color name="current_user_border_color">@color/primary_color</color>
+    <color name="current_user_border_color">@color/system_accent_color</color>
 
-    <!-- Our material color palette (deep teal) -->
-    <color name="primary_color">#ff7fcac3</color>
-    <color name="background_color_1">#ff384248</color>
-    <color name="background_color_1_press">#ff54656e</color>
+    <color name="segmented_button_selected">#ff3d4549</color><!-- #33afbdc4 = 20% #afbdc4 = #ff3d4549 composite -->
+    <color name="segmented_button_background">#ff21272b</color>
+    <color name="segmented_button_text_inactive">#b3afbdc4</color><!-- 70% -->
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2737f7e..5c7dc90 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -147,9 +147,6 @@
          be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
     <string name="velocity_tracker_impl" translatable="false">platform</string>
 
-    <!-- Wait on the touch feedback this long before performing an action. -->
-    <integer name="feedback_start_delay">300</integer>
-
     <!-- Set to true to enable the classic notification ticker that scrolls
          Notification.tickerText across the status bar for what seems like an
          eternity. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index dfb891d..ddacd82 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -169,14 +169,16 @@
     <dimen name="qs_tile_spacing">4dp</dimen>
     <dimen name="qs_panel_padding_bottom">8dp</dimen>
     <dimen name="qs_detail_item_height">48dp</dimen>
-    <dimen name="qs_detail_item_height_connected">72dp</dimen>
+    <dimen name="qs_detail_item_height_twoline">72dp</dimen>
 
 
+    <dimen name="segmented_button_spacing">4dp</dimen>
+    <dimen name="segmented_button_radius">2dp</dimen>
+
     <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
     <dimen name="qs_peek_height">8dp</dimen>
 
     <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
-    <dimen name="zen_mode_condition_height">48dp</dimen>
 
     <!-- used by DessertCase -->
     <dimen name="dessert_case_cell_size">192dp</dimen>
@@ -202,9 +204,6 @@
     <!-- The translation in the Z index for each task above the last task. -->
     <dimen name="recents_task_view_z_increment">10dp</dimen>
 
-    <!-- The amount of bottom inset in the shadow outline. -->
-    <dimen name="recents_task_view_shadow_outline_bottom_inset">5dp</dimen>
-
     <!-- The amount to translate when animating the removal of a task. -->
     <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
 
@@ -287,10 +286,10 @@
     <dimen name="keyguard_min_swipe_amount">75dp</dimen>
 
     <!-- Volume panel dialog y offset -->
-    <dimen name="volume_panel_top">16dp</dimen>
+    <dimen name="volume_panel_top">0dp</dimen>
 
     <!-- Volume panel dialog width -->
-    <dimen name="volume_panel_width">300dp</dimen>
+    <dimen name="volume_panel_width">344dp</dimen>
 
     <!-- Volume panel z depth -->
     <dimen name="volume_panel_z">3dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a5cfdbc..e0d48bd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -583,8 +583,14 @@
     <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
     <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
 
-    <!-- Zen mode: Short title. [CHAR LIMIT=40] -->
-    <string name="zen_mode_title">Do not disturb</string>
+    <!-- Zen mode: Alarm warning. [CHAR LIMIT=40] -->
+    <string name="zen_alarm_warning">You won\'t hear your alarms</string>
+
+    <!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
+    <string name="zen_no_interruptions">No interruptions</string>
+
+    <!-- Zen mode: Only important interruptions. [CHAR LIMIT=40] -->
+    <string name="zen_important_interruptions">Priority interruptions only</string>
 
     <!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
     <string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
@@ -607,7 +613,16 @@
     <string name="bugreport_tile_extended" translatable="false">%s\n%s (%s)</string>
 
     <!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
-    <string name="zen_mode_forever">Until you turn this off</string>
+    <string name="zen_mode_forever">Indefinitely</string>
+
+    <!-- Interruption level: None. [CHAR LIMIT=20] -->
+    <string name="interruption_level_none">None</string>
+
+    <!-- Interruption level: Priority. [CHAR LIMIT=20] -->
+    <string name="interruption_level_priority">Priority</string>
+
+    <!-- Interruption level: All. [CHAR LIMIT=20] -->
+    <string name="interruption_level_all">All</string>
 
     <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
     <string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
@@ -639,4 +654,7 @@
 
     <!-- Battery saver notification action text. [CHAR LIMIT=60] -->
     <string name="battery_saver_notification_action_text">Open battery saver settings</string>
+
+    <!-- Battery level for expanded quick settings [CHAR LIMIT=2] -->
+    <string name="battery_level_template"><xliff:g id="level" example="45">%d</xliff:g>%%</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index dd1b749..3f68c31 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -179,6 +179,23 @@
         <item name="android:gravity">center</item>
     </style>
 
+    <style name="TextAppearance.QS.Subhead">
+        <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textColor">@color/qs_subhead</item>
+    </style>
+
+    <style name="TextAppearance.QS.Warning">
+        <item name="android:textSize">12sp</item>
+        <item name="android:textColor">@color/qs_subhead</item>
+    </style>
+
+    <style name="TextAppearance.QS.SegmentedButton">
+        <item name="android:textSize">12sp</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
     <style name="BaseBrightnessDialogContainer">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
@@ -216,7 +233,7 @@
     </style>
 
     <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
-        <item name="android:colorPrimary">@color/primary_color</item>
+        <item name="android:colorPrimary">@color/system_primary_color</item>
         <item name="android:colorControlActivated">@color/system_accent_color</item>
     </style>
 
@@ -239,10 +256,10 @@
         <item name="android:background">@drawable/btn_borderless_rect</item>
         <item name="android:gravity">center</item>
     </style>
-    <style name="BorderlessButton" parent="@android:style/Widget.Material.Button.Borderless" />
 
-    <style name="BorderlessButton.Tiny">
-        <item name="android:minHeight">12dip</item>
-        <item name="android:minWidth">12dip</item>
+    <!-- Window animations used for volume panel. -->
+    <style name="VolumePanelAnimation">
+        <item name="android:windowEnterAnimation">@*android:anim/dock_top_enter</item>
+        <item name="android:windowExitAnimation">@*android:anim/dock_top_exit</item>
     </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
deleted file mode 100644
index 3bdea79..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.volume.VolumePanel;
-import com.android.systemui.volume.ZenModePanel;
-
-/** Quick settings tile: Notifications **/
-public class NotificationsTile extends QSTile<NotificationsTile.NotificationsState> {
-    private final ZenModeController mZenController;
-    private final AudioManager mAudioManager;
-
-    public NotificationsTile(Host host) {
-        super(host);
-        mZenController = host.getZenModeController();
-        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-    }
-
-    @Override
-    public DetailAdapter getDetailAdapter() {
-        return mDetailAdapter;
-    }
-
-    @Override
-    protected NotificationsState newTileState() {
-        return new NotificationsState();
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (listening) {
-            mZenController.addCallback(mCallback);
-            final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
-            mContext.registerReceiver(mReceiver, filter);
-        } else {
-            mZenController.removeCallback(mCallback);
-            mContext.unregisterReceiver(mReceiver);
-        }
-    }
-
-    @Override
-    protected void handleClick() {
-        showDetail(true);
-    }
-
-    @Override
-    protected void handleUpdateState(NotificationsState state, Object arg) {
-        state.visible = true;
-        state.zen = arg instanceof Boolean ? (Boolean) arg : mZenController.isZen();
-        state.ringerMode = mAudioManager.getRingerMode();
-        if (state.zen) {
-            state.iconId = R.drawable.ic_qs_zen_on;
-        } else if (state.ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            state.iconId = R.drawable.ic_qs_ringer_vibrate;
-        } else if (state.ringerMode == AudioManager.RINGER_MODE_SILENT) {
-            state.iconId = R.drawable.ic_qs_ringer_silent;
-        } else {
-            state.iconId = R.drawable.ic_qs_ringer_audible;
-        }
-        state.label = mContext.getString(R.string.quick_settings_notifications_label);
-    }
-
-    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
-        @Override
-        public void onZenChanged(boolean zen) {
-            if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
-            refreshState(zen);
-        }
-    };
-
-    public static final class NotificationsState extends QSTile.State {
-        public boolean zen;
-        public int ringerMode;
-
-        @Override
-        public boolean copyTo(State other) {
-            final NotificationsState o = (NotificationsState) other;
-            final boolean changed = o.zen != zen || o.ringerMode != ringerMode;
-            o.zen = zen;
-            o.ringerMode = ringerMode;
-            return super.copyTo(other) || changed;
-        }
-
-        @Override
-        protected StringBuilder toStringBuilder() {
-            final StringBuilder rt = super.toStringBuilder();
-            rt.insert(rt.length() - 1, ",zen=" + zen + ",ringerMode=" + ringerMode);
-            return rt;
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
-                refreshState();
-            }
-        }
-    };
-
-    private final DetailAdapter mDetailAdapter = new DetailAdapter() {
-
-        @Override
-        public int getTitle() {
-            return R.string.quick_settings_notifications_label;
-        }
-
-        @Override
-        public Boolean getToggleState() {
-            return null;
-        }
-
-        public void setToggleState(boolean state) {
-            // noop
-        }
-
-        public Intent getSettingsIntent() {
-            return ZenModePanel.ZEN_SETTINGS;
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            if (convertView != null) return convertView;
-            final VolumeComponent volumeComponent = mHost.getVolumeComponent();
-            final VolumePanel vp = new VolumePanel(mContext, parent, mZenController);
-            final View v = vp.getContentView();
-            v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    volumeComponent.setVolumePanel(null);
-                }
-
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    vp.updateStates();
-                    volumeComponent.setVolumePanel(vp);
-                }
-            });
-            vp.setZenModePanelCallback(new ZenModePanel.Callback() {
-                @Override
-                public void onMoreSettings() {
-                    mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS);
-                }
-
-                @Override
-                public void onInteraction() {
-                    // noop
-                }
-            });
-            vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI);
-            return v;
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 84eee24..5d1b16c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -266,7 +266,7 @@
                 final TextView summary = (TextView) item.findViewById(android.R.id.summary);
                 if (ap.isConnected) {
                     item.setMinimumHeight(mContext.getResources()
-                            .getDimensionPixelSize(R.dimen.qs_detail_item_height_connected));
+                            .getDimensionPixelSize(R.dimen.qs_detail_item_height_twoline));
                     summary.setText(R.string.quick_settings_connected);
                 } else {
                     summary.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 1b215c9..3e2ef94 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -40,7 +40,8 @@
 import android.view.View;
 
 import com.android.systemui.R;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.ArrayList;
@@ -200,7 +201,7 @@
         final ActivityManager am = (ActivityManager)
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
         final PackageManager pm = mContext.getPackageManager();
-        final Bitmap thumbnail = Utilities.getThumbnail(am, td.persistentTaskId);
+        final Bitmap thumbnail = SystemServicesProxy.getThumbnail(am, td.persistentTaskId);
         Drawable icon = getFullResIcon(td.resolveInfo, pm);
         if (td.userId != UserHandle.myUserId()) {
             // Need to badge the icon
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 52ccb59..3c4f030 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -22,126 +22,43 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.ServiceConnection;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.view.View;
-import android.view.WindowManager;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewTransform;
 
-import java.lang.ref.WeakReference;
-import java.util.Iterator;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /** A proxy implementation for the recents component */
 public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
 
-    /** A handler for messages from the recents implementation */
-    class RecentsMessageHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == MSG_UPDATE_FOR_CONFIGURATION) {
-                Resources res = mContext.getResources();
-                float statusBarHeight = res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height);
-                Bundle replyData = msg.getData().getParcelable(KEY_CONFIGURATION_DATA);
-                mSingleCountFirstTaskRect = replyData.getParcelable(KEY_SINGLE_TASK_STACK_RECT);
-                mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
-                mTwoCountFirstTaskRect = replyData.getParcelable(KEY_TWO_TASK_STACK_RECT);
-                mTwoCountFirstTaskRect.offset(0, (int) statusBarHeight);
-                mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT);
-                mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
-                if (Console.Enabled) {
-                    Console.log(Constants.Log.App.RecentsComponent,
-                            "[RecentsComponent|RecentsMessageHandler|handleMessage]",
-                            "singleTaskRect: " + mSingleCountFirstTaskRect +
-                            " twoTaskRect: " + mTwoCountFirstTaskRect +
-                            " multipleTaskRect: " + mMultipleCountFirstTaskRect);
-                }
-
-                // If we had the update the animation rects as a result of onServiceConnected, then
-                // we check for whether we need to toggle the recents here.
-                if (mToggleRecentsUponServiceBound) {
-                    startRecentsActivity();
-                    mToggleRecentsUponServiceBound = false;
-                }
-            }
-        }
-    }
-
-    /** A service connection to the recents implementation */
-    class RecentsServiceConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (Console.Enabled) {
-                Console.log(Constants.Log.App.RecentsComponent,
-                        "[RecentsComponent|ServiceConnection|onServiceConnected]",
-                        "toggleRecents: " + mToggleRecentsUponServiceBound);
-            }
-            mService = new Messenger(service);
-            mServiceIsBound = true;
-
-            if (hasValidTaskRects()) {
-                // Start recents if this new service connection was triggered by hitting recents
-                if (mToggleRecentsUponServiceBound) {
-                    startRecentsActivity();
-                    mToggleRecentsUponServiceBound = false;
-                }
-            } else {
-                // Otherwise, update the animation rects before starting the recents if requested
-                updateAnimationRects();
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (Console.Enabled) {
-                Console.log(Constants.Log.App.RecentsComponent,
-                        "[RecentsComponent|ServiceConnection|onServiceDisconnected]");
-            }
-            mService = null;
-            mServiceIsBound = false;
-        }
-    }
-
-    final public static int MSG_UPDATE_FOR_CONFIGURATION = 0;
-    final public static int MSG_UPDATE_TASK_THUMBNAIL = 1;
-    final public static int MSG_PRELOAD_TASKS = 2;
-    final public static int MSG_CANCEL_PRELOAD_TASKS = 3;
-    final public static int MSG_SHOW_RECENTS = 4;
-    final public static int MSG_HIDE_RECENTS = 5;
-    final public static int MSG_TOGGLE_RECENTS = 6;
-    final public static int MSG_START_ENTER_ANIMATION = 7;
-
     final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
     final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
     final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
     final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
-    final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
-    final public static String KEY_WINDOW_RECT = "recents.windowRect";
-    final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
-    final public static String KEY_SINGLE_TASK_STACK_RECT = "recents.singleCountTaskRect";
-    final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
-    final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
+    final public static String EXTRA_TRIGGERED_FROM_TASK_ID = "recents.activeTaskId";
 
     final static int sMinToggleDelay = 425;
 
     final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
     final static String sRecentsPackage = "com.android.systemui";
     final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
-    final static String sRecentsService = "com.android.systemui.recents.RecentsService";
 
     static Bitmap sLastScreenshot;
     static RecentsComponent.Callbacks sRecentsComponentCallbacks;
@@ -150,37 +67,37 @@
     SystemServicesProxy mSystemServicesProxy;
 
     // Recents service binding
-    Messenger mService = null;
-    Messenger mMessenger;
-    RecentsMessageHandler mHandler;
+    Handler mHandler;
     boolean mBootCompleted = false;
-    boolean mServiceIsBound = false;
-    boolean mToggleRecentsUponServiceBound;
-    RecentsServiceConnection mConnection = new RecentsServiceConnection();
+
+    // Task launching
+    RecentsConfiguration mConfig;
+    Rect mWindowRect;
+    Rect mTaskStackBounds;
+    TaskViewTransform mTmpTransform = new TaskViewTransform();
+    int mStatusBarHeight;
 
     // Variables to keep track of if we need to start recents after binding
     View mStatusBarView;
     boolean mTriggeredFromAltTab;
-
-    Rect mSingleCountFirstTaskRect = new Rect();
-    Rect mTwoCountFirstTaskRect = new Rect();
-    Rect mMultipleCountFirstTaskRect = new Rect();
     long mLastToggleTime;
 
     public AlternateRecentsComponent(Context context) {
         mContext = context;
         mSystemServicesProxy = new SystemServicesProxy(context);
-        mHandler = new RecentsMessageHandler();
-        mMessenger = new Messenger(mHandler);
+        mHandler = new Handler();
+        mConfig = RecentsConfiguration.reinitialize(context);
+        mWindowRect = mSystemServicesProxy.getWindowRect();
+        mTaskStackBounds = new Rect();
+        mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
+        mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
     }
 
     public void onStart() {
         if (Console.Enabled) {
             Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|start]");
         }
-
-        // Try to create a long-running connection to the recents service
-        bindToRecentsService(false);
     }
 
     public void onBootCompleted() {
@@ -194,12 +111,6 @@
         }
         mStatusBarView = statusBarView;
         mTriggeredFromAltTab = triggeredFromAltTab;
-        if (!mServiceIsBound) {
-            // Try to create a long-running connection to the recents service before toggling
-            // recents
-            bindToRecentsService(true);
-            return;
-        }
 
         try {
             startRecentsActivity();
@@ -214,18 +125,14 @@
             Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
         }
 
-        if (mServiceIsBound && mBootCompleted) {
-            if (isRecentsTopMost(null)) {
-                // Notify recents to close it
-                try {
-                    Bundle data = new Bundle();
-                    Message msg = Message.obtain(null, MSG_HIDE_RECENTS,
-                            triggeredFromAltTab ? 1 : 0, 0);
-                    msg.setData(data);
-                    mService.send(msg);
-                } catch (RemoteException re) {
-                    re.printStackTrace();
-                }
+        if (mBootCompleted) {
+            if (isRecentsTopMost(getTopMostTask(), null)) {
+                // Notify recents to hide itself
+                Intent intent = new Intent(RecentsActivity.ACTION_HIDE_RECENTS_ACTIVITY);
+                intent.setPackage(mContext.getPackageName());
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                intent.putExtra(RecentsActivity.EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
+                mContext.sendBroadcast(intent);
             }
         }
     }
@@ -237,17 +144,10 @@
                     Constants.Log.App.TimeRecentsStartupKey);
             Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
                     Constants.Log.App.TimeRecentsLaunchKey);
-            Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
-                    "serviceIsBound: " + mServiceIsBound);
+            Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]", "");
         }
         mStatusBarView = statusBarView;
         mTriggeredFromAltTab = false;
-        if (!mServiceIsBound) {
-            // Try to create a long-running connection to the recents service before toggling
-            // recents
-            bindToRecentsService(true);
-            return;
-        }
 
         try {
             toggleRecentsActivity();
@@ -265,94 +165,26 @@
     }
 
     public void onConfigurationChanged(Configuration newConfig) {
-        updateAnimationRects();
+        mConfig = RecentsConfiguration.reinitialize(mContext);
+        mWindowRect = mSystemServicesProxy.getWindowRect();
+        mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
+        sLastScreenshot = null;
     }
 
-    /** Binds to the recents implementation */
-    private void bindToRecentsService(boolean toggleRecentsUponConnection) {
-        mToggleRecentsUponServiceBound = toggleRecentsUponConnection;
-        Intent intent = new Intent();
-        intent.setClassName(sRecentsPackage, sRecentsService);
-        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-    }
-
-    /** Returns whether we have valid task rects to animate to. */
-    boolean hasValidTaskRects() {
-        return mSingleCountFirstTaskRect != null && mSingleCountFirstTaskRect.width() > 0 &&
-                mSingleCountFirstTaskRect.height() > 0 && mTwoCountFirstTaskRect != null &&
-                mTwoCountFirstTaskRect.width() > 0 && mTwoCountFirstTaskRect.height() > 0 &&
-                mMultipleCountFirstTaskRect != null && mMultipleCountFirstTaskRect.width() > 0 &&
-                mMultipleCountFirstTaskRect.height() > 0;
-    }
-
-    /** Updates each of the task animation rects. */
-    void updateAnimationRects() {
-        if (mServiceIsBound) {
-            Resources res = mContext.getResources();
-            int statusBarHeight = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.status_bar_height);
-            int navBarHeight = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.navigation_bar_height);
-            Rect rect = new Rect();
-            WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-            wm.getDefaultDisplay().getRectSize(rect);
-
-            // Try and update the recents configuration
-            try {
-                Bundle data = new Bundle();
-                data.putParcelable(KEY_WINDOW_RECT, rect);
-                data.putParcelable(KEY_SYSTEM_INSETS, new Rect(0, statusBarHeight, 0, 0));
-                Message msg = Message.obtain(null, MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
-                msg.setData(data);
-                msg.replyTo = mMessenger;
-                mService.send(msg);
-            } catch (RemoteException re) {
-                re.printStackTrace();
-            }
-        }
-    }
-
-    /** Loads the first task thumbnail */
-    Bitmap loadFirstTaskThumbnail() {
+    /** Gets the top task. */
+    ActivityManager.RunningTaskInfo getTopMostTask() {
         SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
-
-        for (ActivityManager.RunningTaskInfo t : tasks) {
-            return ssp.getTaskThumbnail(t.id);
+        if (!tasks.isEmpty()) {
+            return tasks.get(0);
         }
         return null;
     }
 
-    /** Returns the proper rect to use for the animation, given the number of tasks. */
-    Rect getAnimationTaskRect(List<ActivityManager.RecentTaskInfo> tasks) {
-        // NOTE: Currently there's no method to get the number of non-home tasks, so we have to
-        // compute this ourselves
-        SystemServicesProxy ssp = mSystemServicesProxy;
-        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
-        while (iter.hasNext()) {
-            ActivityManager.RecentTaskInfo t = iter.next();
-
-            // Skip tasks in the home stack
-            if (ssp.isInHomeStack(t.persistentId)) {
-                iter.remove();
-                continue;
-            }
-        }
-        if (tasks.size() <= 1) {
-            return mSingleCountFirstTaskRect;
-        } else if (tasks.size() <= 2) {
-            return mTwoCountFirstTaskRect;
-        } else {
-            return mMultipleCountFirstTaskRect;
-        }
-    }
-
     /** Returns whether the recents is currently running */
-    boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
+    boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask, AtomicBoolean isHomeTopMost) {
         SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
-        if (!tasks.isEmpty()) {
-            ActivityManager.RunningTaskInfo topTask = tasks.get(0);
+        if (topTask != null) {
             ComponentName topActivity = topTask.topActivity;
 
             // Check if the front most activity is recents
@@ -382,39 +214,37 @@
 
         // If Recents is the front most activity, then we should just communicate with it directly
         // to launch the first task or dismiss itself
+        ActivityManager.RunningTaskInfo topTask = getTopMostTask();
         AtomicBoolean isTopTaskHome = new AtomicBoolean();
-        if (isRecentsTopMost(isTopTaskHome)) {
-            // Notify recents to close itself
-            try {
-                Bundle data = new Bundle();
-                Message msg = Message.obtain(null, MSG_TOGGLE_RECENTS, 0, 0);
-                msg.setData(data);
-                mService.send(msg);
+        if (isRecentsTopMost(topTask, isTopTaskHome)) {
+            // Notify recents to toggle itself
+            Intent intent = new Intent(RecentsActivity.ACTION_TOGGLE_RECENTS_ACTIVITY);
+            intent.setPackage(mContext.getPackageName());
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            mContext.sendBroadcast(intent);
 
-                // Time this path
-                if (Console.Enabled) {
-                    Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                            Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
-                    Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
-                            Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
-                }
-            } catch (RemoteException re) {
-                re.printStackTrace();
+            // Time this path
+            if (Console.Enabled) {
+                Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                        Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
+                Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+                        Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
             }
             mLastToggleTime = System.currentTimeMillis();
             return;
         } else {
             // Otherwise, start the recents activity
-            startRecentsActivity(isTopTaskHome.get());
+            startRecentsActivity(topTask, isTopTaskHome.get());
         }
     }
 
     /** Starts the recents activity if it is not already running */
     void startRecentsActivity() {
         // Check if the top task is in the home stack, and start the recents activity
+        ActivityManager.RunningTaskInfo topTask = getTopMostTask();
         AtomicBoolean isTopTaskHome = new AtomicBoolean();
-        if (!isRecentsTopMost(isTopTaskHome)) {
-            startRecentsActivity(isTopTaskHome.get());
+        if (!isRecentsTopMost(topTask, isTopTaskHome)) {
+            startRecentsActivity(topTask, isTopTaskHome.get());
         }
     }
 
@@ -422,8 +252,6 @@
      * Creates the activity options for a unknown state->recents transition.
      */
     ActivityOptions getUnknownTransitionActivityOptions() {
-        // Reset the last screenshot
-        consumeLastScreenshot();
         return ActivityOptions.makeCustomAnimation(mContext,
                 R.anim.recents_from_unknown_enter,
                 R.anim.recents_from_unknown_exit, mHandler, this);
@@ -433,8 +261,6 @@
      * Creates the activity options for a home->recents transition.
      */
     ActivityOptions getHomeTransitionActivityOptions() {
-        // Reset the last screenshot
-        consumeLastScreenshot();
         return ActivityOptions.makeCustomAnimation(mContext,
                 R.anim.recents_from_launcher_enter,
                 R.anim.recents_from_launcher_exit, mHandler, this);
@@ -444,13 +270,13 @@
      * Creates the activity options for an app->recents transition.  If this method sets the static
      * screenshot, then we will use that for the transition.
      */
-    ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
+    ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask) {
         // Recycle the last screenshot
         consumeLastScreenshot();
 
         // Take the full screenshot
         if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
-            sLastScreenshot = mSystemServicesProxy.takeScreenshot();
+            sLastScreenshot = mSystemServicesProxy.takeAppScreenshot();
             if (sLastScreenshot != null) {
                 return ActivityOptions.makeCustomAnimation(mContext,
                         R.anim.recents_from_app_enter,
@@ -459,8 +285,10 @@
         }
 
         // If the screenshot fails, then load the first task thumbnail and use that
-        Bitmap firstThumbnail = loadFirstTaskThumbnail();
+        Bitmap firstThumbnail = mSystemServicesProxy.getTaskThumbnail(topTask.id);
         if (firstThumbnail != null) {
+            Rect taskRect = getThumbnailTransitionRect(topTask.id);
+
             // Create the new thumbnail for the animation down
             // XXX: We should find a way to optimize this so we don't need to create a new bitmap
             Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
@@ -480,8 +308,46 @@
         return getUnknownTransitionActivityOptions();
     }
 
+    /** Returns the transition rect for the given task id. */
+    Rect getThumbnailTransitionRect(int runningTaskId) {
+        // Get the stack of tasks that we are animating into
+        TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy);
+        TaskStackView tsv = new TaskStackView(mContext, stack);
+        TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
+        tsv.computeRects(mTaskStackBounds.width(), mTaskStackBounds.height() - mStatusBarHeight, 0, 0);
+        tsv.setStackScrollToInitialState();
+
+        // Find the running task in the TaskStack
+        Task task = null;
+        ArrayList<Task> tasks = stack.getTasks();
+        if (runningTaskId != -1) {
+            // Otherwise, try and find the task with the
+            int taskCount = tasks.size();
+            for (int i = taskCount - 1; i >= 0; i--) {
+                Task t = tasks.get(i);
+                if (t.key.id == runningTaskId) {
+                    task = t;
+                    break;
+                }
+            }
+        }
+        if (task == null) {
+            // If no task is specified or we can not find the task just use the front most one
+            task = tasks.get(tasks.size() - 1);
+        }
+
+        // Get the transform for the running task
+        TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+        stack.getGroupIndexForTask(task, groupTaskIndex);
+        mTmpTransform = algo.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+                tsv.getStackScroll(), mTmpTransform);
+        mTmpTransform.rect.offset(mTaskStackBounds.left, mTaskStackBounds.top);
+        mTmpTransform.rect.offset(0, mStatusBarHeight);
+        return new Rect(mTmpTransform.rect);
+    }
+
     /** Starts the recents activity */
-    void startRecentsActivity(boolean isTopTaskHome) {
+    void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
         // If Recents is not the front-most activity and we should animate into it.  If
         // the activity at the root of the top task stack in the home stack, then we just do a
         // simple transition.  Otherwise, we animate to the rects defined by the Recents service,
@@ -489,34 +355,34 @@
         SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> recentTasks =
                 ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier());
-        Rect taskRect = getAnimationTaskRect(recentTasks);
-        boolean useThumbnailTransition = !isTopTaskHome &&
-                hasValidTaskRects();
+        boolean useThumbnailTransition = !isTopTaskHome;
         boolean hasRecentTasks = !recentTasks.isEmpty();
 
         if (useThumbnailTransition) {
             // Try starting with a thumbnail transition
-            ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
+            ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask);
             if (opts != null) {
                 if (sLastScreenshot != null) {
-                    startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
+                    startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
                 } else {
-                    startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL);
+                    startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL);
                 }
             } else {
                 // Fall through below to the non-thumbnail transition
                 useThumbnailTransition = false;
             }
-        } else {
+        }
+
+        if (!useThumbnailTransition) {
             // If there is no thumbnail transition, but is launching from home into recents, then
             // use a quick home transition and do the animation from home
             if (hasRecentTasks) {
                 ActivityOptions opts = getHomeTransitionActivityOptions();
-                startAlternateRecentsActivity(opts, EXTRA_FROM_HOME);
+                startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME);
             } else {
                 // Otherwise we do the normal fade from an unknown source
                 ActivityOptions opts = getUnknownTransitionActivityOptions();
-                startAlternateRecentsActivity(opts, null);
+                startAlternateRecentsActivity(topTask, opts, null);
             }
         }
 
@@ -528,7 +394,8 @@
     }
 
     /** Starts the recents activity */
-    void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) {
+    void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+                                       ActivityOptions opts, String extraFlag) {
         Intent intent = new Intent(sToggleRecentsAction);
         intent.setClassName(sRecentsPackage, sRecentsActivity);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -538,6 +405,7 @@
             intent.putExtra(extraFlag, true);
         }
         intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
+        intent.putExtra(EXTRA_TRIGGERED_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
         if (opts != null) {
             mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
                     UserHandle.USER_CURRENT));
@@ -576,11 +444,9 @@
     @Override
     public void onAnimationStarted() {
         // Notify recents to start the enter animation
-        try {
-            Message msg = Message.obtain(null, MSG_START_ENTER_ANIMATION, 0, 0);
-            mService.send(msg);
-        } catch (RemoteException re) {
-            re.printStackTrace();
-        }
+        Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
+        intent.setPackage(mContext.getPackageName());
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mContext.sendBroadcast(intent);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 7a49a04..8a80b76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -25,10 +25,12 @@
         public static final boolean Verbose = false;
 
         public static class App {
+            // Enables the simulated task affiliations
+            public static final boolean EnableSimulatedTaskGroups = false;
             // Enables the screenshot app->Recents transition
             public static final boolean EnableScreenshotAppTransition = false;
             // Enables the filtering of tasks according to their grouping
-            public static final boolean EnableTaskFiltering = true;
+            public static final boolean EnableTaskFiltering = false;
             // Enables clipping of tasks against each other
             public static final boolean EnableTaskStackClipping = true;
             // Enables tapping on the TaskBar to launch the task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 19a38c7..bf894a7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -35,6 +35,11 @@
 import android.view.ViewStub;
 import android.widget.Toast;
 import com.android.systemui.R;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DebugTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.TaskStack;
@@ -51,6 +56,11 @@
         RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
         FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks {
 
+    final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
+    final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
+    final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+    final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+
     RecentsView mRecentsView;
     SystemBarScrimViews mScrimViews;
     ViewStub mEmptyViewStub;
@@ -123,8 +133,8 @@
                 Console.log(Constants.Log.App.SystemUIHandshake,
                         "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
             }
-            if (action.equals(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY)) {
-                if (intent.getBooleanExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
+            if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) {
+                if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
                     // Dismiss recents, launching the focused task
                     dismissRecentsIfVisible();
                 } else {
@@ -138,13 +148,13 @@
                                 new ViewAnimation.TaskViewExitContext(exitTrigger));
                     }
                 }
-            } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
+            } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) {
                 // Try and unfilter and filtered stacks
                 if (!mRecentsView.unfilterFilteredStacks()) {
                     // If there are no filtered stacks, dismiss recents and launch the first task
                     dismissRecentsIfVisible();
                 }
-            } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
+            } else if (action.equals(ACTION_START_ENTER_ANIMATION)) {
                 // Try and start the enter animation (or restart it on configuration changed)
                 ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
                 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
@@ -438,9 +448,9 @@
 
         // Register the broadcast receiver to handle messages from our service
         IntentFilter filter = new IntentFilter();
-        filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
-        filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
-        filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
+        filter.addAction(ACTION_HIDE_RECENTS_ACTIVITY);
+        filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY);
+        filter.addAction(ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Start listening for widget package changes if there is one bound
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index 3754340..43d7a54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -19,6 +19,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 
 /** Our special app widget host for the Search widget */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 3041a3c..a2407eb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -28,6 +28,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import com.android.systemui.R;
+import com.android.systemui.recents.misc.Console;
 
 
 /** A static Recents configuration for the current context
@@ -75,7 +76,6 @@
     public int taskViewRemoveAnimTranslationXPx;
     public int taskViewTranslationZMinPx;
     public int taskViewTranslationZIncrementPx;
-    public int taskViewShadowOutlineBottomInsetPx;
     public int taskViewRoundedCornerRadiusPx;
     public int taskViewHighlightPx;
 
@@ -202,8 +202,6 @@
         taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
         taskViewTranslationZIncrementPx =
                 res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
-        taskViewShadowOutlineBottomInsetPx =
-                res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
 
         // Task bar colors
         taskBarViewDefaultBackgroundColor =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
deleted file mode 100644
index 49149a6..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewTransform;
-
-import java.lang.ref.WeakReference;
-
-
-/** The message handler to process Recents SysUI messages */
-class SystemUIMessageHandler extends Handler {
-    WeakReference<Context> mContext;
-
-    SystemUIMessageHandler(Context context) {
-        // Keep a weak ref to the context instead of a strong ref
-        mContext = new WeakReference<Context>(context);
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake,
-                    "[RecentsService|handleMessage]", msg);
-        }
-
-        Context context = mContext.get();
-        if (context == null) return;
-
-        if (msg.what == AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION) {
-            RecentsTaskLoader.initialize(context);
-            RecentsConfiguration config = RecentsConfiguration.reinitialize(context);
-            config.updateOnConfigurationChange();
-
-            try {
-                Bundle data = msg.getData();
-                Rect windowRect = data.getParcelable(AlternateRecentsComponent.KEY_WINDOW_RECT);
-                Rect systemInsets = data.getParcelable(AlternateRecentsComponent.KEY_SYSTEM_INSETS);
-
-                // NOTE: None of the rects computed below need to be offset for the status bar,
-                // since that is done when we compute the animation itself in the Recents component
-
-                // Create a dummy task stack & compute the rect for the thumbnail to animate to
-                TaskStack stack = new TaskStack();
-                TaskStackView tsv = new TaskStackView(context, stack);
-                TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
-                Bundle replyData = new Bundle();
-                TaskViewTransform transform;
-
-                // Get the task stack and search bar bounds
-                Rect taskStackBounds = new Rect();
-                config.getTaskStackBounds(windowRect.width(), windowRect.height(), taskStackBounds);
-
-                // Calculate the target task rect for when there is one task.
-
-                // NOTE: Since the nav bar height is already accounted for in the windowRect, don't
-                // pass in a left or bottom inset
-                stack.addTask(new Task());
-                tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
-                        systemInsets.top - systemInsets.bottom, 0, 0);
-                tsv.setStackScrollToInitialState();
-                transform = algo.getStackTransform(0, tsv.getStackScroll());
-                transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
-                replyData.putParcelable(AlternateRecentsComponent.KEY_SINGLE_TASK_STACK_RECT,
-                        new Rect(transform.rect));
-
-                // Also calculate the target task rect when there are two tasks.
-                stack.addTask(new Task());
-                tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
-                        systemInsets.top - systemInsets.bottom, 0, 0);
-                tsv.setStackScrollToInitialState();
-                transform = algo.getStackTransform(1, tsv.getStackScroll());
-                transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
-                replyData.putParcelable(AlternateRecentsComponent.KEY_TWO_TASK_STACK_RECT,
-                        new Rect(transform.rect));
-
-                // Also calculate the target task rect when there are two tasks.
-                stack.addTask(new Task());
-                tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
-                        systemInsets.top - systemInsets.bottom, 0, 0);
-                tsv.setStackScrollToInitialState();
-                transform = algo.getStackTransform(2, tsv.getStackScroll());
-                transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
-                replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
-                        new Rect(transform.rect));
-
-                data.putParcelable(AlternateRecentsComponent.KEY_CONFIGURATION_DATA, replyData);
-                Message reply = Message.obtain(null,
-                        AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
-                reply.setData(data);
-                msg.replyTo.send(reply);
-            } catch (RemoteException re) {
-                re.printStackTrace();
-            }
-        } else if (msg.what == AlternateRecentsComponent.MSG_HIDE_RECENTS) {
-            // Send a broadcast to hide recents
-            Intent intent = new Intent(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
-            intent.setPackage(context.getPackageName());
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            if (msg.arg1 != 0) {
-                intent.putExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, true);
-            }
-            context.sendBroadcast(intent);
-        } else if (msg.what == AlternateRecentsComponent.MSG_TOGGLE_RECENTS) {
-            // Send a broadcast to toggle recents
-            Intent intent = new Intent(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
-            intent.setPackage(context.getPackageName());
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            context.sendBroadcast(intent);
-
-            // Time this path
-            if (Console.Enabled) {
-                Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                        Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
-                Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
-                        Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
-            }
-        } else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
-            // Send a broadcast to start the enter animation
-            Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
-            intent.setPackage(context.getPackageName());
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            context.sendBroadcast(intent);
-        }
-    }
-}
-
-/* Service */
-public class RecentsService extends Service {
-    final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
-    final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
-    final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
-    final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
-
-    Messenger mSystemUIMessenger = new Messenger(new SystemUIMessageHandler(this));
-
-    @Override
-    public void onCreate() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onCreate]");
-        }
-        super.onCreate();
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onBind]");
-        }
-        return mSystemUIMessenger.getBinder();
-    }
-
-    @Override
-    public boolean onUnbind(Intent intent) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onUnbind]");
-        }
-        return super.onUnbind(intent);
-    }
-
-    @Override
-    public void onRebind(Intent intent) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onRebind]");
-        }
-        super.onRebind(intent);
-    }
-
-    @Override
-    public void onDestroy() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onDestroy]");
-        }
-        super.onDestroy();
-    }
-
-    @Override
-    public void onTrimMemory(int level) {
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        if (loader != null) {
-            loader.onTrimMemory(level);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Console.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/recents/Console.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
index 0cb74b9..28ac9d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Console.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
 
 
 import android.content.ComponentCallbacks2;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
index d90e2be..d000985 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
 
 import android.os.Handler;
 import android.os.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index f0b2cb6..4456066 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
 
 import android.os.Handler;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java b/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java
new file mode 100644
index 0000000..ec3c39c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java
@@ -0,0 +1,39 @@
+/*
+ * 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 com.android.systemui.recents.misc;
+
+/**
+ * Used to generate successive incremented names.
+ */
+public class NamedCounter {
+
+    int mCount;
+    String mPrefix = "";
+    String mSuffix = "";
+
+    public NamedCounter(String prefix, String suffix) {
+        mPrefix = prefix;
+        mSuffix = suffix;
+    }
+
+    /** Returns the next name. */
+    public String nextName() {
+        String name = mPrefix + mCount + mSuffix;
+        mCount++;
+        return name;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index d525546..31825af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
 
 import android.content.Context;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3765d1c..05c0f58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -30,14 +30,17 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -47,7 +50,9 @@
 import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import com.android.systemui.recents.Constants;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -128,6 +133,7 @@
                 rti.baseIntent = new Intent();
                 rti.baseIntent.setComponent(cn);
                 rti.description = description;
+                rti.firstActiveTime = rti.lastActiveTime = i;
                 if (i % 2 == 0) {
                     rti.taskDescription = new ActivityManager.TaskDescription(description,
                         Bitmap.createBitmap(mDummyIcon),
@@ -208,7 +214,7 @@
             return thumbnail;
         }
 
-        Bitmap thumbnail = Utilities.getThumbnail(mAm, taskId);
+        Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
         if (thumbnail != null) {
             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
             // left pixel, then assume the whole thumbnail is transparent. Generally, proper
@@ -224,6 +230,25 @@
         return thumbnail;
     }
 
+    /**
+     * Returns a task thumbnail from the activity manager
+     */
+    public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
+        ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
+        Bitmap thumbnail = taskThumbnail.mainThumbnail;
+        ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
+        if (thumbnail == null && descriptor != null) {
+            thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor());
+        }
+        if (descriptor != null) {
+            try {
+                descriptor.close();
+            } catch (IOException e) {
+            }
+        }
+        return thumbnail;
+    }
+
     /** Moves a task to the front with the specified activity options */
     public void moveTaskToFront(int taskId, ActivityOptions opts) {
         if (mAm == null) return;
@@ -371,6 +396,17 @@
     }
 
     /**
+     * Returns the window rect.
+     */
+    public Rect getWindowRect() {
+        Rect windowRect = new Rect();
+        if (mWm == null) return windowRect;
+
+        mWm.getDefaultDisplay().getRectSize(windowRect);
+        return windowRect;
+    }
+
+    /**
      * Takes a screenshot of the current surface.
      */
     public Bitmap takeScreenshot() {
@@ -378,4 +414,11 @@
         mDisplay.getDisplayInfo(di);
         return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight());
     }
+
+    /**
+     * Takes a screenshot of the current app.
+     */
+    public Bitmap takeAppScreenshot() {
+        return takeScreenshot();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/recents/Utilities.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 6a7cfcc..bda195b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
 
 import android.app.ActivityManager;
 import android.graphics.Bitmap;
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.ParcelFileDescriptor;
+import com.android.systemui.recents.RecentsConfiguration;
 
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
@@ -95,21 +96,4 @@
             throws IllegalAccessException, InvocationTargetException {
         sPropertyMethod.invoke(null, property, value);
     }
-
-    /** Retrieves a task thumbnail from the activity manager */
-    public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
-        ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
-        Bitmap thumbnail = taskThumbnail.mainThumbnail;
-        final ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
-        if (thumbnail == null && descriptor != null) {
-            thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor());
-        }
-        if (descriptor != null) {
-            try {
-                descriptor.close();
-            } catch (IOException e) {
-            }
-        }
-        return thumbnail;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index cbe39e3..2d50659 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -21,7 +21,7 @@
 import android.content.Context;
 import android.os.Looper;
 import com.android.internal.content.PackageMonitor;
-import com.android.systemui.recents.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import java.util.HashSet;
 import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 9d8d746..29f1440 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -28,10 +28,10 @@
 import android.os.HandlerThread;
 import android.os.UserHandle;
 import android.util.Pair;
-import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.SystemServicesProxy;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -368,10 +368,9 @@
         return mSystemServicesProxy;
     }
 
-    private List<ActivityManager.RecentTaskInfo> getRecentTasks() {
+    private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
         long t1 = System.currentTimeMillis();
 
-        SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> tasks =
                 ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
         Collections.reverse(tasks);
@@ -402,7 +401,7 @@
 
         // Get the recent tasks
         SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks();
+        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
 
         // Add each task to the task stack
         t1 = System.currentTimeMillis();
@@ -430,7 +429,7 @@
 
             // Create a new task
             Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel,
-                    activityIcon, activityColor, t.userId, t.lastActiveTime);
+                    activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime);
 
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
@@ -495,6 +494,9 @@
                     "" + (System.currentTimeMillis() - t1) + "ms");
         }
 
+        // Simulate the groupings that we describe
+        stack.createSimulatedAffiliatedGroupings();
+
         // Start the task loader
         mLoader.start(context);
 
@@ -509,6 +511,24 @@
         return root;
     }
 
+    /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
+    public static TaskStack getShallowTaskStack(SystemServicesProxy ssp) {
+        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
+        TaskStack stack = new TaskStack();
+
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            ActivityManager.RecentTaskInfo t = tasks.get(i);
+            ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
+            if (info == null) continue;
+
+            stack.addTask(new Task(t.persistentId, true, t.baseIntent, null, null, 0, 0,
+                    t.firstActiveTime, t.lastActiveTime));
+        }
+        stack.createSimulatedAffiliatedGroupings();
+        return stack;
+    }
+
     /** Acquires the task resource data directly from the pool. */
     public void loadTaskData(Task t) {
         Drawable applicationIcon = mApplicationIconCache.get(t.key);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index abfb221..002395f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -19,7 +19,7 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
 
 
 /**
@@ -39,12 +39,14 @@
         public final int id;
         public final Intent baseIntent;
         public final int userId;
+        public long firstActiveTime;
         public long lastActiveTime;
 
-        public TaskKey(int id, Intent intent, int userId, long lastActiveTime) {
+        public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
             this.id = id;
             this.baseIntent = intent;
             this.userId = userId;
+            this.firstActiveTime = firstActiveTime;
             this.lastActiveTime = lastActiveTime;
         }
 
@@ -76,6 +78,7 @@
     }
 
     public TaskKey key;
+    public TaskGrouping group;
     public Drawable applicationIcon;
     public Drawable activityIcon;
     public String activityLabel;
@@ -92,8 +95,9 @@
     }
 
     public Task(int id, boolean isActive, Intent intent, String activityTitle,
-                Drawable activityIcon, int colorPrimary, int userId, long lastActiveTime) {
-        this.key = new TaskKey(id, intent, userId, lastActiveTime);
+                Drawable activityIcon, int colorPrimary, int userId, long firstActiveTime,
+                long lastActiveTime) {
+        this.key = new TaskKey(id, intent, userId, firstActiveTime, lastActiveTime);
         this.activityLabel = activityTitle;
         this.activityIcon = activityIcon;
         this.colorPrimary = colorPrimary;
@@ -107,6 +111,14 @@
         mCb = cb;
     }
 
+    /** Set the grouping */
+    public void setGroup(TaskGrouping group) {
+        if (group != null && this.group != null) {
+            throw new RuntimeException("This task is already assigned to a group.");
+        }
+        this.group = group;
+    }
+
     /** Notifies the callback listeners that this task has been loaded */
     public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
         this.applicationIcon = applicationIcon;
@@ -134,6 +146,11 @@
 
     @Override
     public String toString() {
-        return "Task: " + key.baseIntent.getComponent().getPackageName() + " [" + super.toString() + "]";
+        String groupAffiliation = "no group";
+        if (group != null) {
+            groupAffiliation = group.affiliation;
+        }
+        return "Task (" + groupAffiliation + "): " + key.baseIntent.getComponent().getPackageName() +
+                " [" + super.toString() + "]";
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
new file mode 100644
index 0000000..3a18bce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -0,0 +1,53 @@
+package com.android.systemui.recents.model;
+
+import java.util.ArrayList;
+
+/** Represents a grouping of tasks witihin a stack. */
+public class TaskGrouping {
+
+    String affiliation;
+    long latestActiveTimeInGroup;
+
+    ArrayList<Task.TaskKey> mTasks = new ArrayList<Task.TaskKey>();
+
+    /** Creates a group with a specified affiliation. */
+    public TaskGrouping(String affiliation) {
+        this.affiliation = affiliation;
+    }
+
+    /** Adds a new task to this group. */
+    void addTask(Task t) {
+        mTasks.add(t.key);
+        if (t.key.lastActiveTime > latestActiveTimeInGroup) {
+            latestActiveTimeInGroup = t.key.lastActiveTime;
+        }
+        t.setGroup(this);
+    }
+
+    /** Removes a task from this group. */
+    void removeTask(Task t) {
+        mTasks.remove(t.key);
+        latestActiveTimeInGroup = 0;
+        int taskCount = mTasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            long lastActiveTime = mTasks.get(i).lastActiveTime;
+            if (lastActiveTime > latestActiveTimeInGroup) {
+                latestActiveTimeInGroup = lastActiveTime;
+            }
+        }
+        t.setGroup(null);
+    }
+
+    /** Gets the front task */
+    public boolean isFrontMostTask(Task t) {
+        return t.key.equals(mTasks.get(mTasks.size() - 1));
+    }
+
+    /** Finds the index of a given task in a group. */
+    public int indexOf(Task t) {
+        return mTasks.indexOf(t.key);
+    }
+
+    /** Returns the number of tasks in this group. */
+    public int getTaskCount() { return mTasks.size(); }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 24e01bd..ffd2857 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,9 +16,13 @@
 
 package com.android.systemui.recents.model;
 
-import android.content.Context;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.misc.NamedCounter;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 
 
@@ -36,6 +40,7 @@
 class FilteredTaskList {
     ArrayList<Task> mTasks = new ArrayList<Task>();
     ArrayList<Task> mFilteredTasks = new ArrayList<Task>();
+    HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<Task.TaskKey, Integer>();
     TaskFilter mFilter;
 
     /** Sets the task filter, saving the current touch state */
@@ -83,7 +88,7 @@
 
     /** Returns the index of this task in the list of filtered tasks */
     int indexOf(Task t) {
-        return mFilteredTasks.indexOf(t);
+        return mTaskIndices.get(t.key);
     }
 
     /** Returns the size of the list of filtered tasks */
@@ -93,7 +98,7 @@
 
     /** Returns whether the filtered list contains this task */
     boolean contains(Task t) {
-        return mFilteredTasks.contains(t);
+        return mTaskIndices.containsKey(t.key);
     }
 
     /** Updates the list of filtered tasks whenever the base task list changes */
@@ -110,6 +115,17 @@
         } else {
             mFilteredTasks.addAll(mTasks);
         }
+        updateFilteredTaskIndices();
+    }
+
+    /** Updates the mapping of tasks to indices. */
+    private void updateFilteredTaskIndices() {
+        mTaskIndices.clear();
+        int taskCount = mFilteredTasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task t = mFilteredTasks.get(i);
+            mTaskIndices.put(t.key, i);
+        }
     }
 
     /** Returns whether this task list is filtered */
@@ -127,7 +143,8 @@
  * The task stack contains a list of multiple tasks.
  */
 public class TaskStack {
-    /* Task stack callbacks */
+
+    /** Task stack callbacks */
     public interface TaskStackCallbacks {
         /* Notifies when a task has been added to the stack */
         public void onStackTaskAdded(TaskStack stack, Task t);
@@ -139,11 +156,25 @@
         public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
     }
 
+    /** A pair of indices representing the group and task positions in the stack and group. */
+    public static class GroupTaskIndex {
+        public int groupIndex; // Index in the stack
+        public int taskIndex;  // Index in the group
+
+        public GroupTaskIndex() {}
+
+        public GroupTaskIndex(int gi, int ti) {
+            groupIndex = gi;
+            taskIndex = ti;
+        }
+    }
+
     FilteredTaskList mTaskList = new FilteredTaskList();
     TaskStackCallbacks mCb;
 
-    public TaskStack() {
-    }
+    ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
+    HashMap<String, TaskGrouping> mAffinitiesGroups = new HashMap<String, TaskGrouping>();
+    HashMap<TaskGrouping, Integer> mGroupsIndices = new HashMap<TaskGrouping, Integer>();
 
     /** Sets the callbacks for this task stack */
     public void setCallbacks(TaskStackCallbacks cb) {
@@ -161,8 +192,16 @@
     /** Removes a task */
     public void removeTask(Task t) {
         if (mTaskList.contains(t)) {
+            // Remove the task from the list
             mTaskList.remove(t);
+            // Remove it from the group as well, and if it is empty, remove the group
+            TaskGrouping group = t.group;
+            group.removeTask(t);
+            if (group.getTaskCount() == 0) {
+                removeGroup(group);
+            }
             if (mCb != null) {
+                // Notify that a task has been removed
                 mCb.onStackTaskRemoved(this, t);
             }
         }
@@ -170,10 +209,20 @@
 
     /** Sets a few tasks in one go */
     public void setTasks(List<Task> tasks) {
-        int taskCount = mTaskList.getTasks().size();
+        ArrayList<Task> taskList = mTaskList.getTasks();
+        int taskCount = taskList.size();
         for (int i = 0; i < taskCount; i++) {
-            Task t = mTaskList.getTasks().get(i);
+            Task t = taskList.get(i);
+            // Remove the task from the list
+            mTaskList.remove(t);
+            // Remove it from the group as well, and if it is empty, remove the group
+            TaskGrouping group = t.group;
+            group.removeTask(t);
+            if (group.getTaskCount() == 0) {
+                removeGroup(group);
+            }
             if (mCb != null) {
+                // Notify that a task has been removed
                 mCb.onStackTaskRemoved(this, t);
             }
         }
@@ -185,6 +234,11 @@
         }
     }
 
+    /** Gets the front task */
+    public Task getFrontMostTask() {
+        return mTaskList.getTasks().get(mTaskList.size() - 1);
+    }
+
     /** Gets the tasks */
     public ArrayList<Task> getTasks() {
         return mTaskList.getTasks();
@@ -200,10 +254,7 @@
         return mTaskList.indexOf(t);
     }
 
-    /** Tests whether a task is in this current task stack */
-    public boolean containsTask(Task t) {
-        return mTaskList.contains(t);
-    }
+    /******** Filtering ********/
 
     /** Filters the stack into tasks similar to the one specified */
     public void filterTasks(final Task t) {
@@ -238,6 +289,135 @@
         return mTaskList.hasFilter();
     }
 
+    /******** Grouping ********/
+
+    /** Adds a group to the set */
+    public void addGroup(TaskGrouping group) {
+        mGroups.add(group);
+        mAffinitiesGroups.put(group.affiliation, group);
+        updateTaskGroupingIndices();
+    }
+
+    public void removeGroup(TaskGrouping group) {
+        // XXX: Ensure that there are no more tasks in this group
+        mGroups.remove(group);
+        mAffinitiesGroups.remove(group.affiliation);
+        mGroupsIndices.remove(group);
+        updateTaskGroupingIndices();
+    }
+
+    /** Adds a mapping from a task to a group. */
+    public void addTaskToGroup(TaskGrouping group, Task task) {
+        if (!mAffinitiesGroups.containsKey(group.affiliation)) {
+            throw new RuntimeException("Unexpected group");
+        }
+        group.addTask(task);
+    }
+
+    /** Returns the group with the specified affiliation. */
+    public TaskGrouping getGroupWithAffiliation(String affiliation) {
+        return mAffinitiesGroups.get(affiliation);
+    }
+
+    /** Returns the number of groups. */
+    public int getGroupingCount() {
+        return mGroups.size();
+    }
+
+    /** Returns the group and task indices for a given task. */
+    public void getGroupIndexForTask(Task task, GroupTaskIndex indices) {
+        indices.groupIndex = mGroupsIndices.get(task.group);
+        indices.taskIndex = task.group.indexOf(task);
+    }
+
+    /**
+     * Temporary: This method will simulate affiliation groups by
+     */
+    public void createSimulatedAffiliatedGroupings() {
+        if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
+            HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
+            // Sort all tasks by increasing firstActiveTime of the task
+            ArrayList<Task> tasks = mTaskList.getTasks();
+            Collections.sort(tasks, new Comparator<Task>() {
+                @Override
+                public int compare(Task task, Task task2) {
+                    return (int) (task.key.firstActiveTime - task2.key.firstActiveTime);
+                }
+            });
+            // Create groups when sequential packages are the same
+            NamedCounter counter = new NamedCounter("task-group", "");
+            int taskCount = tasks.size();
+            String prevPackage = "";
+            String prevAffiliation = "";
+            for (int i = 0; i < taskCount; i++) {
+                Task t = tasks.get(i);
+                String packageName = t.key.baseIntent.getComponent().getPackageName();
+                TaskGrouping group;
+                if (packageName.equals(prevPackage)) {
+                    group = getGroupWithAffiliation(prevAffiliation);
+                } else {
+                    String affiliation = counter.nextName();
+                    group = new TaskGrouping(affiliation);
+                    addGroup(group);
+                    prevAffiliation = affiliation;
+                    prevPackage = packageName;
+                }
+                group.addTask(t);
+                taskMap.put(t.key, t);
+            }
+            // Sort groups by increasing latestActiveTime of the group
+            Collections.sort(mGroups, new Comparator<TaskGrouping>() {
+                @Override
+                public int compare(TaskGrouping taskGrouping, TaskGrouping taskGrouping2) {
+                    return (int) (taskGrouping.latestActiveTimeInGroup -
+                            taskGrouping2.latestActiveTimeInGroup);
+                }
+            });
+            updateTaskGroupingIndices();
+            // Sort group tasks by increasing firstActiveTime of the task, and also build a new list of
+            // tasks
+            int taskIndex = 0;
+            int groupCount = mGroups.size();
+            for (int i = 0; i < groupCount; i++) {
+                TaskGrouping group = mGroups.get(i);
+                Collections.sort(group.mTasks, new Comparator<Task.TaskKey>() {
+                    @Override
+                    public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
+                        return (int) (taskKey.firstActiveTime - taskKey2.firstActiveTime);
+                    }
+                });
+                ArrayList<Task.TaskKey> groupTasks = group.mTasks;
+                int groupTaskCount = groupTasks.size();
+                for (int j = 0; j < groupTaskCount; j++) {
+                    tasks.set(taskIndex, taskMap.get(groupTasks.get(j)));
+                    taskIndex++;
+                }
+            }
+            mTaskList.set(tasks);
+        } else {
+            // Create a group per task
+            NamedCounter counter = new NamedCounter("task-group", "");
+            ArrayList<Task> tasks = mTaskList.getTasks();
+            int taskCount = tasks.size();
+            for (int i = 0; i < taskCount; i++) {
+                Task t = tasks.get(i);
+                TaskGrouping group = new TaskGrouping(counter.nextName());
+                addGroup(group);
+                group.addTask(t);
+            }
+        }
+    }
+
+    /** Updates the mapping of tasks to indices. */
+    private void updateTaskGroupingIndices() {
+        mGroupsIndices.clear();
+        int groupsCount = mGroups.size();
+        for (int i = 0; i < groupsCount; i++) {
+            TaskGrouping g = mGroups.get(i);
+            mGroupsIndices.put(g, i);
+        }
+    }
+
     @Override
     public String toString() {
         String str = "Tasks:\n";
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
index 6568b1a..49a7ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -31,7 +30,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index f203d3e..87d45ba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,7 +34,7 @@
 import android.view.View;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
@@ -438,23 +438,25 @@
         }
 
         // Upfront the processing of the thumbnail
-        TaskViewTransform transform;
+        TaskViewTransform transform = new TaskViewTransform();
         View sourceView = tv;
         int offsetX = 0;
         int offsetY = 0;
         int stackScroll = stackView.getStackScroll();
+        TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+        stack.getGroupIndexForTask(task, groupTaskIndex);
         if (tv == null) {
             // If there is no actual task view, then use the stack view as the source view
             // and then offset to the expected transform rect, but bound this to just
             // outside the display rect (to ensure we don't animate from too far away)
             sourceView = stackView;
-            transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
-                    stackScroll);
+            transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+                    groupTaskIndex.taskIndex, stackScroll, transform);
             offsetX = transform.rect.left;
             offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
         } else {
-            transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
-                    stackScroll);
+            transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+                    groupTaskIndex.taskIndex, stackScroll, transform);
         }
 
         // Compute the thumbnail to scale up from
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index cae6bd7..8409227 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -28,7 +28,7 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 9c60603..4ed3b59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -34,7 +34,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index dd47fdd..4a1faee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -31,18 +31,19 @@
 import android.widget.FrameLayout;
 import android.widget.OverScroller;
 import com.android.systemui.R;
-import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.DozeTrigger;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.ReferenceCountedTrigger;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Set;
 
 
@@ -180,7 +181,18 @@
         mStackViewsDirty = true;
     }
 
-    /** Finds the child view given a specific task */
+    /** Returns a mapping of child view to Task. */
+    HashMap<Task, TaskView> getTaskChildViewMap() {
+        HashMap<Task, TaskView> taskViewMap = new HashMap<Task, TaskView>();
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            TaskView tv = (TaskView) getChildAt(i);
+            taskViewMap.put(tv.getTask(), tv);
+        }
+        return taskViewMap;
+    }
+
+    /** Finds the child view given a specific task. */
     TaskView getChildViewForTask(Task t) {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -225,9 +237,11 @@
         }
 
         // Update the stack transforms
+        TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
         for (int i = taskCount - 1; i >= 0; i--) {
-            TaskViewTransform transform = mStackAlgorithm.getStackTransform(i, stackScroll,
-                    taskTransforms.get(i));
+            mStack.getGroupIndexForTask(tasks.get(i), groupTaskIndex);
+            TaskViewTransform transform = mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex,
+                    groupTaskIndex.taskIndex, stackScroll, taskTransforms.get(i));
             if (transform.visible) {
                 if (frontMostVisibleIndex < 0) {
                     frontMostVisibleIndex = i;
@@ -253,6 +267,11 @@
         if (visibleRangeOut != null) {
             visibleRangeOut[0] = frontMostVisibleIndex;
             visibleRangeOut[1] = backMostVisibleIndex;
+            if (Console.Enabled) {
+                Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
+                        "[TaskStackView|updateStackTransforms]",
+                        "Back: " + backMostVisibleIndex + " Front: " + frontMostVisibleIndex);
+            }
         }
     }
 
@@ -278,58 +297,57 @@
                     "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
         }
         if (mStackViewsDirty) {
-            // XXX: Consider using TaskViewTransform pool to prevent allocations
-            // XXX: Iterate children views, update transforms and remove all that are not visible
-            //      For all remaining tasks, update transforms and if visible add the view
-
             // Get all the task transforms
-            int[] visibleRange = mTmpVisibleRange;
-            int stackScroll = getStackScroll();
             ArrayList<Task> tasks = mStack.getTasks();
+            int stackScroll = getStackScroll();
+            int[] visibleRange = mTmpVisibleRange;
             updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
+            TaskViewTransform tmpTransform = new TaskViewTransform();
+            TaskStack.GroupTaskIndex gti = new TaskStack.GroupTaskIndex();
 
-            // Update the visible state of all the tasks
-            int taskCount = tasks.size();
-            for (int i = 0; i < taskCount; i++) {
-                Task task = tasks.get(i);
-                TaskViewTransform transform = mCurrentTaskTransforms.get(i);
-                TaskView tv = getChildViewForTask(task);
-
-                if (transform.visible) {
-                    if (tv == null) {
-                        tv = mViewPool.pickUpViewFromPool(task, task);
-                        // When we are picking up a new view from the view pool, prepare it for any
-                        // following animation by putting it in a reasonable place
-                        if (mStackViewsAnimationDuration > 0 && i != 0) {
-                            int fromIndex = (transform.t < 0) ?
-                                    Math.max(0, (visibleRange[1] - 1)) :
-                                    Math.min(taskCount - 1, (visibleRange[0] + 1));
-                            tv.updateViewPropertiesToTaskTransform(
-                                    mStackAlgorithm.getStackTransform(fromIndex, stackScroll), 0);
-                        }
-                    }
-                } else {
-                    if (tv != null) {
-                        mViewPool.returnViewToPool(tv);
-                    }
-                }
-            }
-
-            // Update all the remaining view children
-            // NOTE: We have to iterate in reverse where because we are removing views directly
+            // Return all the invisible children to the pool
+            HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
             int childCount = getChildCount();
             for (int i = childCount - 1; i >= 0; i--) {
                 TaskView tv = (TaskView) getChildAt(i);
                 Task task = tv.getTask();
                 int taskIndex = mStack.indexOfTask(task);
-                if (taskIndex < 0 || !mCurrentTaskTransforms.get(taskIndex).visible) {
+                if (taskIndex < visibleRange[1] || taskIndex > visibleRange[0]) {
+                    taskChildViewMap.remove(task);
                     mViewPool.returnViewToPool(tv);
-                } else {
-                    tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
-                            mStackViewsAnimationDuration);
                 }
             }
 
+            // Pick up all the newly visible children and update all the existing children
+            boolean isValidVisibleRange = visibleRange[0] != -1 && visibleRange[1] != -1;
+            for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
+                Task task = tasks.get(i);
+                TaskViewTransform transform = mCurrentTaskTransforms.get(i);
+                TaskView tv = taskChildViewMap.get(task);
+                int taskIndex = mStack.indexOfTask(task);
+
+                if (tv == null) {
+                    tv = mViewPool.pickUpViewFromPool(task, task);
+                    if (mStackViewsAnimationDuration > 0) {
+                        // For items in the list, put them in start animating them from the
+                        // approriate ends of the list where they are expected to appear
+                        Task fromTask = (transform.t < 0) ?
+                                tasks.get(visibleRange[1]) :
+                                tasks.get(visibleRange[0]);
+                        mStack.getGroupIndexForTask(fromTask, gti);
+                        tmpTransform = mStackAlgorithm.getStackTransform(
+                                (transform.t < 0) ? gti.groupIndex - 1 : gti.groupIndex + 1,
+                                (transform.t < 0) ? gti.taskIndex - 1 : gti.taskIndex + 1,
+                                stackScroll, tmpTransform);
+                        tv.updateViewPropertiesToTaskTransform(tmpTransform, 0);
+                    }
+                }
+
+                // Update and animate the task into place
+                tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
+                        mStackViewsAnimationDuration);
+            }
+
             if (Console.Enabled) {
                 Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
                         "  [TaskStackView|viewChildren]", "" + getChildCount());
@@ -357,7 +375,7 @@
     }
     /** Computes the initial stack scroll for the stack. */
     int getInitialStackScroll() {
-        if (mStack.getTaskCount() > 2) {
+        if (mStack.getGroupingCount() > 2) {
             return mMaxScroll - mStackAlgorithm.mTaskRect.height() / 2;
         }
         return mMaxScroll;
@@ -477,7 +495,7 @@
     /** Updates the min and max virtual scroll bounds */
     void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
         // Compute the min and max scroll values
-        mStackAlgorithm.computeMinMaxScroll(mStack.getTaskCount());
+        mStackAlgorithm.computeMinMaxScroll(mStack.getGroupingCount());
         mMinScroll = mStackAlgorithm.mMinScroll;
         mMaxScroll = mStackAlgorithm.mMaxScroll;
 
@@ -794,30 +812,37 @@
             return;
         }
 
-        // Animate all the task views into view
-        TaskViewTransform transform = mStackAlgorithm.getStackTransform(mStack.getTaskCount() - 1,
-                getInitialStackScroll());
-        ctx.taskRect = transform.rect;
-        ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
-        int childCount = getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            TaskView tv = (TaskView) getChildAt(i);
-            ctx.stackViewIndex = i;
-            ctx.stackViewCount = childCount;
-            ctx.isFrontMost = (i == (getChildCount() - 1));
-            ctx.transform = mStackAlgorithm.getStackTransform(
-                    mStack.indexOfTask(tv.getTask()), getStackScroll());
-            tv.startEnterRecentsAnimation(ctx);
-        }
-
-        // Add a runnable to the post animation ref counter to clear all the views
-        ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-            @Override
-            public void run() {
-                // Start dozing
-                mUIDozeTrigger.startDozing();
+        if (mStack.getTaskCount() > 0) {
+            // Animate all the task views into view
+            TaskViewTransform transform = new TaskViewTransform();
+            TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+            mStack.getGroupIndexForTask(mStack.getFrontMostTask(), groupTaskIndex);
+            mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+                    getInitialStackScroll(), transform);
+            ctx.taskRect = transform.rect;
+            ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
+            int childCount = getChildCount();
+            for (int i = childCount - 1; i >= 0; i--) {
+                TaskView tv = (TaskView) getChildAt(i);
+                ctx.stackViewIndex = i;
+                ctx.stackViewCount = childCount;
+                ctx.isFrontMost = (i == (getChildCount() - 1));
+                ctx.transform = new TaskViewTransform();
+                mStack.getGroupIndexForTask(tv.getTask(), groupTaskIndex);
+                mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+                        getStackScroll(), ctx.transform);
+                tv.startEnterRecentsAnimation(ctx);
             }
-        });
+
+            // Add a runnable to the post animation ref counter to clear all the views
+            ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    // Start dozing
+                    mUIDozeTrigger.startDozing();
+                }
+            });
+        }
     }
 
     /** Requests this task stacks to start it's exit-recents animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index daa18bc..ab47757 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -19,7 +19,7 @@
 import android.graphics.Rect;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
 
 /* The layout logic for a TaskStackView */
 public class TaskStackViewLayoutAlgorithm {
@@ -94,17 +94,11 @@
         }
     }
 
-    /** Update/get the transform (creates a new TaskViewTransform) */
-    public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
-        TaskViewTransform transform = new TaskViewTransform();
-        return getStackTransform(indexInStack, stackScroll, transform);
-    }
-
     /** Update/get the transform */
-    public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
-                                               TaskViewTransform transformOut) {
+    public TaskViewTransform getStackTransform(int groupIndexInStack, int taskIndexInGroup,
+                                               int stackScroll, TaskViewTransform transformOut) {
         // Return early if we have an invalid index
-        if (indexInStack < 0) {
+        if (groupIndexInStack < 0) {
             transformOut.reset();
             return transformOut;
         }
@@ -113,7 +107,7 @@
         int numPeekCards = StackPeekNumCards;
         float overlapHeight = StackOverlapPct * mTaskRect.height();
         float peekHeight = StackPeekHeightPct * mStackRect.height();
-        float t = ((indexInStack * overlapHeight) - stackScroll) / overlapHeight;
+        float t = ((groupIndexInStack * overlapHeight) - stackScroll) / overlapHeight;
         float boundedT = Math.max(t, -(numPeekCards + 1));
 
         // Set the scale relative to its position
@@ -133,6 +127,7 @@
         } else {
             transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
+        transformOut.translationY += 100 * taskIndexInGroup;
 
         // Set the z translation
         int minZ = mConfig.taskViewTranslationZMinPx;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 304d45c..e186e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -22,7 +22,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 
 /* Handles touch events for a TaskStackView. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 6b06945..0d0fccc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -25,18 +25,14 @@
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.Task;
@@ -61,6 +57,7 @@
     Task mTask;
     boolean mTaskDataLoaded;
     boolean mIsFocused;
+    boolean mIsStub;
     boolean mClipViewInStack;
     Rect mTmpRect = new Rect();
     Paint mLayerPaint = new Paint();
@@ -133,8 +130,8 @@
 
         // Update the outline
         Outline o = new Outline();
-        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
-                mConfig.taskViewShadowOutlineBottomInsetPx, mConfig.taskViewRoundedCornerRadiusPx);
+        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+                mConfig.taskViewRoundedCornerRadiusPx);
         setOutline(o);
     }
 
@@ -469,6 +466,21 @@
         mBarView.disableHwLayers();
     }
 
+    /** Sets the stubbed state of this task view. */
+    void setStubState(boolean isStub) {
+        if (!mIsStub && isStub) {
+            // This is now a stub task view, so clip to the bar height, hide the thumbnail
+            setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
+            mThumbnailView.setVisibility(View.INVISIBLE);
+            // Temporary
+            mBarView.mActivityDescription.setText("Stub");
+        } else if (mIsStub && !isStub) {
+            setClipBounds(null);
+            mThumbnailView.setVisibility(View.VISIBLE);
+        }
+        mIsStub = isStub;
+    }
+
     /**
      * Returns whether this view should be clipped, or any views below should clip against this
      * view.
@@ -573,7 +585,9 @@
             mThumbnailView.rebindToTask(mTask);
             mBarView.rebindToTask(mTask);
             // Rebind any listeners
-            mBarView.mApplicationIcon.setOnClickListener(this);
+            if (Constants.DebugFlags.App.EnableTaskFiltering) {
+                mBarView.mApplicationIcon.setOnClickListener(this);
+            }
             mBarView.mDismissButton.setOnClickListener(this);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 if (mConfig.developerOptionsEnabled) {
@@ -592,7 +606,9 @@
             mThumbnailView.unbindFromTask();
             mBarView.unbindFromTask();
             // Unbind any listeners
-            mBarView.mApplicationIcon.setOnClickListener(null);
+            if (Constants.DebugFlags.App.EnableTaskFiltering) {
+                mBarView.mApplicationIcon.setOnClickListener(null);
+            }
             mBarView.mDismissButton.setOnClickListener(null);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 mBarView.mApplicationIcon.setOnLongClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index fa97d2c..ec24198 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -17,7 +17,7 @@
 package com.android.systemui.recents.views;
 
 import android.graphics.Rect;
-import com.android.systemui.recents.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 
 /* Common code related to view animations */
 public class ViewAnimation {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 0a3fdef..a11e610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -60,7 +60,7 @@
         } else if (mDemoMode && command.equals(COMMAND_STATUS)) {
             String volume = args.getString("volume");
             if (volume != null) {
-                int iconId = volume.equals("zen") ? R.drawable.stat_sys_ringer_zen
+                int iconId = volume.equals("important") ? R.drawable.stat_sys_zen_important
                         : volume.equals("silent") ? R.drawable.stat_sys_ringer_silent
                         : volume.equals("vibrate") ? R.drawable.stat_sys_ringer_vibrate
                         : 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c9606ca..bea5d49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -147,7 +147,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mHeader = (StatusBarHeaderView) findViewById(R.id.header);
-        mHeader.getBackgroundView().setOnClickListener(this);
+        mHeader.setOnClickListener(this);
         mHeader.setOverlayParent(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
         mQsContainer = findViewById(R.id.quick_settings_container);
@@ -484,6 +484,9 @@
             mInitialTouchY = event.getX();
             mInitialTouchX = event.getY();
         }
+        if (mExpandedHeight != 0) {
+            handleQsDown(event);
+        }
         if (mQsTracking || mQsExpanded) {
             onQsTouch(event);
             if (!mConflictingQsExpansionGesture) {
@@ -498,6 +501,17 @@
         return true;
     }
 
+    private void handleQsDown(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN
+                && shouldQuickSettingsIntercept(event.getX(), event.getY(), 0)) {
+            mQsTracking = true;
+            onQsExpansionStarted();
+            mInitialHeightOnTouch = mQsExpansionHeight;
+            mInitialTouchY = event.getX();
+            mInitialTouchX = event.getY();
+        }
+    }
+
     @Override
     protected boolean flingExpands(float vel, float vectorVel) {
         boolean expands = super.flingExpands(vel, vectorVel);
@@ -1120,7 +1134,7 @@
 
     @Override
     public void onClick(View v) {
-        if (v == mHeader.getBackgroundView()) {
+        if (v == mHeader) {
             onQsExpansionStarted();
             if (mQsExpanded) {
                 flingSettings(0 /* vel */, false /* expand */);
@@ -1251,4 +1265,9 @@
     public boolean isQsExpanded() {
         return mQsExpanded;
     }
+
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return true;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f697098..6532b71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -538,15 +538,6 @@
                 R.id.notification_panel);
         mNotificationPanel.setStatusBar(this);
 
-        // make the header non-responsive to clicks
-        mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
-                new View.OnTouchListener() {
-                    @Override
-                    public boolean onTouch(View v, MotionEvent event) {
-                        return true; // e eats everything
-                    }
-                });
-
         if (!ActivityManager.isHighEndGfx()) {
             mStatusBarWindow.setBackground(null);
             mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
@@ -700,7 +691,6 @@
 //                    updateCarrierLabelVisibility(false);
         }
 
-        mBatteryController.setStatusBarHeaderView(mHeader);
         mFlashlightController = new FlashlightController(mContext);
 
         // Set up the quick settings tile panel
@@ -720,6 +710,8 @@
         mHeader.setUserInfoController(mUserInfoController);
         mUserInfoController.reloadUserInfo();
 
+        mHeader.setBatteryController(mBatteryController);
+
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mBroadcastReceiver.onReceive(mContext,
                 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
@@ -738,6 +730,14 @@
         return mStatusBarView;
     }
 
+    @Override
+    protected void setZenMode(int mode) {
+        super.setZenMode(mode);
+        if (mIconPolicy != null) {
+            mIconPolicy.setZenMode(mode);
+        }
+    }
+
     private void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b6f5ae0..186b618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -24,6 +24,7 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.os.Handler;
+import android.provider.Settings.Global;
 import android.util.Log;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -64,7 +65,7 @@
     private boolean mVolumeVisible;
 
     // zen mode
-    private boolean mZen;
+    private int mZen;
 
     // bluetooth device status
     private boolean mBluetoothEnabled = false;
@@ -155,7 +156,7 @@
         updateVolume();
     }
 
-    public void setZenMode(boolean zen) {
+    public void setZenMode(int zen) {
         mZen = zen;
         updateVolume();
     }
@@ -202,21 +203,21 @@
     private final void updateVolume() {
         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         final int ringerMode = audioManager.getRingerMode();
-        final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
-                ringerMode == AudioManager.RINGER_MODE_VIBRATE ||
-                mZen;
-
-        final int iconId;
+        int iconId = 0;
         String contentDescription = null;
-        if (mZen) {
-            iconId = R.drawable.stat_sys_ringer_zen;
-            contentDescription = mContext.getString(R.string.zen_mode_title);
+        boolean visible = false;
+        if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+            visible = true;
+            iconId = R.drawable.stat_sys_ringer_silent;
+            contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
+        } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+            visible = true;
+            iconId = R.drawable.stat_sys_zen_important;
+            contentDescription = mContext.getString(R.string.zen_important_interruptions);
         } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            visible = true;
             iconId = R.drawable.stat_sys_ringer_vibrate;
             contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
-        } else {
-            iconId =  R.drawable.stat_sys_ringer_silent;
-            contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
         }
 
         if (visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 04b1443..5fbade1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -21,19 +21,16 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 
-import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
 import com.android.systemui.qs.tiles.BluetoothTile;
-import com.android.systemui.qs.tiles.BugreportTile;
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
 import com.android.systemui.qs.tiles.FlashlightTile;
-import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.NotificationsTile;
-import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.HotspotTile;
+import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.WifiTile;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.BluetoothController;
@@ -93,7 +90,6 @@
         mTiles.add(new ColorInversionTile(this));
         mTiles.add(new CellularTile(this));
         mTiles.add(new AirplaneModeTile(this));
-        mTiles.add(new NotificationsTile(this));
         mTiles.add(new RotationLockTile(this));
         mTiles.add(new LocationTile(this));
         mTiles.add(new CastTile(this));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 2119316..70a664f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.graphics.Outline;
 import android.graphics.Rect;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -36,24 +37,20 @@
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSlider;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.UserInfoController;
 
 /**
  * The view to manage the header area in the expanded status bar.
  */
-public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener {
-
-    /**
-     * How much the header expansion gets rubberbanded while expanding the panel.
-     */
-    private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
+public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
+        BatteryController.BatteryStateChangeCallback {
 
     private boolean mExpanded;
     private boolean mListening;
     private boolean mOverscrolled;
     private boolean mKeyguardShowing;
 
-    private View mBackground;
     private ViewGroup mSystemIconsContainer;
     private View mSystemIconsSuperContainer;
     private View mDateTime;
@@ -66,10 +63,9 @@
     private View mBrightnessContainer;
     private View mQsDetailHeader;
     private View mEmergencyCallsOnly;
-    private TextView mChargingInfo;
+    private TextView mBatteryLevel;
 
     private boolean mShowEmergencyCallsOnly;
-    private boolean mShowChargingInfo;
     private boolean mKeyguardUserSwitcherShowing;
 
     private int mCollapsedHeight;
@@ -84,6 +80,7 @@
 
     private ActivityStarter mActivityStarter;
     private BrightnessController mBrightnessController;
+    private BatteryController mBatteryController;
     private QSPanel mQSPanel;
 
     private final Rect mClipBounds = new Rect();
@@ -96,9 +93,9 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mBackground = findViewById(R.id.background);
         mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
         mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
+        mSystemIconsSuperContainer.setOnClickListener(this);
         mDateTime = findViewById(R.id.datetime);
         mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
         mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
@@ -112,7 +109,7 @@
         mQsDetailHeader = findViewById(R.id.qs_detail_header);
         mQsDetailHeader.setAlpha(0);
         mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
-        mChargingInfo = (TextView) findViewById(R.id.header_charging_info);
+        mBatteryLevel = (TextView) findViewById(R.id.battery_level);
         loadDimens();
         updateVisibilities();
         addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@@ -145,6 +142,10 @@
         mActivityStarter = activityStarter;
     }
 
+    public void setBatteryController(BatteryController batteryController) {
+        mBatteryController = batteryController;
+    }
+
     public int getCollapsedHeight() {
         return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
     }
@@ -159,6 +160,7 @@
         }
         mListening = listening;
         updateBrightnessControllerState();
+        updateBatteryListening();
     }
 
     public void setExpanded(boolean expanded, boolean overscrolled) {
@@ -220,7 +222,11 @@
 
     private void updateVisibilities() {
         boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
-        mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+        if (onKeyguardAndCollapsed) {
+            setBackground(null);
+        } else {
+            setBackgroundResource(R.drawable.notification_header_bg);
+        }
         mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
         mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
         mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
@@ -235,38 +241,23 @@
         }
         mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly
                 ? VISIBLE : GONE);
-        mChargingInfo.setVisibility(mExpanded && !mOverscrolled && mShowChargingInfo
-                && !mShowEmergencyCallsOnly ? VISIBLE : GONE);
         mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
                 ? VISIBLE : GONE);
+        mBatteryLevel.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
     }
 
     private void updateSystemIconsLayoutParams() {
         RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
-        boolean systemIconsAboveClock = mExpanded && !mOverscrolled
-                && mShowChargingInfo && !mShowEmergencyCallsOnly;
-        lp.setMarginEnd(0);
-        if (systemIconsAboveClock) {
-            lp.addRule(ALIGN_PARENT_START);
-            lp.removeRule(START_OF);
+        lp.addRule(RelativeLayout.START_OF, mExpanded
+                ? mSettingsButton.getId()
+                : mMultiUserSwitch.getId());
+        lp.removeRule(ALIGN_PARENT_START);
+        if (mMultiUserSwitch.getVisibility() == GONE) {
+            lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
         } else {
-            lp.addRule(RelativeLayout.START_OF, mExpanded
-                    ? mSettingsButton.getId()
-                    : mMultiUserSwitch.getId());
-            lp.removeRule(ALIGN_PARENT_START);
-            if (mMultiUserSwitch.getVisibility() == GONE) {
-                lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
-            }
+            lp.setMarginEnd(0);
         }
         mSystemIconsSuperContainer.setLayoutParams(lp);
-
-        RelativeLayout.LayoutParams clockLp = (LayoutParams) mDateTime.getLayoutParams();
-        if (systemIconsAboveClock) {
-            clockLp.addRule(BELOW, mChargingInfo.getId());
-        } else {
-            clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
-        }
-        mDateTime.setLayoutParams(clockLp);
     }
 
     private void updateBrightnessControllerState() {
@@ -277,9 +268,24 @@
         }
     }
 
+    private void updateBatteryListening() {
+        if (mListening) {
+            mBatteryController.addStateChangedCallback(this);
+        } else {
+            mBatteryController.removeStateChangedCallback(this);
+        }
+    }
+
+    @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        mBatteryLevel.setText(getResources().getString(R.string.battery_level_template, level));
+    }
+
     private void updateClickTargets() {
+        setClickable(!mKeyguardShowing || mExpanded);
         mDateTime.setClickable(mExpanded);
         mMultiUserSwitch.setClickable(mExpanded);
+        mSystemIconsSuperContainer.setClickable(mExpanded);
     }
 
     private void updateZTranslation() {
@@ -326,10 +332,6 @@
         setOutline(mOutline);
     }
 
-    public View getBackgroundView() {
-        return mBackground;
-    }
-
     public void attachSystemIcons(LinearLayout systemIcons) {
         mSystemIconsContainer.addView(systemIcons);
         mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
@@ -355,6 +357,7 @@
         updateZTranslation();
         updatePadding();
         updateMultiUserSwitch();
+        updateClickTargets();
     }
 
     public void setUserInfoController(UserInfoController userInfoController) {
@@ -369,6 +372,8 @@
     public void onClick(View v) {
         if (v == mSettingsButton) {
             startSettingsActivity();
+        } else if (v == mSystemIconsSuperContainer) {
+            startBatteryActivity();
         }
     }
 
@@ -376,6 +381,10 @@
         mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
     }
 
+    private void startBatteryActivity() {
+        mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+    }
+
     public void setQSPanel(QSPanel qsp) {
         mQSPanel = qsp;
         if (mQSPanel != null) {
@@ -392,22 +401,9 @@
         mShowEmergencyCallsOnly = show;
         if (mExpanded) {
             updateVisibilities();
-            updateSystemIconsLayoutParams();
         }
     }
 
-    public void setShowChargingInfo(boolean showChargingInfo) {
-        mShowChargingInfo = showChargingInfo;
-        if (mExpanded) {
-            updateVisibilities();
-            updateSystemIconsLayoutParams();
-        }
-    }
-
-    public void setChargingInfo(CharSequence chargingInfo) {
-        mChargingInfo.setText(chargingInfo);
-    }
-
     public void setKeyguardUserSwitcherShowing(boolean showing) {
         // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
         mKeyguardUserSwitcherShowing = showing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 4cf66a3..ccfe18d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -36,29 +36,18 @@
 public class BatteryController extends BroadcastReceiver {
     private static final String TAG = "StatusBar.BatteryController";
 
-    private ArrayList<BatteryStateChangeCallback> mChangeCallbacks =
-            new ArrayList<BatteryStateChangeCallback>();
-
-    private Context mContext;
-    private StatusBarHeaderView mStatusBarHeaderView;
-    private IBatteryStats mBatteryInfo;
+    private ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
 
     private int mLevel;
     private boolean mPluggedIn;
     private boolean mCharging;
     private boolean mCharged;
 
-
     public interface BatteryStateChangeCallback {
         public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
     }
 
     public BatteryController(Context context) {
-        mContext = context;
-
-        mBatteryInfo = IBatteryStats.Stub.asInterface(
-                ServiceManager.getService(BatteryStats.SERVICE_NAME));
-
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         context.registerReceiver(this, filter);
@@ -66,17 +55,19 @@
 
     public void addStateChangedCallback(BatteryStateChangeCallback cb) {
         mChangeCallbacks.add(cb);
+        cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
     }
 
-    public void setStatusBarHeaderView(StatusBarHeaderView statusBarHeaderView) {
-        mStatusBarHeaderView = statusBarHeaderView;
-        updateStatusBarHeaderView();
+    public void removeStateChangedCallback(BatteryStateChangeCallback cb) {
+        mChangeCallbacks.remove(cb);
     }
 
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
         if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-            mLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+            mLevel = (int)(100f
+                    * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
+                    / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
             mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
 
             final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
@@ -84,43 +75,9 @@
             mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
             mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
 
-            updateStatusBarHeaderView();
             for (BatteryStateChangeCallback cb : mChangeCallbacks) {
                 cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
             }
         }
     }
-
-    private void updateStatusBarHeaderView() {
-        if (mStatusBarHeaderView != null) {
-            mStatusBarHeaderView.setShowChargingInfo(mPluggedIn);
-            mStatusBarHeaderView.setChargingInfo(computeChargingInfo());
-        }
-    }
-
-    private String computeChargingInfo() {
-        if (!mPluggedIn || !mCharged && !mCharging) {
-            return mContext.getResources().getString(R.string.expanded_header_battery_not_charging);
-        }
-
-        if (mCharged) {
-            return mContext.getResources().getString(R.string.expanded_header_battery_charged);
-        }
-
-        // Try fetching charging time from battery stats.
-        try {
-            long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
-            if (chargingTimeRemaining > 0) {
-                String chargingTimeFormatted =
-                        Formatter.formatShortElapsedTime(mContext, chargingTimeRemaining);
-                return mContext.getResources().getString(
-                        R.string.expanded_header_battery_charging_with_time, chargingTimeFormatted);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IBatteryStats: ", e);
-        }
-
-        // Fall back to simple charging label.
-        return mContext.getResources().getString(R.string.expanded_header_battery_charging);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 4cf72a2..61902a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -22,14 +22,15 @@
 public interface ZenModeController {
     void addCallback(Callback callback);
     void removeCallback(Callback callback);
-    void setZen(boolean zen);
-    boolean isZen();
+    void setZen(int zen);
+    int getZen();
     void requestConditions(boolean request);
     void setExitConditionId(Uri exitConditionId);
     Uri getExitConditionId();
+    boolean hasNextAlarm();
 
     public static class Callback {
-        public void onZenChanged(boolean zen) {}
+        public void onZenChanged(int zen) {}
         public void onExitConditionChanged(Uri exitConditionId) {}
         public void onConditionsChanged(Condition[] conditions) {}
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index da8fd32..7703966 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -28,6 +28,7 @@
 import android.service.notification.ZenModeConfig;
 import android.util.Slog;
 
+import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.qs.GlobalSetting;
 
 import java.util.ArrayList;
@@ -44,15 +45,17 @@
     private final GlobalSetting mConfigSetting;
     private final INotificationManager mNoMan;
     private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
+    private final LockPatternUtils mUtils;
 
     private boolean mRequesting;
 
     public ZenModeControllerImpl(Context context, Handler handler) {
         mContext = context;
+        mUtils = new LockPatternUtils(mContext);
         mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
             @Override
             protected void handleValueChanged(int value) {
-                fireZenChanged(value != 0);
+                fireZenChanged(value);
             }
         };
         mConfigSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE_CONFIG_ETAG) {
@@ -78,13 +81,13 @@
     }
 
     @Override
-    public boolean isZen() {
-        return mModeSetting.getValue() != 0;
+    public int getZen() {
+        return mModeSetting.getValue();
     }
 
     @Override
-    public void setZen(boolean zen) {
-        mModeSetting.setValue(zen ? 1 : 0);
+    public void setZen(int zen) {
+        mModeSetting.setValue(zen);
     }
 
     @Override
@@ -122,7 +125,12 @@
         return null;
     }
 
-    private void fireZenChanged(boolean zen) {
+    @Override
+    public boolean hasNextAlarm() {
+        return mUtils.getNextAlarm() != null;
+    }
+
+    private void fireZenChanged(int zen) {
         for (Callback cb : mCallbacks) {
             cb.onZenChanged(zen);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
new file mode 100644
index 0000000..d339dd4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -0,0 +1,93 @@
+/*
+ * 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 com.android.systemui.volume;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+import java.util.Objects;
+
+public class SegmentedButtons extends LinearLayout {
+
+    private final Context mContext;
+    private final LayoutInflater mInflater;
+
+    private Callback mCallback;
+    private Object mSelectedValue;
+
+    public SegmentedButtons(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mInflater = LayoutInflater.from(mContext);
+        setOrientation(HORIZONTAL);
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    public Object getSelectedValue() {
+        return mSelectedValue;
+    }
+
+    public void setSelectedValue(Object value) {
+        if (Objects.equals(value, mSelectedValue)) return;
+        mSelectedValue = value;
+        for (int i = 0; i < getChildCount(); i++) {
+            final View c = getChildAt(i);
+            final Object tag = c.getTag();
+            c.setSelected(Objects.equals(mSelectedValue, tag));
+        }
+        fireOnSelected();
+    }
+
+    public void addButton(int labelResId, Object value) {
+        final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+        b.setText(labelResId);
+        final LayoutParams lp = (LayoutParams) b.getLayoutParams();
+        if (getChildCount() == 0) {
+            lp.leftMargin = lp.rightMargin = 0; // first button has no margin
+        }
+        b.setLayoutParams(lp);
+        addView(b);
+        b.setTag(value);
+        b.setOnClickListener(mClick);
+    }
+
+    private void fireOnSelected() {
+        if (mCallback != null) {
+            mCallback.onSelected(mSelectedValue);
+        }
+    }
+
+    private final View.OnClickListener mClick = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            setSelectedValue(v.getTag());
+        }
+    };
+
+    public interface Callback {
+        void onSelected(Object value);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 53daaae..08216c4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.volume;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.BroadcastReceiver;
@@ -38,24 +36,21 @@
 import android.media.session.MediaController;
 import android.media.session.MediaController.VolumeInfo;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
+import android.provider.Settings.Global;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -71,7 +66,8 @@
  * @hide
  */
 public class VolumePanel extends Handler {
-    private static boolean LOGD = false;
+    private static final String TAG = "VolumePanel";
+    private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
 
@@ -88,8 +84,8 @@
     private static final int MAX_VOLUME = 100;
     private static final int FREE_DELAY = 10000;
     private static final int TIMEOUT_DELAY = 3000;
+    private static final int TIMEOUT_DELAY_SHORT = 1500;
     private static final int TIMEOUT_DELAY_EXPANDED = 10000;
-    private static final float ICON_PULSE_SCALE = 1.3f;
 
     private static final int MSG_VOLUME_CHANGED = 0;
     private static final int MSG_FREE_RESOURCES = 1;
@@ -105,6 +101,7 @@
     private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
     private static final int MSG_LAYOUT_DIRECTION = 12;
     private static final int MSG_ZEN_MODE_CHANGED = 13;
+    private static final int MSG_USER_ACTIVITY = 14;
 
     // Pseudo stream type for master volume
     private static final int STREAM_MASTER = -100;
@@ -114,10 +111,10 @@
     protected final Context mContext;
     private final AudioManager mAudioManager;
     private final ZenModeController mZenController;
-    private final Interpolator mFastOutSlowInInterpolator;
     private boolean mRingIsSilent;
     private boolean mVoiceCapable;
     private boolean mZenModeCapable;
+    private boolean mZenPanelExpanded;
     private int mTimeoutDelay = TIMEOUT_DELAY;
 
     // True if we want to play tones on the system stream when the master stream is specified.
@@ -135,18 +132,12 @@
     private final ViewGroup mPanel;
     /** Contains the slider and its touchable icons */
     private final ViewGroup mSliderPanel;
-    /** The button that expands the dialog to show the zen panel */
-    private final ImageView mExpandButton;
-    /** Dummy divider icon that needs to vanish with the expand button */
-    private final View mExpandDivider;
     /** The zen mode configuration panel view stub */
     private final ViewStub mZenPanelStub;
     /** The zen mode configuration panel view, once inflated */
     private ZenModePanel mZenPanel;
-    /** Dummy divider icon that needs to vanish with the zen panel */
-    private final View mZenPanelDivider;
 
-    private ZenModePanel.Callback mZenPanelCallback;
+    private Callback mCallback;
 
     /** Currently active stream that shows up at the top of the list of sliders */
     private int mActiveStreamType = -1;
@@ -231,7 +222,6 @@
         ViewGroup group;
         ImageView icon;
         SeekBar seekbarView;
-        View seekbarContainer;
         int iconRes;
         int iconMuteRes;
     }
@@ -280,13 +270,11 @@
 
 
     public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
-        mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
+        mTag = String.format("%s.%s.%08x", TAG, parent == null ? "Dialog" : "Embed", hashCode());
         mContext = context;
         mParent = parent;
         mZenController = zenController;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.fast_out_slow_in);
 
         // For now, only show master volume if master volume is supported
         final Resources res = context.getResources();
@@ -318,12 +306,11 @@
             lp.token = null;
             // Offset from the top
             lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
-            lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
-            lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
+            lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
             lp.format = PixelFormat.TRANSLUCENT;
-            lp.windowAnimations = R.style.Animation_VolumePanel;
+            lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
+            lp.gravity = Gravity.TOP;
             window.setAttributes(lp);
-            window.setGravity(Gravity.TOP);
             window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
             window.requestFeature(Window.FEATURE_NO_TITLE);
             window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
@@ -337,7 +324,7 @@
                 public void onDismiss(DialogInterface dialog) {
                     mActiveStreamType = -1;
                     mAudioManager.forceVolumeControlStream(mActiveStreamType);
-                    collapse();
+                    setZenPanelVisible(false);
                 }
             });
 
@@ -362,19 +349,14 @@
         }
         mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
         mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
-        mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
-        mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
         mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
-        mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
 
         mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
 
         mZenModeCapable = !useMasterVolume && mZenController != null;
-        mZenPanelDivider.setVisibility(View.GONE);
-        mExpandButton.setOnClickListener(mClickListener);
-        updateZenMode(mZenController == null ? false : mZenController.isZen());
+        updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
         mZenController.addCallback(mZenCallback);
 
         final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
@@ -502,17 +484,7 @@
                         toggle(sc);
                     }
                 });
-                sc.icon.setOnLongClickListener(new OnLongClickListener() {
-                    @Override
-                    public boolean onLongClick(View v) {
-                        resetTimeout();
-                        longToggle(sc);
-                        return true;
-                    }
-                });
             }
-            sc.seekbarContainer =
-                    sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
             sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
             final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
                     streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
@@ -533,29 +505,20 @@
         }
     }
 
-    private void longToggle(StreamControl sc) {
-        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
-            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
-            postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
-        } else {
-            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
-            postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
-        }
-    }
-
     private void reorderSliders(int activeStreamType) {
         mSliderPanel.removeAllViews();
 
         final StreamControl active = mStreamControls.get(activeStreamType);
         if (active == null) {
-            Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
+            Log.e(TAG, "Missing stream type! - " + activeStreamType);
             mActiveStreamType = -1;
         } else {
             mSliderPanel.addView(active.group);
             mActiveStreamType = activeStreamType;
             active.group.setVisibility(View.VISIBLE);
             updateSlider(active);
-            updateZenMode(mZenController == null ? false : mZenController.isZen());
+            updateTimeoutDelay();
+            setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
         }
     }
 
@@ -575,6 +538,7 @@
 
     private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
         final boolean wasEnabled = sc.seekbarView.isEnabled();
+        final boolean isRinger = isNotificationOrRing(sc.streamType);
         if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
             // never disable touch interactions for remote playback, the muting is not tied to
             // the state of the phone.
@@ -583,40 +547,37 @@
                         (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
                         (sConfirmSafeVolumeDialog != null)) {
             sc.seekbarView.setEnabled(false);
-        } else if (isNotificationOrRing(sc.streamType)
-                && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
+        } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
             sc.seekbarView.setEnabled(false);
+            sc.icon.setEnabled(false);
+            sc.icon.setClickable(false);
         } else {
             sc.seekbarView.setEnabled(true);
+            sc.icon.setEnabled(true);
         }
-        // pulse the ringer icon when the disabled slider is touched in silent mode
-        if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
+        // show the silent hint when the disabled slider is touched in silent mode
+        if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
             if (sc.seekbarView.isEnabled()) {
-                sc.seekbarContainer.setOnTouchListener(null);
+                sc.group.setOnTouchListener(null);
+                sc.icon.setClickable(true);
             } else {
-                sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
+                final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
                     @Override
                     public boolean onTouch(View v, MotionEvent event) {
                         resetTimeout();
-                        pulseIcon(sc.icon);
+                        showSilentHint();
                         return false;
                     }
-                });
+                };
+                sc.group.setOnTouchListener(showHintOnTouch);
             }
         }
     }
 
-    private void pulseIcon(final ImageView icon) {
-        if (icon.getScaleX() != 1) return;  // already running
-        icon.animate().cancel();
-        icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
-                .setInterpolator(mFastOutSlowInInterpolator)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        icon.animate().scaleX(1).scaleY(1).setListener(null);
-                    }
-                });
+    private void showSilentHint() {
+        if (mZenPanel != null) {
+            mZenPanel.showSilentHint();
+        }
     }
 
     private static boolean isNotificationOrRing(int streamType) {
@@ -624,47 +585,50 @@
                 || streamType == AudioManager.STREAM_NOTIFICATION;
     }
 
-    public void setZenModePanelCallback(ZenModePanel.Callback callback) {
-        mZenPanelCallback = callback;
+    public void setCallback(Callback callback) {
+        mCallback = callback;
     }
 
-    private void expand() {
-        if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
-        if (mZenPanel == null) {
-            mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
-            final boolean isDialog = mDialog != null;
-            mZenPanel.init(mZenController, isDialog ? 'D' : 'E', isDialog);
-            mZenPanel.setCallback(new ZenModePanel.Callback() {
-                @Override
-                public void onMoreSettings() {
-                    if (mZenPanelCallback != null) {
-                        mZenPanelCallback.onMoreSettings();
-                    }
-                }
-
-                @Override
-                public void onInteraction() {
-                    resetTimeout();
-                    if (mZenPanelCallback != null) {
-                        mZenPanelCallback.onInteraction();
-                    }
-                }
-            });
-        }
-        mZenPanel.setVisibility(View.VISIBLE);
-        mZenPanelDivider.setVisibility(View.VISIBLE);
-        mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
-        resetTimeout();
+    private void updateTimeoutDelay() {
+        mTimeoutDelay = mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
+                : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED : TIMEOUT_DELAY;
     }
 
-    private void collapse() {
-        if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
-        if (mZenPanel != null) {
-            mZenPanel.setVisibility(View.GONE);
+    private void setZenPanelVisible(boolean visible) {
+        if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
+        if (visible) {
+            if (mZenPanel == null) {
+                mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
+                mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
+                mZenPanel.setCallback(new ZenModePanel.Callback() {
+                    @Override
+                    public void onMoreSettings() {
+                        if (mCallback != null) {
+                            mCallback.onZenSettings();
+                        }
+                    }
+
+                    @Override
+                    public void onInteraction() {
+                        resetTimeout();
+                    }
+
+                    @Override
+                    public void onExpanded(boolean expanded) {
+                        if (mZenPanelExpanded == expanded) return;
+                        mZenPanelExpanded = expanded;
+                        updateTimeoutDelay();
+                        resetTimeout();
+                    }
+                });
+            }
+            mZenPanel.setVisibility(View.VISIBLE);
+            resetTimeout();
+        } else {
+            if (mZenPanel != null) {
+                mZenPanel.setVisibility(View.GONE);
+            }
         }
-        mZenPanelDivider.setVisibility(View.GONE);
-        mTimeoutDelay = TIMEOUT_DELAY;
-        resetTimeout();
     }
 
     public void updateStates() {
@@ -675,25 +639,14 @@
         }
     }
 
-    private void updateZenMode(boolean zen) {
-        if (mZenModeCapable) {
-            final boolean show = isNotificationOrRing(mActiveStreamType);
-            mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
-            mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
-            mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
-                    : com.android.systemui.R.drawable.ic_vol_zen_off);
-            if (show && !zen) {
-                collapse();
-            }
-        } else {
-            mExpandButton.setVisibility(View.GONE);
-            mExpandDivider.setVisibility(View.GONE);
-        }
+    private void updateZenMode(int zen) {
+        final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
+        setZenPanelVisible(show);
     }
 
-    public void postZenModeChanged(boolean zen) {
+    public void postZenModeChanged(int zen) {
         removeMessages(MSG_ZEN_MODE_CHANGED);
-        obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
+        obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
     }
 
     public void postVolumeChanged(int streamType, int flags) {
@@ -955,8 +908,8 @@
         }
 
         // Pulse the slider icon if an adjustment was suppressed due to silent mode.
-        if (sc != null && (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
-            pulseIcon(sc.icon);
+        if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
+            showSilentHint();
         }
     }
 
@@ -1233,15 +1186,23 @@
                 break;
 
             case MSG_ZEN_MODE_CHANGED:
-                updateZenMode(msg.arg1 != 0);
+                updateZenMode(msg.arg1);
+                break;
+
+            case MSG_USER_ACTIVITY:
+                if (mCallback != null) {
+                    mCallback.onInteraction();
+                }
                 break;
         }
     }
 
-    public void resetTimeout() {
+    private void resetTimeout() {
         if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
         removeMessages(MSG_TIMEOUT);
         sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
+        removeMessages(MSG_USER_ACTIVITY);
+        sendEmptyMessage(MSG_USER_ACTIVITY);
     }
 
     private void forceTimeout() {
@@ -1273,29 +1234,8 @@
         }
     };
 
-    private final View.OnClickListener mClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (v == mExpandButton && mZenController != null) {
-                final boolean newZen = !mZenController.isZen();
-                AsyncTask.execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        mZenController.setZen(newZen);
-                    }
-                });
-                if (newZen) {
-                    expand();
-                } else {
-                    collapse();
-                }
-            }
-            resetTimeout();
-        }
-    };
-
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
-        public void onZenChanged(boolean zen) {
+        public void onZenChanged(int zen) {
             postZenModeChanged(zen);
         }
     };
@@ -1305,4 +1245,9 @@
             onRemoteVolumeUpdateIfShown();
         }
     };
+
+    public interface Callback {
+        void onZenSettings();
+        void onInteraction();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index c1f92ff..51be833 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -1,8 +1,8 @@
 package com.android.systemui.volume;
 
+import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
@@ -17,8 +17,8 @@
 import android.provider.Settings;
 import android.util.Log;
 
-import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 
@@ -80,17 +80,19 @@
 
     private void initPanel() {
         mPanel = new VolumePanel(mContext, null, new ZenModeControllerImpl(mContext, mHandler));
-        final int delay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
-        mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
+        mPanel.setCallback(new VolumePanel.Callback() {
             @Override
-            public void onMoreSettings() {
+            public void onZenSettings() {
                 mHandler.removeCallbacks(mStartZenSettings);
-                mHandler.postDelayed(mStartZenSettings, delay);
+                mHandler.post(mStartZenSettings);
             }
 
             @Override
             public void onInteraction() {
-                mDialogPanel.resetTimeout();
+                final KeyguardViewMediator kvm = getComponent(KeyguardViewMediator.class);
+                if (kvm != null) {
+                    kvm.userActivity();
+                }
             }
         });
         mDialogPanel = mPanel;
@@ -108,6 +110,11 @@
         @Override
         public void run() {
             mDialogPanel.postDismiss();
+            try {
+                // Dismiss the lock screen when Settings starts.
+                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+            } catch (RemoteException e) {
+            }
             final Intent intent = ZenModePanel.ZEN_SETTINGS;
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
             mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 9917944..afa5bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.volume;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -25,6 +27,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.util.AttributeSet;
@@ -32,7 +35,8 @@
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.ImageView;
@@ -47,12 +51,19 @@
 import java.util.Objects;
 
 public class ZenModePanel extends LinearLayout {
-    private static final boolean DEBUG = false;
+    private static final String TAG = "ZenModePanel";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+    private static final int[] MINUTE_BUCKETS = DEBUG
+            ? new int[] { 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 }
+            : new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
     private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
     private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
     private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
+    private static final int FOREVER_CONDITION_INDEX = 0;
+    private static final int TIME_CONDITION_INDEX = 1;
+    private static final int FIRST_CONDITION_INDEX = 2;
+    private static final float SILENT_HINT_PULSE_SCALE = 1.1f;
 
     private static final int SECONDS_MS = 1000;
     private static final int MINUTES_MS = 60 * SECONDS_MS;
@@ -63,73 +74,110 @@
     private final LayoutInflater mInflater;
     private final H mHandler = new H();
     private final Favorites mFavorites;
+    private final Interpolator mFastOutSlowInInterpolator;
 
     private char mLogTag = '?';
     private String mTag;
-    private LinearLayout mConditions;
+
+    private SegmentedButtons mZenButtons;
+    private View mZenSubhead;
+    private TextView mZenSubheadCollapsed;
+    private TextView mZenSubheadExpanded;
     private View mMoreSettings;
+    private LinearLayout mZenConditions;
+    private View mAlarmWarning;
+
     private Callback mCallback;
     private ZenModeController mController;
     private boolean mRequestingConditions;
     private Uri mExitConditionId;
     private int mBucketIndex = -1;
-    private boolean mShowing;
+    private boolean mExpanded;
+    private int mAttachedZen;
+    private String mExitConditionText;
 
     public ZenModePanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
         mFavorites = new Favorites();
         mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
+                android.R.interpolator.fast_out_slow_in);
         updateTag();
         if (DEBUG) Log.d(mTag, "new ZenModePanel");
     }
 
     private void updateTag() {
-        mTag = "ZenModePanel/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
+        mTag = TAG + "/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mConditions = (LinearLayout) findViewById(android.R.id.content);
-        mMoreSettings = findViewById(android.R.id.button2);
+
+        mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
+        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);
+
+        mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed);
+        mZenSubheadCollapsed.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setExpanded(true);
+                fireInteraction();
+            }
+        });
+
+        mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded);
+        mZenSubheadExpanded.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setExpanded(false);
+                fireInteraction();
+            }
+        });
+
+        mMoreSettings = findViewById(R.id.zen_more_settings);
         mMoreSettings.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 fireMoreSettings();
+                fireInteraction();
             }
         });
+
+        mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
+
+        mAlarmWarning = findViewById(R.id.zen_alarm_warning);
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         if (DEBUG) Log.d(mTag, "onAttachedToWindow");
-        final ViewGroup p = (ViewGroup) getParent();
-        updateShowing(p != null && p.getVisibility() == VISIBLE);
+        mAttachedZen = getSelectedZen(-1);
+        refreshExitConditionText();
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
-        updateShowing(false);
+        mAttachedZen = -1;
+        setExpanded(false);
     }
 
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-        final boolean vis = visibility == VISIBLE;
-        updateShowing(isAttachedToWindow() && vis);
-    }
-
-    private void updateShowing(boolean showing) {
-        if (showing == mShowing) return;
-        mShowing = showing;
-        if (DEBUG) Log.d(mTag, "mShowing=" + mShowing);
-        if (mController != null) {
-            setRequestingConditions(mShowing);
-        }
+    private void setExpanded(boolean expanded) {
+        if (expanded == mExpanded) return;
+        mExpanded = expanded;
+        updateWidgets();
+        setRequestingConditions(mExpanded);
+        fireExpanded();
     }
 
     /** Start or stop requesting relevant zen mode exit conditions */
@@ -149,24 +197,40 @@
                 timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
             }
             if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
-            bind(timeCondition, mConditions.getChildAt(0));
-            handleUpdateConditions(new Condition[0]);
+            handleUpdateConditions(new Condition[0]);  // ensures forever exists
+            bind(timeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+            checkForDefault();
         } else {
-            mConditions.removeAllViews();
+            mZenConditions.removeAllViews();
         }
     }
 
-    public void init(ZenModeController controller, char logTag, boolean moreSettings) {
+    public void init(ZenModeController controller, char logTag) {
         mController = controller;
         mLogTag = logTag;
         updateTag();
-        mExitConditionId = mController.getExitConditionId();
+        setExitConditionId(mController.getExitConditionId());
+        refreshExitConditionText();
+        mAttachedZen = getSelectedZen(-1);
+        handleUpdateZen(mController.getZen());
         if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
-        mMoreSettings.setVisibility(moreSettings ? VISIBLE : GONE);
-        mConditions.removeAllViews();
+        mZenConditions.removeAllViews();
         mController.addCallback(mZenCallback);
-        if (mShowing) {
-            setRequestingConditions(true);
+    }
+
+    private void setExitConditionId(Uri exitConditionId) {
+        if (Objects.equals(mExitConditionId, exitConditionId)) return;
+        mExitConditionId = exitConditionId;
+        refreshExitConditionText();
+    }
+
+    private void refreshExitConditionText() {
+        if (mExitConditionId == null) {
+            mExitConditionText = mContext.getString(R.string.zen_mode_forever);
+        } else if (ZenModeConfig.isValidCountdownConditionId(mExitConditionId)) {
+            mExitConditionText = parseExistingTimeCondition(mExitConditionId).summary;
+        } else {
+            mExitConditionText = "(until condition ends)";  // TODO persist current description
         }
     }
 
@@ -174,6 +238,59 @@
         mCallback = callback;
     }
 
+    public void showSilentHint() {
+        if (DEBUG) Log.d(mTag, "showSilentHint");
+        if (mZenButtons == null || mZenButtons.getChildCount() == 0) return;
+        final View noneButton = mZenButtons.getChildAt(0);
+        if (noneButton.getScaleX() != 1) return;  // already running
+        noneButton.animate().cancel();
+        noneButton.animate().scaleX(SILENT_HINT_PULSE_SCALE).scaleY(SILENT_HINT_PULSE_SCALE)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        noneButton.animate().scaleX(1).scaleY(1).setListener(null);
+                    }
+                });
+    }
+
+    private void handleUpdateZen(int zen) {
+        if (mAttachedZen != -1 && mAttachedZen != zen) {
+            setExpanded(zen != Global.ZEN_MODE_OFF);
+            mAttachedZen = zen;
+        }
+        mZenButtons.setSelectedValue(zen);
+        updateWidgets();
+    }
+
+    private int getSelectedZen(int defValue) {
+        final Object zen = mZenButtons.getSelectedValue();
+        return zen != null ? (Integer) zen : defValue;
+    }
+
+    private void updateWidgets() {
+        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
+        final boolean zenOff = zen == Global.ZEN_MODE_OFF;
+        final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+        mZenSubhead.setVisibility(!zenOff ? VISIBLE : GONE);
+        mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
+        mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
+        mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
+        mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
+        mAlarmWarning.setVisibility(zenNone && mExpanded && mController != null
+                && mController.hasNextAlarm() ? VISIBLE : GONE);
+
+        if (zenNone) {
+            mZenSubheadExpanded.setText(R.string.zen_no_interruptions);
+            mZenSubheadCollapsed.setText(mExitConditionText);
+        } else if (zenImportant) {
+            mZenSubheadExpanded.setText(R.string.zen_important_interruptions);
+            mZenSubheadCollapsed.setText(mExitConditionText);
+        }
+    }
+
     private Condition parseExistingTimeCondition(Uri conditionId) {
         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
         if (time == 0) return null;
@@ -200,23 +317,23 @@
 
     private void handleUpdateConditions(Condition[] conditions) {
         final int newCount = conditions == null ? 0 : conditions.length;
-        for (int i = mConditions.getChildCount() - 1; i > newCount; i--) {
-            mConditions.removeViewAt(i);
+        if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
+        for (int i = mZenConditions.getChildCount(); i >= newCount + FIRST_CONDITION_INDEX; i--) {
+            mZenConditions.removeViewAt(i);
         }
+        bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
         for (int i = 0; i < newCount; i++) {
-            bind(conditions[i], mConditions.getChildAt(i + 1));
+            bind(conditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
         }
-        bind(null, mConditions.getChildAt(newCount + 1));
-        checkForDefault();
     }
 
     private ConditionTag getConditionTagAt(int index) {
-        return (ConditionTag) mConditions.getChildAt(index).getTag();
+        return (ConditionTag) mZenConditions.getChildAt(index).getTag();
     }
 
     private void checkForDefault() {
         // are we left without anything selected?  if so, set a default
-        for (int i = 0; i < mConditions.getChildCount(); i++) {
+        for (int i = 0; i < mZenConditions.getChildCount(); i++) {
             if (getConditionTagAt(i).rb.isChecked()) {
                 return;
             }
@@ -224,19 +341,19 @@
         if (DEBUG) Log.d(mTag, "Selecting a default");
         final int favoriteIndex = mFavorites.getMinuteIndex();
         if (favoriteIndex == -1) {
-            getConditionTagAt(mConditions.getChildCount() - 1).rb.setChecked(true);
+            getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
         } else {
             final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
             mBucketIndex = favoriteIndex;
-            bind(c, mConditions.getChildAt(0));
-            getConditionTagAt(0).rb.setChecked(true);
+            bind(c, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+            getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true);
         }
     }
 
     private void handleExitConditionChanged(Uri exitCondition) {
-        mExitConditionId = exitCondition;
+        setExitConditionId(exitCondition);
         if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId);
-        final int N = mConditions.getChildCount();
+        final int N = mZenConditions.getChildCount();
         for (int i = 0; i < N; i++) {
             final ConditionTag tag = getConditionTagAt(i);
             tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition));
@@ -249,7 +366,7 @@
         if (convertView == null) {
             row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
             if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition);
-            mConditions.addView(row);
+            mZenConditions.addView(row);
         } else {
             row = convertView;
         }
@@ -264,9 +381,9 @@
         tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                if (mShowing && isChecked) {
+                if (mExpanded && isChecked) {
                     if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId);
-                    final int N = mConditions.getChildCount();
+                    final int N = mZenConditions.getChildCount();
                     for (int i = 0; i < N; i++) {
                         ConditionTag childTag = getConditionTagAt(i);
                         if (childTag == tag) continue;
@@ -284,7 +401,7 @@
             title.setText(condition.summary);
         }
         title.setEnabled(enabled);
-        title.setAlpha(enabled ? 1 : .5f);
+        title.setAlpha(enabled ? 1 : .4f);
         final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
         button1.setOnClickListener(new OnClickListener() {
             @Override
@@ -365,7 +482,7 @@
         if (mController != null) {
             mController.setExitConditionId(conditionId);
         }
-        mExitConditionId = conditionId;
+        setExitConditionId(conditionId);
         if (conditionId == null) {
             mFavorites.setMinuteIndex(-1);
         } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
@@ -385,8 +502,18 @@
         }
     }
 
+    private void fireExpanded() {
+        if (mCallback != null) {
+            mCallback.onExpanded(mExpanded);
+        }
+    }
+
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
         @Override
+        public void onZenChanged(int zen) {
+            mHandler.obtainMessage(H.UPDATE_ZEN, zen, 0).sendToTarget();
+        }
+        @Override
         public void onConditionsChanged(Condition[] conditions) {
             mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
         }
@@ -400,6 +527,7 @@
     private final class H extends Handler {
         private static final int UPDATE_CONDITIONS = 1;
         private static final int EXIT_CONDITION_CHANGED = 2;
+        private static final int UPDATE_ZEN = 3;
 
         private H() {
             super(Looper.getMainLooper());
@@ -409,8 +537,11 @@
         public void handleMessage(Message msg) {
             if (msg.what == UPDATE_CONDITIONS) {
                 handleUpdateConditions((Condition[])msg.obj);
+                checkForDefault();
             } else if (msg.what == EXIT_CONDITION_CHANGED) {
                 handleExitConditionChanged((Uri)msg.obj);
+            } else if (msg.what == UPDATE_ZEN) {
+                handleUpdateZen(msg.arg1);
             }
         }
     }
@@ -418,6 +549,7 @@
     public interface Callback {
         void onMoreSettings();
         void onInteraction();
+        void onExpanded(boolean expanded);
     }
 
     // used as the view tag on condition rows
@@ -466,4 +598,15 @@
             return Math.max(-1, Math.min(MINUTE_BUCKETS.length - 1, index));
         }
     }
+
+    private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
+        @Override
+        public void onSelected(Object value) {
+            if (value != null && mZenButtons.isShown()) {
+                if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value);
+                mController.setZen((Integer) value);
+                mController.setExitConditionId(null);
+            }
+        }
+    };
 }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3c996b6..b31a3d6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2925,7 +2925,8 @@
 
         private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
             // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
-            final String appSourceDir = pkg.applicationInfo.sourceDir;
+            // TODO: handle backing up split APKs
+            final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
             final String apkDir = new File(appSourceDir).getParent();
             FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
                     apkDir, appSourceDir, output);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6474047..1da8123 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7258,6 +7258,7 @@
         rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
         rti.userId = tr.userId;
         rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
+        rti.firstActiveTime = tr.firstActiveTime;
         rti.lastActiveTime = tr.lastActiveTime;
         return rti;
     }
@@ -9370,8 +9371,11 @@
                     mWindowManager.setAppFullscreen(token, false);
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
                     return true;
+                } else {
+                    r.task.stack.mReturningActivityOptions = options;
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                    return false;
                 }
-                return false;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -9434,6 +9438,17 @@
         }
     }
 
+    @Override
+    public boolean isTopOfTask(IBinder token) {
+        synchronized (this) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            return r.task.getTopActivity() == r;
+        }
+    }
+
     public final void enterSafeMode() {
         synchronized(this) {
             // It only makes sense to do this before the system is ready
@@ -16534,11 +16549,9 @@
                     }
                     cleanUpApplicationRecordLocked(app, false, true, -1);
                     mRemovedProcesses.remove(i);
-                    
+
                     if (app.persistent) {
-                        if (app.persistent) {
-                            addAppLocked(app.info, false, null /* ABI override */);
-                        }
+                        addAppLocked(app.info, false, null /* ABI override */);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 545423b..ca518f3 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -209,7 +209,7 @@
     // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
     // background activity being drawn then the same call will be made with a true value.
     ActivityRecord mTranslucentActivityWaiting = null;
-    ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
+    private ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
             new ArrayList<ActivityRecord>();
     // Options passed from the caller of the convertToTranslucent to the activity that will
     // appear below it.
@@ -1203,7 +1203,17 @@
                         // else to do here.
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Skipping: already visible at " + r);
                         r.stopFreezingScreenLocked(false);
-
+                        try {
+                            if (mReturningActivityOptions != null) {
+                                if (activityNdx > 0) {
+                                    ActivityRecord under = activities.get(activityNdx - 1);
+                                    under.app.thread.scheduleOnNewActivityOptions(under.appToken,
+                                            mReturningActivityOptions);
+                                }
+                                mReturningActivityOptions = null;
+                            }
+                        } catch(RemoteException e) {
+                        }
                     } else if (onlyThisProcess == null) {
                         // This activity is not currently visible, but is running.
                         // Tell it to become visible.
@@ -1291,6 +1301,12 @@
                 }
             }
         }
+
+        if (mTranslucentActivityWaiting != null &&
+                mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
+            // Nothing is getting drawn or everything was already visible, don't wait for timeout.
+            notifyActivityDrawnLocked(null);
+        }
     }
 
     void convertToTranslucent(ActivityRecord r, ActivityOptions options) {
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
old mode 100644
new mode 100755
index 6ede8f8..35bdee0
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -27,6 +27,11 @@
 import android.widget.Button;
 
 class BaseErrorDialog extends AlertDialog {
+    private static final int ENABLE_BUTTONS = 0;
+    private static final int DISABLE_BUTTONS = 1;
+
+    private boolean mConsuming = true;
+
     public BaseErrorDialog(Context context) {
         super(context, com.android.internal.R.style.Theme_Dialog_AppError);
 
@@ -41,8 +46,8 @@
 
     public void onStart() {
         super.onStart();
-        setEnabled(false);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(0), 1000);
+        mHandler.sendEmptyMessage(DISABLE_BUTTONS);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(ENABLE_BUTTONS), 1000);
     }
 
     public boolean dispatchKeyEvent(KeyEvent event) {
@@ -71,12 +76,12 @@
 
     private Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
-            if (msg.what == 0) {
+            if (msg.what == ENABLE_BUTTONS) {
                 mConsuming = false;
                 setEnabled(true);
+            } else if (msg.what == DISABLE_BUTTONS) {
+                setEnabled(false);
             }
         }
     };
-
-    private boolean mConsuming = true;
 }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d221f96..c9890cd3cd 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -57,6 +57,7 @@
     private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
     private static final String ATTR_USERID = "user_id";
     private static final String ATTR_TASKTYPE = "task_type";
+    private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
     private static final String ATTR_LASTACTIVETIME = "last_active_time";
     private static final String ATTR_LASTDESCRIPTION = "last_description";
     private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
@@ -75,6 +76,7 @@
     Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
     ComponentName origActivity; // The non-alias activity component of the intent.
     ComponentName realActivity; // The actual activity component that started the task.
+    long firstActiveTime;   // First time this task was active.
     long lastActiveTime;    // Last time this task was active, including sleep.
     boolean rootWasReset;   // True if the intent at the root of the task had
                             // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
@@ -147,8 +149,8 @@
     TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
             String _affinity, ComponentName _realActivity, ComponentName _origActivity,
             boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
-            String _lastDescription, ArrayList<ActivityRecord> activities, long _lastActiveTime,
-            long lastTimeMoved, boolean neverRelinquishIdentity,
+            String _lastDescription, ArrayList<ActivityRecord> activities, long _firstActiveTime,
+            long _lastActiveTime, long lastTimeMoved, boolean neverRelinquishIdentity,
             ActivityManager.TaskDescription _lastTaskDescription) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION;
@@ -166,6 +168,7 @@
         taskType = _taskType;
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         userId = _userId;
+        firstActiveTime = _firstActiveTime;
         lastActiveTime = _lastActiveTime;
         lastDescription = _lastDescription;
         mActivities = activities;
@@ -176,6 +179,9 @@
 
     void touchActiveTime() {
         lastActiveTime = android.os.SystemClock.elapsedRealtime();
+        if (firstActiveTime == 0) {
+            firstActiveTime = lastActiveTime;
+        }
     }
 
     long getInactiveDuration() {
@@ -668,6 +674,7 @@
         out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
         out.attribute(null, ATTR_USERID, String.valueOf(userId));
         out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
+        out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
         out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
         out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
         out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
@@ -719,6 +726,7 @@
         int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
         int userId = 0;
         String lastDescription = null;
+        long firstActiveTime = -1;
         long lastActiveTime = -1;
         long lastTimeOnTop = 0;
         boolean neverRelinquishIdentity = true;
@@ -747,6 +755,8 @@
                 userId = Integer.valueOf(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
                 taskType = Integer.valueOf(attrValue);
+            } else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
+                firstActiveTime = Long.valueOf(attrValue);
             } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
                 lastActiveTime = Long.valueOf(attrValue);
             } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
@@ -795,8 +805,8 @@
 
         final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
                 affinityIntent, affinity, realActivity, origActivity, rootHasReset,
-                askedCompatMode, taskType, userId, lastDescription, activities, lastActiveTime,
-                lastTimeOnTop, neverRelinquishIdentity, taskDescription);
+                askedCompatMode, taskType, userId, lastDescription, activities, firstActiveTime,
+                lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, taskDescription);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
             activities.get(activityNdx).task = task;
@@ -853,6 +863,7 @@
                 pw.print(" lastThumbnailFile="); pw.print(mLastThumbnailFile);
                 pw.print(" lastDescription="); pw.println(lastDescription);
         pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
+                pw.print(" firstActiveTime="); pw.print(lastActiveTime);
                 pw.print(" lastActiveTime="); pw.print(lastActiveTime);
                 pw.print(" (inactive for ");
                 pw.print((getInactiveDuration()/1000)); pw.println("s)");
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index c860673..a5a502a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -57,7 +57,7 @@
          *
          * @param deviceType requested device type to allocate logical address
          * @param logicalAddress allocated logical address. If it is
-         *                       {@link Constants.ADDR_UNREGISTERED}, it means that
+         *                       {@link Constants#ADDR_UNREGISTERED}, it means that
          *                       it failed to allocate logical address for the given device type
          */
         void onAllocated(int deviceType, int logicalAddress);
@@ -158,7 +158,7 @@
      *
      * @param deviceType type of device to used to determine logical address
      * @param preferredAddress a logical address preferred to be allocated.
-     *                         If sets {@link Constants.ADDR_UNREGISTERED}, scans
+     *                         If sets {@link Constants#ADDR_UNREGISTERED}, scans
      *                         the smallest logical address matched with the given device type.
      *                         Otherwise, scan address will start from {@code preferredAddress}
      * @param callback callback interface to report allocated logical address to caller
@@ -559,10 +559,10 @@
     /**
      * Called by native when a hotplug event issues.
      */
-    private void handleHotplug(boolean connected) {
-        // TODO: once add port number to cec HAL interface, pass port number
-        // to the service.
-        mService.onHotplug(0, connected);
+    @ServiceThreadOnly
+    private void handleHotplug(int port, boolean connected) {
+        assertRunOnServiceThread();
+        mService.onHotplug(port, connected);
     }
 
     private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index e87db50..c5169b9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -156,6 +156,12 @@
                 return handleSetStreamPath(message);
             case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
                 return handleGiveDevicePowerStatus(message);
+            case Constants.MESSAGE_VENDOR_COMMAND:
+                return handleVendorCommand(message);
+            case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
+                return handleVendorCommandWithId(message);
+            case Constants.MESSAGE_SET_OSD_NAME:
+                return handleSetOsdName(message);
             default:
                 return false;
         }
@@ -244,10 +250,6 @@
         return true;
     }
 
-    protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
-        return false;
-    }
-
     protected boolean handleRoutingChange(HdmiCecMessage message) {
         return false;
     }
@@ -337,6 +339,34 @@
         return true;
     }
 
+    protected boolean handleVendorCommand(HdmiCecMessage message) {
+        mService.invokeVendorCommandListeners(mDeviceType, message.getSource(),
+                message.getParams(), false);
+        return true;
+    }
+
+    protected boolean handleVendorCommandWithId(HdmiCecMessage message) {
+        byte[] params = message.getParams();
+        int vendorId = HdmiUtils.threeBytesToInt(params);
+        if (vendorId == mService.getVendorId()) {
+            mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), params, true);
+        } else if (message.getDestination() != Constants.ADDR_BROADCAST &&
+                message.getSource() != Constants.ADDR_UNREGISTERED) {
+            Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
+            mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
+                    message.getSource(), Constants.MESSAGE_VENDOR_COMMAND_WITH_ID,
+                    Constants.ABORT_UNRECOGNIZED_MODE));
+        } else {
+            Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
+        }
+        return true;
+    }
+
+    protected boolean handleSetOsdName(HdmiCecMessage message) {
+        // The default behavior of <Set Osd Name> is doing nothing.
+        return true;
+    }
+
     @ServiceThreadOnly
     final void handleAddressAllocated(int logicalAddress) {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 223deab..02b5344 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -16,12 +16,13 @@
 
 package com.android.server.hdmi;
 
+import android.content.Intent;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.media.AudioSystem;
-import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -29,6 +30,7 @@
 import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -257,7 +259,7 @@
         int address = message.getSource();
         int path = HdmiUtils.twoBytesToInt(message.getParams());
         if (getDeviceInfo(address) == null) {
-            handleNewDeviceAtTheTailOfActivePath(address, path);
+            handleNewDeviceAtTheTailOfActivePath(path);
         } else {
             ActiveSourceHandler.create(this, null).process(address, path);
         }
@@ -338,13 +340,13 @@
         int path = HdmiUtils.twoBytesToInt(message.getParams());
         int address = message.getSource();
         if (!isInDeviceList(path, address)) {
-            handleNewDeviceAtTheTailOfActivePath(address, path);
+            handleNewDeviceAtTheTailOfActivePath(path);
         }
         addAndStartAction(new NewDeviceAction(this, address, path));
         return true;
     }
 
-    private void handleNewDeviceAtTheTailOfActivePath(int address, int path) {
+    private void handleNewDeviceAtTheTailOfActivePath(int path) {
         // Seq #22
         if (isTailOfActivePath(path, getActivePath())) {
             removeAction(RoutingControlAction.class);
@@ -404,33 +406,6 @@
 
     @Override
     @ServiceThreadOnly
-    protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
-        assertRunOnServiceThread();
-        List<VendorSpecificAction> actions = Collections.emptyList();
-        // TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
-
-        // We assume that there can be multiple vendor-specific command actions running
-        // at the same time. Pass the message to each action to see if one of them needs it.
-        for (VendorSpecificAction action : actions) {
-            if (action.processCommand(message)) {
-                return true;
-            }
-        }
-        // Handle the message here if it is not already consumed by one of the running actions.
-        // Respond with a appropriate vendor-specific command or <Feature Abort>, or create another
-        // vendor-specific action:
-        //
-        // mService.addAndStartAction(new VendorSpecificAction(mService, mAddress));
-        //
-        // For now, simply reply with <Feature Abort> and mark it consumed by returning true.
-        mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
-                message.getDestination(), message.getSource(), message.getOpcode(),
-                Constants.ABORT_REFUSED));
-        return true;
-    }
-
-    @Override
-    @ServiceThreadOnly
     protected boolean handleReportAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
 
@@ -465,6 +440,35 @@
         return handleTextViewOn(message);
     }
 
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSetOsdName(HdmiCecMessage message) {
+        int source = message.getSource();
+        HdmiCecDeviceInfo deviceInfo = getDeviceInfo(source);
+        // If the device is not in device list, ignore it.
+        if (deviceInfo == null) {
+            Slog.e(TAG, "No source device info for <Set Osd Name>." + message);
+            return true;
+        }
+        String osdName = null;
+        try {
+            osdName = new String(message.getParams(), "US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
+            return true;
+        }
+
+        if (deviceInfo.getDisplayName().equals(osdName)) {
+            Slog.i(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
+            return true;
+        }
+
+        addCecDevice(new HdmiCecDeviceInfo(deviceInfo.getLogicalAddress(),
+                deviceInfo.getPhysicalAddress(), deviceInfo.getDeviceType(),
+                deviceInfo.getVendorId(), osdName));
+        return true;
+    }
+
     @ServiceThreadOnly
     private void launchDeviceDiscovery() {
         assertRunOnServiceThread();
@@ -702,8 +706,6 @@
         assertRunOnServiceThread();
         // In case where <Terminate Arc> is started by <Request ARC Termination>
         // need to clean up RequestArcInitiationAction.
-        // TODO: check conditions of power status by calling is_connected api
-        // to be added soon.
         removeAction(RequestArcTerminationAction.class);
         SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
                 message.getSource(), false);
@@ -756,7 +758,7 @@
      *         that has the same logical address as new one has.
      */
     @ServiceThreadOnly
-    HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+    private HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
         assertRunOnServiceThread();
         HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
         if (oldDeviceInfo != null) {
@@ -777,7 +779,7 @@
      * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
      */
     @ServiceThreadOnly
-    HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
+    private HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
         assertRunOnServiceThread();
         HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
         if (deviceInfo != null) {
@@ -905,7 +907,8 @@
     }
 
     /**
-     * Called when a device is newly added or a new device is detected.
+     * Called when a device is newly added or a new device is detected or
+     * existing device is updated.
      *
      * @param info device info of a new device.
      */
@@ -1025,6 +1028,7 @@
         if (!hotplugActions.isEmpty()) {
             // Note that hotplug action is single action running on a machine.
             // "pollAllDevicesNow" cleans up timer and start poll action immediately.
+            // It covers seq #40, #43.
             hotplugActions.get(0).pollAllDevicesNow();
         }
     }
@@ -1080,4 +1084,11 @@
     boolean isPowerStandbyOrTransient() {
         return mService.isPowerStandbyOrTransient();
     }
+
+    void displayOsd(int messageId) {
+        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
+        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
+        mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                HdmiControlService.PERMISSION);
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c0ff81d..970568a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -172,6 +172,10 @@
                 return "Active Source";
             case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
                 return "Give Device Power Status";
+            case Constants.MESSAGE_VENDOR_COMMAND:
+                return "Vendor Command";
+            case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
+                return "Vendor Command With ID";
             default:
                 return String.format("Opcode: %02X", opcode);
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index fe35b24..57f89b1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -424,6 +424,37 @@
         return buildCommand(src, dest, Constants.MESSAGE_STANDBY);
     }
 
+    /**
+     * Build &lt;Vendor Command&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @param params vendor-specific parameters
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildVendorCommand(int src, int dest, byte[] params) {
+        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
+    }
+
+    /**
+     * Build &lt;Vendor Command With ID&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @param vendorId vendor ID
+     * @param operands vendor-specific parameters
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildVendorCommandWithId(int src, int dest, int vendorId,
+            byte[] operands) {
+        byte[] params = new byte[operands.length + 3];  // parameter plus len(vendorId)
+        params[0] = (byte) ((vendorId >> 16) & 0xFF);
+        params[1] = (byte) ((vendorId >> 8) & 0xFF);
+        params[2] = (byte) (vendorId & 0xFF);
+        System.arraycopy(operands, 0, params, 3, operands.length);
+        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
+    }
+
     /***** Please ADD new buildXXX() methods above. ******/
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index f927cb6..c32f312 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -32,6 +32,7 @@
 import android.hardware.hdmi.IHdmiHotplugEventListener;
 import android.hardware.hdmi.IHdmiInputChangeListener;
 import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
 import android.media.AudioManager;
 import android.os.Build;
 import android.os.Handler;
@@ -61,8 +62,7 @@
 public final class HdmiControlService extends SystemService {
     private static final String TAG = "HdmiControlService";
 
-    // TODO: Rename the permission to HDMI_CONTROL.
-    private static final String PERMISSION = "android.permission.HDMI_CEC";
+    static final String PERMISSION = "android.permission.HDMI_CEC";
 
     /**
      * Interface to report send result.
@@ -140,6 +140,11 @@
     private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
             new ArrayList<>();
 
+    // List of records for vendor command listener to handle the the caller killed in action.
+    @GuardedBy("mLock")
+    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
+            new ArrayList<>();
+
     @GuardedBy("mLock")
     private IHdmiInputChangeListener mInputChangeListener;
 
@@ -199,8 +204,12 @@
         mCecController = HdmiCecController.create(this);
 
         if (mCecController != null) {
+            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
+            mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE,
+                    HdmiTvClient.ENABLED);
+
             mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL,
-                    HdmiTvClient.DISABLED);
+                    HdmiTvClient.ENABLED);
             initializeLocalDevices(mLocalDevices);
         } else {
             Slog.i(TAG, "Device does not support HDMI-CEC.");
@@ -608,6 +617,23 @@
         }
     }
 
+    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
+        private final IHdmiVendorCommandListener mListener;
+        private final int mDeviceType;
+
+        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
+            mListener = listener;
+            mDeviceType = deviceType;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mVendorCommandListenerRecords.remove(this);
+            }
+        }
+    }
+
     private void enforceAccessPermission() {
         getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
     }
@@ -917,6 +943,42 @@
             }
             HdmiControlService.this.setProhibitMode(enabled);
         }
+
+        @Override
+        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
+                final int deviceType) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiControlService.this.addVendorCommandListener(listener, deviceType);
+                }
+            });
+        }
+
+        @Override
+        public void sendVendorCommand(final int deviceType, final int targetAddress,
+                final byte[] params, final boolean hasVendorId) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
+                    if (device == null) {
+                        Slog.w(TAG, "Local device not available");
+                        return;
+                    }
+                    if (hasVendorId) {
+                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
+                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
+                                getVendorId(), params));
+                    } else {
+                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
+                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
+                    }
+                }
+            });
+         }
     }
 
     @ServiceThreadOnly
@@ -1197,6 +1259,35 @@
         mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED);
     }
 
+    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
+        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
+        try {
+            listener.asBinder().linkToDeath(record, 0);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Listener already died");
+            return;
+        }
+        synchronized (mLock) {
+            mVendorCommandListenerRecords.add(record);
+        }
+    }
+
+    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
+            boolean hasVendorId) {
+        synchronized (mLock) {
+            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
+                if (record.mDeviceType != deviceType) {
+                    continue;
+                }
+                try {
+                    record.mListener.onReceived(srcAddress, params, hasVendorId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to notify vendor command reception", e);
+                }
+            }
+        }
+    }
+
     boolean isProhibitMode() {
         synchronized (mLock) {
             return mProhibitMode;
diff --git a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java b/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
deleted file mode 100644
index ff21a57..0000000
--- a/services/core/java/com/android/server/hdmi/VendorSpecificAction.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.android.server.hdmi;
-
-/**
- * Handles vendor-specific commands that require a sequence of command exchange,
- * or need to manage some states to complete the processing.
- */
-public class VendorSpecificAction extends FeatureAction {
-
-    // Sample state this action can be in.
-    private static final int STATE_1 = 1;
-    private static final int STATE_2 = 2;
-
-    VendorSpecificAction(HdmiCecLocalDevice source) {
-        super(source);
-        // Modify the constructor if additional arguments are necessary.
-    }
-
-    @Override
-    boolean start() {
-        // Do initialization step and update the state accordingly here.
-        mState = STATE_1;
-        addTimer(STATE_1, TIMEOUT_MS);
-        return true;
-    }
-
-    @Override
-    boolean processCommand(HdmiCecMessage cmd) {
-        // Returns true if the command was consumed. Otherwise return false for other
-        // actions in progress can be given its turn to process it.
-        return false;
-    }
-
-    @Override
-    void handleTimerEvent(int state) {
-        // Ignore the timer event if the current state and the state this event should be
-        // handled in are different. Could be an outdated event which should have been cleared by
-        // calling {@code mActionTimer.clearTimerMessage()}.
-        if (mState != state) {
-            return;
-        }
-
-        switch (state) {
-            case STATE_1:
-                mState = STATE_2;
-                addTimer(STATE_2, TIMEOUT_MS);
-                break;
-            case STATE_2:
-                finish();
-                break;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index f8ac2e4..08c8271 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -264,7 +264,7 @@
                         mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
                     } else if (c.state == Condition.STATE_TRUE) {
                         Slog.d(TAG, "Enter zen: automatic condition true: " + c);
-                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_ON);
+                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c8496e4..3a6cec2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -890,7 +890,7 @@
 
             // Grab our optional AudioService
             mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
-
+            mZenModeHelper.setAudioManager(mAudioManager);
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             // This observer will force an update when observe is called, causing us to
             // bind to listener services.
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7bac3dc..92bec14 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -74,6 +74,8 @@
 
     private int mZenMode;
     private ZenModeConfig mConfig;
+    private AudioManager mAudioManager;
+    private int mPreviousRingerMode = -1;
 
     // temporary, until we update apps to provide metadata
     private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
@@ -129,13 +131,17 @@
         mCallbacks.add(callback);
     }
 
+    public void setAudioManager(AudioManager audioManager) {
+        mAudioManager = audioManager;
+    }
+
     public boolean shouldIntercept(NotificationRecord record) {
         if (mZenMode != Global.ZEN_MODE_OFF) {
             if (isSystem(record)) {
                 return false;
             }
             if (isAlarm(record)) {
-                return false;
+                return mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
             }
             // audience has veto power over all following rules
             if (!audienceMatches(record)) {
@@ -185,6 +191,34 @@
         mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
                 zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
                 exceptionPackages);
+
+        // alarm restrictions
+        final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
+                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+                exceptionPackages);
+        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
+                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+                exceptionPackages);
+
+        // force ringer mode into compliance
+        if (mAudioManager != null) {
+            int ringerMode = mAudioManager.getRingerMode();
+            if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+                if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
+                    mPreviousRingerMode = ringerMode;
+                    Slog.d(TAG, "Silencing ringer");
+                    mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
+                }
+            } else {
+                if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+                    Slog.d(TAG, "Unsilencing ringer");
+                    mAudioManager.setRingerMode(mPreviousRingerMode != -1 ? mPreviousRingerMode
+                            : AudioManager.RINGER_MODE_NORMAL);
+                    mPreviousRingerMode = -1;
+                }
+            }
+        }
         dispatchOnZenModeChanged();
     }
 
@@ -201,6 +235,7 @@
         pw.println(Global.zenModeToString(mZenMode));
         pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
         pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
+        pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
     }
 
     public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -308,16 +343,6 @@
         return new Date(time) + " (" + time + ")";
     }
 
-    public static boolean isWeekend(long time, int offsetDays) {
-        final Calendar c = Calendar.getInstance();
-        c.setTimeInMillis(time);
-        if (offsetDays != 0) {
-            c.add(Calendar.DATE, offsetDays);
-        }
-        final int day = c.get(Calendar.DAY_OF_WEEK);
-        return day == Calendar.SATURDAY || day == Calendar.SUNDAY;
-    }
-
     private class SettingsObserver extends ContentObserver {
         private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
 
@@ -344,31 +369,45 @@
     }
 
     private class ZenBroadcastReceiver extends BroadcastReceiver {
+        private final Calendar mCalendar = Calendar.getInstance();
+
         @Override
         public void onReceive(Context context, Intent intent) {
             if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
-                setZenMode(intent, 1, Global.ZEN_MODE_ON);
+                setZenMode(intent, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
             } else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
-                setZenMode(intent, 0, Global.ZEN_MODE_OFF);
+                setZenMode(intent, Global.ZEN_MODE_OFF);
             }
         }
 
-        private void setZenMode(Intent intent, int wkendOffsetDays, int zenModeValue) {
+        private void setZenMode(Intent intent, int zenModeValue) {
             final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
             final long now = System.currentTimeMillis();
             Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
                     intent.getAction(), ts(schTime), ts(now), now - schTime));
 
-            final boolean skip = ZenModeConfig.SLEEP_MODE_WEEKNIGHTS.equals(mConfig.sleepMode) &&
-                    isWeekend(schTime, wkendOffsetDays);
-
-            if (skip) {
-                Slog.d(TAG, "Skipping zen mode update for the weekend");
+            final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
+            if (days != null) {
+                final int day = getDayOfWeek(schTime);
+                for (int i = 0; i < days.length; i++) {
+                    if (days[i] == day) {
+                        Slog.d(TAG, "Enter downtime, day=" + day + " days=" + Arrays.asList(days));
+                        ZenModeHelper.this.setZenMode(zenModeValue);
+                        break;
+                    } else {
+                        Slog.d(TAG, "Skip downtime, day=" + day + " days=" + Arrays.asList(days));
+                    }
+                }
             } else {
-                ZenModeHelper.this.setZenMode(zenModeValue);
+                Slog.d(TAG, "Skip downtime, no days configured");
             }
             updateAlarms();
         }
+
+        private int getDayOfWeek(long time) {
+            mCalendar.setTimeInMillis(time);
+            return mCalendar.get(Calendar.DAY_OF_WEEK);
+        }
     }
 
     public static class Callback {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c011cf9..8067bd9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -344,11 +344,11 @@
 
             final ApkLite info;
             try {
-                info = PackageParser.parseApkLite(new File(app.sourceDir),
+                info = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
                         PackageParser.PARSE_GET_SIGNATURES);
             } catch (PackageParserException e) {
                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
-                        "Failed to parse existing base " + app.sourceDir + ": " + e);
+                        "Failed to parse existing base " + app.getBaseCodePath() + ": " + e);
             }
 
             assertPackageConsistent("Existing base", info.packageName, info.versionCode,
@@ -379,7 +379,7 @@
      */
     private void spliceExistingFilesIntoStage() throws InstallFailedException {
         final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
-        final File existingDir = new File(app.sourceDir).getParentFile();
+        final File existingDir = new File(app.getBaseCodePath());
 
         try {
             linkTreeIgnoringExisting(existingDir, sessionDir);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c80f316..016d612 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -30,8 +30,10 @@
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
 import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.EEXIST;
 import static android.system.OsConstants.O_EXCL;
 import static android.system.OsConstants.O_RDWR;
+import static android.system.OsConstants.O_WRONLY;
 import static android.system.OsConstants.S_IRGRP;
 import static android.system.OsConstants.S_IROTH;
 import static android.system.OsConstants.S_IRWXU;
@@ -103,6 +105,7 @@
 import android.content.pm.PackageInstallerParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
@@ -146,6 +149,7 @@
 import android.security.SystemKeyStore;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.system.OsConstants;
 import android.system.StructStat;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -190,6 +194,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
@@ -375,6 +380,8 @@
     /** Directory where third-party apps are staged before install */
     final File mAppStagingDir;
 
+    private final Random mTempFileRandom = new Random();
+
     // ----------------------------------------------------------------
 
     // Lock for state used when installing and doing other long running
@@ -4085,7 +4092,7 @@
         pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
         int i = 0;
         for (PackageParser.Package p : overlayArray) {
-            pkg.applicationInfo.resourceDirs[i++] = p.applicationInfo.sourceDir;
+            pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath;
         }
         return true;
     }
@@ -4103,7 +4110,8 @@
         }
 
         for (File file : files) {
-            if (!isApkFile(file)) {
+            final boolean isPackage = isApkFile(file) || file.isDirectory();
+            if (!isPackage) {
                 // Ignore entries which are not apk's
                 continue;
             }
@@ -4184,8 +4192,7 @@
     private PackageParser.Package scanPackageLI(File scanFile,
             int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
-        String scanPath = scanFile.getPath();
-        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
+        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
         parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
@@ -4198,7 +4205,7 @@
 
         final PackageParser.Package pkg;
         try {
-            pkg = pp.parseMonolithicPackage(scanFile, parseFlags);
+            pkg = pp.parsePackage(scanFile, parseFlags);
         } catch (PackageParserException e) {
             mLastScanError = e.error;
             return null;
@@ -4357,27 +4364,29 @@
             }
         }
 
-        final String baseCodePath = pkg.baseCodePath;
-        final String[] splitCodePaths = pkg.splitCodePaths;
-
         // TODO: extend to support forward-locked splits
-        String baseResPath = null;
+        String resourcePath = null;
+        String baseResourcePath = null;
         if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
             if (ps != null && ps.resourcePathString != null) {
-                baseResPath = ps.resourcePathString;
+                resourcePath = ps.resourcePathString;
+                baseResourcePath = ps.resourcePathString;
             } else {
                 // Should not happen at all. Just log an error.
                 Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
             }
         } else {
-            baseResPath = pkg.baseCodePath;
+            resourcePath = pkg.codePath;
+            baseResourcePath = pkg.baseCodePath;
         }
 
         // Set application objects path explicitly.
-        pkg.applicationInfo.sourceDir = baseCodePath;
-        pkg.applicationInfo.publicSourceDir = baseResPath;
-        pkg.applicationInfo.splitSourceDirs = splitCodePaths;
-        pkg.applicationInfo.splitPublicSourceDirs = splitCodePaths;
+        pkg.applicationInfo.setCodePath(pkg.codePath);
+        pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
+        pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
+        pkg.applicationInfo.setResourcePath(resourcePath);
+        pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
+        pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
 
         // Note that we invoke the following method only if we are about to unpack an application
         PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
@@ -4906,8 +4915,8 @@
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
             int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
         final File scanFile = new File(pkg.codePath);
-        if (pkg.applicationInfo.sourceDir == null ||
-                pkg.applicationInfo.publicSourceDir == null) {
+        if (pkg.applicationInfo.getCodePath() == null ||
+                pkg.applicationInfo.getResourcePath() == null) {
             // Bail out. The resource and code paths haven't been set.
             Slog.w(TAG, " Code and resource paths haven't been set correctly");
             mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -4978,8 +4987,8 @@
         }
 
         // Initialize package source and resource directories
-        File destCodeFile = new File(pkg.applicationInfo.sourceDir);
-        File destResourceFile = new File(pkg.applicationInfo.publicSourceDir);
+        File destCodeFile = new File(pkg.applicationInfo.getCodePath());
+        File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
 
         SharedUserSetting suid = null;
         PackageSetting pkgSetting = null;
@@ -6137,19 +6146,22 @@
             PackageSetting pkgSetting) {
         // "bundled" here means system-installed with no overriding update
         final boolean bundledApk = isSystemApp(pkg) && !isUpdatedSystemApp(pkg);
-        final String apkName = getApkName(pkg.applicationInfo.sourceDir);
-        final File libDir;
+        final File codeFile = new File(pkg.applicationInfo.getCodePath());
+        final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
+        final String nativeLibraryPath;
         if (bundledApk) {
             // If "/system/lib64/apkname" exists, assume that is the per-package
             // native library directory to use; otherwise use "/system/lib/apkname".
-            String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir);
+            String apkRoot = calculateApkRoot(pkg.applicationInfo.getCodePath());
             File lib64 = new File(apkRoot, LIB64_DIR_NAME);
             File packLib64 = new File(lib64, apkName);
-            libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME);
+            File libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME);
+            nativeLibraryPath = (new File(libDir, apkName)).getAbsolutePath();
         } else {
-            libDir = mAppLibInstallDir;
+            // We're installing an upgrade; use directory found during scan
+            // TODO: consider deriving this based on instructionSet
+            nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir;
         }
-        final String nativeLibraryPath = (new File(libDir, apkName)).getPath();
         pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
         // pkgSetting might be null during rescan following uninstall of updates
         // to a bundled app, so accommodate that possibility.  The settings in
@@ -6161,8 +6173,8 @@
 
     // Deduces the required ABI of an upgraded system app.
     private void setInternalAppAbi(PackageParser.Package pkg, PackageSetting pkgSetting) {
-        final String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir);
-        final String apkName = getApkName(pkg.applicationInfo.sourceDir);
+        final String apkRoot = calculateApkRoot(pkg.applicationInfo.getCodePath());
+        final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
 
         // This is of the form "/system/lib64/<packagename>", "/vendor/lib64/<packagename>"
         // or similar.
@@ -8973,7 +8985,7 @@
             return new AsecInstallArgs(codeFile, cid, instructionSet, installOnSd(flags),
                     installForwardLocked(flags));
         } else {
-            return new FileInstallArgs(codeFile, pkgName, instructionSet);
+            return new FileInstallArgs(codeFile, instructionSet);
         }
     }
 
@@ -9013,7 +9025,12 @@
 
         abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
         abstract int doPreInstall(int status);
-        abstract boolean doRename(int status, String pkgName, String oldCodePath);
+
+        /**
+         * Rename package into final resting place. All paths on the given
+         * scanned package should be updated to reflect the rename.
+         */
+        abstract boolean doRename(int status, PackageParser.Package pkg, String oldCodePath);
         abstract int doPostInstall(int status, int uid);
 
         /** @see PackageSettingBase#codePathString */
@@ -9062,13 +9079,15 @@
      * and renaming logic.
      */
     class FileInstallArgs extends InstallArgs {
-        // TODO: teach about handling cluster directories
+        private File codeFile;
+        private File resourceFile;
+        private File nativeLibraryFile;
 
-        File installDir;
-        String codeFileName;
-        String resourceFileName;
-        String libraryPath;
-        boolean created = false;
+        // Example topology:
+        // /data/app/com.example/base.apk
+        // /data/app/com.example/split_foo.apk
+        // /data/app/com.example/native/arm/libfoo.so
+        // /data/app/com.example/dalvik/arm/base.apk@classes.dex
 
         /** New install */
         FileInstallArgs(InstallParams params) {
@@ -9076,27 +9095,23 @@
                     params.flags, params.installerPackageName, params.getManifestDigest(),
                     params.getUser(), params.packageInstructionSetOverride,
                     params.packageAbiOverride);
+            if (isFwdLocked()) {
+                throw new IllegalArgumentException("Forward locking only supported in ASEC");
+            }
         }
 
         /** Existing install */
-        FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
+        FileInstallArgs(String codePath, String resourcePath, String nativeLibraryPath,
                 String instructionSet) {
             super(null, false, null, null, 0, null, null, null, instructionSet, null);
-            File codeFile = new File(fullCodePath);
-            installDir = codeFile.getParentFile();
-            codeFileName = fullCodePath;
-            resourceFileName = fullResourcePath;
-            libraryPath = nativeLibraryPath;
+            this.codeFile = (codePath != null) ? new File(codePath) : null;
+            this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
+            this.nativeLibraryFile = (nativeLibraryPath != null) ? new File(nativeLibraryPath) : null;
         }
 
         /** New install from existing */
-        FileInstallArgs(File originFile, String pkgName, String instructionSet) {
+        FileInstallArgs(File originFile, String instructionSet) {
             super(originFile, true, null, null, 0, null, null, null, instructionSet, null);
-            installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
-            String apkName = getNextCodePath(null, pkgName, ".apk");
-            codeFileName = new File(installDir, apkName + ".apk").getPath();
-            resourceFileName = getResourcePathFromCodePath();
-            libraryPath = new File(mAppLibInstallDir, pkgName).getPath();
         }
 
         boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
@@ -9120,70 +9135,38 @@
                     lowThreshold);
         }
 
-        void createCopyFile() {
-            installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
-            codeFileName = createTempPackageFile(installDir).getPath();
-            resourceFileName = getResourcePathFromCodePath();
-            libraryPath = getLibraryPathFromCodePath();
-            created = true;
-        }
-
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
-            if (temp) {
-                // Generate temp file name
-                createCopyFile();
-            }
-            // Get a ParcelFileDescriptor to write to the output file
-            final File codeFile = new File(codeFileName);
-            if (!created) {
-                try {
-                    codeFile.createNewFile();
-                    // Set permissions
-                    if (!setPermissions()) {
-                        // Failed setting permissions.
-                        return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                    }
-                } catch (IOException e) {
-                   Slog.w(TAG, "Failed to create file " + codeFile);
-                   return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                }
+            try {
+                final File tempDir = createTempPackageDir(mAppInstallDir);
+                codeFile = tempDir;
+                resourceFile = tempDir;
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to create copy file: " + e);
+                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             }
 
-            // TODO: extend to support copying into clusters
             final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
                 @Override
                 public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
+                    if (!FileUtils.isValidExtFilename(name)) {
+                        throw new IllegalArgumentException("Invalid filename: " + name);
+                    }
                     try {
-                        return ParcelFileDescriptor.open(codeFile,
-                                ParcelFileDescriptor.MODE_READ_WRITE);
-                    } catch (FileNotFoundException e) {
-                        throw new RemoteException(e.getMessage());
+                        final File file = new File(codeFile, name);
+                        final FileDescriptor fd = Os.open(file.getAbsolutePath(),
+                                O_RDWR | O_CREAT, 0644);
+                        Os.chmod(file.getAbsolutePath(), 0644);
+                        return new ParcelFileDescriptor(fd);
+                    } catch (ErrnoException e) {
+                        throw new RemoteException("Failed to open: " + e.getMessage());
                     }
                 }
             };
 
-            // Copy the resource now
             int ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
-
-            if (isFwdLocked()) {
-                final File destResourceFile = new File(getResourcePath());
-
-                // Copy the public files
-                try {
-                    PackageHelper.extractPublicFiles(codeFileName, destResourceFile);
-                } catch (IOException e) {
-                    Slog.e(TAG, "Couldn't create a new zip file for the public parts of a"
-                            + " forward-locked app.");
-                    destResourceFile.delete();
-                    return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                }
-            }
-
-            final File nativeLibraryFile = new File(getNativeLibraryPath());
-            Slog.i(TAG, "Copying native libraries to " + nativeLibraryFile.getPath());
-            if (nativeLibraryFile.exists()) {
-                NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
-                nativeLibraryFile.delete();
+            if (ret != PackageManager.INSTALL_SUCCEEDED) {
+                Slog.e(TAG, "Failed to copy package");
+                return ret;
             }
 
             String[] abiList = (abiOverride != null) ?
@@ -9197,11 +9180,24 @@
                     abiList = Build.SUPPORTED_32_BIT_ABIS;
                 }
 
-                int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
-                if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
-                    return copyRet;
+                // TODO: refactor to avoid double findSupportedAbi()
+                final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+                if (abiIndex < 0 && abiIndex != PackageManager.NO_NATIVE_LIBRARIES) {
+                    return abiIndex;
+                } else if (abiIndex >= 0) {
+                    final File baseLibFile = new File(codeFile, LIB_DIR_NAME);
+                    baseLibFile.mkdir();
+                    Os.chmod(baseLibFile.getAbsolutePath(), 0755);
+
+                    final String abi = Build.SUPPORTED_ABIS[abiIndex];
+                    final String instructionSet = VMRuntime.getInstructionSet(abi);
+                    nativeLibraryFile = new File(baseLibFile, instructionSet);
+                    nativeLibraryFile.mkdir();
+                    Os.chmod(nativeLibraryFile.getAbsolutePath(), 0755);
+
+                    copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
                 }
-            } catch (IOException e) {
+            } catch (IOException | ErrnoException e) {
                 Slog.e(TAG, "Copying native libraries failed", e);
                 ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
             } finally {
@@ -9218,51 +9214,44 @@
             return status;
         }
 
-        boolean doRename(int status, final String pkgName, String oldCodePath) {
+        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
                 return false;
             } else {
-                final File oldCodeFile = new File(getCodePath());
-                final File oldResourceFile = new File(getResourcePath());
-                final File oldLibraryFile = new File(getNativeLibraryPath());
+                final File beforeCodeFile = codeFile;
+                final File afterCodeFile = new File(mAppInstallDir,
+                        getNextCodePath(oldCodePath, pkg.packageName, null));
 
-                // Rename APK file based on packageName
-                final String apkName = getNextCodePath(oldCodePath, pkgName, ".apk");
-                final File newCodeFile = new File(installDir, apkName + ".apk");
-                if (!oldCodeFile.renameTo(newCodeFile)) {
+                Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
+                if (!beforeCodeFile.renameTo(afterCodeFile)) {
                     return false;
                 }
-                codeFileName = newCodeFile.getPath();
-
-                // Rename public resource file if it's forward-locked.
-                final File newResFile = new File(getResourcePathFromCodePath());
-                if (isFwdLocked() && !oldResourceFile.renameTo(newResFile)) {
-                    return false;
-                }
-                resourceFileName = newResFile.getPath();
-
-                // Rename library path
-                final File newLibraryFile = new File(getLibraryPathFromCodePath());
-                if (newLibraryFile.exists()) {
-                    NativeLibraryHelper.removeNativeBinariesFromDirLI(newLibraryFile);
-                    newLibraryFile.delete();
-                }
-                if (!oldLibraryFile.renameTo(newLibraryFile)) {
-                    Slog.e(TAG, "Cannot rename native library directory "
-                            + oldLibraryFile.getPath() + " to " + newLibraryFile.getPath());
-                    return false;
-                }
-                libraryPath = newLibraryFile.getPath();
-
-                // Attempt to set permissions
-                if (!setPermissions()) {
+                if (!SELinux.restoreconRecursive(afterCodeFile)) {
                     return false;
                 }
 
-                if (!SELinux.restorecon(newCodeFile)) {
-                    return false;
-                }
+                // Reflect the rename internally
+                codeFile = afterCodeFile;
+                resourceFile = afterCodeFile;
+                nativeLibraryFile = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+                        nativeLibraryFile);
+
+                // Reflect the rename in scanned details
+                pkg.codePath = afterCodeFile.getAbsolutePath();
+                pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+                        pkg.baseCodePath);
+                pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
+                        pkg.splitCodePaths);
+
+                // Reflect the rename in app info
+                pkg.applicationInfo.setCodePath(pkg.codePath);
+                pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
+                pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
+                pkg.applicationInfo.setResourcePath(pkg.codePath);
+                pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
+                pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+                pkg.applicationInfo.nativeLibraryDir = getNativeLibraryPath();
 
                 return true;
             }
@@ -9275,120 +9264,71 @@
             return status;
         }
 
-        private String getResourcePathFromCodePath() {
-            final String codePath = getCodePath();
-            if (isFwdLocked()) {
-                final StringBuilder sb = new StringBuilder();
-
-                sb.append(mAppInstallDir.getPath());
-                sb.append('/');
-                sb.append(getApkName(codePath));
-                sb.append(".zip");
-
-                /*
-                 * If our APK is a temporary file, mark the resource as a
-                 * temporary file as well so it can be cleaned up after
-                 * catastrophic failure.
-                 */
-                if (codePath.endsWith(".tmp")) {
-                    sb.append(".tmp");
-                }
-
-                return sb.toString();
-            } else {
-                return codePath;
-            }
-        }
-
-        private String getLibraryPathFromCodePath() {
-            return new File(mAppLibInstallDir, getApkName(getCodePath())).getPath();
-        }
-
         @Override
         String getCodePath() {
-            return codeFileName;
+            return (codeFile != null) ? codeFile.getAbsolutePath() : null;
         }
 
         @Override
         String getResourcePath() {
-            return resourceFileName;
+            return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
         }
 
         @Override
         String getNativeLibraryPath() {
-            if (libraryPath == null) {
-                libraryPath = getLibraryPathFromCodePath();
-            }
-            return libraryPath;
+            return (nativeLibraryFile != null) ? nativeLibraryFile.getAbsolutePath() : null;
         }
 
         private boolean cleanUp() {
-            boolean ret = true;
-            String sourceDir = getCodePath();
-            String publicSourceDir = getResourcePath();
-            if (sourceDir != null) {
-                File sourceFile = new File(sourceDir);
-                if (!sourceFile.exists()) {
-                    Slog.w(TAG, "Package source " + sourceDir + " does not exist.");
-                    ret = false;
-                }
-                // Delete application's code and resources
-                sourceFile.delete();
-            }
-            if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {
-                final File publicSourceFile = new File(publicSourceDir);
-                if (!publicSourceFile.exists()) {
-                    Slog.w(TAG, "Package public source " + publicSourceFile + " does not exist.");
-                }
-                if (publicSourceFile.exists()) {
-                    publicSourceFile.delete();
-                }
+            if (codeFile == null || !codeFile.exists()) {
+                return false;
             }
 
-            if (libraryPath != null) {
-                File nativeLibraryFile = new File(libraryPath);
-                NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);
-                if (!nativeLibraryFile.delete()) {
-                    Slog.w(TAG, "Couldn't delete native library directory " + libraryPath);
-                }
+            if (codeFile.isDirectory()) {
+                FileUtils.deleteContents(codeFile);
+            }
+            codeFile.delete();
+
+            if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
+                resourceFile.delete();
             }
 
-            return ret;
+            if (nativeLibraryFile != null && !FileUtils.contains(codeFile, nativeLibraryFile)) {
+                FileUtils.deleteContents(nativeLibraryFile);
+                nativeLibraryFile.delete();
+            }
+
+            return true;
         }
 
         void cleanUpResourcesLI() {
-            String sourceDir = getCodePath();
-            if (cleanUp()) {
+            // Try enumerating all code paths before deleting
+            List<String> allCodePaths = Collections.EMPTY_LIST;
+            if (codeFile != null && codeFile.exists()) {
+                try {
+                    final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
+                    allCodePaths = pkg.getAllCodePaths();
+                } catch (PackageParserException e) {
+                    // Ignored; we tried our best
+                }
+            }
+
+            cleanUp();
+
+            if (!allCodePaths.isEmpty()) {
                 if (instructionSet == null) {
                     throw new IllegalStateException("instructionSet == null");
                 }
-                int retCode = mInstaller.rmdex(sourceDir, instructionSet);
-                if (retCode < 0) {
-                    Slog.w(TAG, "Couldn't remove dex file for package: "
-                            +  " at location "
-                            + sourceDir + ", retcode=" + retCode);
-                    // we don't consider this to be a failure of the core package deletion
-                }
-            }
-        }
 
-        private boolean setPermissions() {
-            // TODO Do this in a more elegant way later on. for now just a hack
-            if (!isFwdLocked()) {
-                final int filePermissions =
-                    FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP
-                    |FileUtils.S_IROTH;
-                int retCode = FileUtils.setPermissions(getCodePath(), filePermissions, -1, -1);
-                if (retCode != 0) {
-                    Slog.e(TAG, "Couldn't set new package file permissions for " +
-                            getCodePath()
-                            + ". The return code was: " + retCode);
-                    // TODO Define new internal error
-                    return false;
+                for (String codePath : allCodePaths) {
+                    int retCode = mInstaller.rmdex(codePath, instructionSet);
+                    if (retCode < 0) {
+                        Slog.w(TAG, "Couldn't remove dex file for package: "
+                                +  " at location " + codePath + ", retcode=" + retCode);
+                        // we don't consider this to be a failure of the core package deletion
+                    }
                 }
-                return true;
             }
-            return true;
         }
 
         boolean doPostDeleteLI(boolean delete) {
@@ -9538,9 +9478,8 @@
             return status;
         }
 
-        boolean doRename(int status, final String pkgName,
-                String oldCodePath) {
-            String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
+        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
+            String newCacheId = getNextCodePath(oldCodePath, pkg.packageName, "/" + RES_FILE_NAME);
             String newCachePath = null;
             if (PackageHelper.isContainerMounted(cid)) {
                 // Unmount the container
@@ -9580,6 +9519,20 @@
                     " at new path: " + newCachePath);
             cid = newCacheId;
             setCachePath(newCachePath);
+
+            // TODO: extend to support split APKs
+            pkg.codePath = getCodePath();
+            pkg.baseCodePath = getCodePath();
+            pkg.splitCodePaths = null;
+
+            pkg.applicationInfo.setCodePath(getCodePath());
+            pkg.applicationInfo.setBaseCodePath(getCodePath());
+            pkg.applicationInfo.setSplitCodePaths(null);
+            pkg.applicationInfo.setResourcePath(getResourcePath());
+            pkg.applicationInfo.setBaseResourcePath(getResourcePath());
+            pkg.applicationInfo.setSplitResourcePaths(null);
+            pkg.applicationInfo.nativeLibraryDir = getNativeLibraryPath();
+
             return true;
         }
 
@@ -9716,7 +9669,7 @@
         if (oldCodePath != null) {
             String subStr = oldCodePath;
             // Drop the suffix right away
-            if (subStr.endsWith(suffix)) {
+            if (suffix != null && subStr.endsWith(suffix)) {
                 subStr = subStr.substring(0, subStr.length() - suffix.length());
             }
             // If oldCodePath already contains prefix find out the
@@ -9747,7 +9700,7 @@
     // Utility method used to ignore ADD/REMOVE events
     // by directory observer.
     private static boolean ignoreCodePath(String fullPathStr) {
-        String apkName = getApkName(fullPathStr);
+        String apkName = deriveCodePathName(fullPathStr);
         int idx = apkName.lastIndexOf(INSTALL_PACKAGE_SUFFIX);
         if (idx != -1 && ((idx+1) < apkName.length())) {
             // Make sure the package ends with a numeral
@@ -9763,33 +9716,21 @@
     // Utility method that returns the relative package path with respect
     // to the installation directory. Like say for /data/data/com.test-1.apk
     // string com.test-1 is returned.
-    static String getApkName(String codePath) {
+    static String deriveCodePathName(String codePath) {
         if (codePath == null) {
             return null;
         }
-        int sidx = codePath.lastIndexOf("/");
-        int eidx = codePath.lastIndexOf(".");
-        if (eidx == -1) {
-            eidx = codePath.length();
-        } else if (eidx == 0) {
-            Slog.w(TAG, " Invalid code path, "+ codePath + " Not a valid apk name");
+        final File codeFile = new File(codePath);
+        final String name = codeFile.getName();
+        if (codeFile.isDirectory()) {
+            return name;
+        } else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
+            final int lastDot = name.lastIndexOf('.');
+            return name.substring(0, lastDot);
+        } else {
+            Slog.w(TAG, "Odd, " + codePath + " doesn't look like an APK");
             return null;
         }
-        return codePath.substring(sidx+1, eidx);
-    }
-
-    private static String[] deriveSplitResPaths(String[] splitCodePaths) {
-        String[] splitResPaths = null;
-        if (!ArrayUtils.isEmpty(splitCodePaths)) {
-            splitResPaths = new String[splitCodePaths.length];
-            for (int i = 0; i < splitCodePaths.length; i++) {
-                final String splitCodePath = splitCodePaths[i];
-                final String resName = getApkName(splitCodePath) + ".zip";
-                splitResPaths[i] = new File(new File(splitCodePath).getParentFile(),
-                        resName).getAbsolutePath();
-            }
-        }
-        return splitResPaths;
     }
 
     class PackageInstalledInfo {
@@ -10058,8 +9999,8 @@
                 // which means we are replacing another update that is already
                 // installed.  We need to make sure to delete the older one's .apk.
                 res.removedInfo.args = createInstallArgsForExisting(0,
-                        deletedPackage.applicationInfo.sourceDir,
-                        deletedPackage.applicationInfo.publicSourceDir,
+                        deletedPackage.applicationInfo.getCodePath(),
+                        deletedPackage.applicationInfo.getResourcePath(),
                         deletedPackage.applicationInfo.nativeLibraryDir,
                         getAppInstructionSet(deletedPackage.applicationInfo));
             } else {
@@ -10223,7 +10164,7 @@
 
         final PackageParser.Package pkg;
         try {
-            pkg = pp.parseMonolithicPackage(tmpPackageFile, parseFlags);
+            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
         } catch (PackageParserException e) {
             res.returnCode = e.error;
             return;
@@ -10341,22 +10282,11 @@
             return;
         }
 
-        if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
+        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
             res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             return;
         }
 
-        // Set application objects path explicitly after the rename
-        // TODO: derive split paths from original scan after rename
-        pkg.codePath = args.getCodePath();
-        pkg.baseCodePath = args.getCodePath();
-        pkg.splitCodePaths = null;
-        pkg.applicationInfo.sourceDir = args.getCodePath();
-        pkg.applicationInfo.publicSourceDir = args.getResourcePath();
-        pkg.applicationInfo.splitSourceDirs = null;
-        pkg.applicationInfo.splitPublicSourceDirs = null;
-        pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
-
         if (replace) {
             replacePackageLI(pkg, parseFlags, scanMode, args.user,
                     installerPackageName, res, args.abiOverride);
@@ -10436,36 +10366,57 @@
 
     private static final void deleteTempPackageFilesInDirectory(File directory,
             FilenameFilter filter) {
-        final String[] tmpFilesList = directory.list(filter);
-        if (tmpFilesList == null) {
-            return;
-        }
-        for (int i = 0; i < tmpFilesList.length; i++) {
-            final File tmpFile = new File(directory, tmpFilesList[i]);
-            tmpFile.delete();
+        final File[] files = directory.listFiles(filter);
+        if (!ArrayUtils.isEmpty(files)) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    FileUtils.deleteContents(file);
+                    file.delete();
+                } else if (file.isFile()) {
+                    file.delete();
+                }
+            }
         }
     }
 
-    private File createTempPackageFile(File installDir) {
-        File tmpPackageFile;
-        try {
-            tmpPackageFile = File.createTempFile("vmdl", ".tmp", installDir);
-        } catch (IOException e) {
-            Slog.e(TAG, "Couldn't create temp file for downloaded package file.");
-            return null;
-        }
-        try {
-            FileUtils.setPermissions(
-                    tmpPackageFile.getCanonicalPath(), FileUtils.S_IRUSR|FileUtils.S_IWUSR,
-                    -1, -1);
-            if (!SELinux.restorecon(tmpPackageFile)) {
-                return null;
+    private File createTempPackageDir(File installDir) throws IOException {
+        int n = 0;
+        while (n++ < 32) {
+            final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
+            try {
+                Os.mkdir(file.getAbsolutePath(), 0755);
+                Os.chmod(file.getAbsolutePath(), 0755);
+                if (!SELinux.restorecon(file)) {
+                    throw new IOException("Failed to restorecon");
+                }
+                return file;
+            } catch (ErrnoException e) {
+                if (e.errno == EEXIST) continue;
+                throw e.rethrowAsIOException();
             }
-        } catch (IOException e) {
-            Slog.e(TAG, "Trouble getting the canoncical path for a temp file.");
-            return null;
         }
-        return tmpPackageFile;
+        throw new IOException("Failed to create temp directory");
+    }
+
+    private File createTempPackageFile(File installDir) throws IOException {
+        int n = 0;
+        while (n++ < 32) {
+            final File file = new File(installDir, "vmdl" + mTempFileRandom.nextInt() + ".tmp");
+            try {
+                final FileDescriptor fd = Os.open(file.getAbsolutePath(),
+                        O_RDWR | O_CREAT | O_EXCL, 0644);
+                IoUtils.closeQuietly(fd);
+                Os.chmod(file.getAbsolutePath(), 0644);
+                if (!SELinux.restorecon(file)) {
+                    throw new IOException("Failed to restorecon");
+                }
+                return file;
+            } catch (ErrnoException e) {
+                if (e.errno == EEXIST) continue;
+                throw e.rethrowAsIOException();
+            }
+        }
+        throw new IOException("Failed to create temp file");
     }
 
     @Override
@@ -11254,7 +11205,7 @@
                 libDirPath = ps.nativeLibraryPathString;
             }
             if (p != null && (isExternal(p) || isForwardLocked(p))) {
-                String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir);
+                String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath());
                 if (secureContainerId != null) {
                     asecPath = PackageHelper.getSdFilesystem(secureContainerId);
                 }
@@ -11268,7 +11219,7 @@
                 return false;
             }
             if (isForwardLocked(p)) {
-                publicSrcDir = applicationInfo.publicSourceDir;
+                publicSrcDir = applicationInfo.getBaseResourcePath();
             }
         }
         // TODO: extend to measure size of split APKs
@@ -12862,7 +12813,7 @@
                 Message msg = mHandler.obtainMessage(INIT_COPY);
                 final String instructionSet = getAppInstructionSet(pkg.applicationInfo);
                 InstallArgs srcArgs = createInstallArgsForExisting(currFlags,
-                        pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir,
+                        pkg.applicationInfo.getCodePath(), pkg.applicationInfo.getResourcePath(),
                         pkg.applicationInfo.nativeLibraryDir, instructionSet);
                 MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
                         instructionSet, pkg.applicationInfo.uid, user);
@@ -12889,10 +12840,11 @@
                             Slog.w(TAG, " Package " + mp.packageName
                                     + " doesn't exist. Aborting move");
                             returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
-                        } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
+                        } else if (!mp.srcArgs.getCodePath().equals(
+                                pkg.applicationInfo.getCodePath())) {
                             Slog.w(TAG, "Package " + mp.packageName + " code path changed from "
                                     + mp.srcArgs.getCodePath() + " to "
-                                    + pkg.applicationInfo.sourceDir
+                                    + pkg.applicationInfo.getCodePath()
                                     + " Aborting move and returning error");
                             returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
                         } else {
@@ -12916,10 +12868,10 @@
                                             + " doesn't exist. Aborting move");
                                     returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
                                 } else if (!mp.srcArgs.getCodePath().equals(
-                                        pkg.applicationInfo.sourceDir)) {
+                                        pkg.applicationInfo.getCodePath())) {
                                     Slog.w(TAG, "Package " + mp.packageName
                                             + " code path changed from " + mp.srcArgs.getCodePath()
-                                            + " to " + pkg.applicationInfo.sourceDir
+                                            + " to " + pkg.applicationInfo.getCodePath()
                                             + " Aborting move and returning error");
                                     returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
                                 } else {
@@ -12974,14 +12926,19 @@
                                     }
 
                                     if (returnCode == PackageManager.MOVE_SUCCEEDED) {
-                                        pkg.applicationInfo.sourceDir = newCodePath;
-                                        pkg.applicationInfo.publicSourceDir = newResPath;
+                                        pkg.applicationInfo.setCodePath(newCodePath);
+                                        pkg.applicationInfo.setBaseCodePath(newCodePath);
+                                        pkg.applicationInfo.setSplitCodePaths(null);
+                                        pkg.applicationInfo.setResourcePath(newResPath);
+                                        pkg.applicationInfo.setBaseResourcePath(newResPath);
+                                        pkg.applicationInfo.setSplitResourcePaths(null);
                                         pkg.applicationInfo.nativeLibraryDir = newNativePath;
+
                                         PackageSetting ps = (PackageSetting) pkg.mExtras;
-                                        ps.codePath = new File(pkg.applicationInfo.sourceDir);
+                                        ps.codePath = new File(pkg.applicationInfo.getCodePath());
                                         ps.codePathString = ps.codePath.getPath();
                                         ps.resourcePath = new File(
-                                                pkg.applicationInfo.publicSourceDir);
+                                                pkg.applicationInfo.getResourcePath());
                                         ps.resourcePathString = ps.resourcePath.getPath();
                                         ps.nativeLibraryPathString = newNativePath;
                                         // Set the application info flag
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3bb487b..ff075e3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -683,8 +683,8 @@
         p.pkg = pkg;
         // pkg.mSetEnabled = p.getEnabled(userId);
         // pkg.mSetStopped = p.getStopped(userId);
-        final String codePath = pkg.applicationInfo.sourceDir;
-        final String resourcePath = pkg.applicationInfo.publicSourceDir;
+        final String codePath = pkg.applicationInfo.getCodePath();
+        final String resourcePath = pkg.applicationInfo.getResourcePath();
         // Update code path if needed
         if (!codePath.equalsIgnoreCase(p.codePathString)) {
             Slog.w(PackageManagerService.TAG, "Code path for pkg : " + p.pkg.packageName +
@@ -1958,6 +1958,13 @@
     void writeSigningKeySetsLPr(XmlSerializer serializer,
             PackageKeySetData data) throws IOException {
         if (data.getSigningKeySets() != null) {
+            // Keep track of the original signing-keyset.
+            // Must be recorded first, since it will be read first and wipe the
+            // current signing-keysets for the package when set.
+            long properSigningKeySet = data.getProperSigningKeySet();
+            serializer.startTag(null, "proper-signing-keyset");
+            serializer.attribute(null, "identifier", Long.toString(properSigningKeySet));
+            serializer.endTag(null, "proper-signing-keyset");
             for (long id : data.getSigningKeySets()) {
                 serializer.startTag(null, "signing-keyset");
                 serializer.attribute(null, "identifier", Long.toString(id));
@@ -2917,6 +2924,9 @@
                 } else if (tagName.equals("perms")) {
                     readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
                     packageSetting.permissionsFixed = true;
+                } else if (tagName.equals("proper-signing-keyset")) {
+                    long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
+                    packageSetting.keySetData.setProperSigningKeySet(id);
                 } else if (tagName.equals("signing-keyset")) {
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
                     packageSetting.keySetData.addSigningKeySet(id);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 02dd1bf..bf767e2 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -485,6 +485,27 @@
             }
 
             @Override
+            public void onChannelRetuned(Uri channelUri) {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+                    }
+                    if (sessionState.mSession == null || sessionState.mClient == null) {
+                        return;
+                    }
+                    try {
+                        // TODO: Consider adding this channel change in the watch log. When we do
+                        // 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.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in onChannelRetuned");
+                    }
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs) {
                 synchronized (mLock) {
                     if (DEBUG) {
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index d275e4d..71a7aa6 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -162,8 +162,10 @@
     void propagateHotplugEvent(const hotplug_event_t& event) {
         // Note that this method should be called in service thread.
         JNIEnv* env = AndroidRuntime::getJNIEnv();
+        jint port = event.port;
+        jboolean connected = (jboolean) event.connected;
         env->CallVoidMethod(mController->getCallbacksObj(),
-                gHdmiCecControllerClassInfo.handleHotplug, event.connected);
+                gHdmiCecControllerClassInfo.handleHotplug, port, connected);
 
         checkAndClearExceptionFromCallback(env, __FUNCTION__);
     }
@@ -314,7 +316,7 @@
     GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz,
             "handleIncomingCecCommand", "(II[B)V");
     GET_METHOD_ID(gHdmiCecControllerClassInfo.handleHotplug, clazz,
-            "handleHotplug", "(Z)V");
+            "handleHotplug", "(IZ)V");
 
     return reinterpret_cast<jlong>(controller);
 }
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 64d418a..a9d5c72 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -68,6 +68,166 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+class BufferProducerThread : public Thread {
+public:
+    BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
+
+    virtual status_t readyToRun();
+
+    void setSurface(const sp<Surface>& surface);
+    void onCaptured(uint32_t seq, bool succeeded);
+    void shutdown();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+    sp<Surface> mSurface;
+    tv_input_device_t* mDevice;
+    int mDeviceId;
+    tv_stream_t mStream;
+    sp<ANativeWindowBuffer_t> mBuffer;
+    enum {
+        CAPTURING,
+        CAPTURED,
+        RELEASED,
+    } mBufferState;
+    uint32_t mSeq;
+    bool mShutdown;
+
+    virtual bool threadLoop();
+
+    void setSurfaceLocked(const sp<Surface>& surface);
+};
+
+BufferProducerThread::BufferProducerThread(
+        tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
+    : Thread(false),
+      mDevice(device),
+      mDeviceId(deviceId),
+      mBuffer(NULL),
+      mBufferState(RELEASED),
+      mSeq(0u),
+      mShutdown(false) {
+    memcpy(&mStream, stream, sizeof(mStream));
+}
+
+status_t BufferProducerThread::readyToRun() {
+    sp<ANativeWindow> anw(mSurface);
+    status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = native_window_set_buffers_dimensions(
+            anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return NO_ERROR;
+}
+
+void BufferProducerThread::setSurface(const sp<Surface>& surface) {
+    Mutex::Autolock autoLock(&mLock);
+    setSurfaceLocked(surface);
+}
+
+void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
+    if (surface == mSurface) {
+        return;
+    }
+
+    if (mBufferState == CAPTURING) {
+        mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
+    }
+    while (mBufferState == CAPTURING) {
+        status_t err = mCondition.waitRelative(mLock, s2ns(1));
+        if (err != NO_ERROR) {
+            ALOGE("error %d while wating for buffer state to change.", err);
+            break;
+        }
+    }
+    mBuffer.clear();
+    mBufferState = RELEASED;
+
+    mSurface = surface;
+    mCondition.broadcast();
+}
+
+void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
+    Mutex::Autolock autoLock(&mLock);
+    if (seq != mSeq) {
+        ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
+    }
+    if (mBufferState != CAPTURING) {
+        ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
+    }
+    if (succeeded) {
+        mBufferState = CAPTURED;
+    } else {
+        mBuffer.clear();
+        mBufferState = RELEASED;
+    }
+    mCondition.broadcast();
+}
+
+void BufferProducerThread::shutdown() {
+    Mutex::Autolock autoLock(&mLock);
+    mShutdown = true;
+    setSurfaceLocked(NULL);
+    requestExitAndWait();
+}
+
+bool BufferProducerThread::threadLoop() {
+    Mutex::Autolock autoLock(&mLock);
+
+    status_t err = NO_ERROR;
+    if (mSurface == NULL) {
+        err = mCondition.waitRelative(mLock, s2ns(1));
+        // It's OK to time out here.
+        if (err != NO_ERROR && err != TIMED_OUT) {
+            ALOGE("error %d while wating for non-null surface to be set", err);
+            return false;
+        }
+        return true;
+    }
+    sp<ANativeWindow> anw(mSurface);
+    while (mBufferState == CAPTURING) {
+        err = mCondition.waitRelative(mLock, s2ns(1));
+        if (err != NO_ERROR) {
+            ALOGE("error %d while wating for buffer state to change.", err);
+            return false;
+        }
+    }
+    if (mBufferState == CAPTURED && anw != NULL) {
+        err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
+        if (err != NO_ERROR) {
+            ALOGE("error %d while queueing buffer to surface", err);
+            return false;
+        }
+        mBuffer.clear();
+        mBufferState = RELEASED;
+    }
+    if (mBuffer == NULL && !mShutdown && anw != NULL) {
+        ANativeWindowBuffer_t* buffer = NULL;
+        err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
+        if (err != NO_ERROR) {
+            ALOGE("error %d while dequeueing buffer to surface", err);
+            return false;
+        }
+        mBuffer = buffer;
+        mBufferState = CAPTURING;
+        mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
+                                 buffer->handle, ++mSeq);
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 class JTvInputHal {
 public:
     ~JTvInputHal();
@@ -79,23 +239,31 @@
     const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
 
 private:
+    // Connection between a surface and a stream.
     class Connection {
     public:
         Connection() {}
 
         sp<Surface> mSurface;
+        tv_stream_type_t mStreamType;
+
+        // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
         sp<NativeHandle> mSourceHandle;
+        // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
+        sp<BufferProducerThread> mThread;
     };
 
     JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
 
     static void notify(
-            tv_input_device_t* dev,tv_input_event_t* event, void* data);
+            tv_input_device_t* dev, tv_input_event_t* event, void* data);
 
     void onDeviceAvailable(const tv_input_device_info_t& info);
     void onDeviceUnavailable(int deviceId);
     void onStreamConfigurationsChanged(int deviceId);
+    void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
 
+    Mutex mLock;
     jweak mThiz;
     tv_input_device_t* mDevice;
     tv_input_callback_ops_t mCallback;
@@ -153,11 +321,16 @@
         // Nothing to do
         return NO_ERROR;
     }
-    if (Surface::isValid(connection.mSurface)) {
+    // Clear the surface in the connection.
+    if (connection.mSurface != NULL) {
+        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+            if (Surface::isValid(connection.mSurface)) {
+                connection.mSurface->setSidebandStream(NULL);
+            }
+        }
         connection.mSurface.clear();
     }
-    connection.mSurface = surface;
-    if (connection.mSourceHandle == NULL) {
+    if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
         // Need to configure stream
         int numConfigs = 0;
         const tv_stream_config_t* configs = NULL;
@@ -177,22 +350,32 @@
             ALOGE("Cannot find a config with given stream ID: %d", streamId);
             return BAD_VALUE;
         }
-        // TODO: handle buffer producer profile.
-        if (configs[configIndex].type !=
-                TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
-            ALOGE("Profiles other than independent video source is not yet "
-                  "supported : type = %d", configs[configIndex].type);
-            return INVALID_OPERATION;
-        }
         tv_stream_t stream;
         stream.stream_id = configs[configIndex].stream_id;
+        if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+            stream.buffer_producer.width = configs[configIndex].max_video_width;
+            stream.buffer_producer.height = configs[configIndex].max_video_height;
+        }
         if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
             ALOGE("Couldn't add stream");
             return UNKNOWN_ERROR;
         }
-        connection.mSourceHandle = NativeHandle::create(
-                stream.sideband_stream_source_handle, false);
+        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+            connection.mSourceHandle = NativeHandle::create(
+                    stream.sideband_stream_source_handle, false);
+        } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+            if (connection.mThread != NULL) {
+                connection.mThread->shutdown();
+            }
+            connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
+            connection.mThread->run();
+        }
+    }
+    connection.mSurface = surface;
+    if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
         connection.mSurface->setSidebandStream(connection.mSourceHandle);
+    } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+        connection.mThread->setSurface(surface);
     }
     return NO_ERROR;
 }
@@ -220,6 +403,12 @@
             ALOGE("Couldn't remove stream");
             return BAD_VALUE;
         }
+
+        // Clear everything
+        if (connection.mThread != NULL) {
+            connection.mThread->shutdown();
+            connection.mThread.clear();
+        }
         connection.mSourceHandle.clear();
     }
     return NO_ERROR;
@@ -249,14 +438,29 @@
         case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
             thiz->onStreamConfigurationsChanged(event->device_info.device_id);
         } break;
+        case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
+            thiz->onCaptured(event->capture_result.device_id,
+                             event->capture_result.stream_id,
+                             event->capture_result.seq,
+                             true /* succeeded */);
+        } break;
+        case TV_INPUT_EVENT_CAPTURE_FAILED: {
+            thiz->onCaptured(event->capture_result.device_id,
+                             event->capture_result.stream_id,
+                             event->capture_result.seq,
+                             false /* succeeded */);
+        } break;
         default:
             ALOGE("Unrecognizable event");
     }
 }
 
 void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+    {
+        Mutex::Autolock autoLock(&mLock);
+        mConnections.add(info.device_id, KeyedVector<int, Connection>());
+    }
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    mConnections.add(info.device_id, KeyedVector<int, Connection>());
 
     jobject builder = env->NewObject(
             gTvInputHardwareInfoBuilderClassInfo.clazz,
@@ -290,13 +494,16 @@
 }
 
 void JTvInputHal::onDeviceUnavailable(int deviceId) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
-    for (size_t i = 0; i < connections.size(); ++i) {
-        removeStream(deviceId, connections.keyAt(i));
+    {
+        Mutex::Autolock autoLock(&mLock);
+        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+        for (size_t i = 0; i < connections.size(); ++i) {
+            removeStream(deviceId, connections.keyAt(i));
+        }
+        connections.clear();
+        mConnections.removeItem(deviceId);
     }
-    connections.clear();
-    mConnections.removeItem(deviceId);
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
             mThiz,
             gTvInputHalClassInfo.deviceUnavailable,
@@ -304,18 +511,36 @@
 }
 
 void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
-    for (size_t i = 0; i < connections.size(); ++i) {
-        removeStream(deviceId, connections.keyAt(i));
+    {
+        Mutex::Autolock autoLock(&mLock);
+        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+        for (size_t i = 0; i < connections.size(); ++i) {
+            removeStream(deviceId, connections.keyAt(i));
+        }
+        connections.clear();
     }
-    connections.clear();
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
             mThiz,
             gTvInputHalClassInfo.streamConfigsChanged,
             deviceId);
 }
 
+void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
+    sp<BufferProducerThread> thread;
+    {
+        Mutex::Autolock autoLock(&mLock);
+        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+        Connection& connection = connections.editValueFor(streamId);
+        if (connection.mThread == NULL) {
+            ALOGE("capture thread not existing.");
+            return;
+        }
+        thread = connection.mThread;
+    }
+    thread->onCaptured(seq, succeeded);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 static jlong nativeOpen(JNIEnv* env, jobject thiz) {
diff --git a/telecomm/java/android/telecomm/CallInfo.aidl b/telecomm/java/android/telecomm/CallInfo.aidl
deleted file mode 100644
index bc5ef96..0000000
--- a/telecomm/java/android/telecomm/CallInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-parcelable CallInfo;
diff --git a/telecomm/java/android/telecomm/CallInfo.java b/telecomm/java/android/telecomm/CallInfo.java
deleted file mode 100644
index 3810d7c..0000000
--- a/telecomm/java/android/telecomm/CallInfo.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A parcelable holder class of Call information data. This class is intended for transferring call
- * information from Telecomm to call services and thus is read-only.
- * TODO(santoscordon): Need final public-facing comments in this file.
- */
-public final class CallInfo implements Parcelable {
-
-    /**
-     * Unique identifier for the call.
-     */
-    private final String mId;
-
-    /**
-     * The state of the call.
-     */
-    private final CallState mState;
-
-    /**
-     * Endpoint to which the call is connected.
-     * This could be the dialed value for outgoing calls or the caller id of incoming calls.
-     */
-    private final Uri mHandle;
-
-    /**
-     * Gateway information for the call.
-     */
-    private final GatewayInfo mGatewayInfo;
-
-    /**
-     * Account information for the call.
-     */
-    private final PhoneAccount mAccount;
-
-    /**
-     * Additional information that can be persisted.
-     */
-    private final Bundle mExtras;
-
-    /** The descriptor for the call service currently routing this call. */
-    private final CallServiceDescriptor mCurrentCallServiceDescriptor;
-
-    public CallInfo(String id, CallState state, Uri handle) {
-        this(id, state, handle, null, null, Bundle.EMPTY, null);
-    }
-
-    /**
-     * Persists handle of the other party of this call.
-     *
-     * @param id The unique ID of the call.
-     * @param state The state of the call.
-     * @param handle The handle to the other party in this call.
-     * @param gatewayInfo Gateway information pertaining to this call.
-     * @param account Account information pertaining to this call.
-     * @param extras Additional information that can be persisted.
-     * @param currentCallServiceDescriptor The descriptor for the call service currently routing
-     *         this call.
-     *
-     * @hide
-     */
-    public CallInfo(
-            String id,
-            CallState state,
-            Uri handle,
-            GatewayInfo gatewayInfo,
-            PhoneAccount account,
-            Bundle extras,
-            CallServiceDescriptor currentCallServiceDescriptor) {
-        mId = id;
-        mState = state;
-        mHandle = handle;
-        mGatewayInfo = gatewayInfo;
-        mAccount = account;
-        mExtras = extras;
-        mCurrentCallServiceDescriptor = currentCallServiceDescriptor;
-    }
-
-    public String getId() {
-        return mId;
-    }
-
-    public CallState getState() {
-        return mState;
-    }
-
-    public Uri getHandle() {
-        return mHandle;
-    }
-
-    /**
-     * @return The actual handle this call is associated with. This is used by call services to
-     * correctly indicate in their UI what handle the user is actually calling, and by other
-     * telecomm components that require the user-dialed handle to function.
-     */
-    public Uri getOriginalHandle() {
-        if (mGatewayInfo != null) {
-            return mGatewayInfo.getOriginalHandle();
-        }
-        return getHandle();
-    }
-
-    public GatewayInfo getGatewayInfo() {
-        return mGatewayInfo;
-    }
-
-    public PhoneAccount getAccount() {
-        return mAccount;
-    }
-
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    public CallServiceDescriptor getCurrentCallServiceDescriptor() {
-        return mCurrentCallServiceDescriptor;
-    }
-
-    /**
-     * Responsible for creating CallInfo objects for deserialized Parcels.
-     */
-    public static final Parcelable.Creator<CallInfo> CREATOR = new Parcelable.Creator<CallInfo> () {
-        @Override
-        public CallInfo createFromParcel(Parcel source) {
-            String id = source.readString();
-            CallState state = CallState.valueOf(source.readString());
-            Uri handle = Uri.CREATOR.createFromParcel(source);
-
-            GatewayInfo gatewayInfo = readProviderInfoIfExists(source, GatewayInfo.CREATOR);
-            PhoneAccount account = readProviderInfoIfExists(source, PhoneAccount.CREATOR);
-
-            ClassLoader classLoader = CallInfo.class.getClassLoader();
-            Bundle extras = source.readParcelable(classLoader);
-            CallServiceDescriptor descriptor = source.readParcelable(classLoader);
-            return new CallInfo(id, state, handle, gatewayInfo, account, extras, descriptor);
-        }
-
-        @Override
-        public CallInfo[] newArray(int size) {
-            return new CallInfo[size];
-        }
-    };
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Writes CallInfo object into a serializeable Parcel.
-     */
-    @Override
-    public void writeToParcel(Parcel destination, int flags) {
-        destination.writeString(mId);
-        destination.writeString(mState.name());
-        mHandle.writeToParcel(destination, 0);
-
-        writeProviderInfoIfExists(destination, mGatewayInfo);
-        writeProviderInfoIfExists(destination, mAccount);
-
-        destination.writeParcelable(mExtras, 0);
-        destination.writeParcelable(mCurrentCallServiceDescriptor, 0);
-    }
-
-    /**
-     * Helper function to write provider information (either GatewayInfo or Account) to
-     * parcel. Will write a false byte if the information does not exist.
-     */
-    private void writeProviderInfoIfExists(Parcel destination, Parcelable provider) {
-        if (provider != null) {
-            destination.writeByte((byte) 1);
-            provider.writeToParcel(destination, 0);
-        } else {
-            destination.writeByte((byte) 0);
-        }
-    }
-
-    /**
-     * Helper function to read provider information (either GatewayInfo or Account) from
-     * parcel.
-     */
-    private static <T> T readProviderInfoIfExists(Parcel source,
-            Parcelable.Creator<T> creator) {
-        if (source.readByte() != 0) {
-            return creator.createFromParcel(source);
-        }
-        return null;
-    }
-}
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
deleted file mode 100644
index e77fb23..0000000
--- a/telecomm/java/android/telecomm/CallService.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-
-import com.android.internal.os.SomeArgs;
-import com.android.internal.telecomm.ICallService;
-import com.android.internal.telecomm.ICallServiceAdapter;
-
-/**
- * Base implementation of CallService which can be used to provide calls for the system
- * in-call UI. CallService is a one-way service from the framework's CallsManager to any app
- * that would like to provide calls managed by the default system in-call user interface.
- * TODO(santoscordon): Needs more about AndroidManifest.xml service registrations before
- * we can unhide this API.
- *
- * Most public methods of this function are backed by a one-way AIDL interface which precludes
- * synchronous responses. As a result, most responses are handled by (or have TODOs to handle)
- * response objects instead of return values.
- * TODO(santoscordon): Improve paragraph above once the final design is in place.
- */
-public abstract class CallService extends Service {
-
-    private static final int MSG_SET_CALL_SERVICE_ADAPTER = 1;
-    private static final int MSG_CALL = 2;
-    private static final int MSG_ABORT = 3;
-    private static final int MSG_SET_INCOMING_CALL_ID = 4;
-    private static final int MSG_ANSWER = 5;
-    private static final int MSG_REJECT = 6;
-    private static final int MSG_DISCONNECT = 7;
-    private static final int MSG_HOLD = 8;
-    private static final int MSG_UNHOLD = 9;
-    private static final int MSG_ON_AUDIO_STATE_CHANGED = 10;
-    private static final int MSG_PLAY_DTMF_TONE = 11;
-    private static final int MSG_STOP_DTMF_TONE = 12;
-    private static final int MSG_CONFERENCE = 13;
-    private static final int MSG_SPLIT_FROM_CONFERENCE = 14;
-    private static final int MSG_ON_POST_DIAL_CONTINUE = 15;
-    private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 16;
-
-    /**
-     * Default Handler used to consolidate binder method calls onto a single thread.
-     */
-    private final class CallServiceMessageHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SET_CALL_SERVICE_ADAPTER:
-                    mAdapter.addAdapter((ICallServiceAdapter) msg.obj);
-                    onAdapterAttached(mAdapter);
-                    break;
-                case MSG_CALL:
-                    call((CallInfo) msg.obj);
-                    break;
-                case MSG_ABORT:
-                    abort((String) msg.obj);
-                    break;
-                case MSG_SET_INCOMING_CALL_ID: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        String callId = (String) args.arg1;
-                        Bundle extras = (Bundle) args.arg2;
-                        setIncomingCallId(callId, extras);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-                case MSG_ANSWER:
-                    answer((String) msg.obj);
-                    break;
-                case MSG_REJECT:
-                    reject((String) msg.obj);
-                    break;
-                case MSG_DISCONNECT:
-                    disconnect((String) msg.obj);
-                    break;
-                case MSG_HOLD:
-                    hold((String) msg.obj);
-                    break;
-                case MSG_UNHOLD:
-                    unhold((String) msg.obj);
-                    break;
-                case MSG_ON_AUDIO_STATE_CHANGED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        String callId = (String) args.arg1;
-                        CallAudioState audioState = (CallAudioState) args.arg2;
-                        onAudioStateChanged(callId, audioState);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-                case MSG_PLAY_DTMF_TONE:
-                    playDtmfTone((String) msg.obj, (char) msg.arg1);
-                    break;
-                case MSG_STOP_DTMF_TONE:
-                    stopDtmfTone((String) msg.obj);
-                    break;
-                case MSG_CONFERENCE: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        String conferenceCallId = (String) args.arg1;
-                        String callId = (String) args.arg2;
-                        conference(conferenceCallId, callId);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-                case MSG_ON_POST_DIAL_CONTINUE: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        String callId = (String) args.arg1;
-                        boolean proceed = (args.argi1 == 1);
-                        onPostDialContinue(callId, proceed);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-                case MSG_SPLIT_FROM_CONFERENCE:
-                    splitFromConference((String) msg.obj);
-                    break;
-                case MSG_ON_PHONE_ACCOUNT_CLICKED:
-                    onPhoneAccountClicked((String) msg.obj);
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Default ICallService implementation provided to CallsManager via {@link #onBind}.
-     */
-    private final class CallServiceBinder extends ICallService.Stub {
-        @Override
-        public void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
-            mMessageHandler.obtainMessage(MSG_SET_CALL_SERVICE_ADAPTER, callServiceAdapter)
-                    .sendToTarget();
-        }
-
-        @Override
-        public void call(CallInfo callInfo) {
-            mMessageHandler.obtainMessage(MSG_CALL, callInfo).sendToTarget();
-        }
-
-        @Override
-        public void abort(String callId) {
-            mMessageHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
-        }
-
-        @Override
-        public void setIncomingCallId(String callId, Bundle extras) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.arg2 = extras;
-            mMessageHandler.obtainMessage(MSG_SET_INCOMING_CALL_ID, args).sendToTarget();
-        }
-
-        @Override
-        public void answer(String callId) {
-            mMessageHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
-        }
-
-        @Override
-        public void reject(String callId) {
-            mMessageHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
-        }
-
-        @Override
-        public void disconnect(String callId) {
-            mMessageHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
-        }
-
-        @Override
-        public void hold(String callId) {
-            mMessageHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
-        }
-
-        @Override
-        public void unhold(String callId) {
-            mMessageHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
-        }
-
-        @Override
-        public void playDtmfTone(String callId, char digit) {
-            mMessageHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
-        }
-
-        @Override
-        public void stopDtmfTone(String callId) {
-            mMessageHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
-        }
-
-        @Override
-        public void onAudioStateChanged(String callId, CallAudioState audioState) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.arg2 = audioState;
-            mMessageHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
-        }
-
-        @Override
-        public void conference(String conferenceCallId, String callId) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = conferenceCallId;
-            args.arg2 = callId;
-            mMessageHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
-        }
-
-        @Override
-        public void splitFromConference(String callId) {
-            mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
-        }
-
-        @Override
-        public void onPostDialContinue(String callId, boolean proceed) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callId;
-            args.argi1 = proceed ? 1 : 0;
-            mMessageHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
-        }
-
-        @Override
-        public void onPhoneAccountClicked(String callId) {
-            mMessageHandler.obtainMessage(MSG_ON_PHONE_ACCOUNT_CLICKED, callId).sendToTarget();
-        }
-
-    }
-
-    /**
-     * Message handler for consolidating binder callbacks onto a single thread.
-     * See {@link CallServiceMessageHandler}.
-     */
-    private final CallServiceMessageHandler mMessageHandler = new CallServiceMessageHandler();
-
-    /**
-     * Default binder implementation of {@link ICallService} interface.
-     */
-    private final CallServiceBinder mBinder = new CallServiceBinder();
-
-    private CallServiceAdapter mAdapter = new CallServiceAdapter();
-
-    /** {@inheritDoc} */
-    @Override
-    public final IBinder onBind(Intent intent) {
-        return getBinder();
-    }
-
-    /**
-     * Returns binder object which can be used across IPC methods.
-     */
-    public final IBinder getBinder() {
-        return mBinder;
-    }
-
-    /** @hide */
-    protected final CallServiceAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /** @hide */
-    protected abstract void onAdapterAttached(CallServiceAdapter adapter);
-
-    /** @hide */
-    protected abstract void call(CallInfo callInfo);
-
-    /** @hide */
-    protected abstract void abort(String callId);
-
-    /** @hide */
-    protected abstract void setIncomingCallId(String callId, Bundle extras);
-
-    /** @hide */
-    protected abstract void answer(String callId);
-
-    /** @hide */
-    protected abstract void reject(String callId);
-
-    /** @hide */
-    protected abstract void disconnect(String callId);
-
-    /** @hide */
-    protected abstract void hold(String callId);
-
-    /** @hide */
-    protected abstract void unhold(String callId);
-
-    /** @hide */
-    protected abstract void playDtmfTone(String callId, char digit);
-
-    /** @hide */
-    protected abstract void stopDtmfTone(String callId);
-
-    /** @hide */
-    protected abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState);
-
-    /** @hide */
-    protected abstract void conference(String conferenceCallId, String callId);
-
-    /** @hide */
-    protected abstract void splitFromConference(String callId);
-
-    /** @hide */
-    protected abstract void onPostDialContinue(String callId, boolean proceed);
-
-    /** @hide */
-    protected abstract void onFeaturesChanged(String callId, int features);
-
-    /** @hide */
-    protected abstract void onPhoneAccountClicked(String callId);
-}
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.java b/telecomm/java/android/telecomm/CallServiceDescriptor.java
index dec3791..5ae07d3 100644
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.java
+++ b/telecomm/java/android/telecomm/CallServiceDescriptor.java
@@ -26,7 +26,7 @@
 import java.util.UUID;
 
 /**
- * An immutable object containing information about a given {@link CallService}. Instances are
+ * An immutable object containing information about a given {@link ConnectionService}. Instances are
  * created using the enclosed {@link Builder}.
  */
 public final class CallServiceDescriptor implements Parcelable {
@@ -40,19 +40,19 @@
 
     /**
      * Indicates that the device must be connected to a Wi-Fi network in order for the backing
-     * {@link CallService} to be used.
+     * {@link ConnectionService} to be used.
      */
     public static final int FLAG_WIFI = 0x01;
 
     /**
      * Indicates that the device must be connected to a cellular PSTN network in order for the
-     * backing {@link CallService} to be used.
+     * backing {@link ConnectionService} to be used.
      */
     public static final int FLAG_PSTN = 0x02;
 
     /**
      * Indicates that the device must be connected to a cellular data network in order for the
-     * backing {@link CallService} to be used.
+     * backing {@link ConnectionService} to be used.
      */
     public static final int FLAG_MOBILE = 0x04;
 
@@ -65,45 +65,46 @@
     /**
      * A unique ID used to identify a given instance.
      */
-    private final String mCallServiceId;
+    private final String mConnectionServiceId;
 
     /**
-     * The {@link ComponentName} of the {@link CallService} implementation which this is describing.
+     * The {@link ComponentName} of the {@link ConnectionService} implementation which this is
+     * describing.
      */
     private final ComponentName mComponentName;
 
     /**
-     * The type of connection that the {@link CallService} requires; will be one of the FLAG_*
+     * The type of connection that the {@link ConnectionService} requires; will be one of the FLAG_*
      * constants defined in this class.
      */
     private final int mNetworkType;
 
     private CallServiceDescriptor(
-            String callServiceId,
+            String connectionServiceId,
             ComponentName componentName,
             int networkType) {
 
-        mCallServiceId = callServiceId;
+        mConnectionServiceId = connectionServiceId;
         mComponentName = componentName;
         mNetworkType = networkType;
     }
 
     /**
-     * @return The ID used to identify this {@link CallService}.
+     * @return The ID used to identify this {@link ConnectionService}.
      */
-    public String getCallServiceId() {
-        return mCallServiceId;
+    public String getConnectionServiceId() {
+        return mConnectionServiceId;
     }
 
     /**
-     * @return The {@link ComponentName} of the {@link CallService}.
+     * @return The {@link ComponentName} of the {@link ConnectionService}.
      */
     public ComponentName getServiceComponent() {
         return mComponentName;
     }
 
     /**
-     * @return The network type required by the {@link CallService} to place a call.
+     * @return The network type required by the {@link ConnectionService} to place a call.
      */
     public int getNetworkType() {
         return mNetworkType;
@@ -119,7 +120,7 @@
             return false;
         }
         CallServiceDescriptor descriptor = (CallServiceDescriptor) obj;
-        return mCallServiceId.equals(descriptor.mCallServiceId) &&
+        return mConnectionServiceId.equals(descriptor.mConnectionServiceId) &&
                 mComponentName.equals(descriptor.mComponentName) &&
                 mNetworkType == descriptor.mNetworkType;
     }
@@ -147,10 +148,10 @@
         /** The {@link Context} to use to verify {@link ComponentName} ownership. */
         private Context mContext;
 
-        /** The {@link ComponentName} pointing to the backing {@link CallService}. */
+        /** The {@link ComponentName} pointing to the backing {@link ConnectionService}. */
         private ComponentName mComponentName;
 
-        /** The required network type that the {@link CallService} needs. */
+        /** The required network type that the {@link ConnectionService} needs. */
         private int mNetworkType = FLAG_INVALID;
 
         private Builder(Context context) {
@@ -158,21 +159,21 @@
         }
 
         /**
-         * Set which {@link CallService} this {@link CallServiceDescriptor} is describing.
+         * Set which {@link ConnectionService} this {@link CallServiceDescriptor} is describing.
          *
-         * @param callServiceClass The {@link CallService} class
+         * @param serviceClass The {@link ConnectionService} class
          * @return This {@link Builder} for method chaining.
          */
-        public Builder setCallService(Class<? extends CallService> callServiceClass) {
-            mComponentName = new ComponentName(mContext, callServiceClass);
+        public Builder setConnectionService(Class<? extends ConnectionService> serviceClass) {
+            mComponentName = new ComponentName(mContext, serviceClass);
             return this;
         }
 
         /**
-         * Which network type the backing {@link CallService} requires. This must be one of the
-         * {@link CallServiceDescriptor}.TYPE_* fields.
+         * Which network type the backing {@link ConnectionService} requires. This must be one of
+         * the {@link CallServiceDescriptor}.TYPE_* fields.
          *
-         * @param networkType Which network type the backing {@link CallService} requires.
+         * @param networkType Which network type the backing {@link ConnectionService} requires.
          * @return This {@link Builder} for method chaining.
          */
         public Builder setNetworkType(int networkType) {
@@ -207,7 +208,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mCallServiceId);
+        dest.writeString(mConnectionServiceId);
         dest.writeParcelable(mComponentName, 0);
         dest.writeInt(mNetworkType);
     }
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 32dcf6c..c55e5ab 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -35,7 +35,6 @@
     public interface Listener {
         void onStateChanged(Connection c, int state);
         void onFeaturesChanged(Connection c, int features);
-        void onAudioStateChanged(Connection c, CallAudioState state);
         void onHandleChanged(Connection c, Uri newHandle);
         void onSignalChanged(Connection c, Bundle details);
         void onDisconnected(Connection c, int cause, String message);
@@ -56,10 +55,6 @@
         @Override
         public void onFeaturesChanged(Connection c, int features) {}
 
-        /** {@inheritDoc} */
-         @Override
-        public void onAudioStateChanged(Connection c, CallAudioState state) {}
-
         @Override
         public void onHandleChanged(Connection c, Uri newHandle) {}
 
@@ -141,7 +136,9 @@
      * @return The features of the call.  These are items for which the InCall UI may wish to
      *         display a visual indicator.
      */
-    public final int getFeatures() { return mFeatures; }
+    public final int getFeatures() {
+        return mFeatures;
+    }
 
     /**
      * @return The audio state of the call, describing how its audio is currently
@@ -153,6 +150,21 @@
     }
 
     /**
+     * Returns whether this connection is requesting that the system play a ringback tone
+     * on its behalf.
+     */
+    public final boolean isRequestingRingback() {
+        return mRequestingRingback;
+    }
+
+    /**
+     * Returns whether this connection is a conference connection (has child connections).
+     */
+    public final boolean isConferenceConnection() {
+        return !mChildConnections.isEmpty();
+    }
+
+    /**
      * Assign a listener to be notified of state changes.
      *
      * @param l A listener.
@@ -179,151 +191,14 @@
     }
 
     /**
-     * Play a DTMF tone in this Connection.
-     *
-     * @param c A DTMF character.
-     *
-     * @hide
-     */
-    public final void playDtmfTone(char c) {
-        Log.d(this, "playDtmfTone %c", c);
-        onPlayDtmfTone(c);
-    }
-
-    /**
-     * Stop any DTMF tones which may be playing in this Connection.
-     *
-     * @hide
-     */
-    public final void stopDtmfTone() {
-        Log.d(this, "stopDtmfTone");
-        onStopDtmfTone();
-    }
-
-    /**
-     * Disconnect this Connection. If and when the Connection can comply with
-     * this request, it will transition to the {@link State#DISCONNECTED}
-     * state and notify its listeners.
-     *
-     * @hide
-     */
-    public final void disconnect() {
-        Log.d(this, "disconnect");
-        onDisconnect();
-    }
-
-    /**
-     * Separates this Connection from a parent connection.
-     *
-     * @hide
-     */
-    public final void separate() {
-        Log.d(this, "separate");
-        onSeparate();
-    }
-
-    /**
-     * Abort this Connection. The Connection will immediately transition to
-     * the {@link State#DISCONNECTED} state, and send no notifications of this
-     * or any other future events.
-     *
-     * @hide
-     */
-    public final void abort() {
-        Log.d(this, "abort");
-        onAbort();
-    }
-
-    /**
-     * Place this Connection on hold. If and when the Connection can comply with
-     * this request, it will transition to the {@link State#HOLDING}
-     * state and notify its listeners.
-     *
-     * @hide
-     */
-    public final void hold() {
-        Log.d(this, "hold");
-        onHold();
-    }
-
-    /**
-     * Un-hold this Connection. If and when the Connection can comply with
-     * this request, it will transition to the {@link State#ACTIVE}
-     * state and notify its listeners.
-     *
-     * @hide
-     */
-    public final void unhold() {
-        Log.d(this, "unhold");
-        onUnhold();
-    }
-
-    /**
-     * Accept a {@link State#RINGING} Connection. If and when the Connection
-     * can comply with this request, it will transition to the {@link State#ACTIVE}
-     * state and notify its listeners.
-     *
-     * @hide
-     */
-    public final void answer() {
-        Log.d(this, "answer");
-        if (mState == State.RINGING) {
-            onAnswer();
-        }
-    }
-
-    /**
-     * Reject a {@link State#RINGING} Connection. If and when the Connection
-     * can comply with this request, it will transition to the {@link State#ACTIVE}
-     * state and notify its listeners.
-     *
-     * @hide
-     */
-    public final void reject() {
-        Log.d(this, "reject");
-        if (mState == State.RINGING) {
-            onReject();
-        }
-    }
-
-    /**
-     * TODO(santoscordon): Needs updated documentation.
-     *
-     * @hide
-     */
-    public final void conference() {
-        Log.d(this, "conference");
-        onConference();
-    }
-
-    /**
-     * Set the features applicable to the connection.  These are items for which the InCall UI may
-     * wish to display a visual indicator.
-     * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
-     * bit-mask.
-     *
-     * @param features The features active.
-     */
-    public final void setFeatures(int features) {
-        Log.d(this, "setFeatures %d", features);
-        this.mFeatures = features;
-        for (Listener l : mListeners) {
-            l.onFeaturesChanged(this, mFeatures);
-        }
-    }
-
-    /**
      * Inform this Connection that the state of its audio output has been changed externally.
      *
      * @param state The new audio state.
      * @hide
      */
-    public final void setAudioState(CallAudioState state) {
+    final void setAudioState(CallAudioState state) {
         Log.d(this, "setAudioState %s", state);
         mCallAudioState = state;
-        for (Listener l : mListeners) {
-            l.onAudioStateChanged(this, state);
-        }
         onSetAudioState(state);
     }
 
@@ -352,21 +227,6 @@
     }
 
     /**
-     * Returns whether this connection is requesting that the system play a ringback tone
-     * on its behalf.
-     */
-    public final boolean isRequestingRingback() {
-        return mRequestingRingback;
-    }
-
-    /**
-     * Returns whether this connection is a conference connection (has child connections).
-     */
-    public final boolean isConferenceConnection() {
-        return !mChildConnections.isEmpty();
-    }
-
-    /**
      * TODO(santoscordon): Needs documentation.
      */
     public final void setParentConnection(Connection parentConnection) {
@@ -415,6 +275,22 @@
     }
 
     /**
+     * Set the features applicable to the connection.  These are items for which the InCall UI may
+     * wish to display a visual indicator.
+     * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+     * bit-mask.
+     *
+     * @param features The features active.
+     */
+    public final void setFeatures(int features) {
+        Log.d(this, "setFeatures %d", features);
+        mFeatures = features;
+        for (Listener l : mListeners) {
+            l.onFeaturesChanged(this, mFeatures);
+        }
+    }
+
+    /**
      * Sets state to active (e.g., an ongoing call where two or more parties can actively
      * communicate).
      */
@@ -604,11 +480,6 @@
     /**
      * TODO(santoscordon): Needs documentation.
      */
-    protected void onConference() {}
-
-    /**
-     * TODO(santoscordon): Needs documentation.
-     */
     protected void onChildrenChanged(List<Connection> children) {}
 
     /**
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index a9aa5dd..ed61622 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -80,9 +80,6 @@
                 mExtras == null ? "" : mExtras);
     }
 
-    /**
-     * Responsible for creating CallInfo objects for deserialized Parcels.
-     */
     public static final Parcelable.Creator<ConnectionRequest> CREATOR =
             new Parcelable.Creator<ConnectionRequest> () {
                 @Override
@@ -107,9 +104,6 @@
         return 0;
     }
 
-    /**
-     * Writes CallInfo object into a serializeable Parcel.
-     */
     @Override
     public void writeToParcel(Parcel destination, int flags) {
         destination.writeString(mCallId);
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index d6970e2..3b763957 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -16,16 +16,19 @@
 
 package android.telecomm;
 
+import android.app.Service;
+import android.content.Intent;
 import android.content.ComponentName;
 import android.net.Uri;
 import android.os.Bundle;
-
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.telephony.DisconnectCause;
+import android.os.Message;
 
-import com.android.internal.telecomm.ICallService;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecomm.IConnectionService;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
 import com.android.internal.telecomm.RemoteServiceCallback;
 
 import java.util.Collection;
@@ -34,23 +37,39 @@
 import java.util.Map;
 
 /**
- * A {@link android.app.Service} that provides telephone connections to
- * processes running on an Android device.
+ * A {@link android.app.Service} that provides telephone connections to processes running on an
+ * Android device.
  */
-public abstract class ConnectionService extends CallService {
+public abstract class ConnectionService extends Service {
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
     private static final Connection NULL_CONNECTION = new Connection() {};
 
-    // Mappings from Connections to IDs as understood by the current CallService implementation
+    private static final int MSG_ADD_CALL_SERVICE_ADAPTER = 1;
+    private static final int MSG_CALL = 2;
+    private static final int MSG_ABORT = 3;
+    private static final int MSG_CREATE_INCOMING_CALL = 4;
+    private static final int MSG_ANSWER = 5;
+    private static final int MSG_REJECT = 6;
+    private static final int MSG_DISCONNECT = 7;
+    private static final int MSG_HOLD = 8;
+    private static final int MSG_UNHOLD = 9;
+    private static final int MSG_ON_AUDIO_STATE_CHANGED = 10;
+    private static final int MSG_PLAY_DTMF_TONE = 11;
+    private static final int MSG_STOP_DTMF_TONE = 12;
+    private static final int MSG_CONFERENCE = 13;
+    private static final int MSG_SPLIT_FROM_CONFERENCE = 14;
+    private static final int MSG_ON_POST_DIAL_CONTINUE = 15;
+    private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 16;
+
     private final Map<String, Connection> mConnectionById = new HashMap<>();
     private final Map<Connection, String> mIdByConnection = new HashMap<>();
     private final RemoteConnectionManager mRemoteConnectionManager = new RemoteConnectionManager();
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     private SimpleResponse<Uri, List<PhoneAccount>> mAccountLookupResponse;
     private Uri mAccountLookupHandle;
     private boolean mAreAccountsInitialized = false;
+    private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
 
     /**
      * A callback for providing the resuilt of creating a connection.
@@ -81,6 +100,180 @@
         void onCancel(ConnectionRequest request);
     }
 
+    private final IBinder mBinder = new IConnectionService.Stub() {
+        @Override
+        public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
+            mHandler.obtainMessage(MSG_ADD_CALL_SERVICE_ADAPTER, adapter).sendToTarget();
+        }
+
+        @Override
+        public void call(ConnectionRequest request) {
+            mHandler.obtainMessage(MSG_CALL, request).sendToTarget();
+        }
+
+        @Override
+        public void abort(String callId) {
+            mHandler.obtainMessage(MSG_ABORT, callId).sendToTarget();
+        }
+
+        @Override
+        public void createIncomingCall(ConnectionRequest request) {
+            mHandler.obtainMessage(MSG_CREATE_INCOMING_CALL, request).sendToTarget();
+        }
+
+        @Override
+        public void answer(String callId) {
+            mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
+        }
+
+        @Override
+        public void reject(String callId) {
+            mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
+        }
+
+        @Override
+        public void disconnect(String callId) {
+            mHandler.obtainMessage(MSG_DISCONNECT, callId).sendToTarget();
+        }
+
+        @Override
+        public void hold(String callId) {
+            mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
+        }
+
+        @Override
+        public void unhold(String callId) {
+            mHandler.obtainMessage(MSG_UNHOLD, callId).sendToTarget();
+        }
+
+        @Override
+        public void onAudioStateChanged(String callId, CallAudioState audioState) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = audioState;
+            mHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
+        }
+
+        @Override
+        public void playDtmfTone(String callId, char digit) {
+            mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, digit, 0, callId).sendToTarget();
+        }
+
+        @Override
+        public void stopDtmfTone(String callId) {
+            mHandler.obtainMessage(MSG_STOP_DTMF_TONE, callId).sendToTarget();
+        }
+
+        @Override
+        public void conference(String conferenceCallId, String callId) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = conferenceCallId;
+            args.arg2 = callId;
+            mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
+        }
+
+        @Override
+        public void splitFromConference(String callId) {
+            mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
+        }
+
+        @Override
+        public void onPostDialContinue(String callId, boolean proceed) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.argi1 = proceed ? 1 : 0;
+            mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
+        }
+
+        @Override
+        public void onPhoneAccountClicked(String callId) {
+            mHandler.obtainMessage(MSG_ON_PHONE_ACCOUNT_CLICKED, callId).sendToTarget();
+        }
+    };
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ADD_CALL_SERVICE_ADAPTER:
+                    mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
+                    onAdapterAttached();
+                    break;
+                case MSG_CALL:
+                    call((ConnectionRequest) msg.obj);
+                    break;
+                case MSG_ABORT:
+                    abort((String) msg.obj);
+                    break;
+                case MSG_CREATE_INCOMING_CALL:
+                    createIncomingCall((ConnectionRequest) msg.obj);
+                    break;
+                case MSG_ANSWER:
+                    answer((String) msg.obj);
+                    break;
+                case MSG_REJECT:
+                    reject((String) msg.obj);
+                    break;
+                case MSG_DISCONNECT:
+                    disconnect((String) msg.obj);
+                    break;
+                case MSG_HOLD:
+                    hold((String) msg.obj);
+                    break;
+                case MSG_UNHOLD:
+                    unhold((String) msg.obj);
+                    break;
+                case MSG_ON_AUDIO_STATE_CHANGED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        String callId = (String) args.arg1;
+                        CallAudioState audioState = (CallAudioState) args.arg2;
+                        onAudioStateChanged(callId, audioState);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_PLAY_DTMF_TONE:
+                    playDtmfTone((String) msg.obj, (char) msg.arg1);
+                    break;
+                case MSG_STOP_DTMF_TONE:
+                    stopDtmfTone((String) msg.obj);
+                    break;
+                case MSG_CONFERENCE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        String conferenceCallId = (String) args.arg1;
+                        String callId = (String) args.arg2;
+                        conference(conferenceCallId, callId);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SPLIT_FROM_CONFERENCE:
+                    splitFromConference((String) msg.obj);
+                    break;
+                case MSG_ON_POST_DIAL_CONTINUE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        String callId = (String) args.arg1;
+                        boolean proceed = (args.argi1 == 1);
+                        onPostDialContinue(callId, proceed);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_ON_PHONE_ACCOUNT_CLICKED:
+                    onPhoneAccountClicked((String) msg.obj);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
     private final Connection.Listener mConnectionListener = new Connection.Listener() {
         @Override
         public void onStateChanged(Connection c, int state) {
@@ -88,22 +281,22 @@
             Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));
             switch (state) {
                 case Connection.State.ACTIVE:
-                    getAdapter().setActive(id);
+                    mAdapter.setActive(id);
                     break;
                 case Connection.State.DIALING:
-                    getAdapter().setDialing(id);
+                    mAdapter.setDialing(id);
                     break;
                 case Connection.State.DISCONNECTED:
                     // Handled in onDisconnected()
                     break;
                 case Connection.State.HOLDING:
-                    getAdapter().setOnHold(id);
+                    mAdapter.setOnHold(id);
                     break;
                 case Connection.State.NEW:
                     // Nothing to tell Telecomm
                     break;
                 case Connection.State.RINGING:
-                    getAdapter().setRinging(id);
+                    mAdapter.setRinging(id);
                     break;
             }
         }
@@ -113,14 +306,14 @@
         public void onFeaturesChanged(Connection c, int features) {
             String id = mIdByConnection.get(c);
             Log.d(this, "Adapter set features %d", features);
-            getAdapter().setFeatures(id, features);
+            mAdapter.setFeatures(id, features);
         }
 
         @Override
         public void onDisconnected(Connection c, int cause, String message) {
             String id = mIdByConnection.get(c);
             Log.d(this, "Adapter set disconnected %d %s", cause, message);
-            getAdapter().setDisconnected(id, cause, message);
+            mAdapter.setDisconnected(id, cause, message);
         }
 
         @Override
@@ -129,11 +322,6 @@
         }
 
         @Override
-        public void onAudioStateChanged(Connection c, CallAudioState state) {
-            // TODO: Unsupported yet
-        }
-
-        @Override
         public void onSignalChanged(Connection c, Bundle details) {
             // TODO: Unsupported yet
         }
@@ -144,23 +332,23 @@
         }
 
         @Override
-        public final void onPostDialWait(Connection c, String remaining) {
+        public void onPostDialWait(Connection c, String remaining) {
             String id = mIdByConnection.get(c);
             Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining);
-            getAdapter().onPostDialWait(id, remaining);
+            mAdapter.onPostDialWait(id, remaining);
         }
 
         @Override
         public void onRequestingRingback(Connection c, boolean ringback) {
             String id = mIdByConnection.get(c);
             Log.d(this, "Adapter onRingback %b", ringback);
-            getAdapter().setRequestingRingback(id, ringback);
+            mAdapter.setRequestingRingback(id, ringback);
         }
 
         @Override
         public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {
             String id = mIdByConnection.get(c);
-            getAdapter().setCanConference(id, isConferenceCapable);
+            mAdapter.setCanConference(id, isConferenceCapable);
         }
 
         /** ${inheritDoc} */
@@ -168,156 +356,120 @@
         public void onParentConnectionChanged(Connection c, Connection parent) {
             String id = mIdByConnection.get(c);
             String parentId = parent == null ? null : mIdByConnection.get(parent);
-            getAdapter().setIsConferenced(id, parentId);
+            mAdapter.setIsConferenced(id, parentId);
         }
 
         @Override
         public void onSetCallVideoProvider(Connection c, CallVideoProvider callVideoProvider) {
             String id = mIdByConnection.get(c);
-            getAdapter().setCallVideoProvider(id, callVideoProvider);
+            mAdapter.setCallVideoProvider(id, callVideoProvider);
         }
     };
 
-    /** @hide */
+    /** {@inheritDoc} */
     @Override
-    protected final void call(final CallInfo callInfo) {
-        Log.d(this, "call %s", callInfo);
+    public final IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    private void call(final ConnectionRequest originalRequest) {
+        Log.d(this, "call %s", originalRequest);
         onCreateConnections(
-                new ConnectionRequest(
-                        callInfo.getId(),
-                        callInfo.getHandle(),
-                        callInfo.getExtras()),
+                originalRequest,
                 new OutgoingCallResponse<Connection>() {
                     @Override
                     public void onSuccess(ConnectionRequest request, Connection connection) {
-                        Log.d(this, "adapter handleSuccessfulOutgoingCall %s",
-                                callInfo.getId());
-                        getAdapter().handleSuccessfulOutgoingCall(callInfo.getId());
-                        addConnection(callInfo.getId(), connection);
+                        Log.d(this, "adapter handleSuccessfulOutgoingCall %s", request.getCallId());
+                        mAdapter.handleSuccessfulOutgoingCall(request);
+                        addConnection(request.getCallId(), connection);
                     }
 
                     @Override
                     public void onFailure(ConnectionRequest request, int code, String msg) {
-                        getAdapter().handleFailedOutgoingCall(request, code, msg);
+                        mAdapter.handleFailedOutgoingCall(request, code, msg);
                     }
 
                     @Override
                     public void onCancel(ConnectionRequest request) {
-                        getAdapter().cancelOutgoingCall(callInfo.getId());
+                        mAdapter.cancelOutgoingCall(request);
                     }
                 }
         );
     }
 
-    /** @hide */
-    @Override
-    protected final void abort(String callId) {
+    private void abort(String callId) {
         Log.d(this, "abort %s", callId);
-        findConnectionForAction(callId, "abort").abort();
+        findConnectionForAction(callId, "abort").onAbort();
     }
 
-    /** @hide */
-    @Override
-    protected final void setIncomingCallId(final String callId, Bundle extras) {
-        Log.d(this, "setIncomingCallId %s %s", callId, extras);
+    private void createIncomingCall(ConnectionRequest originalRequest) {
+        Log.d(this, "createIncomingCall %s", originalRequest);
         onCreateIncomingConnection(
-                new ConnectionRequest(
-                        callId,
-                        null,  // TODO: Can we obtain this from "extras"?
-                        extras),
+                originalRequest,
                 new Response<ConnectionRequest, Connection>() {
                     @Override
                     public void onResult(ConnectionRequest request, Connection... result) {
                         if (result != null && result.length != 1) {
-                            Log.d(this, "adapter handleFailedOutgoingCall %s", callId);
-                            getAdapter().handleFailedOutgoingCall(
-                                    request,
-                                    DisconnectCause.ERROR_UNSPECIFIED,
-                                    "Created " + result.length + " Connections, expected 1");
                             for (Connection c : result) {
-                                c.abort();
+                                c.onAbort();
                             }
                         } else {
-                            addConnection(callId, result[0]);
-                            Log.d(this, "adapter notifyIncomingCall %s", callId);
-                            // TODO: Uri.EMPTY is because CallInfo crashes when Parceled with a
-                            // null URI ... need to fix that at its cause!
-                            getAdapter().notifyIncomingCall(new CallInfo(
-                                    callId,
-                                    connectionStateToCallState(result[0].getState()),
-                                    request.getHandle() /* result[0].getHandle() == null
-                                            ? Uri.EMPTY : result[0].getHandle() */));
+                            addConnection(request.getCallId(), result[0]);
+                            Log.d(this, "adapter notifyIncomingCall %s", request);
+                            mAdapter.notifyIncomingCall(request);
                         }
                     }
 
                     @Override
                     public void onError(ConnectionRequest request, int code, String msg) {
-                        Log.d(this, "adapter failed setIncomingCallId %s %d %s",
+                        Log.d(this, "adapter failed createIncomingCall %s %d %s",
                                 request, code, msg);
                     }
                 }
         );
     }
 
-    /** @hide */
-    @Override
-    protected final void answer(String callId) {
+    private void answer(String callId) {
         Log.d(this, "answer %s", callId);
-        findConnectionForAction(callId, "answer").answer();
+        findConnectionForAction(callId, "answer").onAnswer();
     }
 
-    /** @hide */
-    @Override
-    protected final void reject(String callId) {
+    private void reject(String callId) {
         Log.d(this, "reject %s", callId);
-        findConnectionForAction(callId, "reject").reject();
+        findConnectionForAction(callId, "reject").onReject();
     }
 
-    /** @hide */
-    @Override
-    protected final void disconnect(String callId) {
+    private void disconnect(String callId) {
         Log.d(this, "disconnect %s", callId);
-        findConnectionForAction(callId, "disconnect").disconnect();
+        findConnectionForAction(callId, "disconnect").onDisconnect();
     }
 
-    /** @hide */
-    @Override
-    protected final void hold(String callId) {
+    private void hold(String callId) {
         Log.d(this, "hold %s", callId);
-        findConnectionForAction(callId, "hold").hold();
+        findConnectionForAction(callId, "hold").onHold();
     }
 
-    /** @hide */
-    @Override
-    protected final void unhold(String callId) {
+    private void unhold(String callId) {
         Log.d(this, "unhold %s", callId);
-        findConnectionForAction(callId, "unhold").unhold();
+        findConnectionForAction(callId, "unhold").onUnhold();
     }
 
-    /** @hide */
-    @Override
-    protected final void playDtmfTone(String callId, char digit) {
-        Log.d(this, "playDtmfTone %s %c", callId, digit);
-        findConnectionForAction(callId, "playDtmfTone").playDtmfTone(digit);
-    }
-
-    /** @hide */
-    @Override
-    protected final void stopDtmfTone(String callId) {
-        Log.d(this, "stopDtmfTone %s", callId);
-        findConnectionForAction(callId, "stopDtmfTone").stopDtmfTone();
-    }
-
-    /** @hide */
-    @Override
-    protected final void onAudioStateChanged(String callId, CallAudioState audioState) {
+    private void onAudioStateChanged(String callId, CallAudioState audioState) {
         Log.d(this, "onAudioStateChanged %s %s", callId, audioState);
         findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
     }
 
-    /** @hide */
-    @Override
-    protected final void conference(final String conferenceCallId, String callId) {
+    private void playDtmfTone(String callId, char digit) {
+        Log.d(this, "playDtmfTone %s %c", callId, digit);
+        findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
+    }
+
+    private void stopDtmfTone(String callId) {
+        Log.d(this, "stopDtmfTone %s", callId);
+        findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
+    }
+
+    private void conference(final String conferenceCallId, String callId) {
         Log.d(this, "conference %s, %s", conferenceCallId, callId);
 
         Connection connection = findConnectionForAction(callId, "conference");
@@ -336,7 +488,7 @@
                             Connection conferenceConnection = result[0];
                             if (!mIdByConnection.containsKey(conferenceConnection)) {
                                 Log.v(this, "sending new conference call %s", conferenceCallId);
-                                getAdapter().addConferenceCall(conferenceCallId);
+                                mAdapter.addConferenceCall(conferenceCallId);
                                 addConnection(conferenceCallId, conferenceConnection);
                             }
                         }
@@ -350,9 +502,7 @@
                 });
     }
 
-    /** @hide */
-    @Override
-    protected final void splitFromConference(String callId) {
+    private void splitFromConference(String callId) {
         Log.d(this, "splitFromConference(%s)", callId);
 
         Connection connection = findConnectionForAction(callId, "splitFromConference");
@@ -364,55 +514,36 @@
         // TODO(santoscordon): Find existing conference call and invoke split(connection).
     }
 
-    /** @hide */
-    @Override
-    protected final void onPostDialContinue(String callId, boolean proceed) {
+    private void onPostDialContinue(String callId, boolean proceed) {
         Log.d(this, "onPostDialContinue(%s)", callId);
-
-        Connection connection = findConnectionForAction(callId, "onPostDialContinue");
-        if (connection == NULL_CONNECTION) {
-            Log.w(this, "Connection missing in post-dial request %s.", callId);
-            return;
-        }
-        connection.onPostDialContinue(proceed);
+        findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
     }
 
-    /** @hide */
-    @Override
-    protected final void onFeaturesChanged(String callId, int features) {
-        Log.d(this, "onFeaturesChanged %s %d", callId, features);
-        findConnectionForAction(callId, "onFeaturesChanged").setFeatures(features);
-    }
-
-    /** @hide */
-    @Override
-    protected void onPhoneAccountClicked(String callId) {
+    private void onPhoneAccountClicked(String callId) {
         Log.d(this, "onPhoneAccountClicked %s", callId);
         findConnectionForAction(callId, "onPhoneAccountClicked").onPhoneAccountClicked();
     }
 
-    /** @hide */
-    @Override
-    protected final void onAdapterAttached(CallServiceAdapter adapter) {
+    private void onAdapterAttached() {
         if (mAreAccountsInitialized) {
             // No need to query again if we already did it.
             return;
         }
 
-        getAdapter().queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
+        mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
             @Override
             public void onResult(
                     final List<ComponentName> componentNames,
-                    final List<IBinder> callServices) {
+                    final List<IBinder> services) {
                 mHandler.post(new Runnable() {
                     @Override public void run() {
-                        for (int i = 0; i < componentNames.size() && i < callServices.size(); i++) {
+                        for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
                             mRemoteConnectionManager.addConnectionService(
                                     componentNames.get(i),
-                                    ICallService.Stub.asInterface(callServices.get(i)));
+                                    IConnectionService.Stub.asInterface(services.get(i)));
                         }
                         mAreAccountsInitialized = true;
-                        Log.d(this, "remote call services found: " + callServices);
+                        Log.d(this, "remote call services found: " + services);
                         maybeRespondToAccountLookup();
                     }
                 });
@@ -540,26 +671,6 @@
         return builder.toString();
     }
 
-    private CallState connectionStateToCallState(int connectionState) {
-        switch (connectionState) {
-            case Connection.State.NEW:
-                return CallState.NEW;
-            case Connection.State.RINGING:
-                return CallState.RINGING;
-            case Connection.State.DIALING:
-                return CallState.DIALING;
-            case Connection.State.ACTIVE:
-                return CallState.ACTIVE;
-            case Connection.State.HOLDING:
-                return CallState.ON_HOLD;
-            case Connection.State.DISCONNECTED:
-                return CallState.DISCONNECTED;
-            default:
-                Log.wtf(this, "Unknown Connection.State %d", connectionState);
-                return CallState.NEW;
-        }
-    }
-
     private void addConnection(String callId, Connection connection) {
         mConnectionById.put(callId, connection);
         mIdByConnection.put(connection, callId);
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
similarity index 67%
rename from telecomm/java/android/telecomm/CallServiceAdapter.java
rename to telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index dcab566..1d21888 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -21,8 +21,8 @@
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 
-import com.android.internal.telecomm.ICallService;
-import com.android.internal.telecomm.ICallServiceAdapter;
+import com.android.internal.telecomm.IConnectionService;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
 import com.android.internal.telecomm.ICallVideoProvider;
 import com.android.internal.telecomm.RemoteServiceCallback;
 
@@ -32,24 +32,17 @@
 import java.util.Set;
 
 /**
- * Provides methods for ICallService implementations to interact with the system phone app.
- * TODO(santoscordon): Rename this to CallServiceAdapterDemultiplexer (or something).
+ * Provides methods for IConnectionService implementations to interact with the system phone app.
  *
  * @hide
  */
-public final class CallServiceAdapter implements DeathRecipient {
-    private final Set<ICallServiceAdapter> mAdapters = new HashSet<>();
+final class ConnectionServiceAdapter implements DeathRecipient {
+    private final Set<IConnectionServiceAdapter> mAdapters = new HashSet<>();
 
-    /**
-     * @hide
-     */
-    public CallServiceAdapter() {
+    ConnectionServiceAdapter() {
     }
 
-    /**
-     * @hide
-     */
-    public void addAdapter(ICallServiceAdapter adapter) {
+    void addAdapter(IConnectionServiceAdapter adapter) {
         if (mAdapters.add(adapter)) {
             try {
                 adapter.asBinder().linkToDeath(this, 0);
@@ -59,10 +52,7 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    public void removeAdapter(ICallServiceAdapter adapter) {
+    void removeAdapter(IConnectionServiceAdapter adapter) {
         if (mAdapters.remove(adapter)) {
             adapter.asBinder().unlinkToDeath(this, 0);
         }
@@ -71,31 +61,25 @@
     /** ${inheritDoc} */
     @Override
     public void binderDied() {
-        ICallServiceAdapter adapterToRemove = null;
-        for (ICallServiceAdapter adapter : mAdapters) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             if (!adapter.asBinder().isBinderAlive()) {
-                adapterToRemove = adapter;
-                break;
+                removeAdapter(adapter);
             }
         }
-
-        if (adapterToRemove != null) {
-            removeAdapter(adapterToRemove);
-        }
     }
 
     /**
      * Provides Telecomm with the details of an incoming call. An invocation of this method must
-     * follow {@link CallService#setIncomingCallId} and use the call ID specified therein. Upon the
-     * invocation of this method, Telecomm will bring up the incoming-call interface where the user
-     * can elect to answer or reject a call.
+     * follow {@link ConnectionService#setIncomingCallId} and use the call ID specified therein.
+     * Upon the invocation of this method, Telecomm will bring up the incoming-call interface where
+     * the user can elect to answer or reject a call.
      *
-     * @param callInfo The details of the relevant call.
+     * @param request The connection request.
      */
-    public void notifyIncomingCall(CallInfo callInfo) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void notifyIncomingCall(ConnectionRequest request) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.notifyIncomingCall(callInfo);
+                adapter.notifyIncomingCall(request);
             } catch (RemoteException e) {
             }
         }
@@ -103,15 +87,13 @@
 
     /**
      * Tells Telecomm that an attempt to place the specified outgoing call succeeded.
-     * TODO(santoscordon): Consider adding a CallState parameter in case this outgoing call is
-     * somehow no longer in the DIALING state.
      *
-     * @param callId The ID of the outgoing call.
+     * @param request The originating request for a connection.
      */
-    public void handleSuccessfulOutgoingCall(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.handleSuccessfulOutgoingCall(callId);
+                adapter.handleSuccessfulOutgoingCall(request);
             } catch (RemoteException e) {
             }
         }
@@ -124,11 +106,11 @@
      * @param errorCode The error code associated with the failed call attempt.
      * @param errorMsg The error message associated with the failed call attempt.
      */
-    public void handleFailedOutgoingCall(
+    void handleFailedOutgoingCall(
             ConnectionRequest request,
             int errorCode,
             String errorMsg) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
             } catch (RemoteException e) {
@@ -139,12 +121,12 @@
     /**
      * Tells Telecomm to cancel the call.
      *
-     * @param callId The ID of the outgoing call.
+     * @param request The originating request for a connection.
      */
-    public void cancelOutgoingCall(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void cancelOutgoingCall(ConnectionRequest request) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.cancelOutgoingCall(callId);
+                adapter.cancelOutgoingCall(request);
             } catch (RemoteException e) {
             }
         }
@@ -156,8 +138,8 @@
      *
      * @param callId The unique ID of the call whose state is changing to active.
      */
-    public void setActive(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setActive(String callId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setActive(callId);
             } catch (RemoteException e) {
@@ -170,8 +152,8 @@
      *
      * @param callId The unique ID of the call whose state is changing to ringing.
      */
-    public void setRinging(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setRinging(String callId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setRinging(callId);
             } catch (RemoteException e) {
@@ -184,8 +166,8 @@
      *
      * @param callId The unique ID of the call whose state is changing to dialing.
      */
-    public void setDialing(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setDialing(String callId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setDialing(callId);
             } catch (RemoteException e) {
@@ -201,8 +183,8 @@
      *            {@link android.telephony.DisconnectCause}.
      * @param disconnectMessage Optional call-service-provided message about the disconnect.
      */
-    public void setDisconnected(String callId, int disconnectCause, String disconnectMessage) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setDisconnected(String callId, int disconnectCause, String disconnectMessage) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setDisconnected(callId, disconnectCause, disconnectMessage);
             } catch (RemoteException e) {
@@ -215,8 +197,8 @@
      *
      * @param callId - The unique ID of the call whose state is changing to be on hold.
      */
-    public void setOnHold(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setOnHold(String callId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setOnHold(callId);
             } catch (RemoteException e) {
@@ -230,8 +212,8 @@
      * @param callId The unique ID of the call whose ringback is being changed.
      * @param ringback Whether Telecomm should start playing a ringback tone.
      */
-    public void setRequestingRingback(String callId, boolean ringback) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setRequestingRingback(String callId, boolean ringback) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setRequestingRingback(callId, ringback);
             } catch (RemoteException e) {
@@ -244,10 +226,9 @@
      *
      * @param callId The unique ID of the call.
      * @param canConference Specified whether or not the call can be conferenced.
-     * @hide
      */
-    public void setCanConference(String callId, boolean canConference) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setCanConference(String callId, boolean canConference) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setCanConference(callId, canConference);
             } catch (RemoteException ignored) {
@@ -262,10 +243,9 @@
      * @param callId The unique ID of the call being conferenced.
      * @param conferenceCallId The unique ID of the conference call. Null if call is not
      *            conferenced.
-     * @hide
      */
-    public void setIsConferenced(String callId, String conferenceCallId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setIsConferenced(String callId, String conferenceCallId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setIsConferenced(callId, conferenceCallId);
             } catch (RemoteException ignored) {
@@ -278,10 +258,9 @@
      * call.
      *
      * @param callId The unique ID of the call.
-     * @hide
      */
-    public void removeCall(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void removeCall(String callId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.removeCall(callId);
             } catch (RemoteException ignored) {
@@ -289,8 +268,8 @@
         }
     }
 
-    public void onPostDialWait(String callId, String remaining) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void onPostDialWait(String callId, String remaining) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.onPostDialWait(callId, remaining);
             } catch (RemoteException ignored) {
@@ -303,10 +282,10 @@
      *
      * @param callId The unique ID of the conference call.
      */
-    public void addConferenceCall(String callId) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void addConferenceCall(String callId) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.addConferenceCall(callId, null);
+                adapter.addConferenceCall(callId);
             } catch (RemoteException ignored) {
             }
         }
@@ -314,9 +293,8 @@
 
     /**
      * Retrieves a list of remote connection services usable to place calls.
-     * @hide
      */
-    public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+    void queryRemoteConnectionServices(RemoteServiceCallback callback) {
         // Only supported when there is only one adapter.
         if (mAdapters.size() == 1) {
             try {
@@ -333,8 +311,8 @@
      * @param callId The unique ID of the call to set with the given call video provider.
      * @param callVideoProvider The call video provider instance to set on the call.
      */
-    public void setCallVideoProvider(String callId, CallVideoProvider callVideoProvider) {
-        for (ICallServiceAdapter adapter : mAdapters) {
+    void setCallVideoProvider(String callId, CallVideoProvider callVideoProvider) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setCallVideoProvider(callId, callVideoProvider.getInterface());
             } catch (RemoteException e) {
@@ -350,9 +328,9 @@
     * @param callId The unique ID of the call to set features for.
     * @param features The features.
     */
-    public void setFeatures(String callId, int features) {
+    void setFeatures(String callId, int features) {
         Log.v(this, "setFeatures: %d", features);
-        for (ICallServiceAdapter adapter : mAdapters) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
                 adapter.setFeatures(callId, features);
             } catch (RemoteException ignored) {
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index f200bc0..18df37d 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -22,7 +22,7 @@
 import android.os.RemoteException;
 import android.telephony.DisconnectCause;
 
-import com.android.internal.telecomm.ICallService;
+import com.android.internal.telecomm.IConnectionService;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -36,10 +36,11 @@
         void onDisconnected(RemoteConnection connection, int cause, String message);
         void onRequestingRingback(RemoteConnection connection, boolean ringback);
         void onPostDialWait(RemoteConnection connection, String remainingDigits);
+        void onFeaturesChanged(RemoteConnection connection, int features);
         void onDestroyed(RemoteConnection connection);
     }
 
-    private final ICallService mCallService;
+    private final IConnectionService mConnectionService;
     private final String mConnectionId;
     private final Set<Listener> mListeners = new HashSet<>();
 
@@ -48,12 +49,13 @@
     private String mDisconnectMessage;
     private boolean mRequestingRingback;
     private boolean mConnected;
+    private int mFeatures;
 
     /**
      * @hide
      */
-    RemoteConnection(ICallService callService, String connectionId) {
-        mCallService = callService;
+    RemoteConnection(IConnectionService connectionService, String connectionId) {
+        mConnectionService = connectionService;
         mConnectionId = connectionId;
 
         mConnected = true;
@@ -79,10 +81,14 @@
         return mDisconnectMessage;
     }
 
+    public int getFeatures() {
+        return mFeatures;
+    }
+
     public void abort() {
         try {
             if (mConnected) {
-                mCallService.abort(mConnectionId);
+                mConnectionService.abort(mConnectionId);
             }
         } catch (RemoteException ignored) {
         }
@@ -91,7 +97,7 @@
     public void answer() {
         try {
             if (mConnected) {
-                mCallService.answer(mConnectionId);
+                mConnectionService.answer(mConnectionId);
             }
         } catch (RemoteException ignored) {
         }
@@ -100,7 +106,7 @@
     public void reject() {
         try {
             if (mConnected) {
-                mCallService.reject(mConnectionId);
+                mConnectionService.reject(mConnectionId);
             }
         } catch (RemoteException ignored) {
         }
@@ -109,7 +115,7 @@
     public void hold() {
         try {
             if (mConnected) {
-                mCallService.hold(mConnectionId);
+                mConnectionService.hold(mConnectionId);
             }
         } catch (RemoteException ignored) {
         }
@@ -118,7 +124,7 @@
     public void unhold() {
         try {
             if (mConnected) {
-                mCallService.unhold(mConnectionId);
+                mConnectionService.unhold(mConnectionId);
             }
         } catch (RemoteException ignored) {
         }
@@ -127,7 +133,7 @@
     public void disconnect() {
         try {
             if (mConnected) {
-                mCallService.disconnect(mConnectionId);
+                mConnectionService.disconnect(mConnectionId);
             }
         } catch (RemoteException ignored) {
         }
@@ -136,7 +142,7 @@
     public void playDtmf(char digit) {
         try {
             if (mConnected) {
-                mCallService.playDtmfTone(mConnectionId, digit);
+                mConnectionService.playDtmfTone(mConnectionId, digit);
             }
         } catch (RemoteException ignored) {
         }
@@ -145,7 +151,7 @@
     public void stopDtmf() {
         try {
             if (mConnected) {
-                mCallService.stopDtmfTone(mConnectionId);
+                mConnectionService.stopDtmfTone(mConnectionId);
             }
         } catch (RemoteException ignored) {
         }
@@ -154,7 +160,7 @@
     public void postDialContinue(boolean proceed) {
         try {
             if (mConnected) {
-                mCallService.onPostDialContinue(mConnectionId, proceed);
+                mConnectionService.onPostDialContinue(mConnectionId, proceed);
             }
         } catch (RemoteException ignored) {
         }
@@ -163,7 +169,7 @@
     public void setAudioState(CallAudioState state) {
         try {
             if (mConnected) {
-                mCallService.onAudioStateChanged(mConnectionId, state);
+                mConnectionService.onAudioStateChanged(mConnectionId, state);
             }
         } catch (RemoteException ignored) {
         }
@@ -236,4 +242,14 @@
             l.onPostDialWait(this, remainingDigits);
         }
     }
+
+    /**
+     * @hide
+     */
+    void setFeatures(int features) {
+        mFeatures = features;
+        for (Listener l : mListeners) {
+            l.onFeaturesChanged(this, features);
+        }
+    }
 }
diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java
index eb3154b..9cffdcc 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java
@@ -20,7 +20,7 @@
 import android.net.Uri;
 import android.os.RemoteException;
 
-import com.android.internal.telecomm.ICallService;
+import com.android.internal.telecomm.IConnectionService;
 
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -33,11 +33,11 @@
 public class RemoteConnectionManager {
     private Map<ComponentName, RemoteConnectionService> mRemoteConnectionServices = new HashMap<>();
 
-    void addConnectionService(ComponentName componentName, ICallService callService) {
+    void addConnectionService(ComponentName componentName, IConnectionService connectionService) {
         if (!mRemoteConnectionServices.containsKey(componentName)) {
             try {
                 RemoteConnectionService remoteConnectionService =
-                        new RemoteConnectionService(componentName, callService);
+                        new RemoteConnectionService(componentName, connectionService);
                 mRemoteConnectionServices.put(componentName, remoteConnectionService);
             } catch (RemoteException ignored) {
             }
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 81bee98..73e7d77 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -24,8 +24,8 @@
 
 import android.text.TextUtils;
 
-import com.android.internal.telecomm.ICallService;
-import com.android.internal.telecomm.ICallServiceAdapter;
+import com.android.internal.telecomm.IConnectionService;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
 import com.android.internal.telecomm.ICallVideoProvider;
 import com.android.internal.telecomm.RemoteServiceCallback;
 
@@ -38,8 +38,8 @@
  *
  * @hide
  */
-public final class RemoteConnectionService implements DeathRecipient {
-    private final ICallService mCallService;
+final class RemoteConnectionService implements DeathRecipient {
+    private final IConnectionService mConnectionService;
     private final ComponentName mComponentName;
 
     private String mConnectionId;
@@ -48,26 +48,26 @@
     // Remote connection services only support a single connection.
     private RemoteConnection mConnection;
 
-    private final ICallServiceAdapter mAdapter = new ICallServiceAdapter.Stub() {
+    private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() {
 
         /** ${inheritDoc} */
-            @Override
-        public void notifyIncomingCall(CallInfo callInfo) {
+        @Override
+        public void notifyIncomingCall(ConnectionRequest request) {
             Log.w(this, "notifyIncomingCall not implemented in Remote connection");
         }
 
         /** ${inheritDoc} */
-            @Override
-        public void handleSuccessfulOutgoingCall(String connectionId) {
-            if (isPendingConnection(connectionId)) {
-                mConnection = new RemoteConnection(mCallService, connectionId);
-                mPendingOutgoingCallResponse.onSuccess(mPendingRequest, mConnection);
+        @Override
+        public void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+            if (isPendingConnection(request.getCallId())) {
+                mConnection = new RemoteConnection(mConnectionService, request.getCallId());
+                mPendingOutgoingCallResponse.onSuccess(request, mConnection);
                 clearPendingInformation();
             }
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void handleFailedOutgoingCall(
                 ConnectionRequest request, int errorCode, String errorMessage) {
             if (isPendingConnection(request.getCallId())) {
@@ -78,17 +78,17 @@
         }
 
         /** ${inheritDoc} */
-            @Override
-        public void cancelOutgoingCall(String connectionId) {
-            if (isPendingConnection(connectionId)) {
-                mPendingOutgoingCallResponse.onCancel(mPendingRequest);
+        @Override
+        public void cancelOutgoingCall(ConnectionRequest request) {
+            if (isPendingConnection(request.getCallId())) {
+                mPendingOutgoingCallResponse.onCancel(request);
                 mConnectionId = null;
                 clearPendingInformation();
             }
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setActive(String connectionId) {
             if (isCurrentConnection(connectionId)) {
                 mConnection.setState(Connection.State.ACTIVE);
@@ -96,7 +96,7 @@
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setRinging(String connectionId) {
             if (isCurrentConnection(connectionId)) {
                 mConnection.setState(Connection.State.RINGING);
@@ -110,7 +110,7 @@
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setDialing(String connectionId) {
             if (isCurrentConnection(connectionId)) {
                 mConnection.setState(Connection.State.DIALING);
@@ -118,7 +118,7 @@
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setDisconnected(
                 String connectionId, int disconnectCause, String disconnectMessage) {
             if (isCurrentConnection(connectionId)) {
@@ -127,7 +127,7 @@
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setOnHold(String connectionId) {
             if (isCurrentConnection(connectionId)) {
                 mConnection.setState(Connection.State.HOLDING);
@@ -135,7 +135,7 @@
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setRequestingRingback(String connectionId, boolean isRequestingRingback) {
             if (isCurrentConnection(connectionId)) {
                 mConnection.setRequestingRingback(isRequestingRingback);
@@ -143,25 +143,25 @@
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setCanConference(String connectionId, boolean canConference) {
             // not supported for remote connections.
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void setIsConferenced(String connectionId, String conferenceConnectionId) {
             // not supported for remote connections.
         }
 
         /** ${inheritDoc} */
-            @Override
-        public void addConferenceCall(String connectionId, CallInfo callInfo) {
+        @Override
+        public void addConferenceCall(String connectionId) {
             // not supported for remote connections.
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void removeCall(String connectionId) {
             if (isCurrentConnection(connectionId)) {
                 destroyConnection();
@@ -169,7 +169,7 @@
         }
 
         /** ${inheritDoc} */
-            @Override
+        @Override
         public void onPostDialWait(String connectionId, String remainingDigits) {
             if (isCurrentConnection(connectionId)) {
                 mConnection.setPostDialWait(remainingDigits);
@@ -189,23 +189,24 @@
         /** ${inheritDoc} */
         @Override
         public void setFeatures(String connectionId, int features) {
-            // not supported for remote connections.
+            if (isCurrentConnection(connectionId)) {
+                mConnection.setFeatures(features);
+            }
         }
     };
 
-    RemoteConnectionService(ComponentName componentName, ICallService callService)
+    RemoteConnectionService(ComponentName componentName, IConnectionService connectionService)
             throws RemoteException {
         mComponentName = componentName;
-        mCallService = callService;
+        mConnectionService = connectionService;
 
-        // TODO(santoscordon): Rename from setCallServiceAdapter to addCallServiceAdapter.
-        mCallService.setCallServiceAdapter(mAdapter);
-        mCallService.asBinder().linkToDeath(this, 0);
+        mConnectionService.addConnectionServiceAdapter(mAdapter);
+        mConnectionService.asBinder().linkToDeath(this, 0);
     }
 
     @Override
     public String toString() {
-        return "[RemoteCS - " + mCallService.asBinder().toString() + "]";
+        return "[RemoteCS - " + mConnectionService.asBinder().toString() + "]";
     }
 
     /** ${inheritDoc} */
@@ -221,15 +222,16 @@
     /**
      * Places an outgoing call.
      */
-    public final void createOutgoingConnection(
+    final void createOutgoingConnection(
             ConnectionRequest request,
             ConnectionService.OutgoingCallResponse<RemoteConnection> response) {
 
         if (mConnectionId == null) {
             String id = UUID.randomUUID().toString();
-            CallInfo callInfo = new CallInfo(id, CallState.NEW, request.getHandle());
+            ConnectionRequest newRequest = new ConnectionRequest(request.getAccount(), id,
+                    request.getHandle(), request.getExtras());
             try {
-                mCallService.call(callInfo);
+                mConnectionService.call(newRequest);
                 mConnectionId = id;
                 mPendingOutgoingCallResponse = response;
                 mPendingRequest = request;
@@ -244,7 +246,7 @@
     // TODO(santoscordon): Handle incoming connections
     // public final void handleIncomingConnection() {}
 
-    public final List<PhoneAccount> lookupAccounts(Uri handle) {
+    final List<PhoneAccount> lookupAccounts(Uri handle) {
         // TODO(santoscordon): Update this so that is actually calls into the RemoteConnection
         // each time.
         List<PhoneAccount> accounts = new LinkedList<>();
@@ -264,7 +266,7 @@
      * the remote service is no longer being used.
      */
     void release() {
-        mCallService.asBinder().unlinkToDeath(this, 0);
+        mConnectionService.asBinder().unlinkToDeath(this, 0);
     }
 
     private boolean isPendingConnection(String id) {
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 80901cf..513a5ee 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -48,7 +48,7 @@
     /**
      * The service action used to bind to {@link ConnectionService} implementations.
      */
-    public static final String ACTION_CALL_SERVICE = CallService.class.getName();
+    public static final String ACTION_CONNECTION_SERVICE = ConnectionService.class.getName();
 
     /**
      * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
@@ -94,6 +94,13 @@
             "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
 
     /**
+     * Optional extra for {@link TelephonyManager#ACTION_PHONE_STATE_CHANGED} containing the
+     * component name of the associated connection service.
+     */
+    public static final String EXTRA_CONNECTION_SERVICE =
+            "android.telecomm.extra.CONNECTION_SERVICE";
+
+    /**
      * The dual tone multi-frequency signaling character sent to indicate the dialing system should
      * pause for a predefined period.
      */
diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
similarity index 73%
rename from telecomm/java/com/android/internal/telecomm/ICallService.aidl
rename to telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
index ac4e66c..c8ce4c6 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -18,25 +18,25 @@
 
 import android.os.Bundle;
 import android.telecomm.CallAudioState;
-import android.telecomm.CallInfo;
+import android.telecomm.ConnectionRequest;
 
-import com.android.internal.telecomm.ICallServiceAdapter;
+import com.android.internal.telecomm.IConnectionServiceAdapter;
 
 /**
- * Internal remote interface for call services.
+ * Internal remote interface for connection services.
  *
- * @see android.telecomm.CallService
+ * @see android.telecomm.ConnectionService
  *
  * @hide
  */
-oneway interface ICallService {
-    void setCallServiceAdapter(in ICallServiceAdapter callServiceAdapter);
+oneway interface IConnectionService {
+    void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
 
-    void call(in CallInfo callInfo);
+    void call(in ConnectionRequest request);
 
     void abort(String callId);
 
-    void setIncomingCallId(String callId, in Bundle extras);
+    void createIncomingCall(in ConnectionRequest request);
 
     void answer(String callId);
 
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
similarity index 78%
rename from telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
rename to telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
index eee64da..9acc920 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -16,27 +16,26 @@
 
 package com.android.internal.telecomm;
 
-import android.telecomm.CallInfo;
 import android.telecomm.ConnectionRequest;
 
 import com.android.internal.telecomm.ICallVideoProvider;
 import com.android.internal.telecomm.RemoteServiceCallback;
 
 /**
- * Internal remote callback interface for call services.
+ * Internal remote callback interface for connection services.
  *
- * @see android.telecomm.CallServiceAdapter
+ * @see android.telecomm.ConnectionServiceAdapter
  *
  * {@hide}
  */
-oneway interface ICallServiceAdapter {
-    void notifyIncomingCall(in CallInfo callInfo);
+oneway interface IConnectionServiceAdapter {
+    void notifyIncomingCall(in ConnectionRequest request);
 
-    void handleSuccessfulOutgoingCall(String callId);
+    void handleSuccessfulOutgoingCall(in ConnectionRequest request);
 
     void handleFailedOutgoingCall(in ConnectionRequest request, int errorCode, String errorMessage);
 
-    void cancelOutgoingCall(String callId);
+    void cancelOutgoingCall(in ConnectionRequest request);
 
     void setActive(String callId);
 
@@ -54,7 +53,7 @@
 
     void setIsConferenced(String callId, String conferenceCallId);
 
-    void addConferenceCall(String callId, in CallInfo callInfo);
+    void addConferenceCall(String callId);
 
     void removeCall(String callId);
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bd621e8..b29cc12 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2812,6 +2812,40 @@
     }
 
     /**
+     * Values used to return status for hasCarrierPrivileges call.
+     */
+    public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1;
+    public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0;
+    public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1;
+    public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2;
+
+    /**
+     * Has the calling application been granted carrier privileges by the carrier.
+     *
+     * If any of the packages in the calling UID has carrier privileges, the
+     * call will return true. This access is granted by the owner of the UICC
+     * card and does not depend on the registered carrier.
+     *
+     * TODO: Add a link to documentation.
+     *
+     * @return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS if the app has carrier privileges.
+     *         CARRIER_PRIVILEGE_STATUS_NO_ACCESS if the app does not have carrier privileges.
+     *         CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED if the carrier rules are not loaded.
+     *         CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES if there was an error loading carrier
+     *             rules (or if there are no rules).
+     */
+    public int hasCarrierPrivileges() {
+        try {
+            return getITelephony().hasCarrierPrivileges();
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
+        }
+        return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+    }
+
+    /**
      * Expose the rest of ITelephony to @SystemApi
      */
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ee04c06..5b6db4d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -633,5 +633,17 @@
      */
     void setImsRegistrationState(boolean registered);
 
+    /**
+     * Has the calling application been granted special privileges by the carrier.
+     *
+     * If any of the packages in the calling UID has carrier privileges, the
+     * call will return true. This access is granted by the owner of the UICC
+     * card and does not depend on the registered carrier.
+     *
+     * TODO: Add a link to documentation.
+     *
+     * @return carrier privelege status defined in TelephonyManager.
+     */
+    int hasCarrierPrivileges();
 }