Merge "Add new media navigation keys."
diff --git a/api/current.txt b/api/current.txt
index 2401d7b..14babc8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22694,12 +22694,14 @@
 
   public static class Build.VERSION {
     ctor public Build.VERSION();
+    field public static final java.lang.String BASE_OS;
     field public static final java.lang.String CODENAME;
     field public static final java.lang.String INCREMENTAL;
     field public static final int PREVIEW_SDK_INT;
     field public static final java.lang.String RELEASE;
     field public static final deprecated java.lang.String SDK;
     field public static final int SDK_INT;
+    field public static final java.lang.String SECURITY_PATCH;
   }
 
   public static class Build.VERSION_CODES {
@@ -39717,6 +39719,7 @@
     ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet);
     ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
     ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
     method public void clearListSelection();
     method protected java.lang.CharSequence convertSelectionToString(java.lang.Object);
     method public void dismissDropDown();
diff --git a/api/system-current.txt b/api/system-current.txt
index cb51032..908bf3c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24638,12 +24638,14 @@
 
   public static class Build.VERSION {
     ctor public Build.VERSION();
+    field public static final java.lang.String BASE_OS;
     field public static final java.lang.String CODENAME;
     field public static final java.lang.String INCREMENTAL;
     field public static final int PREVIEW_SDK_INT;
     field public static final java.lang.String RELEASE;
     field public static final deprecated java.lang.String SDK;
     field public static final int SDK_INT;
+    field public static final java.lang.String SECURITY_PATCH;
   }
 
   public static class Build.VERSION_CODES {
@@ -42363,6 +42365,7 @@
     ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet);
     ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet, int);
     ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public AutoCompleteTextView(android.content.Context, android.util.AttributeSet, int, int, android.content.res.Resources.Theme);
     method public void clearListSelection();
     method protected java.lang.CharSequence convertSelectionToString(java.lang.Object);
     method public void dismissDropDown();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 84892a3..07fa52c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -31,7 +31,7 @@
 import android.transition.TransitionManager;
 import android.util.ArrayMap;
 import android.util.SuperNotCalledException;
-import android.view.Window.WindowStackCallback;
+import android.view.Window.WindowControllerCallback;
 import android.widget.Toolbar;
 
 import com.android.internal.app.IVoiceInteractor;
@@ -60,6 +60,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.graphics.Rect;
 import android.media.AudioManager;
 import android.media.session.MediaController;
 import android.net.Uri;
@@ -673,7 +674,7 @@
         implements LayoutInflater.Factory2,
         Window.Callback, KeyEvent.Callback,
         OnCreateContextMenuListener, ComponentCallbacks2,
-        Window.OnWindowDismissedCallback, WindowStackCallback {
+        Window.OnWindowDismissedCallback, WindowControllerCallback {
     private static final String TAG = "Activity";
     private static final boolean DEBUG_LIFECYCLE = false;
 
@@ -2712,7 +2713,8 @@
     }
 
 
-    /** Called to move the window and its activity/task to a different stack container.
+    /**
+     * Called to move the window and its activity/task to a different stack container.
      * For example, a window can move between
      * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and
      * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack.
@@ -2734,6 +2736,31 @@
     }
 
     /**
+     * Returns the bounds of the task that contains this activity.
+     *
+     * @return Rect The bounds that contains the activity.
+     * @hide
+     */
+    @Override
+    public Rect getActivityBounds() throws RemoteException {
+        return ActivityManagerNative.getDefault().getActivityBounds(mToken);
+    }
+
+    /**
+     * Sets the bounds (size and position) of the task or stack that contains this
+     * activity.
+     * NOTE: The requested bounds might not the fully honored by the system depending
+     * on the window placement policy.
+     *
+     * @param newBounds The new target bounds of the activity in task or stack.
+     * @hide
+     */
+    @Override
+    public void setActivityBounds(Rect newBounds) throws RemoteException {
+        ActivityManagerNative.getDefault().setActivityBounds(mToken, newBounds);
+    }
+
+    /**
      * Called to process key events.  You can override this to intercept all
      * key events before they are dispatched to the window.  Be sure to call
      * this implementation for key events that should be handled normally.
@@ -6211,7 +6238,7 @@
         mFragments.attachHost(null /*parent*/);
 
         mWindow = new PhoneWindow(this);
-        mWindow.setWindowStackCallback(this);
+        mWindow.setWindowControllerCallback(this);
         mWindow.setCallback(this);
         mWindow.setOnWindowDismissedCallback(this);
         mWindow.getLayoutInflater().setPrivateFactory(this);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 10c122a..5f33344 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -752,6 +752,24 @@
             return true;
         }
 
+        case GET_ACTIVITY_BOUNDS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            Rect r = getActivityBounds(token);
+            reply.writeNoException();
+            r.writeToParcel(reply, 0);
+            return true;
+        }
+
+        case SET_ACTIVITY_BOUNDS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            Rect r = Rect.CREATOR.createFromParcel(data);
+            setActivityBounds(token, r);
+            reply.writeNoException();
+            return true;
+        }
+
         case POSITION_TASK_IN_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int taskId = data.readInt();
@@ -5903,6 +5921,35 @@
     }
 
     @Override
+    public void setActivityBounds(IBinder token, Rect r) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        r.writeToParcel(data, 0);
+        mRemote.transact(SET_ACTIVITY_BOUNDS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
+    public Rect getActivityBounds(IBinder token) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(GET_ACTIVITY_BOUNDS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        Rect rect = Rect.CREATOR.createFromParcel(reply);
+        data.recycle();
+        reply.recycle();
+        return rect;
+    }
+
+    @Override
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index ff1d595..d355219 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -490,6 +490,9 @@
             throws RemoteException;
     public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
     public void resizeTask(int taskId, Rect bounds) throws RemoteException;
+    public void setActivityBounds(IBinder token, Rect bounds) throws RemoteException;
+    public Rect getActivityBounds(IBinder token) throws RemoteException;
+
     public Rect getTaskBounds(int taskId) throws RemoteException;
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
 
@@ -887,4 +890,6 @@
     int GET_ACTIVITY_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 343;
     int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
     int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
+    int GET_ACTIVITY_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
+    int SET_ACTIVITY_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a0162f7..2374899 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -157,6 +157,17 @@
         public static final String RELEASE = getString("ro.build.version.release");
 
         /**
+         * The base OS build the product is based on.
+         */
+        public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
+
+        /**
+         * The user-visible security patch level.
+         */
+        public static final String SECURITY_PATCH = SystemProperties.get(
+                "ro.build.version.security_patch", "");
+
+        /**
          * The user-visible SDK version of the framework in its raw String
          * representation; use {@link #SDK_INT} instead.
          *
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index dc1d6f6..3d8ab1e 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -514,6 +514,12 @@
             startA(mSpannableStringBuilder, attributes);
         } else if (tag.equalsIgnoreCase("u")) {
             start(mSpannableStringBuilder, new Underline());
+        } else if (tag.equalsIgnoreCase("del")) {
+            start(mSpannableStringBuilder, new Strikethrough());
+        } else if (tag.equalsIgnoreCase("s")) {
+            start(mSpannableStringBuilder, new Strikethrough());
+        } else if (tag.equalsIgnoreCase("strike")) {
+            start(mSpannableStringBuilder, new Strikethrough());
         } else if (tag.equalsIgnoreCase("sup")) {
             start(mSpannableStringBuilder, new Super());
         } else if (tag.equalsIgnoreCase("sub")) {
@@ -565,6 +571,12 @@
             endA(mSpannableStringBuilder);
         } else if (tag.equalsIgnoreCase("u")) {
             end(mSpannableStringBuilder, Underline.class, new UnderlineSpan());
+        } else if (tag.equalsIgnoreCase("del")) {
+            end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan());
+        } else if (tag.equalsIgnoreCase("s")) {
+            end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan());
+        } else if (tag.equalsIgnoreCase("strike")) {
+            end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan());
         } else if (tag.equalsIgnoreCase("sup")) {
             end(mSpannableStringBuilder, Super.class, new SuperscriptSpan());
         } else if (tag.equalsIgnoreCase("sub")) {
@@ -822,6 +834,7 @@
     private static class Bold { }
     private static class Italic { }
     private static class Underline { }
+    private static class Strikethrough { }
     private static class Big { }
     private static class Small { }
     private static class Monospace { }
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 992dc4d..40315ad 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.Nullable;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.util.Log;
@@ -814,8 +815,9 @@
      * a list of all the spans regardless of type.
      */
     @SuppressWarnings("unchecked")
-    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
-        if (kind == null || mSpanCount == 0) return ArrayUtils.emptyArray(kind);
+    public <T> T[] getSpans(int queryStart, int queryEnd, @Nullable Class<T> kind) {
+        if (kind == null) return (T[]) ArrayUtils.emptyArray(Object.class);
+        if (mSpanCount == 0) return ArrayUtils.emptyArray(kind);
         int count = countSpans(queryStart, queryEnd, kind, treeRoot());
         if (count == 0) {
             return ArrayUtils.emptyArray(kind);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index eecda89..1a9fb34 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -28,8 +28,9 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
@@ -183,7 +184,7 @@
     private TypedArray mWindowStyle;
     private Callback mCallback;
     private OnWindowDismissedCallback mOnWindowDismissedCallback;
-    private WindowStackCallback mWindowStackCallback;
+    private WindowControllerCallback mWindowControllerCallback;
     private WindowManager mWindowManager;
     private IBinder mAppToken;
     private String mAppName;
@@ -479,8 +480,9 @@
     }
 
     /** @hide */
-    public interface WindowStackCallback {
-        /** Called to move the window and its activity/task to a different stack container.
+    public interface WindowControllerCallback {
+        /**
+         * Called to move the window and its activity/task to a different stack container.
          * For example, a window can move between
          * {@link android.app.ActivityManager#FULLSCREEN_WORKSPACE_STACK_ID} stack and
          * {@link android.app.ActivityManager#FREEFORM_WORKSPACE_STACK_ID} stack.
@@ -491,6 +493,23 @@
 
         /** Returns the current stack Id for the window. */
         int getWindowStackId() throws RemoteException;
+
+        /**
+         * Returns the bounds of the task that contains this activity.
+         *
+         * @return Rect The bounds that contains the activity.
+         */
+        Rect getActivityBounds() throws RemoteException;
+
+        /**
+         * Sets the bounds (size and position) of the task or stack that contains this
+         * activity.
+         * NOTE: The requested bounds might not the fully honored by the system depending
+         * on the window placement policy.
+         *
+         * @param newBounds The new target bounds of the activity in task or stack.
+         */
+        void setActivityBounds(Rect newBounds) throws RemoteException;
     }
 
     public Window(Context context) {
@@ -682,13 +701,13 @@
     }
 
     /** @hide */
-    public final void setWindowStackCallback(WindowStackCallback wscb) {
-        mWindowStackCallback = wscb;
+    public final void setWindowControllerCallback(WindowControllerCallback wccb) {
+        mWindowControllerCallback = wccb;
     }
 
     /** @hide */
-    public final WindowStackCallback getWindowStackCallback() {
-        return mWindowStackCallback;
+    public final WindowControllerCallback getWindowControllerCallback() {
+        return mWindowControllerCallback;
     }
 
     /**
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 01767d5..7d57cb8 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -18,6 +18,7 @@
 
 import android.annotation.DrawableRes;
 import android.content.Context;
+import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
 import android.graphics.Rect;
@@ -28,10 +29,12 @@
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
@@ -94,6 +97,12 @@
 
     static final int EXPAND_MAX = 3;
 
+    /** Context used to inflate the popup window or dialog. */
+    private final Context mPopupContext;
+
+    private final ListPopupWindow mPopup;
+    private final PassThroughClickListener mPassThroughClickListener;
+
     private CharSequence mHintText;
     private TextView mHintView;
     private int mHintResource;
@@ -102,7 +111,6 @@
     private Filter mFilter;
     private int mThreshold;
 
-    private ListPopupWindow mPopup;
     private int mDropDownAnchorId;
 
     private AdapterView.OnItemClickListener mItemClickListener;
@@ -122,71 +130,172 @@
     // Set to false when the list is hidden to prevent asynchronous updates to popup the list again.
     private boolean mPopupCanBeUpdated = true;
 
-    private PassThroughClickListener mPassThroughClickListener;
     private PopupDataSetObserver mObserver;
 
+    /**
+     * Constructs a new auto-complete text view with the given context's theme.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     */
     public AutoCompleteTextView(Context context) {
         this(context, null);
     }
 
+    /**
+     * Constructs a new auto-complete text view with the given context's theme
+     * and the supplied attribute set.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     */
     public AutoCompleteTextView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.autoCompleteTextViewStyle);
     }
 
+    /**
+     * Constructs a new auto-complete text view with the given context's theme,
+     * the supplied attribute set, and default style attribute.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     */
     public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
+    /**
+     * Constructs a new auto-complete text view with the given context's theme,
+     * the supplied attribute set, and default styles.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     * @param defStyleRes A resource identifier of a style resource that
+     *                    supplies default values for the view, used only if
+     *                    defStyleAttr is 0 or can not be found in the theme.
+     *                    Can be 0 to not look for defaults.
+     */
     public AutoCompleteTextView(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
+        this(context, attrs, defStyleAttr, defStyleRes, null);
+    }
 
-        mPopup = new ListPopupWindow(context, attrs, defStyleAttr, defStyleRes);
-        mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
-        mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
+    /**
+     * Constructs a new auto-complete text view with the given context, the
+     * supplied attribute set, default styles, and the theme against which the
+     * completion popup should be inflated.
+     *
+     * @param context The context against which the view is inflated, which
+     *                provides access to the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     * @param defStyleRes A resource identifier of a style resource that
+     *                    supplies default values for the view, used only if
+     *                    defStyleAttr is 0 or can not be found in the theme.
+     *                    Can be 0 to not look for defaults.
+     * @param popupTheme The theme against which the completion popup window
+     *                   should be inflated. May be {@code null} to use the
+     *                   view theme. If set, this will override any value
+     *                   specified by
+     *                   {@link android.R.styleable#AutoCompleteTextView_popupTheme}.
+     */
+    public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes, Theme popupTheme) {
+        super(context, attrs, defStyleAttr, defStyleRes);
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes);
 
+        if (popupTheme != null) {
+            mPopupContext = new ContextThemeWrapper(context, popupTheme);
+        } else {
+            final int popupThemeResId = a.getResourceId(
+                    R.styleable.AutoCompleteTextView_popupTheme, 0);
+            if (popupThemeResId != 0) {
+                mPopupContext = new ContextThemeWrapper(context, popupThemeResId);
+            } else {
+                mPopupContext = context;
+            }
+        }
+
+        // Load attributes used within the popup against the popup context.
+        final TypedArray pa;
+        if (mPopupContext != context) {
+            pa = mPopupContext.obtainStyledAttributes(
+                    attrs, R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes);
+        } else {
+            pa = a;
+        }
+
+        final Drawable popupListSelector = pa.getDrawable(
+                R.styleable.AutoCompleteTextView_dropDownSelector);
+        final int popupWidth = pa.getLayoutDimension(
+                R.styleable.AutoCompleteTextView_dropDownWidth, LayoutParams.WRAP_CONTENT);
+        final int popupHeight = pa.getLayoutDimension(
+                R.styleable.AutoCompleteTextView_dropDownHeight, LayoutParams.WRAP_CONTENT);
+        final int popupHintLayoutResId = pa.getResourceId(
+                R.styleable.AutoCompleteTextView_completionHintView, R.layout.simple_dropdown_hint);
+        final CharSequence popupHintText = pa.getText(
+                R.styleable.AutoCompleteTextView_completionHint);
+
+        if (pa != a) {
+            pa.recycle();
+        }
+
+        mPopup = new ListPopupWindow(mPopupContext, attrs, defStyleAttr, defStyleRes);
+        mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+        mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
+        mPopup.setListSelector(popupListSelector);
+        mPopup.setOnItemClickListener(new DropDownItemClickListener());
+
+        // For dropdown width, the developer can specify a specific width, or
+        // MATCH_PARENT (for full screen width), or WRAP_CONTENT (to match the
+        // width of the anchored view).
+        mPopup.setWidth(popupWidth);
+        mPopup.setHeight(popupHeight);
+
+        // Completion hint must be set after specifying hint layout.
+        mHintResource = popupHintLayoutResId;
+        setCompletionHint(popupHintText);
+
+        // Get the anchor's id now, but the view won't be ready, so wait to
+        // actually get the view and store it in mDropDownAnchorView lazily in
+        // getDropDownAnchorView later. Defaults to NO_ID, in which case the
+        // getDropDownAnchorView method will simply return this TextView, as a
+        // default anchoring point.
+        mDropDownAnchorId = a.getResourceId(
+                R.styleable.AutoCompleteTextView_dropDownAnchor, View.NO_ID);
+
         mThreshold = a.getInt(R.styleable.AutoCompleteTextView_completionThreshold, 2);
 
-        mPopup.setListSelector(a.getDrawable(R.styleable.AutoCompleteTextView_dropDownSelector));
-
-        // Get the anchor's id now, but the view won't be ready, so wait to actually get the
-        // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later.
-        // Defaults to NO_ID, in which case the getDropDownAnchorView method will simply return
-        // this TextView, as a default anchoring point.
-        mDropDownAnchorId = a.getResourceId(R.styleable.AutoCompleteTextView_dropDownAnchor,
-                View.NO_ID);
-        
-        // For dropdown width, the developer can specify a specific width, or MATCH_PARENT
-        // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view).
-        mPopup.setWidth(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth,
-                ViewGroup.LayoutParams.WRAP_CONTENT));
-        mPopup.setHeight(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight,
-                ViewGroup.LayoutParams.WRAP_CONTENT));
-
-        mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView,
-                R.layout.simple_dropdown_hint);
-        
-        mPopup.setOnItemClickListener(new DropDownItemClickListener());
-        setCompletionHint(a.getText(R.styleable.AutoCompleteTextView_completionHint));
+        a.recycle();
 
         // Always turn on the auto complete input type flag, since it
         // makes no sense to use this widget without it.
         int inputType = getInputType();
