Merge "Camera2: Minor update for hyperfocalDistance spec"
diff --git a/api/current.txt b/api/current.txt
index 06d1ab0..67c9276 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3316,7 +3316,6 @@
     method public final boolean requestWindowFeature(int);
     method public final void runOnUiThread(java.lang.Runnable);
     method public void setActionBar(android.widget.Toolbar);
-    method public void setActivityLabelAndIcon(java.lang.CharSequence, android.graphics.Bitmap);
     method public void setActivityTransitionListener(android.app.ActivityOptions.ActivityTransitionListener);
     method public void setContentTransitionManager(android.transition.TransitionManager);
     method public void setContentView(int);
@@ -3334,6 +3333,7 @@
     method public final void setProgressBarIndeterminate(boolean);
     method public final void setProgressBarIndeterminateVisibility(boolean);
     method public final void setProgressBarVisibility(boolean);
+    method public void setRecentsActivityValues(android.app.ActivityManager.RecentsActivityValues);
     method public void setRequestedOrientation(int);
     method public final void setResult(int);
     method public final void setResult(int, android.content.Intent);
@@ -3454,8 +3454,7 @@
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
-    field public android.graphics.Bitmap activityIcon;
-    field public java.lang.CharSequence activityLabel;
+    field public android.app.ActivityManager.RecentsActivityValues activityValues;
     field public android.content.Intent baseIntent;
     field public java.lang.CharSequence description;
     field public int id;
@@ -3463,6 +3462,21 @@
     field public int persistentId;
   }
 
+  public static class ActivityManager.RecentsActivityValues implements android.os.Parcelable {
+    ctor public ActivityManager.RecentsActivityValues(android.app.ActivityManager.RecentsActivityValues);
+    ctor public ActivityManager.RecentsActivityValues(java.lang.CharSequence, android.graphics.Bitmap, int);
+    ctor public ActivityManager.RecentsActivityValues(java.lang.CharSequence, android.graphics.Bitmap);
+    ctor public ActivityManager.RecentsActivityValues(java.lang.CharSequence);
+    ctor public ActivityManager.RecentsActivityValues();
+    method public int describeContents();
+    method public void readFromParcel(android.os.Parcel);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public int colorPrimary;
+    field public android.graphics.Bitmap icon;
+    field public java.lang.CharSequence label;
+  }
+
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
     ctor public ActivityManager.RunningAppProcessInfo();
     ctor public ActivityManager.RunningAppProcessInfo(java.lang.String, int, java.lang.String[]);
@@ -12446,6 +12460,7 @@
     field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
     field public static final int POWER_TRANSIENT_TO_ON = 2; // 0x2
     field public static final int POWER_TRANSIENT_TO_STANDBY = 3; // 0x3
+    field public static final int UNKNOWN_VENDOR_ID = 16777215; // 0xffffff
   }
 
   public final class HdmiCecClient {
@@ -12487,6 +12502,7 @@
     method public int getSource();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final byte[] EMPTY_PARAM;
   }
 
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8981c88..5ec3117 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3493,6 +3493,16 @@
             }
             theme.applyStyle(resid, false);
         }
+
+        // Get the primary color and update the RecentsActivityValues for this activity
+        TypedArray a = getTheme().obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+        int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+        a.recycle();
+        if (colorPrimary != 0) {
+            ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues();
+            v.colorPrimary = colorPrimary;
+            setRecentsActivityValues(v);
+        }
     }
 
     /**
@@ -4779,31 +4789,26 @@
     }
 
     /**
-     * Set a label and icon to be used in the Recents task display. When {@link
-     * ActivityManager#getRecentTasks} is called, the activities of each task are
-     * traversed in order from the topmost activity to the bottommost. As soon as one activity is
-     * found with either a non-null label or a non-null icon set by this call the traversal is
-     * ended. For each task those values will be returned in {@link
-     * ActivityManager.RecentTaskInfo#activityLabel} and {@link
-     * ActivityManager.RecentTaskInfo#activityIcon}.
+     * Sets information describing this Activity for presentation inside the Recents System UI. When
+     * {@link ActivityManager#getRecentTasks} is called, the activities of each task are
+     * traversed in order from the topmost activity to the bottommost. The traversal continues for
+     * each property until a suitable value is found. For each task those values will be returned in
+     * {@link android.app.ActivityManager.RecentsActivityValues}.
      *
      * @see ActivityManager#getRecentTasks
-     * @see ActivityManager.RecentTaskInfo
+     * @see android.app.ActivityManager.RecentsActivityValues
      *
-     * @param activityLabel The label to use in the RecentTaskInfo.
-     * @param activityIcon The Bitmap to use in the RecentTaskInfo.
+     * @param values The Recents values that describe this activity.
      */