-        if ((inputType&EditorInfo.TYPE_MASK_CLASS)
-                == EditorInfo.TYPE_CLASS_TEXT) {
+        if ((inputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {
             inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
             setRawInputType(inputType);
         }
 
-        a.recycle();
-
         setFocusable(true);
 
         addTextChangedListener(new MyWatcher());
-        
+
         mPassThroughClickListener = new PassThroughClickListener();
         super.setOnClickListener(mPassThroughClickListener);
     }
@@ -222,8 +331,8 @@
         mHintText = hint;
         if (hint != null) {
             if (mHintView == null) {
-                final TextView hintView = (TextView) LayoutInflater.from(getContext()).inflate(
-                        mHintResource, null).findViewById(com.android.internal.R.id.text1);
+                final TextView hintView = (TextView) LayoutInflater.from(mPopupContext).inflate(
+                        mHintResource, null).findViewById(R.id.text1);
                 hintView.setText(mHintText);
                 mHintView = hintView;
                 mPopup.setPromptView(hintView);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 6c37282..33595f2 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -5199,7 +5199,7 @@
      **/
     private int getWorkspaceId() {
         int workspaceId = FULLSCREEN_WORKSPACE_STACK_ID;
-        WindowStackCallback callback = getWindowStackCallback();
+        WindowControllerCallback callback = getWindowControllerCallback();
         if (callback != null) {
             try {
                 workspaceId = callback.getWindowStackId();
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index d8626cd..dd5c5d7 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -17,6 +17,7 @@
 package com.android.internal.widget;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.AttributeSet;
 import android.view.View;
@@ -243,7 +244,7 @@
      * Maximize the window by moving it to the maximized workspace stack.
      **/
     private void maximizeWindow() {
-        Window.WindowStackCallback callback = mOwner.getWindowStackCallback();
+        Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
         if (callback != null) {
             try {
                 callback.changeWindowStack(
@@ -253,4 +254,40 @@
             }
         }
     }
+
+    /**
+     * Returns the bounds of this activity.
+     * @return Returns bounds of the activity. It will return null if either the window is
+     *     fullscreen or the bounds could not be retrieved.
+     */
+    private Rect getActivityBounds() {
+        Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
+        if (callback != null) {
+            try {
+                return callback.getActivityBounds();
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to get the activity bounds.");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sets the bounds of this Activity on the stack.
+     * @param newBounds The bounds of the activity. Passing null is not allowed.
+     */
+    private void setActivityBounds(Rect newBounds) {
+        if (newBounds == null) {
+            Log.e(TAG, "Failed to set null bounds to the activity.");
+            return;
+        }
+        Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
+        if (callback != null) {
+            try {
+                callback.setActivityBounds(newBounds);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to set the activity bounds.");
+            }
+        }
+    }
 }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6dab142..61af6c5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4485,6 +4485,8 @@
             <enum name="wrap_content" value="-2" />
         </attr>
         <attr name="inputType" />
+        <!-- Theme to use for the completion popup window. -->
+        <attr name="popupTheme" />
     </declare-styleable>
     <declare-styleable name="PopupWindow">
         <!-- The background to use for the popup window. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 398c584..01ab624 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1271,7 +1271,7 @@
 
          If this string is empty or the specified package does not exist, then
          the platform will search for an SMS app and use that (if there is one)-->
-    <string name="default_sms_application" translatable="false"></string>
+    <string name="default_sms_application" translatable="false">com.android.messaging</string>
 
     <!-- Default web browser.  This is the package name of the application that will
          be the default browser when the device first boots.  Afterwards the user
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index 7b2bba1..fe4f1be 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -19,7 +19,6 @@
     renderthread/RenderTask.cpp \
     renderthread/RenderThread.cpp \
     renderthread/TimeLord.cpp \
-    renderthread/DirtyHistory.cpp \
     thread/TaskManager.cpp \
     utils/Blur.cpp \
     utils/GLUtils.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 693b28e..f663f07 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -68,8 +68,6 @@
     mRegionMesh = nullptr;
     mProgram = nullptr;
 
-    mFunctorsCount = 0;
-
     patchCache.init();
 
     mInitialized = true;
@@ -276,38 +274,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Tiling
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
-    if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) {
-        glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
-    }
-}
-
-void Caches::endTiling() {
-    if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) {
-        glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
-    }
-}
-
-bool Caches::hasRegisteredFunctors() {
-    return mFunctorsCount > 0;
-}
-
-void Caches::registerFunctors(uint32_t functorCount) {
-    mFunctorsCount += functorCount;
-}
-
-void Caches::unregisterFunctors(uint32_t functorCount) {
-    if (functorCount > mFunctorsCount) {
-        mFunctorsCount = 0;
-    } else {
-        mFunctorsCount -= functorCount;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // Regions
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 049d58b..a02e15d 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -124,10 +124,6 @@
      */
     void deleteLayerDeferred(Layer* layer);
 
-
-    void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard);
-    void endTiling();
-
     /**
      * Returns the mesh used to draw regions. Calling this method will
      * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
@@ -141,10 +137,6 @@
     void dumpMemoryUsage();
     void dumpMemoryUsage(String8& log);
 
-    bool hasRegisteredFunctors();
-    void registerFunctors(uint32_t functorCount);
-    void unregisterFunctors(uint32_t functorCount);
-
     // Misc
     GLint maxTextureSize;
 
@@ -206,8 +198,6 @@
 
     bool mInitialized;
 
-    uint32_t mFunctorsCount;
-
     // TODO: move below to RenderState
     PixelBufferState* mPixelBufferState = nullptr;
     TextureState* mTextureState = nullptr;
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 814bada..3d350c9 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -53,21 +53,10 @@
     mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
     mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
     mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
-    mHasTiledRendering = hasGlExtension("GL_QCOM_tiled_rendering");
     mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
     mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
     mHasUnpackSubImage = hasGlExtension("GL_EXT_unpack_subimage");
 
-    // Query EGL extensions
-    findExtensions(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS), mEglExtensionList);
-
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, nullptr) > 0) {
-        mHasNvSystemTime = !strcmp(property, "true") && hasEglExtension("EGL_NV_system_time");
-    } else {
-        mHasNvSystemTime = false;
-    }
-
     const char* version = (const char*) glGetString(GL_VERSION);
 
     // Section 6.1.5 of the OpenGL ES specification indicates the GL version
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index a4eef0f..636b631 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -40,10 +40,8 @@
     inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
     inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
     inline bool hasDebugMarker() const { return mHasDebugMarker; }
-    inline bool hasTiledRendering() const { return mHasTiledRendering; }
     inline bool has1BitStencil() const { return mHas1BitStencil; }
     inline bool has4BitStencil() const { return mHas4BitStencil; }
-    inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
     inline bool hasUnpackRowLength() const { return mVersionMajor >= 3 || mHasUnpackSubImage; }
     inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
     inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
@@ -67,10 +65,8 @@
     bool mHasFramebufferFetch;
     bool mHasDiscardFramebuffer;
     bool mHasDebugMarker;
-    bool mHasTiledRendering;
     bool mHas1BitStencil;
     bool mHas4BitStencil;
-    bool mHasNvSystemTime;
     bool mHasUnpackSubImage;
 
     int mVersionMajor;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8e04bdf..7a56d42 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -70,8 +70,6 @@
         , mRenderState(renderState)
         , mFrameStarted(false)
         , mScissorOptimizationDisabled(false)
-        , mSuppressTiling(false)
-        , mFirstFrameAfterResize(true)
         , mDirty(false)
         , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
         , mLightRadius(FLT_MIN)
@@ -113,7 +111,6 @@
 void OpenGLRenderer::onViewportInitialized() {
     glDisable(GL_DITHER);
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-    mFirstFrameAfterResize = true;
 }
 
 void OpenGLRenderer::setupFrameState(float left, float top,
@@ -134,15 +131,6 @@
 
     mRenderState.setViewport(mState.getWidth(), mState.getHeight());
 
-    // Functors break the tiling extension in pretty spectacular ways
-    // This ensures we don't use tiling when a functor is going to be
-    // invoked during the frame
-    mSuppressTiling = mCaches.hasRegisteredFunctors()
-            || mFirstFrameAfterResize;
-    mFirstFrameAfterResize = false;
-
-    startTilingCurrentClip(true);
-
     debugOverdraw(true, true);
 
     clear(mTilingClip.left, mTilingClip.top,
@@ -192,46 +180,8 @@
     mRenderState.scissor().reset();
 }
 
-void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
-    if (!mSuppressTiling) {
-        const Snapshot* snapshot = currentSnapshot();
-
-        const Rect* clip = &mTilingClip;
-        if (snapshot->flags & Snapshot::kFlagFboTarget) {
-            clip = &(snapshot->layer->clipRect);
-        }
-
-        startTiling(*clip, getViewportHeight(), opaque, expand);
-    }
-}
-
-void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) {
-    if (!mSuppressTiling) {
-        if(expand) {
-            // Expand the startTiling region by 1
-            int leftNotZero = (clip.left > 0) ? 1 : 0;
-            int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0;
-
-            mCaches.startTiling(
-                clip.left - leftNotZero,
-                windowHeight - clip.bottom - topNotZero,
-                clip.right - clip.left + leftNotZero + 1,
-                clip.bottom - clip.top + topNotZero + 1,
-                opaque);
-        } else {
-            mCaches.startTiling(clip.left, windowHeight - clip.bottom,
-                clip.right - clip.left, clip.bottom - clip.top, opaque);
-        }
-    }
-}
-
-void OpenGLRenderer::endTiling() {
-    if (!mSuppressTiling) mCaches.endTiling();
-}
-
 bool OpenGLRenderer::finish() {
     renderOverdraw();
-    endTiling();
     mTempPaths.clear();
 
     // When finish() is invoked on FBO 0 we've reached the end
@@ -381,7 +331,6 @@
             && layer->renderNode.get() && layer->renderNode->isRenderable()) {
 
         if (inFrame) {
-            endTiling();
             debugOverdraw(false, false);
         }
 
@@ -393,7 +342,6 @@
 
         if (inFrame) {
             resumeAfterLayer();
-            startTilingCurrentClip();
         }
 
         layer->debugDrawUpdate = Properties::debugLayersUpdates;
@@ -736,7 +684,6 @@
     writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
     writableSnapshot()->roundRectClipState = nullptr;
 
-    endTiling();
     debugOverdraw(false, false);
     // Bind texture to FBO
     mRenderState.bindFramebuffer(layer->getFbo());
@@ -751,9 +698,6 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             layer->getTextureId(), 0);
 
-    // Expand the startTiling region by 1
-    startTilingCurrentClip(true, true);
-
     // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
     mRenderState.scissor().setEnabled(true);
     mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
@@ -786,8 +730,6 @@
     mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
 
     if (fboLayer) {
-        endTiling();
-
         // Detach the texture from the FBO
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
 
@@ -796,8 +738,6 @@
         // Unbind current FBO and restore previous one
         mRenderState.bindFramebuffer(restored.fbo);
         debugOverdraw(true, false);
-
-        startTilingCurrentClip();
     }
 
     if (!fboLayer && layer->getAlpha() < 255) {
@@ -1267,17 +1207,10 @@
 void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
     // The layer's FBO is already bound when we reach this stage
     if (!layer->getStencilRenderBuffer()) {
-        // GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
-        // is attached after we initiated tiling. We must turn it off,
-        // attach the new render buffer then turn tiling back on
-        endTiling();
-
         RenderBuffer* buffer = mCaches.renderBufferCache.get(
                 Stencil::getLayerStencilFormat(),
                 layer->getWidth(), layer->getHeight());
         layer->setStencilRenderBuffer(buffer);
-
-        startTiling(layer->clipRect, layer->layer.getHeight());
     }
 }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 390f620..4f75482 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -555,27 +555,6 @@
     void discardFramebuffer(float left, float top, float right, float bottom);
 
     /**
-     * Tells the GPU what part of the screen is about to be redrawn.
-     * This method will use the current layer space clip rect.
-     * This method needs to be invoked every time getTargetFbo() is
-     * bound again.
-     */
-    void startTilingCurrentClip(bool opaque = false, bool expand = false);
-
-    /**
-     * Tells the GPU what part of the screen is about to be redrawn.
-     * This method needs to be invoked every time getTargetFbo() is
-     * bound again.
-     */
-    void startTiling(const Rect& clip, int windowHeight, bool opaque = false, bool expand = false);
-
-    /**
-     * Tells the GPU that we are done drawing the frame or that we
-     * are switching to another render target.
-     */
-    void endTiling();
-
-    /**
      * Sets the clipping rectangle using glScissor. The clip is defined by
      * the current snapshot's clipRect member.
      */
@@ -862,10 +841,6 @@
     // Properties.h
     bool mScissorOptimizationDisabled;
 
-    // No-ops start/endTiling when set
-    bool mSuppressTiling;
-    bool mFirstFrameAfterResize;
-
     bool mSkipOutlineClip;
 
     // True if anything has been drawn since the last call to
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 2e63793..b8f8585 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -30,6 +30,8 @@
 bool Properties::showDirtyRegions = false;
 bool Properties::skipEmptyFrames = true;
 bool Properties::swapBuffersWithDamage = true;
+bool Properties::useBufferAge = true;
+bool Properties::enablePartialUpdates = true;
 
 DebugLevel Properties::debugLevel = kDebugDisabled;
 OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
@@ -105,6 +107,8 @@
 
     skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true);
     swapBuffersWithDamage = property_get_bool(PROPERTY_SWAP_WITH_DAMAGE, true);
+    useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
+    enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
 
     return (prevDebugLayersUpdates != debugLayersUpdates)
             || (prevDebugOverdraw != debugOverdraw)
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1a70d7c..7602848 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -84,12 +84,6 @@
 #define PROPERTY_DEBUG_OVERDRAW "debug.hwui.overdraw"
 
 /**
- * Used to enable/disable PerfHUD ES profiling. The accepted values
- * are "true" and "false". The default value is "false".
- */
-#define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
-
-/**
  *  System property used to enable or disable hardware rendering profiling.
  * The default value of this property is assumed to be false.
  *
@@ -150,9 +144,25 @@
 /**
  * Setting this property will enable or disable usage of EGL_KHR_swap_buffers_with_damage
  * See: https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_swap_buffers_with_damage.txt
+ * Default is "true"
  */
 #define PROPERTY_SWAP_WITH_DAMAGE "debug.hwui.swap_with_damage"
 
+/**
+ * Controls whether or not HWUI will use the EGL_EXT_buffer_age extension
+ * to do partial invalidates. Setting this to "false" will fall back to
+ * using BUFFER_PRESERVED instead
+ * Default is "true"
+ */
+#define PROPERTY_USE_BUFFER_AGE "debug.hwui.use_buffer_age"
+
+/**
+ * Setting this to "false" will force HWUI to always do full-redraws of the surface.
+ * This will disable the use of EGL_EXT_buffer_age and BUFFER_PRESERVED.
+ * Default is "true"
+ */
+#define PROPERTY_ENABLE_PARTIAL_UPDATES "debug.hwui.enable_partial_updates"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Runtime configuration properties
 ///////////////////////////////////////////////////////////////////////////////
@@ -287,6 +297,8 @@
     static bool skipEmptyFrames;
     // TODO: Remove after stabilization period
     static bool swapBuffersWithDamage;
+    static bool useBufferAge;
+    static bool enablePartialUpdates;
 
     static DebugLevel debugLevel;
     static OverdrawColorSet overdrawColorSet;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 6cf894e..fbfed12 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -303,10 +303,6 @@
         // changes in isRenderable or, in the future, bounds
         damageSelf(info);
         deleteDisplayListData();
-        // TODO: Remove this caches stuff
-        if (mStagingDisplayListData && mStagingDisplayListData->functors.size()) {
-            Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
-        }
         mDisplayListData = mStagingDisplayListData;
         mStagingDisplayListData = nullptr;
         if (mDisplayListData) {
@@ -323,9 +319,6 @@
         for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
             mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
         }
-        if (mDisplayListData->functors.size()) {
-            Caches::getInstance().unregisterFunctors(mDisplayListData->functors.size());
-        }
     }
     delete mDisplayListData;
     mDisplayListData = nullptr;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ea73387..67c42f3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -35,6 +35,14 @@
 #define TRIM_MEMORY_COMPLETE 80
 #define TRIM_MEMORY_UI_HIDDEN 20
 
+#define LOG_FRAMETIME_MMA 0
+
+#if LOG_FRAMETIME_MMA
+static float sBenchMma = 0;
+static int sFrameCount = 0;
+static const float NANOS_PER_MILLIS_F = 1000000.0f;
+#endif
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
@@ -93,16 +101,6 @@
     }
 }
 
-void CanvasContext::swapBuffers(const SkRect& dirty, EGLint width, EGLint height) {
-    if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface, dirty, width, height))) {
-        setSurface(nullptr);
-    }
-    mHaveNewSurface = false;
-    if (mEglManager.useBufferAgeExt()) {
-        mDirtyHistory.prepend(Rect(dirty));
-    }
-}
-
 void CanvasContext::requireSurface() {
     LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
             "requireSurface() called but no surface set!");
@@ -230,8 +228,6 @@
             "drawRenderNode called on a context with no canvas or surface!");
 
     SkRect dirty;