-    public void setActivityLabelAndIcon(CharSequence activityLabel, Bitmap activityIcon) {
-        final Bitmap scaledIcon;
-        if (activityIcon != null) {
+    public void setRecentsActivityValues(ActivityManager.RecentsActivityValues values) {
+        ActivityManager.RecentsActivityValues activityValues =
+                new ActivityManager.RecentsActivityValues(values);
+        if (values.icon != null) {
             final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
-            scaledIcon = Bitmap.createScaledBitmap(activityIcon, size, size, true);
-        } else {
-            scaledIcon = null;
+            activityValues.icon = Bitmap.createScaledBitmap(values.icon, size, size, true);
         }
         try {
-            ActivityManagerNative.getDefault().setActivityLabelAndIcon(mToken, activityLabel,
-                    scaledIcon);
+            ActivityManagerNative.getDefault().setRecentsActivityValues(mToken, activityValues);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5f4ca4e..5d809d8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -475,6 +475,111 @@
     }
 
     /**
+     * Information you can set and retrieve about the current activity within Recents.
+     */
+    public static class RecentsActivityValues implements Parcelable {
+        public CharSequence label;
+        public Bitmap icon;
+        public int colorPrimary;
+
+        public RecentsActivityValues(RecentsActivityValues values) {
+            copyFrom(values);
+        }
+
+        /**
+         * Creates the RecentsActivityValues to the specified values.
+         *
+         * @param label A label and description of the current state of this activity.
+         * @param icon An icon that represents the current state of this activity.
+         * @param color A color to override the theme's primary color.
+         */
+        public RecentsActivityValues(CharSequence label, Bitmap icon, int color) {
+            this.label = label;
+            this.icon = icon;
+            this.colorPrimary = color;
+        }
+
+        /**
+         * Creates the RecentsActivityValues to the specified values.
+         *
+         * @param label A label and description of the current state of this activity.
+         * @param icon An icon that represents the current state of this activity.
+         */
+        public RecentsActivityValues(CharSequence label, Bitmap icon) {
+            this(label, icon, 0);
+        }
+
+        /**
+         * Creates the RecentsActivityValues to the specified values.
+         *
+         * @param label A label and description of the current state of this activity.
+         */
+        public RecentsActivityValues(CharSequence label) {
+            this(label, null, 0);
+        }
+
+        public RecentsActivityValues() {
+            this(null, null, 0);
+        }
+
+        private RecentsActivityValues(Parcel source) {
+            readFromParcel(source);
+        }
+
+        /**
+         * Do a shallow copy of another set of activity values.
+         * @hide
+         */
+        public void copyFrom(RecentsActivityValues v) {
+            if (v != null) {
+                label = v.label;
+                icon = v.icon;
+                colorPrimary = v.colorPrimary;
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            TextUtils.writeToParcel(label, dest,
+                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            if (icon == null) {
+                dest.writeInt(0);
+            } else {
+                dest.writeInt(1);
+                icon.writeToParcel(dest, 0);
+            }
+            dest.writeInt(colorPrimary);
+        }
+
+        public void readFromParcel(Parcel source) {
+            label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            icon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
+            colorPrimary = source.readInt();
+        }
+
+        public static final Creator<RecentsActivityValues> CREATOR
+                = new Creator<RecentsActivityValues>() {
+            public RecentsActivityValues createFromParcel(Parcel source) {
+                return new RecentsActivityValues(source);
+            }
+            public RecentsActivityValues[] newArray(int size) {
+                return new RecentsActivityValues[size];
+            }
+        };
+
+        @Override
+        public String toString() {
+            return "RecentsActivityValues Label: " + label + " Icon: " + icon +
+                    " colorPrimary: " + colorPrimary;
+        }
+    }
+
+    /**
      * Information you can retrieve about tasks that the user has most recently
      * started or visited.
      */
@@ -523,16 +628,10 @@
         public int userId;
 
         /**
-         * The label of the highest activity in the task stack to have set a label using
-         * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}.
+         * The recent activity values for the highest activity in the stack to have set the values.
+         * {@link Activity#setRecentsActivityValues(android.app.ActivityManager.RecentsActivityValues)}.
          */
-        public CharSequence activityLabel;
-
-        /**
-         * The Bitmap icon of the highest activity in the task stack to set a Bitmap using
-         * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}.
-         */
-        public Bitmap activityIcon;
+        public RecentsActivityValues activityValues;
 
         public RecentTaskInfo() {
         }
@@ -555,13 +654,11 @@
             ComponentName.writeToParcel(origActivity, dest);
             TextUtils.writeToParcel(description, dest,
                     Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-            TextUtils.writeToParcel(activityLabel, dest,
-                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-            if (activityIcon == null) {
-                dest.writeInt(0);
-            } else {
+            if (activityValues != null) {
                 dest.writeInt(1);
-                activityIcon.writeToParcel(dest, 0);
+                activityValues.writeToParcel(dest, 0);
+            } else {
+                dest.writeInt(0);
             }
             dest.writeInt(stackId);
             dest.writeInt(userId);
@@ -573,8 +670,8 @@
             baseIntent = source.readInt() > 0 ? Intent.CREATOR.createFromParcel(source) : null;
             origActivity = ComponentName.readFromParcel(source);
             description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            activityIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
+            activityValues = source.readInt() > 0 ?
+                    RecentsActivityValues.CREATOR.createFromParcel(source) : null;
             stackId = source.readInt();
             userId = source.readInt();
         }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6a818ac..57da21e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2119,13 +2119,12 @@
             return true;
         }
 
-        case SET_ACTIVITY_LABEL_ICON_TRANSACTION: {
+        case SET_RECENTS_ACTIVITY_VALUES_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            CharSequence activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
-            Bitmap activityIcon = data.readInt() > 0
-                    ? Bitmap.CREATOR.createFromParcel(data) : null;
-            setActivityLabelAndIcon(token, activityLabel, activityIcon);
+            ActivityManager.RecentsActivityValues values =
+                    ActivityManager.RecentsActivityValues.CREATOR.createFromParcel(data);
+            setRecentsActivityValues(token, values);
             reply.writeNoException();
             return true;
         }
@@ -4883,21 +4882,14 @@
     }
 
     @Override
-    public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
-            Bitmap activityIcon) throws RemoteException
-    {
+    public void setRecentsActivityValues(IBinder token, ActivityManager.RecentsActivityValues values)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        TextUtils.writeToParcel(activityLabel, data, 0);
-        if (activityIcon != null) {
-            data.writeInt(1);
-            activityIcon.writeToParcel(data, 0);
-        } else {
-            data.writeInt(0);
-        }
-        mRemote.transact(SET_ACTIVITY_LABEL_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        values.writeToParcel(data, 0);
+        mRemote.transact(SET_RECENTS_ACTIVITY_VALUES_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5ca51be..2e9cdf3b7 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -435,8 +435,8 @@
     public boolean isInLockTaskMode() throws RemoteException;
 
     /** @hide */
-    public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
-            Bitmap activityBitmap) throws RemoteException;
+    public void setRecentsActivityValues(IBinder token, ActivityManager.RecentsActivityValues values)
+            throws RemoteException;
 
     /*
      * Private non-Binder interfaces
@@ -734,6 +734,6 @@
     int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214;
     int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
     int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
-    int SET_ACTIVITY_LABEL_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
+    int SET_RECENTS_ACTIVITY_VALUES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
     int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
 }
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 4b33799..8670da7 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -54,6 +54,12 @@
     public static final int WIDGET_CATEGORY_KEYGUARD = 2;
 
     /**
+     * Indicates that the widget can be displayed within recents.
+     * @hide
+     */
+    public static final int WIDGET_CATEGORY_RECENTS = 4;
+
+    /**
      * Identity of this AppWidget component.  This component should be a {@link
      * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents
      * {@link android.appwidget as described in the AppWidget package documentation}.
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index eafaed6..7213c78 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -160,6 +160,8 @@
     public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2;
     public static final int MESSAGE_ABORT = 0xFF;
 
+    public static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+
     public static final int POWER_STATUS_UNKNOWN = -1;
     public static final int POWER_STATUS_ON = 0;
     public static final int POWER_STATUS_STANDBY = 1;
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
index be94d97..ddaf870 100644
--- a/core/java/android/hardware/hdmi/HdmiCecMessage.java
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import libcore.util.EmptyArray;
+
 import java.util.Arrays;
 
 /**
@@ -28,6 +30,8 @@
  */
 public final class HdmiCecMessage implements Parcelable {
 
+    public static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
+
     private static final int MAX_MESSAGE_LENGTH = 16;
 
     private final int mSource;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e6cdda0..6b62c25 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -98,6 +98,9 @@
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
 
+    <!-- Recents -->
+    <uses-permission android:name="android.permission.BIND_APPWIDGET" />
+
     <!-- Wifi Display -->
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
 
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index ddc0dbf..f7df18eb 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -58,7 +58,7 @@
             android:textSize="22sp"
             android:textColor="#ffffffff"
             android:text="@string/recents_empty_message"
-            android:fontFamily="sans-serif-thin"
+            android:fontFamily="sans-serif-light"
             android:singleLine="true"
             android:maxLines="2"
             android:ellipsize="marquee"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 722ca15..024f1eb 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -115,8 +115,10 @@
     <integer name="recents_filter_animate_current_views_min_duration">175</integer>
     <!-- The min animation duration for animating views that are newly visible. -->
     <integer name="recents_filter_animate_new_views_min_duration">125</integer>
-    <!-- The min animation duration for animating views that are newly visible. -->
+    <!-- The min animation duration for animating the task bar in. -->
     <integer name="recents_animate_task_bar_enter_duration">200</integer>
+    <!-- The min animation duration for animating the task bar out. -->
+    <integer name="recents_animate_task_bar_exit_duration">150</integer>
     <!-- The animation duration for animating in the info pane. -->
     <integer name="recents_animate_task_view_info_pane_duration">150</integer>
     <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2f2914c..1900fea 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -231,10 +231,7 @@
     <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
 
     <!-- The height of the search bar space. -->
-    <dimen name="recents_search_bar_space_height">40dp</dimen>
-
-    <!-- The search bar edge margins. -->
-    <dimen name="recents_search_bar_space_edge_margins">12dp</dimen>
+    <dimen name="recents_search_bar_space_height">64dp</dimen>
 
     <!-- Used to calculate the translation animation duration, the expected amount of movement 
          in dps over one second of time. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Console.java b/packages/SystemUI/src/com/android/systemui/recents/Console.java
index 4b75c99..c8d97cc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Console.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Console.java
@@ -37,7 +37,7 @@
     public static final String AnsiRed = "\u001B[31m";      // SystemUIHandshake
     public static final String AnsiGreen = "\u001B[32m";    // MeasureAndLayout
     public static final String AnsiYellow = "\u001B[33m";   // SynchronizeViewsWithModel
-    public static final String AnsiBlue = "\u001B[34m";     // TouchEvents
+    public static final String AnsiBlue = "\u001B[34m";     // TouchEvents, Search
     public static final String AnsiPurple = "\u001B[35m";   // Draw
     public static final String AnsiCyan = "\u001B[36m";     // ClickEvents
     public static final String AnsiWhite = "\u001B[37m";
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index a996c1a..bc8ab45 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -28,8 +28,9 @@
         public static class App {
             public static final boolean EnableTaskFiltering = false;
             public static final boolean EnableTaskStackClipping = false;
+            public static final boolean EnableTaskBarThemeColors = true;
             public static final boolean EnableInfoPane = true;
-            public static final boolean EnableSearchButton = false;
+            public static final boolean EnableSearchButton = true;
 
             // This disables the bitmap and icon caches
             public static final boolean DisableBackgroundCache = false;
@@ -51,6 +52,7 @@
             public static final boolean SystemUIHandshake = false;
             public static final boolean TimeSystemCalls = false;
             public static final boolean Memory = false;
+            public static final boolean Search = false;
         }
 
         public static class UI {
@@ -71,6 +73,10 @@
     }
 
     public static class Values {
+        public static class App {
+            public static int AppWidgetHostId = 1024;
+            public static String Key_SearchAppWidgetId = "searchAppWidgetId";
+        }
         public static class Window {
             // The dark background dim is set behind the empty recents view
             public static final float DarkBackgroundDim = 0.5f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 71c45f2..110130b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -17,11 +17,17 @@
 package com.android.systemui.recents;
 
 import android.app.Activity;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
@@ -33,13 +39,37 @@
 
 import java.util.ArrayList;
 
+/** Our special app widget host */
+class RecentsAppWidgetHost extends AppWidgetHost {
+    /* Callbacks to notify when an app package changes */
+    interface RecentsAppWidgetHostCallbacks {
+        public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo);
+    }
+
+    RecentsAppWidgetHostCallbacks mCb;
+
+    public RecentsAppWidgetHost(Context context, int hostId, RecentsAppWidgetHostCallbacks cb) {
+        super(context, hostId);
+        mCb = cb;
+    }
+
+    @Override
+    protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
+        mCb.onProviderChanged(appWidgetId, appWidget);
+    }
+}
 
 /* Activity */
-public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks {
+public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
+        RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks{
     FrameLayout mContainerView;
     RecentsView mRecentsView;
     View mEmptyView;
 
+    AppWidgetHost mAppWidgetHost;
+    AppWidgetProviderInfo mSearchAppWidgetInfo;
+    AppWidgetHostView mSearchAppWidgetHostView;
+
     boolean mVisible;
     boolean mTaskLaunched;
 
@@ -102,6 +132,75 @@
         }
     }
 
+    /** Attempts to allocate and bind the search bar app widget */
+    void bindSearchBarAppWidget() {
+        if (Constants.DebugFlags.App.EnableSearchButton) {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+
+            // Reset the host view and widget info
+            mSearchAppWidgetHostView = null;
+            mSearchAppWidgetInfo = null;
+
+            // Try and load the app widget id from the settings
+            int appWidgetId = config.searchBarAppWidgetId;
+            if (appWidgetId >= 0) {
+                mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
+                if (mSearchAppWidgetInfo == null) {
+                    // If there is no actual widget associated with that id, then delete it and
+                    // prepare to bind another app widget in its place
+                    ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
+                    appWidgetId = -1;
+                }
+                Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                        "[RecentsActivity|onCreate|settings|appWidgetId]",
+                        "Id: " + appWidgetId,
+                        Console.AnsiBlue);
+            }
+
+            // If there is no id, then bind a new search app widget
+            if (appWidgetId < 0) {
+                Pair<Integer, AppWidgetProviderInfo> widgetInfo =
+                        ssp.bindSearchAppWidget(mAppWidgetHost);
+                if (widgetInfo != null) {
+                    Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                            "[RecentsActivity|onCreate|searchWidget]",
+                            "Id: " + widgetInfo.first + " Info: " + widgetInfo.second,
+                            Console.AnsiBlue);
+
+                    // Save the app widget id into the settings
+                    config.updateSearchBarAppWidgetId(this, widgetInfo.first);
+                    mSearchAppWidgetInfo = widgetInfo.second;
+                }
+            }
+        }
+    }
+
+    /** Creates the search bar app widget view */
+    void addSearchBarAppWidgetView() {
+        if (Constants.DebugFlags.App.EnableSearchButton) {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            int appWidgetId = config.searchBarAppWidgetId;
+            if (appWidgetId >= 0) {
+                Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                        "[RecentsActivity|onCreate|addSearchAppWidgetView]",
+                        "Id: " + appWidgetId,
+                        Console.AnsiBlue);
+                mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId,
+                        mSearchAppWidgetInfo);
+                Bundle opts = new Bundle();
+                opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                        AppWidgetProviderInfo.WIDGET_CATEGORY_RECENTS);
+                mSearchAppWidgetHostView.updateAppWidgetOptions(opts);
+                // Set the padding to 0 for this search widget
+                mSearchAppWidgetHostView.setPadding(0, 0, 0, 0);
+                mRecentsView.setSearchBar(mSearchAppWidgetHostView);
+            } else {
+                mRecentsView.setSearchBar(null);
+            }
+        }
+    }
+
     /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
     boolean dismissRecentsIfVisible() {
         if (mVisible) {
@@ -127,6 +226,9 @@
         RecentsTaskLoader.initialize(this);
         RecentsConfiguration.reinitialize(this);
 
+        // Initialize the widget host (the host id is static and does not change)
+        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this);
+
         // Create the view hierarchy
         mRecentsView = new RecentsView(this);
         mRecentsView.setCallbacks(this);
@@ -145,6 +247,11 @@
 
         // Update the recent tasks
         updateRecentsTasks(getIntent());
+
+        // Bind the search app widget when we first start up
+        bindSearchBarAppWidget();
+        // Add the search bar layout
+        addSearchBarAppWidgetView();
     }
 
     @Override
@@ -165,6 +272,9 @@
 
         // Update the recent tasks
         updateRecentsTasks(intent);
+
+        // Don't attempt to rebind the search bar widget, but just add the search bar layout
+        addSearchBarAppWidgetView();
     }
 
     @Override
@@ -172,6 +282,7 @@
         Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
                 Console.AnsiRed);
         super.onStart();
+        mAppWidgetHost.startListening();
         mVisible = true;
     }
 
@@ -225,6 +336,7 @@
                 Console.AnsiRed);
         super.onStop();
 
+        mAppWidgetHost.stopListening();
         mVisible = false;
         mTaskLaunched = false;
     }
@@ -265,4 +377,18 @@
     public void onTaskLaunching() {
         mTaskLaunched = true;
     }
+
+    @Override
+    public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+        if (appWidgetId > -1 && appWidgetId == config.searchBarAppWidgetId) {
+            // The search provider may have changed, so just delete the old widget and bind it again
+            ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
+            config.updateSearchBarAppWidgetId(this, -1);
+            // Load the widget again
+            bindSearchBarAppWidget();
+            addSearchBarAppWidgetView();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index d54df13..5a1dc8d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
@@ -35,17 +36,25 @@
     public Rect systemInsets = new Rect();
     public Rect displayRect = new Rect();
 
+    boolean isLandscape;
+    int searchBarAppWidgetId = -1;
+
     public float animationPxMovementPerSecond;
 
     public int filteringCurrentViewsMinAnimDuration;
     public int filteringNewViewsMinAnimDuration;
     public int taskBarEnterAnimDuration;
+    public int taskBarExitAnimDuration;
     public int taskStackScrollDismissInfoPaneDistance;
     public int taskStackMaxDim;
     public int taskViewInfoPaneAnimDuration;
     public int taskViewRoundedCornerRadiusPx;
     public int searchBarSpaceHeightPx;
-    public int searchBarSpaceEdgeMarginsPx;
+
+    public int taskBarViewDefaultBackgroundColor;
+    public int taskBarViewDefaultTextColor;
+    public int taskBarViewLightTextColor;
+    public int taskBarViewDarkTextColor;
 
     public boolean launchedWithThumbnailAnimation;
 
@@ -72,7 +81,7 @@
         DisplayMetrics dm = res.getDisplayMetrics();
         mDisplayMetrics = dm;
 
-        boolean isLandscape = res.getConfiguration().orientation ==
+        isLandscape = res.getConfiguration().orientation ==
                 Configuration.ORIENTATION_LANDSCAPE;
         Console.log(Constants.DebugFlags.UI.MeasureAndLayout,
                 "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
@@ -87,6 +96,8 @@
                 res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
         taskBarEnterAnimDuration =
                 res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
+        taskBarExitAnimDuration =
+                res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
         taskStackScrollDismissInfoPaneDistance = res.getDimensionPixelSize(
                 R.dimen.recents_task_stack_scroll_dismiss_info_pane_distance);
         taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
@@ -95,8 +106,20 @@
         taskViewRoundedCornerRadiusPx =
                 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-        searchBarSpaceEdgeMarginsPx =
-                res.getDimensionPixelSize(R.dimen.recents_search_bar_space_edge_margins);
+
+
+        taskBarViewDefaultBackgroundColor =
+                res.getColor(R.color.recents_task_bar_default_background_color);
+        taskBarViewDefaultTextColor =
+                res.getColor(R.color.recents_task_bar_default_text_color);
+        taskBarViewLightTextColor =
+                res.getColor(R.color.recents_task_bar_light_text_color);
+        taskBarViewDarkTextColor =
+                res.getColor(R.color.recents_task_bar_dark_text_color);
+
+        // Update the search widget id
+        SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
+        searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
     }
 
     /** Updates the system insets */
@@ -104,24 +127,57 @@
         systemInsets.set(insets);
     }
 