-    bool useBufferAgeExt = mEglManager.useBufferAgeExt();
-    Rect patchedDirty;
     mDamageAccumulator.finish(&dirty);
 
     // TODO: Re-enable after figuring out cause of b/22592975
@@ -242,40 +238,59 @@
 
     mCurrentFrameInfo->markIssueDrawCommandsStart();
 
-    EGLint width, height, framebufferAge;
-    mEglManager.beginFrame(mEglSurface, &width, &height, &framebufferAge);
-
-    if (useBufferAgeExt && mHaveNewSurface) {
-        mDirtyHistory.clear();
-    }
-
-    if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
-        mCanvas->setViewport(width, height);
+    Frame frame = mEglManager.beginFrame(mEglSurface);
+    if (frame.width() != mCanvas->getViewportWidth()
+            || frame.height() != mCanvas->getViewportHeight()) {
+        mCanvas->setViewport(frame.width(), frame.height());
         dirty.setEmpty();
-    } else if (!mBufferPreserved || mHaveNewSurface) {
-        mDirtyHistory.clear();
+    } else if (mHaveNewSurface || frame.bufferAge() == 0) {
+        // New surface needs a full draw
         dirty.setEmpty();
     } else {
-        if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
+        if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) {
             ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
-                    SK_RECT_ARGS(dirty), width, height);
+                    SK_RECT_ARGS(dirty), frame.width(), frame.height());
             dirty.setEmpty();
         }
         profiler().unionDirty(&dirty);
     }
 