-    /** Returns the search bar bounds in the specified orientation */
-    public void getSearchBarBounds(int width, int height,
-                                   Rect searchBarSpaceBounds, Rect searchBarBounds) {
+    /** Updates the search bar app widget */
+    public void updateSearchBarAppWidgetId(Context context, int appWidgetId) {
+        searchBarAppWidgetId = appWidgetId;
+        SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
+        settings.edit().putInt(Constants.Values.App.Key_SearchAppWidgetId,
+                appWidgetId).apply();
+    }
+
+    /** Returns whether the search bar app widget exists */
+    public boolean hasSearchBarAppWidget() {
+        return searchBarAppWidgetId >= 0;
+    }
+
+    /**
+     * Returns the task stack bounds in the current orientation. These bounds do not account for
+     * the system insets.
+     */
+    public void getTaskStackBounds(int width, int height, Rect taskStackBounds) {
+        if (hasSearchBarAppWidget()) {
+            Rect searchBarBounds = new Rect();
+            getSearchBarBounds(width, height, searchBarBounds);
+            if (isLandscape) {
+                // In landscape, the search bar appears on the left, so shift the task rect right
+                taskStackBounds.set(searchBarBounds.width(), 0, width, height);
+            } else {
+                // In portrait, the search bar appears on the top, so shift the task rect below
+                taskStackBounds.set(0, searchBarBounds.height(), width, height);
+            }
+        } else {
+            taskStackBounds.set(0, 0, width, height);
+        }
+    }
+
+    /**
+     * Returns the search bar bounds in the current orientation.  These bounds do not account for
+     * the system insets.
+     */
+    public void getSearchBarBounds(int width, int height, Rect searchBarSpaceBounds) {
         // Return empty rects if search is not enabled
         if (!Constants.DebugFlags.App.EnableSearchButton) {
             searchBarSpaceBounds.set(0, 0, 0, 0);
-            searchBarBounds.set(0, 0, 0, 0);
             return;
         }
 
-        // Calculate the search bar bounds, and account for the system insets
-        int edgeMarginPx = searchBarSpaceEdgeMarginsPx;
-        int availableWidth = width - systemInsets.left - systemInsets.right;
-        searchBarSpaceBounds.set(0, 0, availableWidth, 2 * edgeMarginPx + searchBarSpaceHeightPx);
-
-        // Inset from the search bar space to get the search bar bounds
-        searchBarBounds.set(searchBarSpaceBounds);
-        searchBarBounds.inset(edgeMarginPx, edgeMarginPx);
+        if (isLandscape) {
+            // In landscape, the search bar appears on the left
+            searchBarSpaceBounds.set(0, 0, searchBarSpaceHeightPx, height);
+        } else {
+            // In portrait, the search bar appears on the top
+            searchBarSpaceBounds.set(0, 0, width, searchBarSpaceHeightPx);
+        }
     }
 
     /** Converts from DPs to PXs */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 36b761e..0c6ed84 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -60,39 +60,41 @@
                 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(context);
                 TaskStackView tsv = new TaskStackView(context, stack);
                 Bundle replyData = new Bundle();
                 TaskViewTransform transform;
 
-                // Get the search bar bounds so that we can account for its height in the children
-                Rect searchBarSpaceBounds = new Rect();
-                Rect searchBarBounds = new Rect();
+                // Get the task stack and search bar bounds
+                Rect taskStackBounds = new Rect();
                 RecentsConfiguration config = RecentsConfiguration.getInstance();
-                config.getSearchBarBounds(windowRect.width(), windowRect.height(),
-                        searchBarSpaceBounds, searchBarBounds);
+                config.getTaskStackBounds(windowRect.width(), windowRect.height(), taskStackBounds);
 
-                // Calculate the target task rect for when there is one task
+                // 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 bottom inset
+                // pass in a left or bottom inset
                 stack.addTask(new Task());
-                tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
-                        systemInsets.bottom - searchBarSpaceBounds.height(), 0);
+                tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
+                        systemInsets.top - systemInsets.bottom, 0, 0);
                 tsv.boundScroll();
                 transform = tsv.getStackTransform(0, tsv.getStackScroll());
-                transform.rect.offset(0, searchBarSpaceBounds.height());
+                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 multiple tasks
+                // Also calculate the target task rect when there are multiple tasks.
                 stack.addTask(new Task());
-                tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
-                        systemInsets.bottom - searchBarSpaceBounds.height(), 0);
+                tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
+                        systemInsets.top - systemInsets.bottom, 0, 0);
                 tsv.setStackScrollRaw(Integer.MAX_VALUE);
                 tsv.boundScroll();
                 transform = tsv.getStackTransform(1, tsv.getStackScroll());
-                transform.rect.offset(0, searchBarSpaceBounds.height());
+                transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
                 replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
                         new Rect(transform.rect));
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index 52bba4a..4a19027 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -415,17 +415,23 @@
             ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
             if (info == null) continue;
 
-            String activityLabel = (t.activityLabel == null ? ssp.getActivityLabel(info) :
-                    t.activityLabel.toString());
+            ActivityManager.RecentsActivityValues av = t.activityValues;
+            String activityLabel = null;
             BitmapDrawable activityIcon = null;
-            if (t.activityIcon != null) {
-                activityIcon = new BitmapDrawable(res, t.activityIcon);
+            int activityColor = 0;
+            if (av != null) {
+                activityLabel = (av.label != null ? av.label.toString() :
+                        ssp.getActivityLabel(info));
+                activityIcon = (av.icon != null) ? new BitmapDrawable(res, av.icon) : null;
+                activityColor = av.colorPrimary;
+            } else {
+                activityLabel = ssp.getActivityLabel(info);
             }
             boolean isForemostTask = (i == (taskCount - 1));
 
             // Create a new task
             Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel,
-                    activityIcon, t.userId);
+                    activityIcon, activityColor, t.userId);
 
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 33ac0a8..68af663 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -20,7 +20,9 @@
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -35,8 +37,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.Log;
+import android.util.Pair;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -48,6 +49,7 @@
  */
 public class SystemServicesProxy {
     ActivityManager mAm;
+    AppWidgetManager mAwm;
     PackageManager mPm;
     IPackageManager mIpm;
     UserManager mUm;
@@ -59,6 +61,7 @@
     /** Private constructor */
     public SystemServicesProxy(Context context) {
         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        mAwm = AppWidgetManager.getInstance(context);
         mPm = context.getPackageManager();
         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mIpm = AppGlobals.getPackageManager();
@@ -91,10 +94,13 @@
                 rti.id = rti.persistentId = i;
                 rti.baseIntent = new Intent();
                 rti.baseIntent.setComponent(cn);
-                rti.description = rti.activityLabel = "" + i + " - " +
+                rti.activityValues = new ActivityManager.RecentsActivityValues();
+                rti.description = "" + i + " - " +
                         Long.toString(Math.abs(new Random().nextLong()), 36);
                 if (i % 2 == 0) {
-                    rti.activityIcon = Bitmap.createBitmap(mDummyIcon);
+                    rti.activityValues.label = rti.description;
+                    rti.activityValues.icon = Bitmap.createBitmap(mDummyIcon);
+                    rti.activityValues.colorPrimary = new Random().nextInt();
                 }
                 tasks.add(rti);
             }
@@ -103,7 +109,7 @@
 
         return mAm.getRecentTasksForUser(numTasks,
                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
-                ActivityManager.RECENT_INCLUDE_PROFILES, userId);
+                        ActivityManager.RECENT_INCLUDE_PROFILES, userId);
     }
 
     /** Returns a list of the running tasks */
@@ -162,7 +168,7 @@
     /**
      * Returns the activity info for a given component name.
      * 
-     * @param ComponentName The component name of the activity.
+     * @param cn The component name of the activity.
      * @param userId The userId of the user that this is for.
      */
     public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
@@ -177,6 +183,23 @@
         }
     }
 
+    /**
+     * Returns the activity info for a given component name.
+     *
+     * @param cn The component name of the activity.
+     */
+    public ActivityInfo getActivityInfo(ComponentName cn) {
+        if (mPm == null) return null;
+        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null;
+
+        try {
+            return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
     /** Returns the activity label */
     public String getActivityLabel(ActivityInfo info) {
         if (mPm == null) return null;
@@ -208,27 +231,70 @@
         return icon;
     }
 
-
     /**
-     * Composes an intent to launch the global search activity.
+     * Resolves and binds the search app widget that is to appear in the recents.
      */
-    public Intent getGlobalSearchIntent(Rect sourceBounds) {
-        if (mSm == null) return null;
+    public Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host) {
+        if (mAwm == null) return null;
 
-        // Try and get the global search activity
+        // Ensure we have a global search activity
         ComponentName globalSearchActivity = mSm.getGlobalSearchActivity();
         if (globalSearchActivity == null) return null;
 
-        // Bundle the source of the search
-        Bundle appSearchData = new Bundle();
-        appSearchData.putString("source", mPackage);
+        // Resolve the search widget provider from the search activity
+        ActivityInfo searchActivityInfo = getActivityInfo(globalSearchActivity);
+        if (searchActivityInfo == null) return null;
 
-        // Compose the intent and Start the search activity
-        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.setComponent(globalSearchActivity);
-        intent.putExtra(SearchManager.APP_DATA, appSearchData);
-        intent.setSourceBounds(sourceBounds);
-        return intent;
+        String key = "com.android.recents.search_widget_provider";
+        ComponentName searchWidgetCn = null;
+        Bundle searchMetaData = searchActivityInfo.metaData;
+        String searchWidgetProvider = searchMetaData.getString(key, "");
+        if (searchWidgetProvider.length() != 0) {
+            searchWidgetCn = ComponentName.unflattenFromString(searchWidgetProvider);
+        } else {
+            return null;
+        }
+
+        // Find the first Recents widget from the same package as the global search activity
+        List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders();
+        AppWidgetProviderInfo searchWidgetInfo = null;
+        for (AppWidgetProviderInfo info : widgets) {
+            if (info.provider.equals(searchWidgetCn)) {
+                searchWidgetInfo = info;
+                break;
+            }
+        }
+
+        // Return early if there is no search widget
+        if (searchWidgetInfo == null) return null;
+
+        // Allocate a new widget id and try and bind the app widget (if that fails, then just skip)
+        int searchWidgetId = host.allocateAppWidgetId();
+        Bundle opts = new Bundle();
+        opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                AppWidgetProviderInfo.WIDGET_CATEGORY_RECENTS);
+        if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, searchWidgetInfo.provider, opts)) {
+            return null;
+        }
+        return new Pair<Integer, AppWidgetProviderInfo>(searchWidgetId, searchWidgetInfo);
+    }
+
+    /**
+     * Returns the app widget info for the specified app widget id.
+     */
+    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+        if (mAwm == null) return null;
+
+        return mAwm.getAppWidgetInfo(appWidgetId);
+    }
+
+    /**
+     * Destroys the specified app widget.
+     */
+    public void unbindSearchAppWidget(AppWidgetHost host, int appWidgetId) {
+        if (mAwm == null) return;
+
+        // Delete the app widget
+        host.deleteAppWidgetId(appWidgetId);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
index 4a1b3b2..b602f84 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import android.graphics.Color;
 import android.graphics.Rect;
 
 /* Common code */
@@ -46,4 +47,19 @@
             r.offset(cx, cy);
         }
     }
+
+    /** Calculates the luminance-preserved greyscale of a given color. */
+    private static int colorToGreyscale(int color) {
+        return Math.round(0.2126f * Color.red(color) + 0.7152f * Color.green(color) +
+                0.0722f * Color.blue(color));
+    }
+
+    /** Returns the ideal text color to draw on top of a specified background color. */
+    public static int getIdealTextColorForBackgroundColor(int color) {
+        RecentsConfiguration configuration = RecentsConfiguration.getInstance();
+        int greyscale = colorToGreyscale(color);
+        return (greyscale < 128) ? configuration.taskBarViewLightTextColor :
+                configuration.taskBarViewDarkTextColor;
+
+    }
 }
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 1566a49..47a506c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -72,6 +72,7 @@
     public Drawable applicationIcon;
     public Drawable activityIcon;
     public String activityLabel;
+    public int colorPrimary;
     public Bitmap thumbnail;
     public boolean isActive;
     public int userId;
@@ -83,10 +84,11 @@
     }
 
     public Task(int id, boolean isActive, Intent intent, String activityTitle,
-                BitmapDrawable activityIcon, int userId) {
+                BitmapDrawable activityIcon, int colorPrimary, int userId) {
         this.key = new TaskKey(id, intent, userId);
         this.activityLabel = activityTitle;
         this.activityIcon = activityIcon;
+        this.colorPrimary = colorPrimary;
         this.isActive = isActive;
         this.userId = userId;
     }
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 a04cd3e..c2e8275 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -27,11 +27,9 @@
 import android.net.Uri;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
-import android.widget.TextView;
 import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -39,7 +37,6 @@
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.R;
 
 import java.util.ArrayList;
 
@@ -57,6 +54,8 @@
 
     // The space partitioning root of this container
     SpaceNode mBSP;
+    // Whether there are any tasks
+    boolean mHasTasks;
     // Search bar view
     View mSearchBar;
     // Recents view callbacks
@@ -80,22 +79,14 @@
         mBSP = n;
 
         // Create and add all the stacks for this partition of space.
-        boolean hasTasks = false;
+        mHasTasks = false;
         removeAllViews();
         ArrayList<TaskStack> stacks = mBSP.getStacks();
         for (TaskStack stack : stacks) {
             TaskStackView stackView = new TaskStackView(getContext(), stack);
             stackView.setCallbacks(this);
             addView(stackView);
-            hasTasks |= (stack.getTaskCount() > 0);
-        }
-
-        // Create the search bar (and hide it if we have no recent tasks)
-        if (Constants.DebugFlags.App.EnableSearchButton) {
-            createSearchBar();
-            if (!hasTasks) {
-                mSearchBar.setVisibility(View.GONE);
-            }
+            mHasTasks |= (stack.getTaskCount() > 0);
         }
     }
 
@@ -130,19 +121,30 @@
         return false;
     }
 
-    /** Creates and adds the search bar */
-    void createSearchBar() {
-        // Create a temporary search bar
-        mSearchBar = mInflater.inflate(R.layout.recents_search_bar, this, false);
-        mSearchBar.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onSearchTriggered();
+    /** Adds the search bar */
+    public void setSearchBar(View searchBar) {
+        // Create the search bar (and hide it if we have no recent tasks)
+        if (Constants.DebugFlags.App.EnableSearchButton) {
+            // Remove the previous search bar if one exists
+            if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
+                removeView(mSearchBar);
             }
-        });
-        addView(mSearchBar);
+            // Add the new search bar
+            if (searchBar != null) {
+                mSearchBar = searchBar;
+                mSearchBar.setVisibility(mHasTasks ? View.VISIBLE : View.GONE);
+                addView(mSearchBar);
+
+                Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsView|setSearchBar]",
+                        "" + (mSearchBar.getVisibility() == View.VISIBLE),
+                        Console.AnsiBlue);
+            }
+        }
     }
 
+    /**
+     * This is called with the full size of the window since we are handling our own insets.
+     */
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -155,22 +157,26 @@
         Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
                 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
 