-    patchedDirty = dirty;
-    if (useBufferAgeExt && !dirty.isEmpty()) {
-        patchedDirty = mDirtyHistory.unionWith(Rect(dirty), framebufferAge-1);
+    if (dirty.isEmpty()) {
+        dirty.set(0, 0, frame.width(), frame.height());
     }
 
-    if (!patchedDirty.isEmpty()) {
-        mCanvas->prepareDirty(patchedDirty.left, patchedDirty.top,
-                patchedDirty.right, patchedDirty.bottom, mOpaque);
-    } else {
-        mCanvas->prepare(mOpaque);
+    // At this point dirty is the area of the screen to update. However,
+    // the area of the frame we need to repaint is potentially different, so
+    // stash the screen area for later
+    SkRect screenDirty(dirty);
+
+    // If the buffer age is 0 we do a full-screen repaint (handled above)
+    // If the buffer age is 1 the buffer contents are the same as they were
+    // last frame so there's nothing to union() against
+    // Therefore we only care about the > 1 case.
+    if (frame.bufferAge() > 1) {
+        if (frame.bufferAge() > (int) mDamageHistory.size()) {
+            // We don't have enough history to handle this old of a buffer
+            // Just do a full-draw
+            dirty.set(0, 0, frame.width(), frame.height());
+        } else {
+            // At this point we haven't yet added the latest frame
+            // to the damage history (happens below)
+            // So we need to damage
+            for (int i = mDamageHistory.size() - 1;
+                    i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) {
+                dirty.join(mDamageHistory[i]);
+            }
+        }
     }
 
+    // Add the screen damage to the ring buffer.
+    mDamageHistory.next() = screenDirty;
+
+    mEglManager.damageFrame(frame, dirty);
+    mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
+            dirty.fRight, dirty.fBottom, mOpaque);
+
     Rect outBounds;
     mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
 
@@ -288,11 +303,30 @@
     mCurrentFrameInfo->markSwapBuffers();
 
     if (drew) {
-        swapBuffers(dirty, width, height);
+        if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
+            setSurface(nullptr);
+        }
+        mHaveNewSurface = false;
     }
 
     // TODO: Use a fence for real completion?
     mCurrentFrameInfo->markFrameCompleted();
+
+#if LOG_FRAMETIME_MMA
+    float thisFrame = mCurrentFrameInfo->duration(
+            FrameInfoIndex::IssueDrawCommandsStart,
+            FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F;
+    if (sFrameCount) {
+        sBenchMma = ((9 * sBenchMma) + thisFrame) / 10;
+    } else {
+        sBenchMma = thisFrame;
+    }
+    if (++sFrameCount == 10) {
+        sFrameCount = 1;
+        ALOGD("Average frame time: %.4f", sBenchMma);
+    }
+#endif
+
     mJankTracker.addFrame(*mCurrentFrameInfo);
     mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
 }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 59f9c3a..0ceb9f1 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,7 +25,6 @@
 #include "utils/RingBuffer.h"
 #include "renderthread/RenderTask.h"
 #include "renderthread/RenderThread.h"
-#include "renderthread/DirtyHistory.h"
 
 #include <cutils/compiler.h>
 #include <EGL/egl.h>
@@ -119,7 +118,6 @@
     friend class android::uirenderer::RenderState;
 
     void setSurface(ANativeWindow* window);
-    void swapBuffers(const SkRect& dirty, EGLint width, EGLint height);
     void requireSurface();
 
     void freePrefetechedLayers();
@@ -130,6 +128,7 @@
     EGLSurface mEglSurface = EGL_NO_SURFACE;
     bool mBufferPreserved = false;
     SwapBehavior mSwapBehavior = kSwap_default;
+    RingBuffer<SkRect, 3> mDamageHistory;
 
     bool mOpaque;
     OpenGLRenderer* mCanvas = nullptr;
@@ -147,8 +146,6 @@
     FrameInfoVisualizer mProfiler;
 
     std::set<RenderNode*> mPrefetechedLayers;
-
-    DirtyHistory mDirtyHistory;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DirtyHistory.cpp b/libs/hwui/renderthread/DirtyHistory.cpp
deleted file mode 100644
index 1419e84..0000000
--- a/libs/hwui/renderthread/DirtyHistory.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "DirtyHistory.h"
-
-namespace android {
-namespace uirenderer {
-namespace renderthread {
-
-DirtyHistory::DirtyHistory()
-        : mBack(DIRTY_HISTORY_SIZE - 1) {
-    clear();
-}
-
-void DirtyHistory::clear()
-{
-    for (int i = 0; i < DIRTY_HISTORY_SIZE; i++) {
-        mHistory[i].clear();
-    }
-}
-
-Rect DirtyHistory::get(int index) {
-    if (index >= DIRTY_HISTORY_SIZE || index < 0)
-        return Rect();
-    return mHistory[(1 + mBack + index) % DIRTY_HISTORY_SIZE];
-}
-
-Rect DirtyHistory::unionWith(Rect rect, int count) {
-    if (rect.isEmpty() || count > DIRTY_HISTORY_SIZE || count < 0)
-        return Rect();
-
-    for (int i = 0; i < count; i++) {
-        Rect ith = get(i);
-        if (ith.isEmpty())
-            return Rect();
-
-        // rect union
-        rect.left = fminf(rect.left, ith.left);
-        rect.top = fminf(rect.top, ith.top);
-        rect.right = fmaxf(rect.right, ith.right);
-        rect.bottom = fmaxf(rect.bottom, ith.bottom);
-    }
-    return rect;
-}
-
-void DirtyHistory::prepend(Rect rect) {
-    if (rect.isEmpty()) {
-        mHistory[mBack].clear();
-    } else {
-        mHistory[mBack].set(rect);
-    }
-    mBack = (mBack + DIRTY_HISTORY_SIZE - 1) % DIRTY_HISTORY_SIZE;
-}
-
-} /* namespace renderthread */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderthread/DirtyHistory.h b/libs/hwui/renderthread/DirtyHistory.h
deleted file mode 100644
index d5ea597..0000000
--- a/libs/hwui/renderthread/DirtyHistory.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef DIRTYHISTORY_H
-#define DIRTYHISTORY_H
-
-#include <Rect.h>
-
-namespace android {
-namespace uirenderer {
-namespace renderthread {
-
-#define DIRTY_HISTORY_SIZE 4
-
-class DirtyHistory {
-public:
-    DirtyHistory();
-    ~DirtyHistory() {}
-
-    Rect get(int index);
-    Rect unionWith(Rect rect, int count);
-    void prepend(Rect rect);
-    void clear();
-private:
-    Rect mHistory[DIRTY_HISTORY_SIZE];
-    int mBack;
-};
-
-} /* namespace renderthread */
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* DIRTYHISTORY_H */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index ac36f53..d2ce49f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -25,7 +25,8 @@
 #include <cutils/properties.h>
 #include <EGL/eglext.h>
 
-#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#include <string>
+
 #define GLES_VERSION 2
 
 #define WAIT_FOR_GPU_COMPLETION 0
@@ -63,10 +64,27 @@
     return egl_error_str(eglGetError());
 }
 
-static bool load_dirty_regions_property() {
-    char buf[PROPERTY_VALUE_MAX];
-    int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
-    return !strncasecmp("true", buf, len);
+static struct {
+    bool bufferAge = false;
+    bool setDamage = false;
+} EglExtensions;
+
+void Frame::map(const SkRect& in, EGLint* out) const {
+    /* The rectangles are specified relative to the bottom-left of the surface
+     * and the x and y components of each rectangle specify the bottom-left
+     * position of that rectangle.
+     *
+     * HWUI does everything with 0,0 being top-left, so need to map
+     * the rect
+     */
+    SkIRect idirty;
+    in.roundOut(&idirty);
+    EGLint y = mHeight - (idirty.y() + idirty.height());
+    // layout: {x, y, width, height}
+    out[0] = idirty.x();
+    out[1] = y;
+    out[2] = idirty.width();
+    out[3] = idirty.height();
 }
 
 EglManager::EglManager(RenderThread& thread)
@@ -75,13 +93,9 @@
         , mEglConfig(nullptr)
         , mEglContext(EGL_NO_CONTEXT)
         , mPBufferSurface(EGL_NO_SURFACE)
-        , mAllowPreserveBuffer(load_dirty_regions_property())
-        , mHasBufferAgeExt(false)
         , mCurrentSurface(EGL_NO_SURFACE)
         , mAtlasMap(nullptr)
         , mAtlasMapSize(0) {
-    mCanSetPreserveBuffer = mAllowPreserveBuffer;
-    ALOGD("Use EGL_SWAP_BEHAVIOR_PRESERVED: %s", mAllowPreserveBuffer ? "true" : "false");
 }
 
 void EglManager::initialize() {
@@ -99,10 +113,18 @@
 
     ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
 
-    findExtensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS), mEglExtensionList);
-    mHasBufferAgeExt = hasEglExtension("EGL_EXT_buffer_age");
+    initExtensions();
 
-    loadConfig(mHasBufferAgeExt);
+    // Now that extensions are loaded, pick a swap behavior
+    if (Properties::enablePartialUpdates) {
+        if (Properties::useBufferAge && EglExtensions.bufferAge) {
+            mSwapBehavior = SwapBehavior::BufferAge;
+        } else {
+            mSwapBehavior = SwapBehavior::Preserved;
+        }
+    }
+
+    loadConfig();
     createContext();
     createPBufferSurface();
     makeCurrent(mPBufferSurface);
@@ -110,17 +132,23 @@
     initAtlas();
 }
 
+void EglManager::initExtensions() {
+    std::string extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+    auto has = [&](const char* ext) {
+        return extensions.find(ext) != std::string::npos;
+    };
+    EglExtensions.bufferAge = has("EGL_EXT_buffer_age");
+    EglExtensions.setDamage = has("EGL_KHR_partial_update");
+}
+
 bool EglManager::hasEglContext() {
     return mEglDisplay != EGL_NO_DISPLAY;
 }
 
-bool EglManager::hasEglExtension(const char* extension) const {
-   const std::string s(extension);
-   return mEglExtensionList.find(s) != mEglExtensionList.end();
-}
-
-void EglManager::loadConfig(bool useBufferAgeExt) {
-    EGLint swapBehavior = (!useBufferAgeExt && mCanSetPreserveBuffer) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+void EglManager::loadConfig() {
+    ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
+    EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved)
+            ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
     EGLint attribs[] = {
             EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
             EGL_RED_SIZE, 8,
@@ -137,13 +165,13 @@
     EGLint num_configs = 1;
     if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
             || num_configs != 1) {
-        // Failed to get a valid config
-        if (mCanSetPreserveBuffer) {
-            ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+        if (mSwapBehavior == SwapBehavior::Preserved) {
             // Try again without dirty regions enabled
-            mCanSetPreserveBuffer = false;
-            loadConfig(useBufferAgeExt);
+            ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+            mSwapBehavior = SwapBehavior::Discard;
+            loadConfig();
         } else {
+            // Failed to get a valid config
             LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
         }
     }
@@ -247,24 +275,47 @@
     return true;
 }
 
-void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height, EGLint* framebufferAge) {
+EGLint EglManager::queryBufferAge(EGLSurface surface) {
+    switch (mSwapBehavior) {
+    case SwapBehavior::Discard:
+        return 0;
+    case SwapBehavior::Preserved:
+        return 1;
+    case SwapBehavior::BufferAge:
+        EGLint bufferAge;
+        eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge);
+        return bufferAge;
+    }
+    return 0;
+}
+
+Frame EglManager::beginFrame(EGLSurface surface) {
     LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
             "Tried to beginFrame on EGL_NO_SURFACE!");
     makeCurrent(surface);
-    if (width) {
-        eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
-    }
-    if (height) {
-        eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
-    }
-    if (useBufferAgeExt()) {
-        eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, framebufferAge);
-    }
+    Frame frame;
+    frame.mSurface = surface;
+    eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
+    eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
+    frame.mBufferAge = queryBufferAge(surface);
     eglBeginFrame(mEglDisplay, surface);
+    return frame;
 }
 