-        // Get the search bar bounds so that we can account for its height in the children
-        Rect searchBarSpaceBounds = new Rect();
-        Rect searchBarBounds = new Rect();
+        // Get the search bar bounds and measure the search bar layout
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
-                searchBarSpaceBounds, searchBarBounds);
         if (mSearchBar != null) {
-            mSearchBar.measure(MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), widthMode),
-                    MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), heightMode));
+            Rect searchBarSpaceBounds = new Rect();
+            config.getSearchBarBounds(width, height - config.systemInsets.top, searchBarSpaceBounds);
+            mSearchBar.measure(
+                    MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
         }
 
-        // We measure our stack views sans the status bar.  It will handle the nav bar itself.
+        // We give the full width of the space, not including the right nav bar insets in landscape,
+        // to the stack view, since we want the tasks to render under the search bar in landscape.
+        // In addition, we give it the full height, not including the top inset or search bar space,
+        // since we want the tasks to render under the navigation buttons in portrait.
+        Rect taskStackBounds = new Rect();
+        config.getTaskStackBounds(width, height, taskStackBounds);
         int childWidth = width - config.systemInsets.right;
-        int childHeight = height - config.systemInsets.top - searchBarSpaceBounds.height();
+        int childHeight = taskStackBounds.height() - config.systemInsets.top;
 
-        // Measure each child
+        // Measure each TaskStackView
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -183,6 +189,9 @@
         setMeasuredDimension(width, height);
     }
 
+    /**
+     * This is called with the full size of the window since we are handling our own insets.
+     */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]",
@@ -190,21 +199,24 @@
         Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
                 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onLayout");
 
-        // Get the search bar bounds so that we can account for its height in the children
-        Rect searchBarSpaceBounds = new Rect();
-        Rect searchBarBounds = new Rect();
+        // Get the search bar bounds so that we lay it out
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
-                searchBarSpaceBounds, searchBarBounds);
         if (mSearchBar != null) {
+            Rect searchBarSpaceBounds = new Rect();
+            config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
             mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left,
                     config.systemInsets.top + searchBarSpaceBounds.top,
                     config.systemInsets.left + mSearchBar.getMeasuredWidth(),
                     config.systemInsets.top + mSearchBar.getMeasuredHeight());
         }
 
-        // We offset our stack views by the status bar height.  It will handle the nav bar itself.
-        top += config.systemInsets.top + searchBarSpaceBounds.height();
+        // We offset the stack view by the left inset (if any), but lay it out under the search bar.
+        // In addition, we offset our stack views by the top inset and search bar height, but not
+        // the bottom insets because we want it to render under the navigation buttons.
+        Rect taskStackBounds = new Rect();
+        config.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
+        left += config.systemInsets.left;
+        top += config.systemInsets.top + taskStackBounds.top;
 
         // Layout each child
         // XXX: Based on the space node for that task view
@@ -212,9 +224,8 @@
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
             if (child instanceof TaskStackView && child.getVisibility() != GONE) {
-                int width = child.getMeasuredWidth();
-                int height = child.getMeasuredHeight();
-                child.layout(left, top, left + width, top + height);
+                TaskStackView tsv = (TaskStackView) child;
+                child.layout(left, top, left + tsv.getMeasuredWidth(), top + tsv.getMeasuredHeight());
             }
         }
     }
@@ -374,24 +385,4 @@
         TaskStackBuilder.create(getContext())
                 .addNextIntentWithParentStack(intent).startActivities();
     }
-
-    public void onSearchTriggered() {
-        // Get the search bar source bounds
-        Rect searchBarSpaceBounds = new Rect();
-        Rect searchBarBounds = new Rect();
-        RecentsConfiguration config = RecentsConfiguration.getInstance();
-        config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
-                searchBarSpaceBounds, searchBarBounds);
-
-        // Get the search intent and start it
-        Intent searchIntent = RecentsTaskLoader.getInstance().getSystemServicesProxy()
-                .getGlobalSearchIntent(searchBarBounds);
-        if (searchIntent != null) {
-            try {
-                getContext().startActivity(searchIntent);
-            } catch (ActivityNotFoundException anfe) {
-                Console.logError(getContext(), "Could not start Search activity");
-            }
-        }
-    }
 }
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 124f11e..c6cb812 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -18,11 +18,13 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 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.model.Task;
 
 
@@ -58,6 +60,7 @@
 
     /** Binds the bar view to the task */
     void rebindToTask(Task t, boolean animate) {
+        RecentsConfiguration configuration = RecentsConfiguration.getInstance();
         mTask = t;
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
@@ -67,6 +70,15 @@
             mApplicationIcon.setImageDrawable(t.applicationIcon);
         }
         mActivityDescription.setText(t.activityLabel);
+        // Try and apply the system ui tint
+        int tint = t.colorPrimary;
+        if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
+            setBackgroundColor(tint);
+            mActivityDescription.setTextColor(Utilities.getIdealTextColorForBackgroundColor(tint));
+        } else {
+            setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
+            mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor);
+        }
         if (animate) {
             // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index a81d01c..983ad49 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -29,7 +29,10 @@
 import android.widget.FrameLayout;
 import com.android.systemui.R;
 import com.android.systemui.recents.BakedBezierInterpolator;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.model.Task;
 
 
 /* The task info view */
@@ -143,6 +146,16 @@
                 .start();
     }
 
+    /** Binds the info view to the task */
+    void rebindToTask(Task t, boolean animate) {
+        RecentsConfiguration configuration = RecentsConfiguration.getInstance();
+        if (Constants.DebugFlags.App.EnableTaskBarThemeColors && t.colorPrimary != 0) {
+            setBackgroundColor(t.colorPrimary);
+        } else {
+            setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
+        }
+    }
+
     @Override
     public void draw(Canvas canvas) {
         int saveCount = 0;
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 0a8e76f..e273ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -547,7 +547,7 @@
     }
 
     /** Computes the stack and task rects */