-bool EglManager::swapBuffers(EGLSurface surface, const SkRect& dirty,
-        EGLint width, EGLint height) {
+void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
+#ifdef EGL_KHR_partial_update
+    if (EglExtensions.setDamage && mSwapBehavior == SwapBehavior::BufferAge) {
+        EGLint rects[4];
+        frame.map(dirty, rects);
+        if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
+            LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
+                    (void*)frame.mSurface, egl_error_str());
+        }
+    }
+#endif
+}
+
+bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
 
 #if WAIT_FOR_GPU_COMPLETION
     {
@@ -275,28 +326,15 @@
 
 #ifdef EGL_KHR_swap_buffers_with_damage
     if (CC_LIKELY(Properties::swapBuffersWithDamage)) {
-        SkIRect idirty;
-        dirty.roundOut(&idirty);
-        /*
-         * EGL_KHR_swap_buffers_with_damage spec states:
-         *
-         * The rectangles are specified relative to the bottom-left of the surface
-         * and the x and y components of each rectangle specify the bottom-left
-         * position of that rectangle.
-         *
-         * HWUI does everything with 0,0 being top-left, so need to map
-         * the rect
-         */
-        EGLint y = height - (idirty.y() + idirty.height());
-        // layout: {x, y, width, height}
-        EGLint rects[4] = { idirty.x(), y, idirty.width(), idirty.height() };
-        EGLint numrects = dirty.isEmpty() ? 0 : 1;
-        eglSwapBuffersWithDamageKHR(mEglDisplay, surface, rects, numrects);
+        EGLint rects[4];
+        frame.map(screenDirty, rects);
+        eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
+                screenDirty.isEmpty() ? 0 : 1);
     } else {
-        eglSwapBuffers(mEglDisplay, surface);
+        eglSwapBuffers(mEglDisplay, frame.mSurface);
     }
 #else
-    eglSwapBuffers(mEglDisplay, surface);
+    eglSwapBuffers(mEglDisplay, frame.mSurface);
 #endif
 
     EGLint err = eglGetError();
@@ -307,7 +345,8 @@
         // For some reason our surface was destroyed out from under us
         // This really shouldn't happen, but if it does we can recover easily
         // by just not trying to use the surface anymore
-        ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...", surface);
+        ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...",
+                frame.mSurface);
         return false;
     }
     LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering",
@@ -316,10 +355,6 @@
     return false;
 }
 
-bool EglManager::useBufferAgeExt() {
-    return mAllowPreserveBuffer && mHasBufferAgeExt;
-}
-
 void EglManager::fence() {
     EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
     eglClientWaitSyncKHR(mEglDisplay, fence,
@@ -328,21 +363,13 @@
 }
 
 bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
-    if (CC_UNLIKELY(!mAllowPreserveBuffer)) return false;
+    if (mSwapBehavior != SwapBehavior::Preserved) return false;
 
-    // Use EGL_EXT_buffer_age instead if supported
-    if (mHasBufferAgeExt) return true;
-
-    bool preserved = false;
-    if (mCanSetPreserveBuffer) {
-        preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
-                preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
-        if (CC_UNLIKELY(!preserved)) {
-            ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
-                    (void*) surface, egl_error_str());
-        }
-    }
-    if (CC_UNLIKELY(!preserved)) {
+    bool preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
+            preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
+    if (!preserved) {
+        ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
+                (void*) surface, egl_error_str());
         // Maybe it's already set?
         EGLint swapBehavior;
         if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
@@ -356,19 +383,6 @@
     return preserved;
 }
 
-void EglManager::findExtensions(const char* extensions, std::set<std::string>& list) const {
-    const char* current = extensions;
-    const char* head = current;
-    do {
-        head = strchr(current, ' ');
-        std::string s(current, head ? head - current : strlen(current));
-        if (s.length()) {
-            list.insert(s);
-        }
-        current = head + 1;
-    } while (head);
-}
-
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index bb5d24b..62b5b99 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -21,13 +21,35 @@
 #include <SkRect.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
-#include <set>
 
 namespace android {
 namespace uirenderer {
 namespace renderthread {
 
 class RenderThread;
+class EglManager;
+
+class Frame {
+public:
+    EGLint width() const { return mWidth; }
+    EGLint height() const { return mHeight; }
+
+    // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
+    // for what this means
+    EGLint bufferAge() const { return mBufferAge; }
+
+private:
+    friend class EglManager;
+
+    EGLSurface mSurface;
+    EGLint mWidth;
+    EGLint mHeight;
+    EGLint mBufferAge;
+
+    // Maps from 0,0 in top-left to 0,0 in bottom-left
+    // If out is not an EGLint[4] you're going to have a bad time
+    void map(const SkRect& in, EGLint* out) const;
+};
 
 // This class contains the shared global EGL objects, such as EGLDisplay
 // and EGLConfig, which are re-used by CanvasContext
@@ -38,8 +60,6 @@
 
     bool hasEglContext();
 
-    bool hasEglExtension(const char* extension) const;
-
     EGLSurface createSurface(EGLNativeWindowType window);
     void destroySurface(EGLSurface surface);
 
@@ -48,14 +68,13 @@
     bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
     // Returns true if the current surface changed, false if it was already current
     bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr);
-    void beginFrame(EGLSurface surface, EGLint* width, EGLint* height, EGLint* framebufferAge);
-    bool swapBuffers(EGLSurface surface, const SkRect& dirty, EGLint width, EGLint height);
+    Frame beginFrame(EGLSurface surface);
+    void damageFrame(const Frame& frame, const SkRect& dirty);
+    bool swapBuffers(const Frame& frame, const SkRect& screenDirty);
 
     // Returns true iff the surface is now preserving buffers.
     bool setPreserveBuffer(EGLSurface surface, bool preserve);
 
-    bool useBufferAgeExt();
-
     void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
 
     void fence();
@@ -67,12 +86,12 @@
     // EglContext is never destroyed, method is purposely not implemented
     ~EglManager();
 
+    void initExtensions();
     void createPBufferSurface();
-    void loadConfig(bool useBufferAgeExt);
+    void loadConfig();
     void createContext();
     void initAtlas();
-
-    void findExtensions(const char* extensions, std::set<std::string>& list) const;
+    EGLint queryBufferAge(EGLSurface surface);
 
     RenderThread& mRenderThread;
 
@@ -81,18 +100,18 @@
     EGLContext mEglContext;
     EGLSurface mPBufferSurface;
 
-    const bool mAllowPreserveBuffer;
-    bool mCanSetPreserveBuffer;
-
-    bool mHasBufferAgeExt;
-
     EGLSurface mCurrentSurface;
 
     sp<GraphicBuffer> mAtlasBuffer;
     int64_t* mAtlasMap;
     size_t mAtlasMapSize;
 
-    std::set<std::string> mEglExtensionList;
+    enum class SwapBehavior {
+        Discard,
+        Preserved,
+        BufferAge,
+    };
+    SwapBehavior mSwapBehavior = SwapBehavior::Discard;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 69e8225..483fb35 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -283,6 +283,69 @@
     }
 };
 
+class PartialInvalTest : public TreeContentAnimation {
+public:
+    std::vector< sp<RenderNode> > cards;
+    void createContent(int width, int height, DisplayListCanvas* renderer) override {
+        static SkColor COLORS[] = {
+                0xFFF44336,
+                0xFF9C27B0,
+                0xFF2196F3,
+                0xFF4CAF50,
+        };
+
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+        for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+            for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+                sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
+                        COLORS[static_cast<int>((y / dp(116))) % 4]);
+                renderer->drawRenderNode(card.get());
+                cards.push_back(card);
+            }
+        }
+    }
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+        cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+        cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+        DisplayListCanvas* renderer = startRecording(cards[0].get());
+        renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+                SkXfermode::kSrcOver_Mode);
+        endRecording(renderer, cards[0].get());
+    }
+
+    static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
+        int startA = (start >> 24) & 0xff;
+        int startR = (start >> 16) & 0xff;
+        int startG = (start >> 8) & 0xff;
+        int startB = start & 0xff;
+
+        int endA = (end >> 24) & 0xff;
+        int endR = (end >> 16) & 0xff;
+        int endG = (end >> 8) & 0xff;
+        int endB = end & 0xff;
+
+        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+                (int)((startB + (int)(fraction * (endB - startB))));
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+        DisplayListCanvas* renderer = startRecording(node.get());
+        renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+
 struct cstr_cmp {
     bool operator()(const char *a, const char *b) const {
         return std::strcmp(a, b) < 0;
@@ -296,6 +359,7 @@
     {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
     {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
     {"oval", TreeContentAnimation::run<OvalAnimation> },
+    {"partialinval", TreeContentAnimation::run<PartialInvalTest> },
 };
 
 int main(int argc, char* argv[]) {
diff --git a/libs/hwui/tests/nullgles.cpp b/libs/hwui/tests/nullgles.cpp
index 8ca7598..f8e8c98 100644
--- a/libs/hwui/tests/nullgles.cpp
+++ b/libs/hwui/tests/nullgles.cpp
@@ -261,8 +261,6 @@
 void glPushGroupMarkerEXT(GLsizei length, const GLchar *marker) {}
 void glPopGroupMarkerEXT(void) {}
 void glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments) {}
-void glStartTilingQCOM(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) {}
-void glEndTilingQCOM(GLbitfield preserveMask) {}
 void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) {}
 
 // GLES3
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 41b1059..d1e84f5 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -316,6 +316,15 @@
     <!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
     <string name="kg_prompt_reason_restart_password">Password required when you restart device.</string>
 
+    <!-- An explanation text that the pattern needs to be solved since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_timeout_pattern">Pattern required for additional security.</string>
+
+    <!-- An explanation text that the pin needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_timeout_pin">PIN required for additional security.</string>
+
+    <!-- An explanation text that the password needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_timeout_password">Password required for additional security.</string>
+
     <!-- An explanation text that the pattern needs to be solved since profiles have just been switched. [CHAR LIMIT=80] -->
     <string name="kg_prompt_reason_switch_profiles_pattern">Pattern required when you switch profiles.</string>
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 32892cf..ec2a173 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -163,8 +163,9 @@
      * Show a string explaining why the security view needs to be solved.
      *
      * @param reason a flag indicating which string should be shown, see
-     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE}
-     *               and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
+     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
+     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
      */
     public void showPromptReason(int reason) {
         mSecurityContainer.showPromptReason(reason);
@@ -213,9 +214,12 @@
     /**
      * Authentication has happened and it's time to dismiss keyguard. This function
      * should clean up and inform KeyguardViewMediator.
+     *
+     * @param strongAuth whether the user has authenticated with strong authentication like
+     *                   pattern, password or PIN but not by trust agents or fingerprint
      */
     @Override
-    public void finish() {
+    public void finish(boolean strongAuth) {
         // If there's a pending runnable because the user interacted with a widget
         // and we're leaving keyguard, then run it.
         boolean deferKeyguardDone = false;
@@ -226,9 +230,9 @@
         }
         if (mViewMediatorCallback != null) {
             if (deferKeyguardDone) {
-                mViewMediatorCallback.keyguardDonePending();
+                mViewMediatorCallback.keyguardDonePending(strongAuth);
             } else {
-                mViewMediatorCallback.keyguardDone(true);
+                mViewMediatorCallback.keyguardDone(strongAuth);
             }
         }
     }
@@ -285,32 +289,6 @@
     }
 
     /**
-     * Verify that the user can get past the keyguard securely.  This is called,
-     * for example, when the phone disables the keyguard but then wants to launch
-     * something else that requires secure access.
-     *
-     * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
-     */
-    public void verifyUnlock() {
-        SecurityMode securityMode = mSecurityContainer.getSecurityMode();
-        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
-            if (mViewMediatorCallback != null) {
-                mViewMediatorCallback.keyguardDone(true);
-            }
-        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
-                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
-                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
-            // can only verify unlock when in pattern/password mode
-            if (mViewMediatorCallback != null) {
-                mViewMediatorCallback.keyguardDone(false);
-            }
-        } else {
-            // otherwise, go to the unlock screen, see if they can verify it
-            mSecurityContainer.verifyUnlock();
-        }
-    }
-
-    /**
      * Called before this view is being removed.
      */
     public void cleanUp() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 2db87b3..3a7e6d0 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -115,6 +115,8 @@
         switch (reason) {
             case PROMPT_REASON_RESTART:
                 return R.string.kg_prompt_reason_restart_password;
+            case PROMPT_REASON_TIMEOUT:
+                return R.string.kg_prompt_reason_timeout_password;
             default:
                 return 0;
         }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index a96c79f..9a91ca4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -332,7 +332,12 @@
                 mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern,
                         true /* important */);
                 break;
+            case PROMPT_REASON_TIMEOUT:
+                mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern,
+                        true /* important */);
+                break;
             default:
+                break;
         }
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 07947b1..4cd4845 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -98,6 +98,8 @@
         switch (reason) {
             case PROMPT_REASON_RESTART:
                 return R.string.kg_prompt_reason_restart_pin;
+            case PROMPT_REASON_TIMEOUT:
+                return R.string.kg_prompt_reason_timeout_pin;
             default:
                 return 0;
         }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 8fc3cde..77215a7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -54,7 +54,12 @@
         public boolean dismiss(boolean authenticated);
         public void userActivity();
         public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
-        public void finish();
+
+        /**
+         * @param strongAuth wheher the user has authenticated with strong authentication like
+         *                   pattern, password or PIN but not by trust agents or fingerprint
+         */
+        public void finish(boolean strongAuth);
         public void reset();
     }
 
@@ -282,7 +287,7 @@
                 showWipeDialog(failedAttempts, userType);
             }
         }
-        monitor.reportFailedUnlockAttempt();
+        monitor.reportFailedStrongAuthUnlockAttempt();
         mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser());
         if (timeoutMs > 0) {
             showTimeoutDialog(timeoutMs);
@@ -308,6 +313,7 @@
     boolean showNextSecurityScreenOrFinish(boolean authenticated) {
         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
         boolean finish = false;
+        boolean strongAuth = false;
         if (mUpdateMonitor.getUserCanSkipBouncer(
                 KeyguardUpdateMonitor.getCurrentUser())) {
             finish = true;
@@ -323,6 +329,7 @@
                 case Pattern:
                 case Password:
                 case PIN:
+                    strongAuth = true;
                     finish = true;
                     break;
 
@@ -346,7 +353,7 @@
             }
         }
         if (finish) {
-            mSecurityCallback.finish();
+            mSecurityCallback.finish(strongAuth);
         }
         return finish;
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index 7e82c63..38302fb 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -22,9 +22,18 @@
     static public final int VIEW_REVEALED = 2;
 
     int PROMPT_REASON_NONE = 0;
+
+    /**
+     * Strong auth is required because the device has just booted.
+     */
     int PROMPT_REASON_RESTART = 1;
 
     /**
+     * Strong auth is required because the user hasn't used strong auth since a while.
+     */
+    int PROMPT_REASON_TIMEOUT = 2;
+
+    /**
      * Interface back to keyguard to tell it when security
      * @param callback
      */
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0ee68fd..3232f65 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -64,6 +64,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -103,6 +104,12 @@
             = "com.android.facelock.FACE_UNLOCK_STOPPED";
     private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
 
+    private static final String ACTION_STRONG_AUTH_TIMEOUT =
+            "com.android.systemui.ACTION_STRONG_AUTH_TIMEOUT";
+    private static final String USER_ID = "com.android.systemui.USER_ID";
+
+    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
     /**
      * Mode in which we don't need to wake up the device when we get a fingerprint.
      */
@@ -126,6 +133,12 @@
      * */
     private static final int FP_ONLY_WAKE = 3;
 
+    /**
+     * Milliseconds after unlocking with fingerprint times out, i.e. the user has to use a
+     * strong auth method like password, PIN or pattern.
+     */
+    private static final long FINGERPRINT_UNLOCK_TIMEOUT_MS = 72 * 60 * 60 * 1000;
+
     // Callback messages
     private static final int MSG_TIME_UPDATE = 301;
     private static final int MSG_BATTERY_UPDATE = 302;
@@ -173,7 +186,8 @@
     // Password attempts
     private SparseIntArray mFailedAttempts = new SparseIntArray();
 
-    private boolean mClockVisible;
+    /** Tracks whether strong authentication hasn't been used since quite some time per user. */
+    private ArraySet<Integer> mStrongAuthTimedOut = new ArraySet<>();
 
     private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
             mCallbacks = Lists.newArrayList();
@@ -184,6 +198,7 @@
     private boolean mDeviceInteractive;
     private boolean mScreenOn;
     private SubscriptionManager mSubscriptionManager;
+    private AlarmManager mAlarmManager;
     private List<SubscriptionInfo> mSubscriptionInfo;
     private boolean mFingerprintDetectionRunning;
     private TrustManager mTrustManager;
@@ -549,7 +564,39 @@
     }
 
     public boolean isUnlockingWithFingerprintAllowed() {
-        return mUserHasAuthenticatedSinceBoot;
+        return mUserHasAuthenticatedSinceBoot && !hasFingerprintUnlockTimedOut(sCurrentUser);
+    }
+
+    /**
+     * @return true if the user hasn't use strong authentication (pattern, PIN, password) since a
+     *         while and thus can't unlock with fingerprint, false otherwise
+     */
+    public boolean hasFingerprintUnlockTimedOut(int userId) {
+        return mStrongAuthTimedOut.contains(userId);
+    }
+
+    public void reportSuccessfulStrongAuthUnlockAttempt() {
+        mStrongAuthTimedOut.remove(sCurrentUser);
+        scheduleStrongAuthTimeout();
+    }
+
+    private void scheduleStrongAuthTimeout() {
+        long when = SystemClock.elapsedRealtime() + FINGERPRINT_UNLOCK_TIMEOUT_MS;
+        Intent intent = new Intent(ACTION_STRONG_AUTH_TIMEOUT);
+        intent.putExtra(USER_ID, sCurrentUser);
+        PendingIntent sender = PendingIntent.getBroadcast(mContext,
+                sCurrentUser, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, sender);
+        notifyStrongAuthTimedOutChanged(sCurrentUser);
+    }
+
+    private void notifyStrongAuthTimedOutChanged(int userId) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onStrongAuthTimeoutExpiredChanged(userId);
+            }
+        }
     }
 
     static class DisplayClientState {
@@ -639,6 +686,17 @@
         }
     };
 