-    public void computeRects(int width, int height, int insetBottom) {
+    public void computeRects(int width, int height, int insetLeft, int insetBottom) {
         // Note: We let the stack view be the full height because we want the cards to go under the
         //       navigation bar if possible.  However, the stack rects which we use to calculate
         //       max scroll, etc. need to take the nav bar into account
@@ -555,6 +555,7 @@
         // Compute the stack rects
         mRect.set(0, 0, width, height);
         mStackRect.set(mRect);
+        mStackRect.left += insetLeft;
         mStackRect.bottom -= insetBottom;
 
         int smallestDimension = Math.min(width, height);
@@ -582,6 +583,11 @@
         updateMinMaxScroll(false);
     }
 
+    /**
+     * This is called with the size of the space not including the top or right insets, or the
+     * search bar height in portrait (but including the search bar width in landscape, since we want
+     * to draw under it.
+     */
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -592,7 +598,9 @@
 
         // Compute our stack/task rects
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        computeRects(width, height, config.systemInsets.bottom);
+        Rect taskStackBounds = new Rect();
+        config.getTaskStackBounds(width, height, taskStackBounds);
+        computeRects(width, height, taskStackBounds.left, config.systemInsets.bottom);
 
         // Debug logging
         if (Constants.DebugFlags.UI.MeasureAndLayout) {
@@ -630,6 +638,11 @@
         setMeasuredDimension(width, height);
     }
 
+    /**
+     * This is called with the size of the space not including the top or right insets, or the
+     * search bar height in portrait (but including the search bar width in landscape, since we want
+     * to draw under it.
+     */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[TaskStackView|layout]",
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 d3b79d6..ecd0c45 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -197,18 +197,9 @@
     /** Animates this task view as it enters recents */
     public void animateOnEnterRecents() {
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        int translate = config.pxFromDp(10);
-        mBarView.setScaleX(1.25f);
-        mBarView.setScaleY(1.25f);
         mBarView.setAlpha(0f);
-        mBarView.setTranslationX(translate / 2);
-        mBarView.setTranslationY(-translate);
         mBarView.animate()
                 .alpha(1f)
-                .scaleX(1f)
-                .scaleY(1f)
-                .translationX(0)
-                .translationY(0)
                 .setStartDelay(235)
                 .setInterpolator(BakedBezierInterpolator.INSTANCE)
                 .setDuration(config.taskBarEnterAnimDuration)
@@ -219,16 +210,11 @@
     /** Animates this task view as it exits recents */
     public void animateOnLeavingRecents(final Runnable r) {
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        int translate = config.pxFromDp(10);
         mBarView.animate()
             .alpha(0f)
-            .scaleX(1.1f)
-            .scaleY(1.1f)
-            .translationX(translate / 2)
-            .translationY(-translate)
             .setStartDelay(0)
             .setInterpolator(BakedBezierInterpolator.INSTANCE)
-            .setDuration(Utilities.calculateTranslationAnimationDuration(translate))
+            .setDuration(config.taskBarExitAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
                 @Override
@@ -352,6 +338,7 @@
             // Bind each of the views to the new task data
             mThumbnailView.rebindToTask(mTask, reloadingTaskData);
             mBarView.rebindToTask(mTask, reloadingTaskData);
+            mInfoView.rebindToTask(mTask, reloadingTaskData);
             // Rebind any listeners
             mBarView.mApplicationIcon.setOnClickListener(this);
             mInfoView.mAppInfoButton.setOnClickListener(this);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bcc6359..f908de2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7070,8 +7070,7 @@
                     final ArrayList<ActivityRecord> activities = tr.mActivities;
                     int activityNdx;
                     final int numActivities = activities.size();
-                    for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
-                            ++activityNdx) {
+                    for (activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
                         final ActivityRecord r = activities.get(activityNdx);
                         if (r.intent != null &&
                                 (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
@@ -7079,14 +7078,34 @@
                             break;
                         }
                     }
-                    // Traverse downwards starting below break looking for set label and icon.
-                    for (--activityNdx; activityNdx >= 0; --activityNdx) {
-                        final ActivityRecord r = activities.get(activityNdx);
-                        if (r.activityLabel != null || r.activityIcon != null) {
-                            rti.activityLabel = r.activityLabel;
-                            rti.activityIcon = r.activityIcon;
-                            break;
+                    if (activityNdx > 0) {
+                        // Traverse downwards starting below break looking for set label, icon.
+                        // Note that if there are activities in the task but none of them set the
+                        // recent activity values, then we do not fall back to the last set
+                        // values in the TaskRecord.
+                        rti.activityValues = new ActivityManager.RecentsActivityValues();
+                        for (--activityNdx; activityNdx >= 0; --activityNdx) {
+                            final ActivityRecord r = activities.get(activityNdx);
+                            if (r.activityValues != null) {
+                                if (rti.activityValues.label == null) {
+                                    rti.activityValues.label = r.activityValues.label;
+                                    tr.lastActivityValues.label = r.activityValues.label;
+                                }
+                                if (rti.activityValues.icon == null) {
+                                    rti.activityValues.icon = r.activityValues.icon;
+                                    tr.lastActivityValues.icon = r.activityValues.icon;
+                                }
+                                if (rti.activityValues.colorPrimary == 0) {
+                                    rti.activityValues.colorPrimary = r.activityValues.colorPrimary;
+                                    tr.lastActivityValues.colorPrimary = r.activityValues.colorPrimary;
+                                }
+                            }
                         }
+                    } else {
+                        // If there are no activity records in this task, then we use the last
+                        // resolved values
+                        rti.activityValues =
+                                new ActivityManager.RecentsActivityValues(tr.lastActivityValues);
                     }
 
                     if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) {
@@ -7154,13 +7173,11 @@
     }
 
     @Override
-    public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
-            Bitmap activityIcon) {
+    public void setRecentsActivityValues(IBinder token, ActivityManager.RecentsActivityValues rav) {
         synchronized (this) {
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
-                r.activityLabel = activityLabel.toString();
-                r.activityIcon = activityIcon;
+                r.activityValues = rav;
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 7a08cdd..f506eab 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -23,6 +23,7 @@
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
 
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ResultInfo;
 import android.content.ComponentName;
@@ -147,8 +148,7 @@
     boolean mStartingWindowShown = false;
     ActivityContainer mInitialActivityContainer;
 
-    String activityLabel;
-    Bitmap activityIcon;
+    ActivityManager.RecentsActivityValues activityValues; // the recents information for this activity
 
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index f4dd15e..9f0bc10 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -56,6 +56,11 @@
 
     int numFullscreen;      // Number of fullscreen activities.
 
+    // This represents the last resolved activity values for this task
+    // NOTE: This value needs to be persisted with each task
+    ActivityManager.RecentsActivityValues lastActivityValues =
+            new ActivityManager.RecentsActivityValues();
+
     /** List of all activities in the task arranged in history order */
     final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();
 
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
new file mode 100644
index 0000000..296cc5b
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -0,0 +1,211 @@
+/*
+ * 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.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Encapsulates a sequence of CEC/MHL command exchange for a certain feature.
+ *
+ * <p>Many CEC/MHL features are accomplished by CEC devices on the bus exchanging
+ * more than one command. {@link FeatureAction} represents the life cycle of the communication,
+ * manages the state as the process progresses, and if necessary, returns the result
+ * to the caller which initiates the action, through the callback given at the creation
+ * of the object. All the actual action classes inherit FeatureAction.
+ *
+ * <p>More than one FeatureAction objects can be up and running simultaneously,
+ * maintained by {@link HdmiControlService}. Each action is passed a new command
+ * arriving from the bus, and either consumes it if the command is what the action expects,
+ * or yields it to other action.
+ *
+ * Declared as package private, accessed by {@link HdmiControlService} only.
+ */
+abstract class FeatureAction {
+
+    private static final String TAG = "FeatureAction";
+
+    // Timer handler message used for timeout event
+    protected static final int MSG_TIMEOUT = 100;
+
+    // Default timeout for the incoming command to arrive in response to a request
+    protected static final int TIMEOUT_MS = 1000;
+
+    // Default state used in common by all the feature actions.
+    protected static final int STATE_NONE = 0;
+
+    // Internal state indicating the progress of action.
+    protected int mState = STATE_NONE;
+
+    protected final HdmiControlService mService;
+
+    // Logical address of the device for which the feature action is taken. The commands
+    // generated in an action all use this field as source address.
+    protected final int mSourceAddress;
+
+    // Timer that manages timeout events.
+    protected ActionTimer mActionTimer;
+
+    FeatureAction(HdmiControlService service, int sourceAddress) {
+        mService = service;
+        mSourceAddress = sourceAddress;
+        mActionTimer = createActionTimer(service.getServiceLooper());
+    }
+
+    @VisibleForTesting
+    void setActionTimer(ActionTimer actionTimer) {
+        mActionTimer = actionTimer;
+    }
+
+    /**
+     * Called right after the action is created. Initialization or first step to take
+     * for the action can be done in this method.
+     *
+     * @return true if the operation is successful; otherwise false.
+     */
+    abstract boolean start();
+
+    /**
+     * Process the command. Called whenever a new command arrives.
+     *
+     * @param cmd command to process
+     * @return true if the command was consumed in the process; Otherwise false, which
+     *          indicates that the command shall be handled by other actions.
+     */
+    abstract boolean processCommand(HdmiCecMessage cmd);
+
+    /**
+     * Called when the action should handle the timer event it created before.
+     *
+     * <p>CEC standard mandates each command transmission should be responded within
+     * certain period of time. The method is called when the timer it created as it transmitted
+     * a command gets expired. Inner logic should take an appropriate action.
+     *
+     * @param state the state associated with the time when the timer was created
+     */
+    abstract void handleTimerEvent(int state);
+
+    /**
+     * Timer handler interface used for FeatureAction classes.
+     */
+    interface ActionTimer {
+        /**
+         * Send a timer message.
+         *
+         * Also carries the state of the action when the timer is created. Later this state is
+         * compared to the one the action is in when it receives the timer to let the action tell
+         * the right timer to handle.
+         *
+         * @param state state of the action is in
+         * @param delayMillis amount of delay for the timer
+         */
+        void sendTimerMessage(int state, long delayMillis);
+    }
+
+    private class ActionTimerHandler extends Handler implements ActionTimer {
+
+        public ActionTimerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void sendTimerMessage(int state, long delayMillis) {
+            sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MSG_TIMEOUT:
+                handleTimerEvent(msg.arg1);
+                break;
+            default:
+                Slog.w(TAG, "Unsupported message:" + msg.what);
+                break;
+            }
+        }
+    }
+
+    private ActionTimer createActionTimer(Looper looper) {
+        return new ActionTimerHandler(looper);
+    }
+
+    // Add a new timer. The timer event will come to mActionTimer.handleMessage() in
+    // delayMillis.
+    protected void addTimer(int state, int delayMillis) {
+        mActionTimer.sendTimerMessage(state, delayMillis);
+    }
+
+    static HdmiCecMessage buildCommand(int src, int dst, int opcode, byte[] params) {
+        return new HdmiCecMessage(src, dst, opcode, params);
+    }
+
+    // Build a CEC command that does not have parameter.
+    static HdmiCecMessage buildCommand(int src, int dst, int opcode) {
+        return new HdmiCecMessage(src, dst, opcode, HdmiCecMessage.EMPTY_PARAM);
+    }
+
+    protected final void sendCommand(HdmiCecMessage cmd) {
+        mService.sendCecCommand(cmd);
+    }
+
+    protected final void sendBroadcastCommand(int opcode, byte[] param) {
+        sendCommand(buildCommand(mSourceAddress, HdmiCec.ADDR_BROADCAST, opcode, param));
+    }
+
+    /**
+     * Finish up the action. Reset the state, and remove itself from the action queue.
+     */
+    protected void finish() {
+        mState = STATE_NONE;
+        removeAction(this);
+    }
+
+    /**
+     * Remove the action from the action queue. This is called after the action finishes
+     * its role.
+     *
+     * @param action
+     */
+    private void removeAction(FeatureAction action) {
+        mService.removeAction(action);
+    }
+
+    // Utility methods for generating parameter byte arrays for CEC commands.
+    protected static byte[] uiCommandParam(int uiCommand) {
+        return new byte[] {(byte) uiCommand};
+    }
+
+    protected static byte[] physicalAddressParam(int physicalAddress) {
+        return new byte[] {
+                (byte) ((physicalAddress >> 8) & 0xFF),
+                (byte) (physicalAddress & 0xFF)
+        };
+    }
+
+    protected static byte[] pathPairParam(int oldPath, int newPath) {
+        return new byte[] {
+                (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
+                (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
+        };
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 7c1995e..f99c717 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,6 +18,8 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.util.Slog;
@@ -77,4 +79,41 @@
     Looper getServiceLooper() {
         return Looper.myLooper();
     }
+
+    /**
+     * Add a new {@link FeatureAction} to the action queue.
+     *
+     * @param action {@link FeatureAction} to add
+     */
+    void addAction(FeatureAction action) {
+        // TODO: Implement this.
+    }
+
+
+    /**
+     * Remove the given {@link FeatureAction} object from the action queue.
+     *
+     * @param action {@link FeatureAction} to add
+     */
+    void removeAction(FeatureAction action) {
+        // TODO: Implement this.
+    }
+
+    /**
+     * Transmit a CEC command to CEC bus.
+     *
+     * @param command CEC command to send out
+     */
+    void sendCecCommand(HdmiCecMessage command) {
+        // TODO: Implement this.
+    }
+
+    /**
+     * Add a new {@link HdmiCecDeviceInfo} to controller.
+     *
+     * @param deviceInfo new device information object to add
+     */
+    void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
+        // TODO: Implement this.
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
new file mode 100644
index 0000000..c84a067
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -0,0 +1,165 @@
+/*
+ * 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.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Feature action that discovers the information of a newly found logical device.
+ *
+ * This action is created when receiving &lt;Report Physical Address&gt;, a CEC command a newly
+ * connected HDMI-CEC device broadcasts to announce its advent. Additional commands are issued in
+ * this action to gather more information on the device such as OSD name and device vendor ID.
+ *
+ * <p>The result is made in the form of {@link HdmiCecDeviceInfo} object, and passed to service
+ * for the management through its life cycle.
+ *
+ * <p>Package-private, accessed by {@link HdmiControlService} only.
+ */
+final class NewDeviceAction extends FeatureAction {
+
+    private static final String TAG = "NewDeviceAction";
+
+    // State in which the action sent <Give OSD Name> and is waiting for <Set OSD Name>
+    // that contains the name of the device for display on screen.
+    static final int STATE_WAITING_FOR_SET_OSD_NAME = 1;
+
+    // State in which the action sent <Give Device Vendor ID> and is waiting for
+    // <Device Vendor ID> that contains the vendor ID of the device.
+    static final int STATE_WAITING_FOR_DEVICE_VENDOR_ID = 2;
+
+    private final int mDeviceLogicalAddress;
+    private final int mDevicePhysicalAddress;
+
+    private int mVendorId;
+    private String mDisplayName;
+
+    /**
+     * Constructor.
+     *
+     * @param service {@link HdmiControlService} instance
+     * @param sourceAddress logical address to be used as source address
+     * @param deviceLogicalAddress logical address of the device in interest
+     * @param devicePhysicalAddress physical address of the device in interest
+     */
+    NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress,
+            int devicePhysicalAddress) {
+        super(service, sourceAddress);
+        mDeviceLogicalAddress = deviceLogicalAddress;
+        mDevicePhysicalAddress = devicePhysicalAddress;
+        mVendorId = HdmiCec.UNKNOWN_VENDOR_ID;
+    }
+
+    @Override
+    public boolean start() {
+        sendCommand(
+                buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GET_OSD_NAME));
+        mState = STATE_WAITING_FOR_SET_OSD_NAME;
+        addTimer(mState, TIMEOUT_MS);
+        return true;
+    }
+
+    @Override
+    public boolean processCommand(HdmiCecMessage cmd) {
+        // For the logical device in interest, we want two more pieces of information -
+        // osd name and vendor id. They are requested in sequence. In case we don't
+        // get the expected responses (either by timeout or by receiving <feature abort> command),
+        // set them to a default osd name and unknown vendor id respectively.
+        int opcode = cmd.getOpcode();
+        int src = cmd.getSource();
+        byte[] params = cmd.getParams();
+
+        if (mDeviceLogicalAddress != src) {
+            return false;
+        }
+
+        if (mState == STATE_WAITING_FOR_SET_OSD_NAME) {
+            if (opcode == HdmiCec.MESSAGE_SET_OSD_NAME) {
+                try {
+                    mDisplayName = new String(params, "US-ASCII");
+                } catch (UnsupportedEncodingException e) {
+                    Slog.e(TAG, "Failed to get OSD name: " + e.getMessage());
+                }
+                mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
+                requestVendorId();
+                return true;
+            } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
+                int requestOpcode = params[1];
+                if (requestOpcode == HdmiCec.MESSAGE_SET_OSD_NAME) {
+                    mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
+                    requestVendorId();
+                    return true;
+                }
+            }
+        } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
+            if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
+                if (params.length == 3) {
+                    mVendorId = (params[0] << 16) + (params[1] << 8) + params[2];
+                } else {
+                    Slog.e(TAG, "Failed to get device vendor ID: ");
+                }
+                addDeviceInfo();
+                finish();
+                return true;
+            } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
+                int requestOpcode = params[1];
+                if (requestOpcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
+                    addDeviceInfo();
+                    finish();
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void requestVendorId() {
+        sendCommand(buildCommand(mSourceAddress, mDeviceLogicalAddress,
+                HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID));
+        addTimer(mState, TIMEOUT_MS);
+    }
+
+    private void addDeviceInfo() {
+        if (mDisplayName == null) {
+            mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress);
+        }
+        mService.addDeviceInfo(new HdmiCecDeviceInfo(
+                mDeviceLogicalAddress, mDevicePhysicalAddress,
+                HdmiCec.getTypeFromAddress(mDeviceLogicalAddress),
+                mVendorId, mDisplayName));
+    }
+
+    @Override
+    public void handleTimerEvent(int state) {
+        if (mState == STATE_NONE || mState != state) {
+            return;
+        }
+        if (state == STATE_WAITING_FOR_SET_OSD_NAME) {
+            // Osd name request timed out. Try vendor id
+            mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
+            requestVendorId();
+        } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
+            // vendor id timed out. Go ahead creating the device info what we've got so far.
+            addDeviceInfo();
+            finish();
+        }
+    }
+}