+    private final BroadcastReceiver mStrongAuthTimeoutReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_STRONG_AUTH_TIMEOUT.equals(intent.getAction())) {
+                int userId = intent.getIntExtra(USER_ID, -1);
+                mStrongAuthTimedOut.add(userId);
+                notifyStrongAuthTimedOutChanged(userId);
+            }
+        }
+    };
+
     private FingerprintManager.AuthenticationCallback mAuthenticationCallback
             = new AuthenticationCallback() {
 
@@ -871,7 +929,9 @@
         mContext = context;
         mSubscriptionManager = SubscriptionManager.from(context);
         mPowerManager = context.getSystemService(PowerManager.class);
+        mAlarmManager = context.getSystemService(AlarmManager.class);
         mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
+
         // Since device can't be un-provisioned, we only need to register a content observer
         // to update mDeviceProvisioned when we are...
         if (!mDeviceProvisioned) {
@@ -932,6 +992,10 @@
             e.printStackTrace();
         }
 
+        IntentFilter strongAuthTimeoutFilter = new IntentFilter();
+        strongAuthTimeoutFilter.addAction(ACTION_STRONG_AUTH_TIMEOUT);
+        context.registerReceiver(mStrongAuthTimeoutReceiver, strongAuthTimeoutFilter,
+                PERMISSION_SELF, null /* handler */);
         mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
         mTrustManager.registerTrustListener(this);
 
@@ -955,7 +1019,7 @@
     private void startListeningForFingerprint() {
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
         int userId = ActivityManager.getCurrentUser();
-        if (isUnlockWithFingerPrintPossible(userId)) {
+        if (isUnlockWithFingerprintPossible(userId)) {
             mUserHasAuthenticatedSinceBoot = mTrustManager.hasUserAuthenticatedSinceBoot(
                     ActivityManager.getCurrentUser());
             if (mFingerprintCancelSignal != null) {
@@ -967,7 +1031,7 @@
         }
     }
 
-    public boolean isUnlockWithFingerPrintPossible(int userId) {
+    public boolean isUnlockWithFingerprintPossible(int userId) {
         return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
                 && mFpm.getEnrolledFingerprints(userId).size() > 0;
     }
@@ -1433,7 +1497,7 @@
         return mFailedAttempts.get(sCurrentUser, 0);
     }
 
-    public void reportFailedUnlockAttempt() {
+    public void reportFailedStrongAuthUnlockAttempt() {
         mFailedAttempts.put(sCurrentUser, getFailedUnlockAttempts() + 1);
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 6cda2b7..52412f7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -217,4 +217,10 @@
      * Called when the fingerprint running state changed.
      */
     public void onFingerprintRunningStateChanged(boolean running) { }
+
+    /**
+     * Called when the state that the user hasn't used strong authentication since quite some time
+     * has changed.
+     */
+    public void onStrongAuthTimeoutExpiredChanged(int userId) { }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
index ff463c6..8ab3011 100644
--- a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
@@ -28,12 +28,11 @@
 
     /**
      * Report that the keyguard is done.
-     * @param authenticated Whether the user securely got past the keyguard.
-     *   the only reason for this to be false is if the keyguard was instructed
-     *   to appear temporarily to verify the user is supposed to get past the
-     *   keyguard, and the user fails to do so.
+     *
+     * @param strongAuth whether the user has authenticated with strong authentication like
+     *                   pattern, password or PIN but not by trust agents or fingerprint
      */
-    void keyguardDone(boolean authenticated);
+    void keyguardDone(boolean strongAuth);
 
     /**
      * Report that the keyguard is done drawing.
@@ -48,8 +47,11 @@
 
     /**
      * Report that the keyguard is dismissable, pending the next keyguardDone call.
+     *
+     * @param strongAuth whether the user has authenticated with strong authentication like
+     *                   pattern, password or PIN but not by trust agents or fingerprint
      */
-    void keyguardDonePending();
+    void keyguardDonePending(boolean strongAuth);
 
     /**
      * Report when keyguard is actually gone
@@ -85,8 +87,9 @@
     /**
      * @return one of the reasons why the bouncer needs to be shown right now and the user can't use
      *         his normal unlock method like fingerprint or trust agents. See
-     *         {@link KeyguardSecurityView#PROMPT_REASON_NONE}
-     *         and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}.
+     *         {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+     *         {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
+     *         {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
      */
     int getBouncerPromptReason();
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
new file mode 100644
index 0000000..2353205
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+
+class DocumentLoader {
+    static final int NUM_INITIAL_ENTRIES = 10;
+    static final int NUM_LOADING_ENTRIES = 20;
+    static final int NOTIFY_PERIOD_MS = 500;
+
+    private final MtpManager mMtpManager;
+    private final ContentResolver mResolver;
+    private final LinkedList<LoaderTask> mTasks = new LinkedList<LoaderTask>();
+    private boolean mHasBackgroundThread = false;
+
+    DocumentLoader(MtpManager mtpManager, ContentResolver resolver) {
+        mMtpManager = mtpManager;
+        mResolver = resolver;
+    }
+
+    private static MtpDocument[] loadDocuments(MtpManager manager, int deviceId, int[] handles)
+            throws IOException {
+        final MtpDocument[] documents = new MtpDocument[handles.length];
+        for (int i = 0; i < handles.length; i++) {
+            documents[i] = manager.getDocument(deviceId, handles[i]);
+        }
+        return documents;
+    }
+
+    synchronized Cursor queryChildDocuments(String[] columnNames, Identifier parent)
+            throws IOException {
+        LoaderTask task = findTask(parent);
+        if (task == null) {
+            int parentHandle = parent.mObjectHandle;
+            // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
+            // getObjectHandles if we would like to obtain children under the root.
+            if (parentHandle == MtpDocument.DUMMY_HANDLE_FOR_ROOT) {
+                parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
+            }
+            task = new LoaderTask(parent, mMtpManager.getObjectHandles(
+                    parent.mDeviceId, parent.mStorageId, parentHandle));
+            task.fillDocuments(loadDocuments(
+                    mMtpManager,
+                    parent.mDeviceId,
+                    task.getUnloadedObjectHandles(NUM_INITIAL_ENTRIES)));
+        }
+
+        // Move this task to the head of the list to prioritize it.
+        mTasks.remove(task);
+        mTasks.addFirst(task);
+        if (!task.completed() && !mHasBackgroundThread) {
+            mHasBackgroundThread = true;
+            new BackgroundLoaderThread().start();
+        }
+
+        return task.createCursor(mResolver, columnNames);
+    }
+
+    synchronized void clearCache(int deviceId) {
+        int i = 0;
+        while (i < mTasks.size()) {
+            if (mTasks.get(i).mIdentifier.mDeviceId == deviceId) {
+                mTasks.remove(i);
+            } else {
+                i++;
+            }
+        }
+    }
+
+    synchronized void clearCache() {
+        int i = 0;
+        while (i < mTasks.size()) {
+            if (mTasks.get(i).completed()) {
+                mTasks.remove(i);
+            } else {
+                i++;
+            }
+        }
+    }
+
+    private LoaderTask findTask(Identifier parent) {
+        for (int i = 0; i < mTasks.size(); i++) {
+            if (mTasks.get(i).mIdentifier.equals(parent))
+                return mTasks.get(i);
+        }
+        return null;
+    }
+
+    private LoaderTask findUncompletedTask() {
+        for (int i = 0; i < mTasks.size(); i++) {
+            if (!mTasks.get(i).completed())
+                return mTasks.get(i);
+        }
+        return null;
+    }
+
+    private class BackgroundLoaderThread extends Thread {
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            while (true) {
+                LoaderTask task;
+                int deviceId;
+                int[] handles;
+                synchronized (DocumentLoader.this) {
+                    task = findUncompletedTask();
+                    if (task == null) {
+                        mHasBackgroundThread = false;
+                        return;
+                    }
+                    deviceId = task.mIdentifier.mDeviceId;
+                    handles = task.getUnloadedObjectHandles(NUM_LOADING_ENTRIES);
+                }
+                MtpDocument[] documents;
+                try {
+                    documents = loadDocuments(mMtpManager, deviceId, handles);
+                } catch (IOException exception) {
+                    documents = null;
+                    Log.d(MtpDocumentsProvider.TAG, exception.getMessage());
+                }
+                synchronized (DocumentLoader.this) {
+                    if (documents != null) {
+                        task.fillDocuments(documents);
+                        final boolean shouldNotify =
+                                task.mLastNotified.getTime() <
+                                new Date().getTime() - NOTIFY_PERIOD_MS ||
+                                task.completed();
+                        if (shouldNotify) {
+                            task.notify(mResolver);
+                        }
+                    } else {
+                        mTasks.remove(task);
+                    }
+                }
+            }
+        }
+    }
+
+    private static class LoaderTask {
+        final Identifier mIdentifier;
+        final int[] mObjectHandles;
+        final MtpDocument[] mDocuments;
+        Date mLastNotified;
+        int mNumLoaded;
+
+        LoaderTask(Identifier identifier, int[] objectHandles) {
+            mIdentifier = identifier;
+            mObjectHandles = objectHandles;
+            mDocuments = new MtpDocument[mObjectHandles.length];
+            mNumLoaded = 0;
+            mLastNotified = new Date();
+        }
+
+        Cursor createCursor(ContentResolver resolver, String[] columnNames) {
+            final MatrixCursor cursor = new MatrixCursor(columnNames);
+            final Identifier rootIdentifier = new Identifier(
+                    mIdentifier.mDeviceId, mIdentifier.mStorageId);
+            for (int i = 0; i < mNumLoaded; i++) {
+                mDocuments[i].addToCursor(rootIdentifier, cursor.newRow());
+            }
+            final Bundle extras = new Bundle();
+            extras.putBoolean(DocumentsContract.EXTRA_LOADING, !completed());
+            cursor.setNotificationUri(resolver, createUri());
+            cursor.respond(extras);
+            return cursor;
+        }
+
+        boolean completed() {
+            return mNumLoaded == mDocuments.length;
+        }
+
+        int[] getUnloadedObjectHandles(int count) {
+            return Arrays.copyOfRange(
+                    mObjectHandles,
+                    mNumLoaded,
+                    Math.min(mNumLoaded + count, mObjectHandles.length));
+        }
+
+        void notify(ContentResolver resolver) {
+            resolver.notifyChange(createUri(), null, false);
+            mLastNotified = new Date();
+        }
+
+        void fillDocuments(MtpDocument[] documents) {
+            for (int i = 0; i < documents.length; i++) {
+                mDocuments[mNumLoaded++] = documents[i];
+            }
+        }
+
+        private Uri createUri() {
+            return DocumentsContract.buildChildDocumentsUri(
+                    MtpDocumentsProvider.AUTHORITY, mIdentifier.toDocumentId());
+        }
+    }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 32882c8..bcdfb71 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -40,12 +40,12 @@
 public class MtpDocumentsProvider extends DocumentsProvider {
     static final String AUTHORITY = "com.android.mtp.documents";
     static final String TAG = "MtpDocumentsProvider";
-    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
+    static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
             Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
             Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
             Root.COLUMN_AVAILABLE_BYTES,
     };
-    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+    static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
             Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
             Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
             Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
@@ -56,6 +56,7 @@
     private MtpManager mMtpManager;
     private ContentResolver mResolver;
     private PipeManager mPipeManager;
+    private DocumentLoader mDocumentLoader;
 
     /**
      * Provides singleton instance to MtpDocumentsService.
@@ -70,14 +71,15 @@
         mMtpManager = new MtpManager(getContext());
         mResolver = getContext().getContentResolver();
         mPipeManager = new PipeManager();
-
+        mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
         return true;
     }
 
     @VisibleForTesting
     void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) {
-        this.mMtpManager = mtpManager;
-        this.mResolver = resolver;
+        mMtpManager = mtpManager;
+        mResolver = resolver;
+        mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
     }
 
     @Override
@@ -152,7 +154,6 @@
         return cursor;
     }
 
-    // TODO: Support background loading for large number of files.
     @Override
     public Cursor queryChildDocuments(String parentDocumentId,
             String[] projection, String sortOrder) throws FileNotFoundException {
@@ -160,29 +161,8 @@
             projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
         }
         final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId);
-        int parentHandle = parentIdentifier.mObjectHandle;
-        // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
-        // getObjectHandles if we would like to obtain children under the root.
-        if (parentHandle == MtpDocument.DUMMY_HANDLE_FOR_ROOT) {
-            parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
-        }
         try {
-            final MatrixCursor cursor = new MatrixCursor(projection);
-            final Identifier rootIdentifier = new Identifier(
-                    parentIdentifier.mDeviceId, parentIdentifier.mStorageId);
-            final int[] objectHandles = mMtpManager.getObjectHandles(
-                    parentIdentifier.mDeviceId, parentIdentifier.mStorageId, parentHandle);
-            for (int i = 0; i < objectHandles.length; i++) {
-                try {
-                    final MtpDocument document = mMtpManager.getDocument(
-                            parentIdentifier.mDeviceId,  objectHandles[i]);
-                    document.addToCursor(rootIdentifier, cursor.newRow());
-                } catch (IOException error) {
-                    cursor.close();
-                    throw new FileNotFoundException(error.getMessage());
-                }
-            }
-            return cursor;
+            return mDocumentLoader.queryChildDocuments(projection, parentIdentifier);
         } catch (IOException exception) {
             throw new FileNotFoundException(exception.getMessage());
         }
@@ -234,6 +214,11 @@
         }
     }
 
+    @Override
+    public void onTrimMemory(int level) {
+        mDocumentLoader.clearCache();
+    }
+
     void openDevice(int deviceId) throws IOException {
         mMtpManager.openDevice(deviceId);
         notifyRootsChange();
@@ -241,6 +226,7 @@
 
     void closeDevice(int deviceId) throws IOException {
         mMtpManager.closeDevice(deviceId);
+        mDocumentLoader.clearCache(deviceId);
         notifyRootsChange();
     }
 
@@ -249,6 +235,7 @@
         for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
             try {
                 mMtpManager.closeDevice(deviceId);
+                mDocumentLoader.clearCache(deviceId);
                 closed = true;
             } catch (IOException d) {
                 Log.d(TAG, "Failed to close the MTP device: " + deviceId);
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
new file mode 100644
index 0000000..49fcddd
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.DocumentsContract;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+public class DocumentLoaderTest extends AndroidTestCase {
+    private BlockableTestMtpMaanger mManager;
+    private TestContentResolver mResolver;
+    private DocumentLoader mLoader;
+    final private Identifier mParentIdentifier = new Identifier(0, 0, 0);
+
+    @Override
+    public void setUp() {
+        mManager = new BlockableTestMtpMaanger(getContext());
+        mResolver = new TestContentResolver();
+        mLoader = new DocumentLoader(mManager, mResolver);
+    }
+
+    public void testBasic() throws IOException, InterruptedException {
+        final Uri uri = DocumentsContract.buildChildDocumentsUri(
+                MtpDocumentsProvider.AUTHORITY, mParentIdentifier.toDocumentId());
+        setUpDocument(mManager, 40);
+        mManager.blockDocument(0, 15);
+        mManager.blockDocument(0, 35);
+
+        {
+            final Cursor cursor = mLoader.queryChildDocuments(
+                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
+            assertEquals(DocumentLoader.NUM_INITIAL_ENTRIES, cursor.getCount());
+        }
+
+        Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
+        mManager.unblockDocument(0, 15);
+        mResolver.waitForNotification(uri, 1);
+
+        {
+            final Cursor cursor = mLoader.queryChildDocuments(
+                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
+            assertEquals(
+                    DocumentLoader.NUM_INITIAL_ENTRIES + DocumentLoader.NUM_LOADING_ENTRIES,
+                    cursor.getCount());
+        }
+
+        mManager.unblockDocument(0, 35);
+        mResolver.waitForNotification(uri, 2);
+
+        {
+            final Cursor cursor = mLoader.queryChildDocuments(
+                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
+            assertEquals(40, cursor.getCount());
+        }
+
+        assertEquals(2, mResolver.getChangeCount(uri));
+    }
+
+    private void setUpDocument(TestMtpManager manager, int count) {
+        int[] childDocuments = new int[count];
+        for (int i = 0; i < childDocuments.length; i++) {
+            final int objectHandle = i + 1;
+            childDocuments[i] = objectHandle;
+            manager.setDocument(0, objectHandle, new MtpDocument(
+                    objectHandle,
+                    0 /* format */,
+                    "file" + objectHandle,
+                    new Date(),
+                    1024,
+                    0 /* thumbnail size */));
+        }
+        manager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, childDocuments);
+    }
+
+    private static class BlockableTestMtpMaanger extends TestMtpManager {
+        final private Map<String, CountDownLatch> blockedDocuments = new HashMap<>();
+
+        BlockableTestMtpMaanger(Context context) {
+            super(context);
+        }
+
+        void blockDocument(int deviceId, int objectHandle) {
+            blockedDocuments.put(pack(deviceId, objectHandle), new CountDownLatch(1));
+        }
+
+        void unblockDocument(int deviceId, int objectHandle) {
+            blockedDocuments.get(pack(deviceId, objectHandle)).countDown();
+        }
+
+        @Override
+        MtpDocument getDocument(int deviceId, int objectHandle) throws IOException {
+            final CountDownLatch latch = blockedDocuments.get(pack(deviceId, objectHandle));
+            if (latch != null) {
+                try {
+                    latch.await();
+                } catch(InterruptedException e) {
+                    fail();
+                }
+            }
+            return super.getDocument(deviceId, objectHandle);
+        }
+    }
+}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 1031d8a..e0e3ce6 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -16,30 +16,26 @@
 
 package com.android.mtp;
 
-import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Root;
 import android.test.AndroidTestCase;
-import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
 
 @SmallTest
 public class MtpDocumentsProviderTest extends AndroidTestCase {
-    private ContentResolver mResolver;
+    private TestContentResolver mResolver;
     private MtpDocumentsProvider mProvider;
     private TestMtpManager mMtpManager;
 
     @Override
     public void setUp() {
-        mResolver = new ContentResolver();
+        mResolver = new TestContentResolver();
         mMtpManager = new TestMtpManager(getContext());
         mProvider = new MtpDocumentsProvider();
         mProvider.onCreateForTesting(mMtpManager, mResolver);
@@ -291,21 +287,4 @@
                 DocumentsContract.buildChildDocumentsUri(
                         MtpDocumentsProvider.AUTHORITY, "0_0_2")));
     }
-
-    private static class ContentResolver extends MockContentResolver {
-        final Map<Uri, Integer> mChangeCounts = new HashMap<Uri, Integer>();
-
-        @Override
-        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
-            mChangeCounts.put(uri, getChangeCount(uri) + 1);
-        }
-
-        int getChangeCount(Uri uri) {
-            if (mChangeCounts.containsKey(uri)) {
-                return mChangeCounts.get(uri);
-            } else {
-                return 0;
-            }
-        }
-    }
 }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestContentResolver.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestContentResolver.java
new file mode 100644
index 0000000..33e559f
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestContentResolver.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.test.mock.MockContentResolver;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Phaser;
+
+class TestContentResolver extends MockContentResolver {
+    final private Map<Uri, Phaser> mPhasers = new HashMap<>();
+
+    @Override
+    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+        getPhaser(uri).arrive();
+    }
+
+    void waitForNotification(Uri uri, int count) {
+        Assert.assertEquals(count, getPhaser(uri).awaitAdvance(count - 1));
+    }
+
+    int getChangeCount(Uri uri) {
+        if (mPhasers.containsKey(uri)) {
+            return mPhasers.get(uri).getPhase();
+        } else {
+            return 0;
+        }
+    }
+
+    private synchronized Phaser getPhaser(Uri uri) {
+        Phaser phaser = mPhasers.get(uri);
+        if (phaser == null) {
+            phaser = new Phaser(1);
+            mPhasers.put(uri, phaser);
+        }
+        return phaser;
+    }
+}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 7f81686..40de7b4 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -28,7 +28,7 @@
 import java.util.TreeSet;
 
 public class TestMtpManager extends MtpManager {
-    private static String pack(int... args) {
+    protected static String pack(int... args) {
         return Arrays.toString(args);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index d78800f..d2c60ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -80,7 +80,8 @@
         @Override // Binder interface
         public void keyguardDone(boolean authenticated, boolean wakeup) {
             checkPermission();
-            mKeyguardViewMediator.keyguardDone(authenticated, wakeup);
+            // TODO: Remove wakeup
+            mKeyguardViewMediator.keyguardDone(authenticated);
         }
 
         @Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e44d109..428ceca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -465,14 +465,15 @@
                     mUpdateMonitor.isUnlockingWithFingerprintAllowed();
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 if (unlockingWithFingerprintAllowed) {
-                    mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated();
+                    mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
+                            false /* strongAuth */);
                 }
             } else {
                 if (wakeAndUnlocking && mShowing && unlockingWithFingerprintAllowed) {
                     mWakeAndUnlocking = true;
                     mStatusBarKeyguardViewManager.setWakeAndUnlocking();
-                    keyguardDone(true, true);
-                } else if (mShowing && mDeviceInteractive) {
+                    keyguardDone(true);
+                } else if (mShowing) {
                     if (wakeAndUnlocking) {
                         mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
                     }
@@ -490,9 +491,12 @@
             KeyguardViewMediator.this.userActivity();
         }
 
-        public void keyguardDone(boolean authenticated) {
+        public void keyguardDone(boolean strongAuth) {
             if (!mKeyguardDonePending) {
-                KeyguardViewMediator.this.keyguardDone(authenticated, true);
+                KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
+            }
+            if (strongAuth) {
+                mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
             }
         }
 
@@ -506,12 +510,15 @@
         }
 
         @Override
-        public void keyguardDonePending() {
+        public void keyguardDonePending(boolean strongAuth) {
             mKeyguardDonePending = true;
             mHideAnimationRun = true;
             mStatusBarKeyguardViewManager.startPreHideAnimation(null /* finishRunnable */);
             mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT,
                     KEYGUARD_DONE_PENDING_TIMEOUT_MS);
+            if (strongAuth) {
+                mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
+            }
         }
 
         @Override
@@ -524,7 +531,7 @@
             if (mKeyguardDonePending) {
                 // Somebody has called keyguardDonePending before, which means that we are
                 // authenticated
-                KeyguardViewMediator.this.keyguardDone(true /* authenticated */, true /* wakeUp */);
+                KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
             }
         }
 
@@ -552,9 +559,12 @@
         public int getBouncerPromptReason() {
             int currentUser = ActivityManager.getCurrentUser();
             if ((mUpdateMonitor.getUserTrustIsManaged(currentUser)
-                    || mUpdateMonitor.isUnlockWithFingerPrintPossible(currentUser))
+                    || mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser))
                     && !mTrustManager.hasUserAuthenticatedSinceBoot(currentUser)) {
                 return KeyguardSecurityView.PROMPT_REASON_RESTART;
+            } else if (mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser)
+                    && mUpdateMonitor.hasFingerprintUnlockTimedOut(currentUser)) {
+                return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
             }
             return KeyguardSecurityView.PROMPT_REASON_NONE;
         }
@@ -1194,10 +1204,10 @@
         }
     };
 
-    public void keyguardDone(boolean authenticated, boolean wakeup) {
-        if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
+    public void keyguardDone(boolean authenticated) {
+        if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated +")");
         EventLog.writeEvent(70000, 2);
-        Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0, wakeup ? 1 : 0);
+        Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0);
         mHandler.sendMessage(msg);
     }
 
@@ -1240,14 +1250,11 @@
                     handleNotifyStartedWakingUp();
                     break;
                 case KEYGUARD_DONE:
-                    handleKeyguardDone(msg.arg1 != 0, msg.arg2 != 0);
+                    handleKeyguardDone(msg.arg1 != 0);
                     break;
                 case KEYGUARD_DONE_DRAWING:
                     handleKeyguardDoneDrawing();
                     break;
-                case KEYGUARD_DONE_AUTHENTICATING:
-                    keyguardDone(true, true);
-                    break;
                 case SET_OCCLUDED:
                     handleSetOccluded(msg.arg1 != 0);
                     break;
@@ -1277,7 +1284,7 @@
      * @see #keyguardDone
      * @see #KEYGUARD_DONE
      */
-    private void handleKeyguardDone(boolean authenticated, boolean wakeup) {
+    private void handleKeyguardDone(boolean authenticated) {
         if (DEBUG) Log.d(TAG, "handleKeyguardDone");
         synchronized (this) {
             resetKeyguardDonePendingLocked();
@@ -1599,6 +1606,7 @@
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
             mStatusBarKeyguardViewManager.onScreenTurnedOff();
+            mWakeAndUnlocking = false;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
index f36019b..e64f6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
@@ -21,7 +21,6 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b47fb304..d0876fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -33,7 +33,6 @@
 import android.view.ViewStub;
 import android.widget.Toast;
 
-import com.android.internal.logging.MetricsConstants;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index afa32cbdf..89aeabc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -20,10 +20,8 @@
 import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
-import android.app.IActivityContainer;
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
-import android.app.SearchManager;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -54,15 +52,12 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
@@ -71,7 +66,6 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsAppWidgetHost;
 import com.android.systemui.recents.RecentsConfiguration;
 
 import java.io.IOException;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 4878cd92..f1b8873 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -647,6 +647,11 @@
         public void onFingerprintRunningStateChanged(boolean running) {
             mLockIcon.update();
         }
+
+        @Override
+        public void onStrongAuthTimeoutExpiredChanged(int userId) {
+            mLockIcon.update();
+        }
     };
 
     public void setKeyguardIndicationController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 37f563e..8b96e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -26,6 +26,8 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardHostView;
 import com.android.keyguard.KeyguardSecurityView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.R;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.DejankUtils;
@@ -47,6 +49,13 @@
     private ViewGroup mRoot;
     private boolean mShowingSoon;
     private int mBouncerPromptReason;
+    private KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onStrongAuthTimeoutExpiredChanged(int userId) {
+                    mBouncerPromptReason = mCallback.getBouncerPromptReason();
+                }
+            };
 
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
@@ -56,6 +65,7 @@
         mLockPatternUtils = lockPatternUtils;
         mContainer = container;
         mWindowManager = windowManager;
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
     }
 
     public void show(boolean resetSecuritySelection) {
@@ -247,8 +257,8 @@
         return mKeyguardView.interceptMediaKey(event);
     }
 
-    public void notifyKeyguardAuthenticated() {
+    public void notifyKeyguardAuthenticated(boolean strongAuth) {
         ensureView();
-        mKeyguardView.finish();
+        mKeyguardView.finish(strongAuth);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 1bdcf03..3c1272d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -480,8 +480,8 @@
      * Notifies that the user has authenticated by other means than using the bouncer, for example,
      * fingerprint.
      */
-    public void notifyKeyguardAuthenticated() {
-        mBouncer.notifyKeyguardAuthenticated();
+    public void notifyKeyguardAuthenticated(boolean strongAuth) {
+        mBouncer.notifyKeyguardAuthenticated(strongAuth);
     }
 
     public void setWakeAndUnlocking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index bd537f7..d646d0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -143,6 +143,11 @@
         public void onFaceUnlockStateChanged(boolean running, int userId) {
             update(false /* updateAlways */);
         }
+
+        @Override
+        public void onStrongAuthTimeoutExpiredChanged(int userId) {
+            update(false /* updateAlways */);
+        }
     };
 
     public boolean isTrustManaged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index daa84ad..a04edf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -164,11 +164,18 @@
             // Our current device is still valid.
             return;
         }
+        mLastDevice = null;
         for (CachedBluetoothDevice device : getDevices()) {
             if (device.isConnected()) {
                 mLastDevice = device;
             }
         }
+        if (mLastDevice == null && mConnectionState == BluetoothAdapter.STATE_CONNECTED) {
+            // If somehow we think we are connected, but have no connected devices, we aren't
+            // connected.
+            mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
+            mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2be3bf7..19ad930 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8639,6 +8639,54 @@
     }
 
     @Override
+    public void setActivityBounds(IBinder token, Rect bounds) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    Slog.w(TAG, "setActivityBounds: token=" + token + " not found");
+                    return;
+                }
+                final TaskRecord task = r.task;
+                if (task == null) {
+                    Slog.e(TAG, "setActivityBounds: No TaskRecord for the ActivityRecord r=" + r);
+                    return;
+                }
+                mStackSupervisor.resizeTaskLocked(task, bounds);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public Rect getActivityBounds(IBinder token) {
+        long ident = Binder.clearCallingIdentity();
+        Rect rect = null;
+        try {
+            synchronized (this) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    Slog.w(TAG, "getActivityBounds: token=" + token + " not found");
+                    return rect;
+                }
+                final TaskRecord task = r.task;
+                if (task == null) {
+                    Slog.e(TAG, "getActivityBounds: No TaskRecord for the ActivityRecord r=" + r);
+                    return rect;
+                }
+                if (task.mBounds != null) {
+                    rect = new Rect(task.mBounds);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return rect;
+    }
+
+    @Override
     public Bitmap getTaskDescriptionIcon(String filename) {
         if (!FileUtils.isValidExtFilename(filename)
                 || !filename.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 2c9d82b..ffad69d 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -589,10 +589,14 @@
                 result = true; // client not listening
 	    }
 	    if (fpId == 0) {
-                FingerprintUtils.vibrateFingerprintError(getContext());
+                if (receiver != null) {
+                    FingerprintUtils.vibrateFingerprintError(getContext());
+                }
                 result |= handleFailedAttempt(this);
             } else {
-                FingerprintUtils.vibrateFingerprintSuccess(getContext());
+                if (receiver != null) {
+                    FingerprintUtils.vibrateFingerprintSuccess(getContext());
+                }
                 result |= true; // we have a valid fingerprint
                 mLockoutReset.run();
             }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 426578d..45bbf37 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1699,8 +1699,7 @@
      *         navigation bar and touch exploration is not enabled
      */
     private boolean canHideNavigationBar() {
-        return mHasNavigationBar
-                && !mAccessibilityManager.isTouchExplorationEnabled();
+        return mHasNavigationBar;
     }
 
     @Override
@@ -5522,10 +5521,6 @@
         // may happen in a future call to goToSleep.
         synchronized (mLock) {
             mAwake = true;
-            if (mKeyguardDelegate != null) {
-                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
-                mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
-            }
 
             updateWakeGestureListenerLp();
             updateOrientationListenerLp();
@@ -5615,6 +5610,8 @@
             mScreenOnListener = screenOnListener;
 
             if (mKeyguardDelegate != null) {
+                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
+                mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
                 mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
             } else {
                 if (DEBUG_WAKEUP) Slog.d(TAG,
@@ -6780,8 +6777,7 @@
      * R.boolean.config_enableTranslucentDecor is false.
      */
     private boolean areTranslucentBarsAllowed() {
-        return mTranslucentDecorEnabled
-                && !mAccessibilityManager.isTouchExplorationEnabled();
+        return mTranslucentDecorEnabled;
     }
 
     // Use this instead of checking config_showNavigationBar so that it can be consistently