Merge "Update Region parcelling to use SkRegion::Iter" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index a082a23..9e96249 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4275,6 +4275,7 @@
 
   public class DownloadManager {
     method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+    method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
     method public long enqueue(android.app.DownloadManager.Request);
     method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
     method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -28574,7 +28575,6 @@
     field public static java.lang.String DIRECTORY_DCIM;
     field public static java.lang.String DIRECTORY_DOCUMENTS;
     field public static java.lang.String DIRECTORY_DOWNLOADS;
-    field public static java.lang.String DIRECTORY_HOME;
     field public static java.lang.String DIRECTORY_MOVIES;
     field public static java.lang.String DIRECTORY_MUSIC;
     field public static java.lang.String DIRECTORY_NOTIFICATIONS;
@@ -32153,6 +32153,7 @@
     field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
     field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
     field public static final java.lang.String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
+    field public static final java.lang.String ACTION_SCREEN_READER_TUTORIAL = "android.settings.SCREEN_READER_TUTORIAL";
     field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
     field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
diff --git a/api/system-current.txt b/api/system-current.txt
index e4437aa..12187dd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4407,6 +4407,7 @@
 
   public class DownloadManager {
     method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+    method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
     method public long enqueue(android.app.DownloadManager.Request);
     method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
     method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -26806,7 +26807,6 @@
     field public static final int REASON_UNSPECIFIED = -1; // 0xffffffff
     field public static final deprecated int REPORT_EVENT_AFTER_BUFFER_FULL = 0; // 0x0
     field public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; // 0x1
-    field public static final int REPORT_EVENT_CONTEXT_HUB = 8; // 0x8
     field public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; // 0x2
     field public static final int REPORT_EVENT_NO_BATCH = 4; // 0x4
     field public static final int WIFI_BAND_24_GHZ = 1; // 0x1
@@ -30858,7 +30858,6 @@
     field public static java.lang.String DIRECTORY_DCIM;
     field public static java.lang.String DIRECTORY_DOCUMENTS;
     field public static java.lang.String DIRECTORY_DOWNLOADS;
-    field public static java.lang.String DIRECTORY_HOME;
     field public static java.lang.String DIRECTORY_MOVIES;
     field public static java.lang.String DIRECTORY_MUSIC;
     field public static java.lang.String DIRECTORY_NOTIFICATIONS;
@@ -34634,6 +34633,7 @@
     field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
     field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
     field public static final java.lang.String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
+    field public static final java.lang.String ACTION_SCREEN_READER_TUTORIAL = "android.settings.SCREEN_READER_TUTORIAL";
     field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
     field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
@@ -34694,6 +34694,7 @@
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String MODE_RINGER = "mode_ringer";
     field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
+    field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
     field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
     field public static final java.lang.String RADIO_CELL = "cell";
     field public static final java.lang.String RADIO_NFC = "nfc";
diff --git a/api/test-current.txt b/api/test-current.txt
index de1496b..74c6787 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4275,6 +4275,7 @@
 
   public class DownloadManager {
     method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+    method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
     method public long enqueue(android.app.DownloadManager.Request);
     method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
     method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -28583,7 +28584,6 @@
     field public static java.lang.String DIRECTORY_DCIM;
     field public static java.lang.String DIRECTORY_DOCUMENTS;
     field public static java.lang.String DIRECTORY_DOWNLOADS;
-    field public static java.lang.String DIRECTORY_HOME;
     field public static java.lang.String DIRECTORY_MOVIES;
     field public static java.lang.String DIRECTORY_MUSIC;
     field public static java.lang.String DIRECTORY_NOTIFICATIONS;
@@ -32166,6 +32166,7 @@
     field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
     field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
     field public static final java.lang.String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
+    field public static final java.lang.String ACTION_SCREEN_READER_TUTORIAL = "android.settings.SCREEN_READER_TUTORIAL";
     field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
     field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index ed4bb28..1e5f007 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1193,13 +1193,52 @@
             boolean isMediaScannerScannable, String mimeType, String path, long length,
             boolean showNotification) {
         return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
-                length, showNotification, false);
+                length, showNotification, false, null, null);
+    }
+
+    /**
+     * Adds a file to the downloads database system, so it could appear in Downloads App
+     * (and thus become eligible for management by the Downloads App).
+     * <p>
+     * It is helpful to make the file scannable by MediaScanner by setting the param
+     * isMediaScannerScannable to true. It makes the file visible in media managing
+     * applications such as Gallery App, which could be a useful purpose of using this API.
+     *
+     * @param title the title that would appear for this file in Downloads App.
+     * @param description the description that would appear for this file in Downloads App.
+     * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
+     * scanned by MediaScanner appear in the applications used to view media (for example,
+     * Gallery app).
+     * @param mimeType mimetype of the file.
+     * @param path absolute pathname to the file. The file should be world-readable, so that it can
+     * be managed by the Downloads App and any other app that is used to read it (for example,
+     * Gallery app to display the file, if the file contents represent a video/image).
+     * @param length length of the downloaded file
+     * @param showNotification true if a notification is to be sent, false otherwise
+     * @param uri the original HTTP URI of the download
+     * @param referer the HTTP Referer for the download
+     * @return  an ID for the download entry added to the downloads app, unique across the system
+     * This ID is used to make future calls related to this download.
+     */
+    public long addCompletedDownload(String title, String description,
+            boolean isMediaScannerScannable, String mimeType, String path, long length,
+            boolean showNotification, Uri uri, Uri referer) {
+        return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
+                length, showNotification, false, uri, referer);
     }
 
     /** {@hide} */
     public long addCompletedDownload(String title, String description,
             boolean isMediaScannerScannable, String mimeType, String path, long length,
             boolean showNotification, boolean allowWrite) {
+        return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
+                length, showNotification, allowWrite, null, null);
+    }
+
+    /** {@hide} */
+    public long addCompletedDownload(String title, String description,
+            boolean isMediaScannerScannable, String mimeType, String path, long length,
+            boolean showNotification, boolean allowWrite, Uri uri, Uri referer) {
         // make sure the input args are non-null/non-zero
         validateArgumentIsNonEmpty("title", title);
         validateArgumentIsNonEmpty("description", description);
@@ -1210,10 +1249,18 @@
         }
 
         // if there is already an entry with the given path name in downloads.db, return its id
-        Request request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD)
-                .setTitle(title)
+        Request request;
+        if (uri != null) {
+            request = new Request(uri);
+        } else {
+            request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD);
+        }
+        request.setTitle(title)
                 .setDescription(description)
                 .setMimeType(mimeType);
+        if (referer != null) {
+            request.addRequestHeader("Referer", referer.toString());
+        }
         ContentValues values = request.toContentValues(null);
         values.put(Downloads.Impl.COLUMN_DESTINATION,
                 Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index bed91ec..cd8f126 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -376,7 +376,13 @@
      * AppWidget provider. Will animate into these new views as needed
      */
     public void updateAppWidget(RemoteViews remoteViews) {
+        applyRemoteViews(remoteViews);
+    }
 
+    /**
+     * @hide
+     */
+    protected void applyRemoteViews(RemoteViews remoteViews) {
         if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld);
 
         boolean recycled = false;
@@ -573,8 +579,9 @@
     /**
      * Build a {@link Context} cloned into another package name, usually for the
      * purposes of reading remote resources.
+     * @hide
      */
-    private Context getRemoteContext() {
+    protected Context getRemoteContext() {
         try {
             // Return if cloned successfully, otherwise default
             return mContext.createApplicationContext(
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 59bf293..70f9cc5 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -476,11 +476,6 @@
     public static String DIRECTORY_DOCUMENTS = "Documents";
 
     /**
-     * Standard directory in which user managed files are stored.
-     */
-    public static String DIRECTORY_HOME = "Home";
-
-    /**
      * List of standard storage directories.
      * <p>
      * Each of its values have its own constant:
@@ -495,7 +490,6 @@
      *   <li>{@link #DIRECTORY_DOWNLOADS}
      *   <li>{@link #DIRECTORY_DCIM}
      *   <li>{@link #DIRECTORY_DOCUMENTS}
-     *   <li>{@link #DIRECTORY_HOME}
      * </ul>
      * @hide
      */
@@ -509,8 +503,7 @@
             DIRECTORY_MOVIES,
             DIRECTORY_DOWNLOADS,
             DIRECTORY_DCIM,
-            DIRECTORY_DOCUMENTS,
-            DIRECTORY_HOME
+            DIRECTORY_DOCUMENTS
     };
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3169bf4..5ab2b00 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -201,6 +201,21 @@
             "android.settings.ACCESSIBILITY_SETTINGS";
 
     /**
+     * Activity Action: Launch the screen reader tutorial.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SCREEN_READER_TUTORIAL =
+            "android.settings.SCREEN_READER_TUTORIAL";
+
+
+    /**
      * Activity Action: Show settings to control access to usage information.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -6716,6 +6731,16 @@
        public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval";
 
        /**
+        * Whether to disable the automatic scheduling of system updates.
+        * 1 = system updates won't be automatically scheduled (will always
+        * present notification instead).
+        * 0 = system updates will be automatically scheduled. (default)
+        * @hide
+        */
+       @SystemApi
+       public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
+
+       /**
         * Whether the package manager should send package verification broadcasts for verifiers to
         * review apps prior to installation.
         * 1 = request apps to be verified prior to installation, if a verifier exists.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 96853e0..6dc5ccc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1606,10 +1606,6 @@
                         frame.height() < desiredWindowHeight && frame.height() != mHeight));
         windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
 
-        // If the backdrop frame doesn't equal to a frame, we are starting a resize operation, so
-        // force it to be resized.
-        windowShouldResize |= !mPendingBackDropFrame.equals(mWinFrame);
-
         // If the activity was just relaunched, it might have unfrozen the task bounds (while
         // relaunching), so we need to force a call into window manager to pick up the latest
         // bounds.
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 5953a98..8278c5a 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -18,11 +18,13 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 
 import android.Manifest;
+import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +36,9 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.MeasureSpec;
@@ -74,7 +79,7 @@
     private RemoteViewsAdapterServiceConnection mServiceConnection;
     private WeakReference<RemoteAdapterConnectionCallback> mCallback;
     private OnClickHandler mRemoteViewsOnClickHandler;
-    private FixedSizeRemoteViewsCache mCache;
+    private final FixedSizeRemoteViewsCache mCache;
     private int mVisibleWindowLowerBound;
     private int mVisibleWindowUpperBound;
 
@@ -92,13 +97,10 @@
 
     // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
     // structures;
-    private static final HashMap<RemoteViewsCacheKey,
-            FixedSizeRemoteViewsCache> sCachedRemoteViewsCaches
-            = new HashMap<RemoteViewsCacheKey,
-                    FixedSizeRemoteViewsCache>();
+    private static final HashMap<RemoteViewsCacheKey, FixedSizeRemoteViewsCache>
+            sCachedRemoteViewsCaches = new HashMap<>();
     private static final HashMap<RemoteViewsCacheKey, Runnable>
-            sRemoteViewsCacheRemoveRunnables
-            = new HashMap<RemoteViewsCacheKey, Runnable>();
+            sRemoteViewsCacheRemoveRunnables = new HashMap<>();
 
     private static HandlerThread sCacheRemovalThread;
     private static Handler sCacheRemovalQueue;
@@ -286,9 +288,12 @@
      * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
      * they are loaded.
      */
-    private static class RemoteViewsFrameLayout extends FrameLayout {
-        public RemoteViewsFrameLayout(Context context) {
+    private static class RemoteViewsFrameLayout extends AppWidgetHostView {
+        private final FixedSizeRemoteViewsCache mCache;
+
+        public RemoteViewsFrameLayout(Context context, FixedSizeRemoteViewsCache cache) {
             super(context);
+            mCache = cache;
         }
 
         /**
@@ -297,13 +302,24 @@
          *             successfully.
          */
         public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler) {
-            try {
-                // Remove all the children of this layout first
-                removeAllViews();
-                addView(view.apply(getContext(), this, handler));
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to apply RemoteViews.");
-            }
+            setOnClickHandler(handler);
+            applyRemoteViews(view);
+        }
+
+        @Override
+        protected View getDefaultView() {
+            return mCache.getMetaData().createDefaultLoadingView(this);
+        }
+
+        @Override
+        protected Context getRemoteContext() {
+            return null;
+        }
+
+        @Override
+        protected View getErrorView() {
+            // Use the default loading view as the error view.
+            return getDefaultView();
         }
     }
 
@@ -312,29 +328,21 @@
      * adapter that have not yet had their RemoteViews loaded.
      */
     private class RemoteViewsFrameLayoutRefSet {
-        private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences;
-        private HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>>
-                mViewToLinkedList;
-
-        public RemoteViewsFrameLayoutRefSet() {
-            mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>();
-            mViewToLinkedList =
-                    new HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>>();
-        }
+        private final SparseArray<LinkedList<RemoteViewsFrameLayout>> mReferences =
+                new SparseArray<>();
+        private final HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>>
+                mViewToLinkedList = new HashMap<>();
 
         /**
          * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
          */
         public void add(int position, RemoteViewsFrameLayout layout) {
-            final Integer pos = position;
-            LinkedList<RemoteViewsFrameLayout> refs;
+            LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(position);
 
             // Create the list if necessary
-            if (mReferences.containsKey(pos)) {
-                refs = mReferences.get(pos);
-            } else {
+            if (refs == null) {
                 refs = new LinkedList<RemoteViewsFrameLayout>();
-                mReferences.put(pos, refs);
+                mReferences.put(position, refs);
             }
             mViewToLinkedList.put(layout, refs);
 
@@ -349,10 +357,9 @@
         public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) {
             if (view == null) return;
 
-            final Integer pos = position;
-            if (mReferences.containsKey(pos)) {
+            final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(position);
+            if (refs != null) {
                 // Notify all the references for that position of the newly loaded RemoteViews
-                final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos);
                 for (final RemoteViewsFrameLayout ref : refs) {
                     ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler);
                     if (mViewToLinkedList.containsKey(ref)) {
@@ -361,7 +368,7 @@
                 }
                 refs.clear();
                 // Remove this set from the original mapping
-                mReferences.remove(pos);
+                mReferences.remove(position);
             }
         }
 
@@ -402,7 +409,7 @@
         int mFirstViewHeight;
 
         // A mapping from type id to a set of unique type ids
-        private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>();
+        private final SparseIntArray mTypeIdIndexMap = new SparseIntArray();
 
         public RemoteViewsMetaData() {
             reset();
@@ -438,82 +445,47 @@
         }
 
         public int getMappedViewType(int typeId) {
-            if (mTypeIdIndexMap.containsKey(typeId)) {
-                return mTypeIdIndexMap.get(typeId);
-            } else {
+            int mappedTypeId = mTypeIdIndexMap.get(typeId, -1);
+            if (mappedTypeId == -1) {
                 // We +1 because the loading view always has view type id of 0
-                int incrementalTypeId = mTypeIdIndexMap.size() + 1;
-                mTypeIdIndexMap.put(typeId, incrementalTypeId);
-                return incrementalTypeId;
+                mappedTypeId = mTypeIdIndexMap.size() + 1;
+                mTypeIdIndexMap.put(typeId, mappedTypeId);
             }
+            return mappedTypeId;
         }
 
         public boolean isViewTypeInRange(int typeId) {
             int mappedType = getMappedViewType(typeId);
-            if (mappedType >= viewTypeCount) {
-                return false;
-            } else {
-                return true;
-            }
+            return (mappedType < viewTypeCount);
         }
 
-        private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
-                ViewGroup parent, Object lock, LayoutInflater layoutInflater, OnClickHandler
-                handler) {
-            // Create and return a new FrameLayout, and setup the references for this position
+        /**
+         * Creates a default loading view. Uses the size of the first row as a guide for the
+         * size of the loading view.
+         */
+        private synchronized View createDefaultLoadingView(ViewGroup parent) {
             final Context context = parent.getContext();
-            RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context);
-
-            // Create a new loading view
-            synchronized (lock) {
-                boolean customLoadingViewAvailable = false;
-
-                if (mUserLoadingView != null) {
-                    // Try to inflate user-specified loading view
-                    try {
-                        View loadingView = mUserLoadingView.apply(parent.getContext(), parent,
-                                handler);
-                        loadingView.setTagInternal(com.android.internal.R.id.rowTypeId,
-                                new Integer(0));
-                        layout.addView(loadingView);
-                        customLoadingViewAvailable = true;
-                    } catch (Exception e) {
-                        Log.w(TAG, "Error inflating custom loading view, using default loading" +
-                                "view instead", e);
-                    }
+            if (mFirstViewHeight < 0) {
+                try {
+                    View firstView = mFirstView.apply(parent.getContext(), parent);
+                    firstView.measure(
+                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+                    mFirstViewHeight = firstView.getMeasuredHeight();
+                } catch (Exception e) {
+                    float density = context.getResources().getDisplayMetrics().density;
+                    mFirstViewHeight = Math.round(sDefaultLoadingViewHeight * density);
+                    Log.w(TAG, "Error inflating first RemoteViews" + e);
                 }
-                if (!customLoadingViewAvailable) {
-                    // A default loading view
-                    // Use the size of the first row as a guide for the size of the loading view
-                    if (mFirstViewHeight < 0) {
-                        try {
-                            View firstView = mFirstView.apply(parent.getContext(), parent, handler);
-                            firstView.measure(
-                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-                            mFirstViewHeight = firstView.getMeasuredHeight();
-                            mFirstView = null;
-                        } catch (Exception e) {
-                            float density = context.getResources().getDisplayMetrics().density;
-                            mFirstViewHeight = (int)
-                                    Math.round(sDefaultLoadingViewHeight * density);
-                            mFirstView = null;
-                            Log.w(TAG, "Error inflating first RemoteViews" + e);
-                        }
-                    }
-
-                    // Compose the loading view text
-                    TextView loadingTextView = (TextView) layoutInflater.inflate(
-                            com.android.internal.R.layout.remote_views_adapter_default_loading_view,
-                            layout, false);
-                    loadingTextView.setHeight(mFirstViewHeight);
-                    loadingTextView.setTag(new Integer(0));
-
-                    layout.addView(loadingTextView);
-                }
+                mFirstView = null;
             }
 
-            return layout;
+            // Compose the loading view text
+            TextView loadingTextView = (TextView) LayoutInflater.from(context).inflate(
+                    com.android.internal.R.layout.remote_views_adapter_default_loading_view,
+                    parent, false);
+            loadingTextView.setHeight(mFirstViewHeight);
+            return loadingTextView;
         }
     }
 
@@ -548,8 +520,8 @@
         // The meta data objects are made final so that they can be locked on independently
         // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in
         // the order mTemporaryMetaData followed by mMetaData.
-        private final RemoteViewsMetaData mMetaData;
-        private final RemoteViewsMetaData mTemporaryMetaData;
+        private final RemoteViewsMetaData mMetaData = new RemoteViewsMetaData();
+        private final RemoteViewsMetaData mTemporaryMetaData = new RemoteViewsMetaData();
 
         // The cache/mapping of position to RemoteViewsMetaData.  This set is guaranteed to be
         // greater than or equal to the set of RemoteViews.
@@ -558,22 +530,20 @@
         // the heavy RemoteViews around.  The RemoteViews cache is trimmed to fixed constraints wrt.
         // memory and size, but this metadata cache will retain information until the data at the
         // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged).
-        private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData;
+        private final SparseArray<RemoteViewsIndexMetaData> mIndexMetaData = new SparseArray<>();
 
         // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses
         // too much memory.
-        private HashMap<Integer, RemoteViews> mIndexRemoteViews;
+        private final SparseArray<RemoteViews> mIndexRemoteViews = new SparseArray<>();
 
-        // The set of indices that have been explicitly requested by the collection view
-        private HashSet<Integer> mRequestedIndices;
+        // An array of indices to load, Indices which are explicitely requested are set to true,
+        // and those determined by the preloading algorithm to prefetch are set to false.
+        private final SparseBooleanArray mIndicesToLoad = new SparseBooleanArray();
 
         // We keep a reference of the last requested index to determine which item to prune the
         // farthest items from when we hit the memory limit
         private int mLastRequestedIndex;
 
-        // The set of indices to load, including those explicitly requested, as well as those
-        // determined by the preloading algorithm to be prefetched
-        private HashSet<Integer> mLoadIndices;
 
         // The lower and upper bounds of the preloaded range
         private int mPreloadLowerBound;
@@ -584,8 +554,8 @@
         // The maxCountSlack is used to determine if a new position in the cache to be loaded is
         // sufficiently ouside the old set, prompting a shifting of the "window" of items to be
         // preloaded.
-        private int mMaxCount;
-        private int mMaxCountSlack;
+        private final int mMaxCount;
+        private final int mMaxCountSlack;
         private static final float sMaxCountSlackPercent = 0.75f;
         private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024;
 
@@ -594,17 +564,10 @@
             mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2));
             mPreloadLowerBound = 0;
             mPreloadUpperBound = -1;
-            mMetaData = new RemoteViewsMetaData();
-            mTemporaryMetaData = new RemoteViewsMetaData();
-            mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
-            mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
-            mRequestedIndices = new HashSet<Integer>();
             mLastRequestedIndex = -1;
-            mLoadIndices = new HashSet<Integer>();
         }
 
-        public void insert(int position, RemoteViews v, long itemId,
-                ArrayList<Integer> visibleWindow) {
+        public void insert(int position, RemoteViews v, long itemId, int[] visibleWindow) {
             // Trim the cache if we go beyond the count
             if (mIndexRemoteViews.size() >= mMaxCount) {
                 mIndexRemoteViews.remove(getFarthestPositionFrom(position, visibleWindow));
@@ -630,8 +593,8 @@
             }
 
             // Update the metadata cache
-            if (mIndexMetaData.containsKey(position)) {
-                final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
+            final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
+            if (metaData != null) {
                 metaData.set(v, itemId);
             } else {
                 mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId));
@@ -646,16 +609,10 @@
             return mTemporaryMetaData;
         }
         public RemoteViews getRemoteViewsAt(int position) {
-            if (mIndexRemoteViews.containsKey(position)) {
-                return mIndexRemoteViews.get(position);
-            }
-            return null;
+            return mIndexRemoteViews.get(position);
         }
         public RemoteViewsIndexMetaData getMetaDataAt(int position) {
-            if (mIndexMetaData.containsKey(position)) {
-                return mIndexMetaData.get(position);
-            }
-            return null;
+            return mIndexMetaData.get(position);
         }
 
         public void commitTemporaryMetaData() {
@@ -669,8 +626,8 @@
         private int getRemoteViewsBitmapMemoryUsage() {
             // Calculate the memory usage of all the RemoteViews bitmaps being cached
             int mem = 0;
-            for (Integer i : mIndexRemoteViews.keySet()) {
-                final RemoteViews v = mIndexRemoteViews.get(i);
+            for (int i = mIndexRemoteViews.size() - 1; i >= 0; i--) {
+                final RemoteViews v = mIndexRemoteViews.valueAt(i);
                 if (v != null) {
                     mem += v.estimateMemoryUsage();
                 }
@@ -678,24 +635,25 @@
             return mem;
         }
 
-        private int getFarthestPositionFrom(int pos, ArrayList<Integer> visibleWindow) {
+        private int getFarthestPositionFrom(int pos, int[] visibleWindow) {
             // Find the index farthest away and remove that
             int maxDist = 0;
             int maxDistIndex = -1;
             int maxDistNotVisible = 0;
             int maxDistIndexNotVisible = -1;
-            for (int i : mIndexRemoteViews.keySet()) {
-                int dist = Math.abs(i-pos);
-                if (dist > maxDistNotVisible && !visibleWindow.contains(i)) {
+            for (int i = mIndexRemoteViews.size() - 1; i >= 0; i--) {
+                int index = mIndexRemoteViews.keyAt(i);
+                int dist = Math.abs(index-pos);
+                if (dist > maxDistNotVisible && Arrays.binarySearch(visibleWindow, index) < 0) {
                     // maxDistNotVisible/maxDistIndexNotVisible will store the index of the
                     // farthest non-visible position
-                    maxDistIndexNotVisible = i;
+                    maxDistIndexNotVisible = index;
                     maxDistNotVisible = dist;
                 }
                 if (dist >= maxDist) {
                     // maxDist/maxDistIndex will store the index of the farthest position
                     // regardless of whether it is visible or not
-                    maxDistIndex = i;
+                    maxDistIndex = index;
                     maxDist = dist;
                 }
             }
@@ -707,9 +665,8 @@
 
         public void queueRequestedPositionToLoad(int position) {
             mLastRequestedIndex = position;
-            synchronized (mLoadIndices) {
-                mRequestedIndices.add(position);
-                mLoadIndices.add(position);
+            synchronized (mIndicesToLoad) {
+                mIndicesToLoad.put(position, true);
             }
         }
         public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) {
@@ -725,11 +682,13 @@
             synchronized (mMetaData) {
                 count = mMetaData.count;
             }
-            synchronized (mLoadIndices) {
-                mLoadIndices.clear();
-
-                // Add all the requested indices
-                mLoadIndices.addAll(mRequestedIndices);
+            synchronized (mIndicesToLoad) {
+                // Remove all indices which have not been previously requested.
+                for (int i = mIndicesToLoad.size() - 1; i >= 0; i--) {
+                    if (!mIndicesToLoad.valueAt(i)) {
+                        mIndicesToLoad.removeAt(i);
+                    }
+                }
 
                 // Add all the preload indices
                 int halfMaxCount = mMaxCount / 2;
@@ -738,43 +697,40 @@
                 int effectiveLowerBound = Math.max(0, mPreloadLowerBound);
                 int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1);
                 for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) {
-                    mLoadIndices.add(i);
+                    if (mIndexRemoteViews.indexOfKey(i) < 0 && !mIndicesToLoad.get(i)) {
+                        // If the index has not been requested, and has not been loaded.
+                        mIndicesToLoad.put(i, false);
+                    }
                 }
-
-                // But remove all the indices that have already been loaded and are cached
-                mLoadIndices.removeAll(mIndexRemoteViews.keySet());
             }
             return true;
         }
-        /** Returns the next index to load, and whether that index was directly requested or not */
-        public int[] getNextIndexToLoad() {
+        /** Returns the next index to load */
+        public int getNextIndexToLoad() {
             // We try and prioritize items that have been requested directly, instead
             // of items that are loaded as a result of the caching mechanism
-            synchronized (mLoadIndices) {
+            synchronized (mIndicesToLoad) {
                 // Prioritize requested indices to be loaded first
-                if (!mRequestedIndices.isEmpty()) {
-                    Integer i = mRequestedIndices.iterator().next();
-                    mRequestedIndices.remove(i);
-                    mLoadIndices.remove(i);
-                    return new int[]{i.intValue(), 1};
+                int index = mIndicesToLoad.indexOfValue(true);
+                if (index < 0) {
+                    // Otherwise, preload other indices as necessary
+                    index = mIndicesToLoad.indexOfValue(false);
                 }
-
-                // Otherwise, preload other indices as necessary
-                if (!mLoadIndices.isEmpty()) {
-                    Integer i = mLoadIndices.iterator().next();
-                    mLoadIndices.remove(i);
-                    return new int[]{i.intValue(), 0};
+                if (index < 0) {
+                    return -1;
+                } else {
+                    int key = mIndicesToLoad.keyAt(index);
+                    mIndicesToLoad.removeAt(index);
+                    return key;
                 }
-
-                return new int[]{-1, 0};
             }
         }
 
         public boolean containsRemoteViewAt(int position) {
-            return mIndexRemoteViews.containsKey(position);
+            return mIndexRemoteViews.indexOfKey(position) >= 0;
         }
         public boolean containsMetaDataAt(int position) {
-            return mIndexMetaData.containsKey(position);
+            return mIndexMetaData.indexOfKey(position) >= 0;
         }
 
         public void reset() {
@@ -787,9 +743,8 @@
             mLastRequestedIndex = -1;
             mIndexRemoteViews.clear();
             mIndexMetaData.clear();
-            synchronized (mLoadIndices) {
-                mRequestedIndices.clear();
-                mLoadIndices.clear();
+            synchronized (mIndicesToLoad) {
+                mIndicesToLoad.clear();
             }
         }
     }
@@ -942,8 +897,7 @@
                     // Get the next index to load
                     int position = -1;
                     synchronized (mCache) {
-                        int[] res = mCache.getNextIndexToLoad();
-                        position = res[0];
+                        position = mCache.getNextIndexToLoad();
                     }
                     if (position > -1) {
                         // Load the item, and notify any existing RemoteViewsFrameLayouts
@@ -1048,7 +1002,7 @@
         }
         synchronized (mCache) {
             if (viewTypeInRange) {
-                ArrayList<Integer> visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
+                int[] visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
                         mVisibleWindowUpperBound, cacheCount);
                 // Cache the RemoteViews we loaded
                 mCache.insert(position, remoteViews, itemId, visibleWindow);
@@ -1117,21 +1071,6 @@
     }
 
     /**
-     * Returns the item type id for the specified convert view.  Returns -1 if the convert view
-     * is invalid.
-     */
-    private int getConvertViewTypeId(View convertView) {
-        int typeId = -1;
-        if (convertView != null) {
-            Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId);
-            if (tag != null) {
-                typeId = (Integer) tag;
-            }
-        }
-        return typeId;
-    }
-
-    /**
      * This method allows an AdapterView using this Adapter to provide information about which
      * views are currently being displayed. This allows for certain optimizations and preloading
      * which  wouldn't otherwise be possible.
@@ -1145,7 +1084,8 @@
         // "Request" an index so that we can queue it for loading, initiate subsequent
         // preloading, etc.
         synchronized (mCache) {
-            boolean isInCache = mCache.containsRemoteViewAt(position);
+            RemoteViews rv = mCache.getRemoteViewsAt(position);
+            boolean isInCache = (rv != null);
             boolean isConnected = mServiceConnection.isConnected();
             boolean hasNewItems = false;
 
@@ -1162,75 +1102,23 @@
                 hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
             }
 
+            final RemoteViewsFrameLayout layout =
+                    (convertView instanceof RemoteViewsFrameLayout)
+                            ? (RemoteViewsFrameLayout) convertView
+                            : new RemoteViewsFrameLayout(parent.getContext(), mCache);
             if (isInCache) {
-                View convertViewChild = null;
-                int convertViewTypeId = 0;
-                RemoteViewsFrameLayout layout = null;
-
-                if (convertView instanceof RemoteViewsFrameLayout) {
-                    layout = (RemoteViewsFrameLayout) convertView;
-                    convertViewChild = layout.getChildAt(0);
-                    convertViewTypeId = getConvertViewTypeId(convertViewChild);
-                }
-
-                // Second, we try and retrieve the RemoteViews from the cache, returning a loading
-                // view and queueing it to be loaded if it has not already been loaded.
-                Context context = parent.getContext();
-                RemoteViews rv = mCache.getRemoteViewsAt(position);
-                RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position);
-                int typeId = indexMetaData.typeId;
-
-                try {
-                    // Reuse the convert view where possible
-                    if (layout != null) {
-                        if (convertViewTypeId == typeId) {
-                            rv.reapply(context, convertViewChild, mRemoteViewsOnClickHandler);
-                            return layout;
-                        }
-                        layout.removeAllViews();
-                    } else {
-                        layout = new RemoteViewsFrameLayout(context);
-                    }
-
-                    // Otherwise, create a new view to be returned
-                    View newView = rv.apply(context, parent, mRemoteViewsOnClickHandler);
-                    newView.setTagInternal(com.android.internal.R.id.rowTypeId,
-                            new Integer(typeId));
-                    layout.addView(newView);
-                    return layout;
-
-                } catch (Exception e){
-                    // We have to make sure that we successfully inflated the RemoteViews, if not
-                    // we return the loading view instead.
-                    Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" +
-                            "loading view instead" + e);
-
-                    RemoteViewsFrameLayout loadingView = null;
-                    final RemoteViewsMetaData metaData = mCache.getMetaData();
-                    synchronized (metaData) {
-                        loadingView = metaData.createLoadingView(position, convertView, parent,
-                                mCache, mLayoutInflater, mRemoteViewsOnClickHandler);
-                    }
-                    return loadingView;
-                } finally {
-                    if (hasNewItems) loadNextIndexInBackground();
-                }
+                layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler);
+                if (hasNewItems) loadNextIndexInBackground();
             } else {
-                // If the cache does not have the RemoteViews at this position, then create a
-                // loading view and queue the actual position to be loaded in the background
-                RemoteViewsFrameLayout loadingView = null;
-                final RemoteViewsMetaData metaData = mCache.getMetaData();
-                synchronized (metaData) {
-                    loadingView = metaData.createLoadingView(position, convertView, parent,
-                            mCache, mLayoutInflater, mRemoteViewsOnClickHandler);
-                }
-
-                mRequestedViews.add(position, loadingView);
+                // If the views is not loaded, apply the loading view. If the loading view doesn't
+                // exist, the layout will create a default view based on the firstView height.
+                layout.onRemoteViewsLoaded(mCache.getMetaData().mUserLoadingView,
+                        mRemoteViewsOnClickHandler);
+                mRequestedViews.add(position, layout);
                 mCache.queueRequestedPositionToLoad(position);
                 loadNextIndexInBackground();
-
-                return loadingView;
             }
+            return layout;
         }
     }
 
@@ -1276,7 +1164,7 @@
         // Re-request the new metadata (only after the notification to the factory)
         updateTemporaryMetaData();
         int newCount;
-        ArrayList<Integer> visibleWindow;
+        int[] visibleWindow;
         synchronized(mCache.getTemporaryMetaData()) {
             newCount = mCache.getTemporaryMetaData().count;
             visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
@@ -1311,26 +1199,33 @@
         mNotifyDataSetChangedAfterOnServiceConnected = false;
     }
 
-    private ArrayList<Integer> getVisibleWindow(int lower, int upper, int count) {
-        ArrayList<Integer> window = new ArrayList<Integer>();
-
+    /**
+     * Returns a sorted array of all integers between lower and upper.
+     */
+    private int[] getVisibleWindow(int lower, int upper, int count) {
         // In the case that the window is invalid or uninitialized, return an empty window.
         if ((lower == 0 && upper == 0) || lower < 0 || upper < 0) {
-            return window;
+            return new int[0];
         }
 
+        int[] window;
         if (lower <= upper) {
-            for (int i = lower;  i <= upper; i++){
-                window.add(i);
+            window = new int[upper + 1 - lower];
+            for (int i = lower, j = 0;  i <= upper; i++, j++){
+                window[j] = i;
             }
         } else {
             // If the upper bound is less than the lower bound it means that the visible window
             // wraps around.
-            for (int i = lower; i < count; i++) {
-                window.add(i);
+            count = Math.max(count, lower);
+            window = new int[count - lower + upper + 1];
+            int j = 0;
+            // Add the entries in sorted order
+            for (int i = 0; i <= upper; i++, j++) {
+                window[j] = i;
             }
-            for (int i = 0; i <= upper; i++) {
-                window.add(i);
+            for (int i = lower; i < count; i++, j++) {
+                window[j] = i;
             }
         }
         return window;
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index ac77007..cd2c0d6 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "ThreadedRenderer"
 
 #include <algorithm>
+#include <atomic>
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
@@ -231,28 +232,13 @@
 
 class NotifyHandler : public MessageHandler {
 public:
-    NotifyHandler(JavaVM* vm) : mVm(vm) {}
-
-    void setObserver(ObserverProxy* observer) {
-        mObserver = observer;
-    }
-
-    void setBuffer(BufferPool::Buffer* buffer) {
-        mBuffer = buffer;
-    }
-
-    void setDropCount(int dropCount) {
-        mDropCount = dropCount;
-    }
+    NotifyHandler(JavaVM* vm, ObserverProxy* observer) : mVm(vm), mObserver(observer) {}
 
     virtual void handleMessage(const Message& message);
 
 private:
-    JavaVM* mVm;
-
-    sp<ObserverProxy> mObserver;
-    BufferPool::Buffer* mBuffer = nullptr;
-    int mDropCount = 0;
+    JavaVM* const mVm;
+    ObserverProxy* const mObserver;
 };
 
 static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) {
@@ -265,6 +251,9 @@
     return reinterpret_cast<jlongArray>(buffer);
 }
 
+/*
+ * Implements JNI layer for hwui frame metrics reporting.
+ */
 class ObserverProxy : public FrameMetricsObserver {
 public:
     ObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) {
@@ -284,7 +273,7 @@
         mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
         LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");
 
-        mMessageHandler = new NotifyHandler(mVm);
+        mMessageHandler = new NotifyHandler(mVm, this);
         LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr,
                 "OOM: unable to allocate NotifyHandler");
     }
@@ -298,18 +287,53 @@
         return mObserverWeak;
     }
 
-    virtual void notify(BufferPool::Buffer* buffer, int dropCount) {
-        buffer->incRef();
-        mMessageHandler->setBuffer(buffer);
-        mMessageHandler->setObserver(this);
-        mMessageHandler->setDropCount(dropCount);
-        mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
+    bool getNextBuffer(JNIEnv* env, jlongArray sink, int* dropCount) {
+        FrameMetricsNotification& elem = mRingBuffer[mNextInQueue];
+
+        if (elem.hasData.load()) {
+            env->SetLongArrayRegion(sink, 0, kBufferSize, elem.buffer);
+            *dropCount = elem.dropCount;
+            mNextInQueue = (mNextInQueue + 1) % kRingSize;
+            elem.hasData = false;
+            return true;
+        }
+
+        return false;
+    }
+
+    virtual void notify(const int64_t* stats) {
+        FrameMetricsNotification& elem = mRingBuffer[mNextFree];
+
+        if (!elem.hasData.load()) {
+            memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0]));
+
+            elem.dropCount = mDroppedReports;
+            mDroppedReports = 0;
+
+            incStrong(nullptr);
+            mNextFree = (mNextFree + 1) % kRingSize;
+            elem.hasData = true;
+
+            mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
+        } else {
+            mDroppedReports++;
+        }
     }
 
 private:
     static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes);
+    static constexpr int kRingSize = 3;
 
-    JavaVM* mVm;
+    class FrameMetricsNotification {
+    public:
+        FrameMetricsNotification() : hasData(false) {}
+
+        std::atomic_bool hasData;
+        int64_t buffer[kBufferSize];
+        int dropCount = 0;
+    };
+
+    JavaVM* const mVm;
     jweak mObserverWeak;
     jobject mJavaBufferGlobal;
 
@@ -317,28 +341,28 @@
     sp<NotifyHandler> mMessageHandler;
     Message mMessage;
 
+    int mNextFree = 0;
+    int mNextInQueue = 0;
+    FrameMetricsNotification mRingBuffer[kRingSize];
+
+    int mDroppedReports = 0;
 };
 
 void NotifyHandler::handleMessage(const Message& message) {
     JNIEnv* env = getenv(mVm);
 
-    ObserverProxy* observer = mObserver.get();
-    LOG_ALWAYS_FATAL_IF(observer == nullptr, "received message with no observer configured");
-    LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "received message with no data to report");
-
-    jobject target = env->NewLocalRef(observer->getObserverReference());
+    jobject target = env->NewLocalRef(mObserver->getObserverReference());
 
     if (target != nullptr) {
         jlongArray javaBuffer = get_metrics_buffer(env, target);
-        env->SetLongArrayRegion(javaBuffer,
-                0, mBuffer->getSize(), mBuffer->getBuffer());
-        env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback,
-                mDropCount);
+        int dropCount = 0;
+        while (mObserver->getNextBuffer(env, javaBuffer, &dropCount)) {
+            env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback, dropCount);
+        }
         env->DeleteLocalRef(target);
     }
 
-    mBuffer->release();
-    mObserver.clear();
+    mObserver->decStrong(nullptr);
 }
 
 static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 6a565033..6257122 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -232,7 +232,6 @@
 
 LOCAL_SRC_FILES += \
     $(hwui_test_common_src_files) \
-    tests/unit/BufferPoolTests.cpp \
     tests/unit/CanvasStateTests.cpp \
     tests/unit/ClipAreaTests.cpp \
     tests/unit/CrashHandlerInjector.cpp \
diff --git a/libs/hwui/BufferPool.h b/libs/hwui/BufferPool.h
deleted file mode 100644
index 005b399..0000000
--- a/libs/hwui/BufferPool.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#pragma once
-
-#include "utils/RefBase.h"
-#include "utils/Log.h"
-#include "utils/Macros.h"
-
-#include <atomic>
-#include <stdint.h>
-#include <memory>
-#include <mutex>
-
-namespace android {
-namespace uirenderer {
-
-/*
- * Simple thread-safe pool of int64_t arrays of a provided size.
- *
- * Permits allocating a client-provided max number of buffers.
- * If all buffers are in use, refuses to service any more
- * acquire requests until buffers are re-released to the pool.
- */
-class BufferPool : public VirtualLightRefBase {
-public:
-    class Buffer {
-        PREVENT_COPY_AND_ASSIGN(Buffer);
-    public:
-        int64_t* getBuffer() { return mBuffer.get(); }
-        size_t getSize() { return mSize; }
-
-        void release() {
-            LOG_ALWAYS_FATAL_IF(mPool.get() == nullptr, "attempt to release unacquired buffer");
-            mPool->release(this);
-        }
-
-        Buffer* incRef() {
-            mRefs++;
-            return this;
-        }
-
-        int decRef() {
-            int refs = mRefs.fetch_sub(1);
-            LOG_ALWAYS_FATAL_IF(refs == 0, "buffer reference decremented below 0");
-            return refs - 1;
-        }
-
-        bool isUniqueRef() {
-            return mRefs.load() == 1;
-        }
-
-    private:
-        friend class BufferPool;
-
-        Buffer(BufferPool* pool, size_t size) : mRefs(1) {
-            mSize = size;
-            mBuffer.reset(new int64_t[size]);
-            mPool = pool;
-        }
-
-        void setPool(BufferPool* pool) {
-            mPool = pool;
-        }
-
-        std::unique_ptr<Buffer> mNext;
-        std::unique_ptr<int64_t[]> mBuffer;
-        sp<BufferPool> mPool;
-        size_t mSize;
-
-        std::atomic_int mRefs;
-    };
-
-    BufferPool(size_t bufferSize, size_t count)
-            : mBufferSize(bufferSize), mCount(count) {}
-
-    /**
-     * Acquires a buffer from the buffer pool if available.
-     *
-     * Only `mCount` buffers are allowed to be in use at a single
-     * instance.
-     *
-     * If no buffer is available, i.e. `mCount` buffers are in use,
-     * returns nullptr.
-     *
-     * The pointer returned from this method *MUST NOT* be freed, instead
-     * BufferPool::release() must be called upon it when the client
-     * is done with it. Failing to release buffers will eventually make the
-     * BufferPool refuse to service any more BufferPool::acquire() requests.
-     */
-    BufferPool::Buffer* acquire() {
-        std::lock_guard<std::mutex> lock(mLock);
-
-        if (mHead.get() != nullptr) {
-            BufferPool::Buffer* res = mHead.release();
-            mHead = std::move(res->mNext);
-            res->mNext.reset(nullptr);
-            res->setPool(this);
-            res->incRef();
-            return res;
-        }
-
-        if (mAllocatedCount < mCount) {
-            ++mAllocatedCount;
-            return new BufferPool::Buffer(this, mBufferSize);
-        }
-
-        return nullptr;
-    }
-
-    /**
-     * Releases a buffer previously acquired by BufferPool::acquire().
-     *
-     * The released buffer is not valid after calling this method and
-     * attempting to use will result in undefined behavior.
-     */
-    void release(BufferPool::Buffer* buffer) {
-        std::lock_guard<std::mutex> lock(mLock);
-
-        if (buffer->decRef() != 0) {
-            return;
-        }
-
-        buffer->setPool(nullptr);
-
-        BufferPool::Buffer* list = mHead.get();
-        if (list == nullptr) {
-            mHead.reset(buffer);
-            mHead->mNext.reset(nullptr);
-            return;
-        }
-
-        while (list->mNext.get() != nullptr) {
-            list = list->mNext.get();
-        }
-
-        list->mNext.reset(buffer);
-    }
-
-    /*
-     * Used for testing.
-     */
-    size_t getAvailableBufferCount() {
-        size_t remainingToAllocateCount = mCount - mAllocatedCount;
-
-        BufferPool::Buffer* list = mHead.get();
-        if (list == nullptr) return remainingToAllocateCount;
-
-        int count = 1;
-        while (list->mNext.get() != nullptr) {
-            count++;
-            list = list->mNext.get();
-        }
-
-        return count + remainingToAllocateCount;
-    }
-
-private:
-    mutable std::mutex mLock;
-
-    size_t mBufferSize;
-    size_t mCount;
-    size_t mAllocatedCount = 0;
-    std::unique_ptr<BufferPool::Buffer> mHead;
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index 2b42a80..4f81c86 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -18,14 +18,12 @@
 
 #include <utils/RefBase.h>
 
-#include "BufferPool.h"
-
 namespace android {
 namespace uirenderer {
 
 class FrameMetricsObserver : public VirtualLightRefBase {
 public:
-    virtual void notify(BufferPool::Buffer* buffer, int dropCount);
+    virtual void notify(const int64_t* buffer);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index 0831d24..c1cd0a92 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -19,7 +19,6 @@
 #include <utils/RefBase.h>
 #include <utils/Log.h>
 
-#include "BufferPool.h"
 #include "FrameInfo.h"
 #include "FrameMetricsObserver.h"
 
@@ -31,10 +30,7 @@
 
 class FrameMetricsReporter {
 public:
-    FrameMetricsReporter() {
-        mBufferPool = new BufferPool(kBufferSize, kBufferCount);
-        LOG_ALWAYS_FATAL_IF(mBufferPool.get() == nullptr, "OOM: unable to allocate buffer pool");
-    }
+    FrameMetricsReporter() {}
 
     void addObserver(FrameMetricsObserver* observer) {
         mObservers.push_back(observer);
@@ -55,36 +51,13 @@
     }
 
     void reportFrameMetrics(const int64_t* stats) {
-        BufferPool::Buffer* statsBuffer = mBufferPool->acquire();
-
-        if (statsBuffer != nullptr) {
-            // copy in frame stats
-            memcpy(statsBuffer->getBuffer(), stats, kBufferSize * sizeof(*stats));
-
-            // notify on requested threads
-            for (size_t i = 0; i < mObservers.size(); i++) {
-                mObservers[i]->notify(statsBuffer, mDroppedReports);
-            }
-
-            // drop our reference
-            statsBuffer->release();
-            mDroppedReports = 0;
-        } else {
-            mDroppedReports++;
+        for (size_t i = 0; i < mObservers.size(); i++) {
+            mObservers[i]->notify(stats);
         }
     }
 
-    int getDroppedReports() { return mDroppedReports; }
-
 private:
-    static const size_t kBufferCount = 3;
-    static const size_t kBufferSize = static_cast<size_t>(FrameInfoIndex::NumIndexes);
-
     std::vector< sp<FrameMetricsObserver> > mObservers;
-
-    sp<BufferPool> mBufferPool;
-
-    int mDroppedReports = 0;
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1f81970..cb61e51 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -159,14 +159,6 @@
         }
     }
 
-    long getDroppedFrameReportCount() {
-        if (mFrameMetricsReporter.get() != nullptr) {
-            return mFrameMetricsReporter->getDroppedReports();
-        }
-
-        return 0;
-    }
-
 private:
     friend class RegisterFrameCallbackTask;
     // TODO: Replace with something better for layer & other GL object
diff --git a/libs/hwui/tests/unit/BufferPoolTests.cpp b/libs/hwui/tests/unit/BufferPoolTests.cpp
deleted file mode 100644
index 44e6d3a..0000000
--- a/libs/hwui/tests/unit/BufferPoolTests.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2016 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 <gtest/gtest.h>
-
-#include <BufferPool.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-namespace uirenderer {
-
-TEST(BufferPool, acquireThenRelease) {
-    static const int numRuns = 5;
-
-    // 10 buffers of size 1
-    static const size_t bufferSize = 1;
-    static const size_t bufferCount = 10;
-    sp<BufferPool> pool = new BufferPool(bufferSize, bufferCount);
-
-    for (int run = 0; run < numRuns; run++) {
-        BufferPool::Buffer* acquiredBuffers[bufferCount];
-        for (size_t i = 0; i < bufferCount; i++) {
-            ASSERT_EQ(bufferCount - i, pool->getAvailableBufferCount());
-            acquiredBuffers[i] = pool->acquire();
-            ASSERT_NE(nullptr, acquiredBuffers[i]);
-            ASSERT_TRUE(acquiredBuffers[i]->isUniqueRef());
-        }
-
-        for (size_t i = 0; i < bufferCount; i++) {
-            ASSERT_EQ(i, pool->getAvailableBufferCount());
-            acquiredBuffers[i]->release();
-            acquiredBuffers[i] = nullptr;
-        }
-
-        ASSERT_EQ(bufferCount, pool->getAvailableBufferCount());
-    }
-}
-
-TEST(BufferPool, acquireReleaseInterleaved) {
-    static const int numRuns = 5;
-
-    // 10 buffers of size 1
-    static const size_t bufferSize = 1;
-    static const size_t bufferCount = 10;
-
-    sp<BufferPool> pool = new BufferPool(bufferSize, bufferCount);
-
-    for (int run = 0; run < numRuns; run++) {
-        BufferPool::Buffer* acquiredBuffers[bufferCount];
-
-        // acquire all
-        for (size_t i = 0; i < bufferCount; i++) {
-            ASSERT_EQ(bufferCount - i, pool->getAvailableBufferCount());
-            acquiredBuffers[i] = pool->acquire();
-            ASSERT_NE(nullptr, acquiredBuffers[i]);
-        }
-
-        // release half
-        for (size_t i = 0; i < bufferCount / 2; i++) {
-            ASSERT_EQ(i, pool->getAvailableBufferCount());
-            acquiredBuffers[i]->release();
-            acquiredBuffers[i] = nullptr;
-        }
-
-        const size_t expectedRemaining = bufferCount / 2;
-        ASSERT_EQ(expectedRemaining, pool->getAvailableBufferCount());
-
-        // acquire half
-        for (size_t i = 0; i < bufferCount / 2; i++) {
-            ASSERT_EQ(expectedRemaining - i, pool->getAvailableBufferCount());
-            acquiredBuffers[i] = pool->acquire();
-        }
-
-        // acquire one more, should fail
-        ASSERT_EQ(nullptr, pool->acquire());
-
-        // release all
-        for (size_t i = 0; i < bufferCount; i++) {
-            ASSERT_EQ(i, pool->getAvailableBufferCount());
-            acquiredBuffers[i]->release();
-            acquiredBuffers[i] = nullptr;
-        }
-
-        ASSERT_EQ(bufferCount, pool->getAvailableBufferCount());
-    }
-}
-
-};
-};
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 16e1c5c..da4a038 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1940,6 +1940,15 @@
          * </ul>
          */
         public void onHardwareVideoUnavailable(int reason) { }
+
+        @Override
+        void release() {
+            if (mHardwareSession != null) {
+                mHardwareSession.release();
+                mHardwareSession = null;
+            }
+            super.release();
+        }
     }
 
     /** @hide */
diff --git a/packages/DocumentsUI/res/drawable/ic_root_home.xml b/packages/DocumentsUI/res/drawable/ic_root_documents.xml
similarity index 86%
rename from packages/DocumentsUI/res/drawable/ic_root_home.xml
rename to packages/DocumentsUI/res/drawable/ic_root_documents.xml
index 696ee05..afd886d 100644
--- a/packages/DocumentsUI/res/drawable/ic_root_home.xml
+++ b/packages/DocumentsUI/res/drawable/ic_root_documents.xml
@@ -20,5 +20,5 @@
         android:viewportHeight="24.0">
     <path
         android:fillColor="#FF000000"
-        android:pathData="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
+        android:pathData="M10 4H4c-1.1 0,-1.99.9,-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2,-.9 2,-2V8c0,-1.1,-.9,-2,-2,-2h-8l-2,-2z"/>
 </vector>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 470989d..648c79e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -23,7 +23,6 @@
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE;
 import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkState;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -464,8 +463,7 @@
      * Set mode based on explicit user action.
      */
     void setViewMode(@ViewMode int mode) {
-        checkState(mState.stack.root != null);
-        LocalPreferences.setViewMode(this, mState.stack.root, mode);
+        LocalPreferences.setViewMode(this, getCurrentRoot(), mode);
         mState.derivedMode = mode;
 
         // view icon needs to be updated, but we *could* do it
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 4233b36..0ae2a5c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -536,7 +536,11 @@
         @Override
         public void onItemStateChanged(String modelId, boolean selected) {
             final Cursor cursor = mModel.getItem(modelId);
-            checkNotNull(cursor, "Cursor cannot be null.");
+            if (cursor == null) {
+                Log.e(TAG, "Model returned null cursor for document: " + modelId
+                        + ". Ignoring state changed event.");
+                return;
+            }
 
             // TODO: Should this be happening in onSelectionChanged? Technically this callback is
             // triggered on "silent" selection updates (i.e. we might be reacting to unfinalized
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 4b5499a..1c696ad 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -39,6 +39,7 @@
 import java.io.IOException;
 import java.net.ProtocolException;
 import java.text.Collator;
+import java.util.Objects;
 
 /**
  * Representation of a {@link Document}.
@@ -263,16 +264,23 @@
         return derivedUri.hashCode() + mimeType.hashCode();
     }
 
-    public boolean equals(Object other) {
-        if (this == other) {
-            return true;
-        } else if (!(other instanceof DocumentInfo)) {
+    public boolean equals(Object o) {
+        if (o == null) {
             return false;
         }
 
-        DocumentInfo that = (DocumentInfo) other;
-        // Uri + mime type should be totally unique.
-        return derivedUri.equals(that.derivedUri) && mimeType.equals(that.mimeType);
+        if (this == o) {
+            return true;
+        }
+
+        if (o instanceof DocumentInfo) {
+            DocumentInfo other = (DocumentInfo) o;
+            // Uri + mime type should be totally unique.
+            return Objects.equals(derivedUri, other.derivedUri)
+                    && Objects.equals(mimeType, other.mimeType);
+        }
+
+        return false;
     }
 
     public static String getCursorString(Cursor cursor, String columnName) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 3f14a55..f4a97be 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -171,7 +171,7 @@
 
         // TODO: remove these special case icons
         if (isHome()) {
-            derivedIcon = R.drawable.ic_root_home;
+            derivedIcon = R.drawable.ic_root_documents;
             derivedType = TYPE_LOCAL;
         } else if (isExternalStorage()) {
             derivedIcon = R.drawable.ic_root_smartphone;
@@ -276,12 +276,21 @@
 
     @Override
     public boolean equals(Object o) {
-        if (o instanceof RootInfo) {
-            final RootInfo root = (RootInfo) o;
-            return Objects.equals(authority, root.authority) && Objects.equals(rootId, root.rootId);
-        } else {
+        if (o == null) {
             return false;
         }
+
+        if (this == o) {
+            return true;
+        }
+
+        if (o instanceof RootInfo) {
+            RootInfo other = (RootInfo) o;
+            return Objects.equals(authority, other.authority)
+                    && Objects.equals(rootId, other.rootId);
+        }
+
+        return false;
     }
 
     @Override
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
index b74b985..f057850 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
@@ -23,18 +23,44 @@
 
 @SmallTest
 public class StateTest extends AndroidTestCase {
-    public void testPushDocument() {
-        final State state = new State();
-        final DocumentInfo infoFirst = new DocumentInfo();
-        infoFirst.displayName = "firstDirectory";
-        final DocumentInfo infoSecond = new DocumentInfo();
-        infoSecond.displayName = "secondDirectory";
-        assertFalse(state.hasLocationChanged());
-        state.pushDocument(infoFirst);
-        state.pushDocument(infoSecond);
-        assertTrue(state.hasLocationChanged());
-        assertEquals("secondDirectory", state.stack.getFirst().displayName);
-        state.popDocument();
-        assertEquals("firstDirectory", state.stack.getFirst().displayName);
+
+    private static final DocumentInfo DIR_1;
+    private static final DocumentInfo DIR_2;
+
+    private State mState;
+
+    static {
+        DIR_1 = new DocumentInfo();
+        DIR_1.displayName = "firstDirectory";
+        DIR_2 = new DocumentInfo();
+        DIR_2.displayName = "secondDirectory";
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mState = new State();
+    }
+
+    public void testInitialStateEmpty() {
+        assertFalse(mState.hasLocationChanged());
+    }
+
+    public void testPushDocument_ChangesLocation() {
+        mState.pushDocument(DIR_1);
+        mState.pushDocument(DIR_2);
+        assertTrue(mState.hasLocationChanged());
+    }
+
+    public void testPushDocument_ModifiesStack() {
+        mState.pushDocument(DIR_1);
+        mState.pushDocument(DIR_2);
+        assertEquals(DIR_2, mState.stack.getFirst());
+    }
+
+    public void testPopDocument_ModifiesStack() {
+        mState.pushDocument(DIR_1);
+        mState.pushDocument(DIR_2);
+        mState.popDocument();
+        assertEquals(DIR_1, mState.stack.getFirst());
     }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
index a6aba7b..2481dc3 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
@@ -22,30 +22,36 @@
 @SmallTest
 public class DocumentInfoTest extends AndroidTestCase {
 
+    private static final DocumentInfo TEST_DOC
+            = createDocInfo("authority.a", "doc.1", "text/plain");
+
     public void testEquals() throws Exception {
-        DocumentInfo doc = createDocInfo("authority.a", "doc.1", "text/plain");
-        assertEquals(doc, doc);
+        assertEquals(TEST_DOC, TEST_DOC);
+        assertEquals(TEST_DOC, createDocInfo("authority.a", "doc.1", "text/plain"));
+    }
+
+    public void testEquals_HandlesNulls() throws Exception {
+        assertFalse(TEST_DOC.equals(null));
+    }
+
+    public void testEquals_HandlesNullFields() throws Exception {
+        assertFalse(TEST_DOC.equals(new DocumentInfo()));
+        assertFalse(new DocumentInfo().equals(TEST_DOC));
     }
 
     public void testNotEquals_differentAuthority() throws Exception {
-        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
-        DocumentInfo docB = createDocInfo("authority.b", "doc.1", "text/plain");
-        assertFalse(docA.equals(docB));
+        assertFalse(TEST_DOC.equals(createDocInfo("authority.b", "doc.1", "text/plain")));
     }
 
     public void testNotEquals_differentDocId() throws Exception {
-        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
-        DocumentInfo docB = createDocInfo("authority.a", "doc.2", "text/plain");
-        assertFalse(docA.equals(docB));
+        assertFalse(TEST_DOC.equals(createDocInfo("authority.a", "doc.2", "text/plain")));
     }
 
     public void testNotEquals_differentMimetype() throws Exception {
-        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
-        DocumentInfo docB = createDocInfo("authority.a", "doc.1", "image/png");
-        assertFalse(docA.equals(docB));
+        assertFalse(TEST_DOC.equals(createDocInfo("authority.a", "doc.1", "image/png")));
     }
 
-    private DocumentInfo createDocInfo(String authority, String docId, String mimeType) {
+    private static DocumentInfo createDocInfo(String authority, String docId, String mimeType) {
         DocumentInfo doc = new DocumentInfo();
         doc.authority = authority;
         doc.documentId = docId;
diff --git a/packages/ExternalStorageProvider/res/values/strings.xml b/packages/ExternalStorageProvider/res/values/strings.xml
index e48436e..8b16d3c 100644
--- a/packages/ExternalStorageProvider/res/values/strings.xml
+++ b/packages/ExternalStorageProvider/res/values/strings.xml
@@ -20,6 +20,6 @@
 
     <!-- Title for documents backend that offers internal storage. [CHAR LIMIT=24] -->
     <string name="root_internal_storage">Internal storage</string>
-    <!-- Title for user home dir. [CHAR LIMIT=24] -->
-    <string name="root_home">Home</string>
+    <!-- Title for directory in which a user may store their own documents and files. [CHAR LIMIT=24] -->
+    <string name="root_documents">Documents</string>
 </resources>
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index f89934d..97dfd47 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -199,7 +199,7 @@
             final RootInfo root = new RootInfo();
             root.rootId = ROOT_ID_HOME;
             mRoots.put(root.rootId, root);
-            root.title = getContext().getString(R.string.root_home);
+            root.title = getContext().getString(R.string.root_documents);
 
             // Only report bytes on *volumes*...as a matter of policy.
             root.reportAvailableBytes = false;
@@ -214,9 +214,9 @@
             // Create the "Home" directory on disk, but don't the localized root.title
             // since the directories shouldn't be localized.
             root.visiblePath = new File(
-                    primaryVolume.getPathForUser(userId), Environment.DIRECTORY_HOME);
+                    primaryVolume.getPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
             root.path = new File(
-                    primaryVolume.getInternalPathForUser(userId), root.rootId);
+                    primaryVolume.getInternalPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
             try {
                 root.docId = getDocIdForFile(root.path);
             } catch (FileNotFoundException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogConstants.java b/packages/SystemUI/src/com/android/systemui/EventLogConstants.java
index 43a1be1..9238928 100644
--- a/packages/SystemUI/src/com/android/systemui/EventLogConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/EventLogConstants.java
@@ -40,4 +40,15 @@
     public static final int SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS = 9;
     /** The user tapped on the status bar to open quick settings, from shade. */
     public static final int SYSUI_TAP_TO_OPEN_QS = 10;
+
+    /** Secondary user tries binding to the system sysui service */
+    public static final int SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE = 1;
+    /** Secondary user is bound to the system sysui service */
+    public static final int SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND = 2;
+    /** Secondary user loses connection after system sysui has died */
+    public static final int SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND = 3;
+    /** System sysui registers secondary user's callbacks */
+    public static final int SYSUI_RECENTS_CONNECTION_SYSTEM_REGISTER_USER = 4;
+    /** System sysui unregisters secondary user's callbacks (after death) */
+    public static final int SYSUI_RECENTS_CONNECTION_SYSTEM_UNREGISTER_USER = 5;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
index a584cf6..1601675 100644
--- a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
+++ b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
@@ -51,3 +51,13 @@
 # SearchPanelView.java
 # ---------------------------
 36050 sysui_searchpanel_touch (type|1),(x|1),(y|1)
+
+# ---------------------------
+# Recents.java, RecentsSystemUser.java
+# ---------------------------
+## type: 1: USER_BIND_SERVICE        Secondary user tries binding to the system sysui service
+##       2: USER_SYSTEM_BOUND        Secondary user is bound to the system sysui service
+##       3: USER_SYSTEM_UNBOUND      Secondary user loses connection after system sysui has died
+##       4: SYSTEM_REGISTER_USER     System sysui registers user's callbacks
+##       5: SYSTEM_UNREGISTER_USER   System sysui unregisters user's callbacks (after death)
+36060 sysui_recents_connection (type|1),(user|1)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index e4fd31d..f5ae351 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -31,10 +31,13 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.EventLog;
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
 
+import com.android.systemui.EventLogConstants;
+import com.android.systemui.EventLogTags;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
 import com.android.systemui.recents.events.EventBus;
@@ -83,20 +86,23 @@
     private int mDraggingInRecentsCurrentUser;
 
     // Only For system user, this is the callbacks instance we return to each secondary user
-    private RecentsSystemUser mSystemUserCallbacks;
+    private RecentsSystemUser mSystemToUserCallbacks;
 
     // Only for secondary users, this is the callbacks instance provided by the system user to make
     // calls back
-    private IRecentsSystemUserCallbacks mCallbacksToSystemUser;
+    private IRecentsSystemUserCallbacks mUserToSystemCallbacks;
 
     // The set of runnables to run after binding to the system user's service.
     private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
 
     // Only for secondary users, this is the death handler for the binder from the system user
-    private final IBinder.DeathRecipient mCallbacksToSystemUserDeathRcpt = new IBinder.DeathRecipient() {
+    private final IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = new IBinder.DeathRecipient() {
         @Override
         public void binderDied() {
-            mCallbacksToSystemUser = null;
+            mUserToSystemCallbacks = null;
+            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND,
+                    sSystemServicesProxy.getProcessUser());
 
             // Retry after a fixed duration
             mHandler.postDelayed(new Runnable() {
@@ -109,16 +115,19 @@
     };
 
     // Only for secondary users, this is the service connection we use to connect to the system user
-    private final ServiceConnection mServiceConnectionToSystemUser = new ServiceConnection() {
+    private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (service != null) {
-                mCallbacksToSystemUser = IRecentsSystemUserCallbacks.Stub.asInterface(
+                mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(
                         service);
+                EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                        EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND,
+                        sSystemServicesProxy.getProcessUser());
 
                 // Listen for system user's death, so that we can reconnect later
                 try {
-                    service.linkToDeath(mCallbacksToSystemUserDeathRcpt, 0);
+                    service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Lost connection to (System) SystemUI", e);
                 }
@@ -142,7 +151,7 @@
      * Returns the callbacks interface that non-system users can call.
      */
     public IBinder getSystemUserCallbacks() {
-        return mSystemUserCallbacks;
+        return mSystemToUserCallbacks;
     }
 
     public static RecentsTaskLoader getTaskLoader() {
@@ -190,7 +199,7 @@
         if (sSystemServicesProxy.isSystemUser(processUser)) {
             // For the system user, initialize an instance of the interface that we can pass to the
             // secondary user
-            mSystemUserCallbacks = new RecentsSystemUser(mContext, mImpl);
+            mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
         } else {
             // For the secondary user, bind to the primary user's service to get a persistent
             // interface to register its implementation and to later update its state
@@ -224,9 +233,9 @@
             mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
                     true /* animate */, false /* reloadTasks */);
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                 if (callbacks != null) {
                     try {
                         callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
@@ -260,9 +269,9 @@
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                 if (callbacks != null) {
                     try {
                         callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
@@ -295,9 +304,9 @@
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.toggleRecents();
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                 if (callbacks != null) {
                     try {
                         callbacks.toggleRecents();
@@ -326,9 +335,9 @@
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.preloadRecents();
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                 if (callbacks != null) {
                     try {
                         callbacks.preloadRecents();
@@ -354,9 +363,9 @@
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.cancelPreloadingRecents();
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                 if (callbacks != null) {
                     try {
                         callbacks.cancelPreloadingRecents();
@@ -387,9 +396,9 @@
             if (sSystemServicesProxy.isSystemUser(currentUser)) {
                 mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
             } else {
-                if (mSystemUserCallbacks != null) {
+                if (mSystemToUserCallbacks != null) {
                     IRecentsNonSystemUserCallbacks callbacks =
-                            mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                            mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                     if (callbacks != null) {
                         try {
                             callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode,
@@ -413,9 +422,9 @@
         if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
             mImpl.onDraggingInRecents(distanceFromTop);
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(
                                 mDraggingInRecentsCurrentUser);
                 if (callbacks != null) {
                     try {
@@ -436,9 +445,9 @@
         if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
             mImpl.onDraggingInRecentsEnded(velocity);
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(
                                 mDraggingInRecentsCurrentUser);
                 if (callbacks != null) {
                     try {
@@ -484,9 +493,9 @@
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.onConfigurationChanged();
         } else {
-            if (mSystemUserCallbacks != null) {
+            if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
-                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                 if (callbacks != null) {
                     try {
                         callbacks.onConfigurationChanged();
@@ -512,7 +521,7 @@
                 @Override
                 public void run() {
                     try {
-                        mCallbacksToSystemUser.updateRecentsVisibility(event.visible);
+                        mUserToSystemCallbacks.updateRecentsVisibility(event.visible);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
@@ -533,7 +542,7 @@
                 @Override
                 public void run() {
                     try {
-                        mCallbacksToSystemUser.startScreenPinning();
+                        mUserToSystemCallbacks.startScreenPinning();
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
@@ -549,7 +558,7 @@
                 @Override
                 public void run() {
                     try {
-                        mCallbacksToSystemUser.sendRecentsDrawnEvent();
+                        mUserToSystemCallbacks.sendRecentsDrawnEvent();
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
@@ -565,7 +574,7 @@
                 @Override
                 public void run() {
                     try {
-                        mCallbacksToSystemUser.sendDockingTopTaskEvent(event.dragMode);
+                        mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
@@ -581,7 +590,7 @@
                 @Override
                 public void run() {
                     try {
-                        mCallbacksToSystemUser.sendLaunchRecentsEvent();
+                        mUserToSystemCallbacks.sendLaunchRecentsEvent();
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
@@ -599,7 +608,7 @@
             @Override
             public void run() {
                 try {
-                    mCallbacksToSystemUser.registerNonSystemUserCallbacks(
+                    mUserToSystemCallbacks.registerNonSystemUserCallbacks(
                             new RecentsImplProxy(mImpl), processUser);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to register", e);
@@ -614,11 +623,14 @@
      */
     private void postToSystemUser(final Runnable onConnectRunnable) {
         mOnConnectRunnables.add(onConnectRunnable);
-        if (mCallbacksToSystemUser == null) {
+        if (mUserToSystemCallbacks == null) {
             Intent systemUserServiceIntent = new Intent();
             systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
             boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
-                    mServiceConnectionToSystemUser, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+                    mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,
+                    sSystemServicesProxy.getProcessUser());
             if (!bound) {
                 // Retry after a fixed duration
                 mHandler.postDelayed(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 9d4f425..c2a6108 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -230,7 +230,7 @@
      * Dismisses the history view back into the stack view.
      */
     boolean dismissHistory() {
-        if (mRecentsView.isHistoryVisible()) {
+        if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
             EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
             return true;
         }
@@ -447,7 +447,7 @@
 
         // Reset some states
         mIgnoreAltTabRelease = false;
-        if (mRecentsView.isHistoryVisible()) {
+        if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
             EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
         }
 
@@ -503,13 +503,16 @@
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, mRecentsView.isHistoryVisible());
+        if (RecentsDebugFlags.Static.EnableHistory) {
+            outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, mRecentsView.isHistoryVisible());
+        }
     }
 
     @Override
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
-        if (savedInstanceState.getBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, false)) {
+        if (RecentsDebugFlags.Static.EnableHistory &&
+                savedInstanceState.getBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, false)) {
             EventBus.getDefault().send(new ShowHistoryEvent());
         }
     }
@@ -603,7 +606,7 @@
     /**** EventBus events ****/
 
     public final void onBusEvent(ToggleRecentsEvent event) {
-        if (!dismissHistory()) {
+        if (!RecentsDebugFlags.Static.EnableHistory || !dismissHistory()) {
             RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
             if (launchState.launchedFromHome) {
                 dismissRecentsToHome(true /* animateTaskViews */);
@@ -614,7 +617,7 @@
     }
 
     public final void onBusEvent(IterateRecentsEvent event) {
-        if (!dismissHistory()) {
+        if (!RecentsDebugFlags.Static.EnableHistory || !dismissHistory()) {
             final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
 
             // Start dozing after the recents button is clicked
@@ -651,7 +654,7 @@
             }
         } else if (event.triggeredFromHomeKey) {
             // Otherwise, dismiss Recents to Home
-            if (mRecentsView.isHistoryVisible()) {
+            if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
                 // If the history view is visible, then just cross-fade home
                 ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
                                 R.anim.recents_to_launcher_enter,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index 711d834..cd64323 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -37,6 +37,8 @@
         public static final boolean DisableBackgroundCache = false;
         // Enables the task affiliations
         public static final boolean EnableAffiliatedTaskGroups = true;
+        // Enables the history
+        public static final boolean EnableHistory = false;
         // Overrides the Tuner flags and enables the fast toggle and timeout
         public static final boolean EnableFastToggleTimeoutOverride = true;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index e0efaa5..8de964b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -406,9 +406,9 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
         ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
         MutableBoolean topTaskHome = new MutableBoolean(true);
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        sInstanceLoadPlan = loader.createLoadPlan(mContext);
         if (topTask != null && !ssp.isRecentsTopMost(topTask, topTaskHome)) {
+            RecentsTaskLoader loader = Recents.getTaskLoader();
+            sInstanceLoadPlan = loader.createLoadPlan(mContext);
             sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
             loader.preloadTasks(sInstanceLoadPlan, topTask.id, topTaskHome.value);
             TaskStack stack = sInstanceLoadPlan.getTaskStack();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index ae0051c..f8000b8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -19,9 +19,12 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.systemui.EventLogConstants;
+import com.android.systemui.EventLogTags;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockingTopTaskEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
@@ -46,7 +49,8 @@
     }
 
     @Override
-    public void registerNonSystemUserCallbacks(final IBinder nonSystemUserCallbacks, int userId) {
+    public void registerNonSystemUserCallbacks(final IBinder nonSystemUserCallbacks,
+            final int userId) {
         try {
             final IRecentsNonSystemUserCallbacks callback =
                     IRecentsNonSystemUserCallbacks.Stub.asInterface(nonSystemUserCallbacks);
@@ -54,9 +58,14 @@
                 @Override
                 public void binderDied() {
                     mNonSystemUserRecents.removeAt(mNonSystemUserRecents.indexOfValue(callback));
+                    EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                            EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_UNREGISTER_USER,
+                            userId);
                 }
             }, 0);
             mNonSystemUserRecents.put(userId, callback);
+            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
+                    EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_REGISTER_USER, userId);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register NonSystemUserCallbacks", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index c4db485..2e45627 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -143,21 +143,24 @@
         final float cornerRadius = context.getResources().getDimensionPixelSize(
                 R.dimen.recents_task_view_rounded_corners_radius);
         LayoutInflater inflater = LayoutInflater.from(context);
-        mHistoryButton = (TextView) inflater.inflate(R.layout.recents_history_button, this, false);
-        mHistoryButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                EventBus.getDefault().send(new ToggleHistoryEvent());
-            }
-        });
-        addView(mHistoryButton);
-        mHistoryButton.setClipToOutline(true);
-        mHistoryButton.setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
-            }
-        });
+        if (RecentsDebugFlags.Static.EnableHistory) {
+            mHistoryButton = (TextView) inflater.inflate(R.layout.recents_history_button, this,
+                    false);
+            mHistoryButton.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    EventBus.getDefault().send(new ToggleHistoryEvent());
+                }
+            });
+            addView(mHistoryButton);
+            mHistoryButton.setClipToOutline(true);
+            mHistoryButton.setOutlineProvider(new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View view, Outline outline) {
+                    outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
+                }
+            });
+        }
         mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
         addView(mEmptyView);
 
@@ -331,7 +334,9 @@
         mTaskStackView.setVisibility(View.INVISIBLE);
         mEmptyView.setVisibility(View.VISIBLE);
         mEmptyView.bringToFront();
-        mHistoryButton.bringToFront();
+        if (RecentsDebugFlags.Static.EnableHistory) {
+            mHistoryButton.bringToFront();
+        }
     }
 
     /**
@@ -347,7 +352,9 @@
         if (mSearchBar != null) {
             mSearchBar.bringToFront();
         }
-        mHistoryButton.bringToFront();
+        if (RecentsDebugFlags.Static.EnableHistory) {
+            mHistoryButton.bringToFront();
+        }
     }
 
     @Override
@@ -397,21 +404,23 @@
                     MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
         }
 
-        // Measure the history view
-        if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
-            measureChild(mHistoryView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
-        }
+        if (RecentsDebugFlags.Static.EnableHistory) {
+            // Measure the history view
+            if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
+                measureChild(mHistoryView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+            }
 
-        // Measure the history button within the constraints of the space above the stack
-        Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
-        measureChild(mHistoryButton,
-                MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
-                MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
-        if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
-            measureChild(mHistoryClearAllButton,
+            // Measure the history button within the constraints of the space above the stack
+            Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
+            measureChild(mHistoryButton,
                     MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
                     MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
+            if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
+                measureChild(mHistoryClearAllButton,
+                    MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
+                    MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
+            }
         }
 
         setMeasuredDimension(width, height);
@@ -443,36 +452,39 @@
             mEmptyView.layout(left, top, right, bottom);
         }
 
-        // Layout the history view
-        if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
-            mHistoryView.layout(left, top, right, bottom);
-        }
+        if (RecentsDebugFlags.Static.EnableHistory) {
+            // Layout the history view
+            if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
+                mHistoryView.layout(left, top, right, bottom);
+            }
 
-        // Layout the history button such that its drawable is start-aligned with the stack,
-        // vertically centered in the available space above the stack
-        Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
-        int historyLeft = isLayoutRtl()
-                ? historyButtonRect.right + mHistoryButton.getPaddingStart()
-                        - mHistoryButton.getMeasuredWidth()
-                : historyButtonRect.left - mHistoryButton.getPaddingStart();
-        int historyTop = historyButtonRect.top +
-                (historyButtonRect.height() - mHistoryButton.getMeasuredHeight()) / 2;
-        mHistoryButton.layout(historyLeft, historyTop,
-                historyLeft + mHistoryButton.getMeasuredWidth(),
-                historyTop + mHistoryButton.getMeasuredHeight());
+            // Layout the history button such that its drawable is start-aligned with the stack,
+            // vertically centered in the available space above the stack
+            Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
+            int historyLeft = isLayoutRtl()
+                    ? historyButtonRect.right + mHistoryButton.getPaddingStart()
+                    - mHistoryButton.getMeasuredWidth()
+                    : historyButtonRect.left - mHistoryButton.getPaddingStart();
+            int historyTop = historyButtonRect.top +
+                    (historyButtonRect.height() - mHistoryButton.getMeasuredHeight()) / 2;
+            mHistoryButton.layout(historyLeft, historyTop,
+                    historyLeft + mHistoryButton.getMeasuredWidth(),
+                    historyTop + mHistoryButton.getMeasuredHeight());
 
-        // Layout the history clear all button such that it is end-aligned with the stack,
-        // vertically centered in the available space above the stack
-        if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
-            int clearAllLeft = isLayoutRtl()
-                    ? historyButtonRect.left - mHistoryClearAllButton.getPaddingStart()
-                    : historyButtonRect.right + mHistoryClearAllButton.getPaddingStart()
-                            - mHistoryClearAllButton.getMeasuredWidth();
-            int clearAllTop = historyButtonRect.top +
-                    (historyButtonRect.height() - mHistoryClearAllButton.getMeasuredHeight()) / 2;
-            mHistoryClearAllButton.layout(clearAllLeft, clearAllTop,
-                    clearAllLeft + mHistoryClearAllButton.getMeasuredWidth(),
-                    clearAllTop + mHistoryClearAllButton.getMeasuredHeight());
+            // Layout the history clear all button such that it is end-aligned with the stack,
+            // vertically centered in the available space above the stack
+            if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
+                int clearAllLeft = isLayoutRtl()
+                        ? historyButtonRect.left - mHistoryClearAllButton.getPaddingStart()
+                        : historyButtonRect.right + mHistoryClearAllButton.getPaddingStart()
+                        - mHistoryClearAllButton.getMeasuredWidth();
+                int clearAllTop = historyButtonRect.top +
+                        (historyButtonRect.height() - mHistoryClearAllButton.getMeasuredHeight()) /
+                                2;
+                mHistoryClearAllButton.layout(clearAllLeft, clearAllTop,
+                        clearAllLeft + mHistoryClearAllButton.getMeasuredWidth(),
+                        clearAllTop + mHistoryClearAllButton.getMeasuredHeight());
+            }
         }
 
         if (mAwaitingFirstLayout) {
@@ -540,9 +552,11 @@
     }
 
     public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
-        // Hide the history button
         int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
-        hideHistoryButton(taskViewExitToHomeDuration, false /* translate */);
+        if (RecentsDebugFlags.Static.EnableHistory) {
+            // Hide the history button
+            hideHistoryButton(taskViewExitToHomeDuration, false /* translate */);
+        }
         animateBackgroundScrim(0f, taskViewExitToHomeDuration);
     }
 
@@ -675,11 +689,17 @@
             // Reset the view state
             mAwaitingFirstLayout = true;
             mLastTaskLaunchedWasFreeform = false;
-            hideHistoryButton(0, false /* translate */);
+            if (RecentsDebugFlags.Static.EnableHistory) {
+                hideHistoryButton(0, false /* translate */);
+            }
         }
     }
 
     public final void onBusEvent(ToggleHistoryEvent event) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         if (mHistoryView != null && mHistoryView.isVisible()) {
             EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
         } else {
@@ -688,6 +708,10 @@
     }
 
     public final void onBusEvent(ShowHistoryEvent event) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         if (mHistoryView == null) {
             LayoutInflater inflater = LayoutInflater.from(getContext());
             mHistoryView = (RecentsHistoryView) inflater.inflate(R.layout.recents_history, this,
@@ -746,6 +770,10 @@
     }
 
     public final void onBusEvent(HideHistoryEvent event) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         // Animate the empty view in parallel with the history view (the task view animations are
         // handled in TaskStackView)
         Rect stackRect = mTaskStackView.mLayoutAlgorithm.mStackRect;
@@ -765,10 +793,18 @@
     }
 
     public final void onBusEvent(ShowHistoryButtonEvent event) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         showHistoryButton(150, event.translate);
     }
 
     public final void onBusEvent(HideHistoryButtonEvent event) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         hideHistoryButton(100, true /* translate */);
     }
 
@@ -776,6 +812,10 @@
      * Shows the history button.
      */
     private void showHistoryButton(final int duration, final boolean translate) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
         if (mHistoryButton.getVisibility() == View.INVISIBLE) {
             mHistoryButton.setVisibility(View.VISIBLE);
@@ -808,6 +848,10 @@
      * Hides the history button.
      */
     private void hideHistoryButton(int duration, boolean translate) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
         hideHistoryButton(duration, translate, postAnimationTrigger);
         postAnimationTrigger.flushLastDecrementRunnables();
@@ -818,6 +862,10 @@
      */
     private void hideHistoryButton(int duration, boolean translate,
             final ReferenceCountedTrigger postAnimationTrigger) {
+        if (!RecentsDebugFlags.Static.EnableHistory) {
+            return;
+        }
+
         if (mHistoryButton.getVisibility() == View.VISIBLE) {
             if (translate) {
                 mHistoryButton.animate()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 890b445..2cd0c19 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -467,12 +467,13 @@
 
         // Setup the end listener to return all the hidden views to the view pool after the
         // focus animation
-        AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
+        ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
+        postAnimTrigger.addLastDecrementRunnable(new Runnable() {
             @Override
-            public void onAnimationEnd(Animator animation) {
+            public void run() {
                 mStackView.bindVisibleTaskViews(newScroll);
             }
-        };
+        });
 
         List<TaskView> taskViews = mStackView.getTaskViews();
         int taskViewCount = taskViews.size();
@@ -513,7 +514,8 @@
             AnimationProps anim = new AnimationProps()
                     .setDuration(AnimationProps.BOUNDS, duration)
                     .setInterpolator(AnimationProps.BOUNDS, interpolator)
-                    .setListener(endListener);
+                    .setListener(postAnimTrigger.decrementOnAnimationEnd());
+            postAnimTrigger.increment();
             mStackView.updateTaskViewToTransform(tv, toTransform, anim);
         }
         return willScroll;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 79c21f3..33315c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -107,7 +107,7 @@
                     mPanel.setPanelScrimMinFraction((float) expandedHeight
                             / mPanel.getMaxPanelHeight());
                     mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
-                    mPanel.clearNotificattonEffects();
+                    mPanel.clearNotificationEffects();
                     return true;
                 }
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 88a7843..1a0acbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -89,13 +89,13 @@
     private KeyguardAffordanceHelper mAfforanceHelper;
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private KeyguardStatusBarView mKeyguardStatusBar;
-    private QSContainer mQsContainer;
+    protected QSContainer mQsContainer;
     private KeyguardStatusView mKeyguardStatusView;
     private TextView mClockView;
     private View mReserveNotificationSpace;
     private View mQsNavbarScrim;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
-    private NotificationStackScrollLayout mNotificationStackScroller;
+    protected NotificationStackScrollLayout mNotificationStackScroller;
     private boolean mAnimateNextTopPaddingChange;
 
     private int mTrackingPointer;
@@ -126,9 +126,9 @@
     private float mInitialTouchY;
     private float mLastTouchX;
     private float mLastTouchY;
-    private float mQsExpansionHeight;
-    private int mQsMinExpansionHeight;
-    private int mQsMaxExpansionHeight;
+    protected float mQsExpansionHeight;
+    protected int mQsMinExpansionHeight;
+    protected int mQsMaxExpansionHeight;
     private int mQsPeekHeight;
     private boolean mStackScrollerOverscrolling;
     private boolean mQsExpansionFromOverscroll;
@@ -1072,8 +1072,8 @@
 
     private void setKeyguardBottomAreaVisibility(int statusBarState,
             boolean goingToFullShade) {
+        mKeyguardBottomArea.animate().cancel();
         if (goingToFullShade) {
-            mKeyguardBottomArea.animate().cancel();
             mKeyguardBottomArea.animate()
                     .alpha(0f)
                     .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
@@ -1083,13 +1083,11 @@
                     .start();
         } else if (statusBarState == StatusBarState.KEYGUARD
                 || statusBarState == StatusBarState.SHADE_LOCKED) {
-            mKeyguardBottomArea.animate().cancel();
             if (!mDozing) {
                 mKeyguardBottomArea.setVisibility(View.VISIBLE);
             }
             mKeyguardBottomArea.setAlpha(1f);
         } else {
-            mKeyguardBottomArea.animate().cancel();
             mKeyguardBottomArea.setVisibility(View.GONE);
             mKeyguardBottomArea.setAlpha(1f);
         }
@@ -1196,7 +1194,7 @@
         }
     }
 
-    private void updateQsExpansion() {
+    protected void updateQsExpansion() {
         mQsContainer.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation());
     }
 
@@ -1238,7 +1236,7 @@
         }
     }
 
-    private void requestScrollerTopPaddingUpdate(boolean animate) {
+    protected void requestScrollerTopPaddingUpdate(boolean animate) {
         mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(),
                 mAnimateNextTopPaddingChange || animate,
                 mKeyguardShowing
@@ -1520,16 +1518,12 @@
         updateQsExpansion();
     }
 
-    private float getHeaderTranslation() {
+    protected float getHeaderTranslation() {
         if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
             return 0;
         }
         if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
-            if (mExpandedHeight / HEADER_RUBBERBAND_FACTOR >= mQsMinExpansionHeight) {
-                return 0;
-            } else {
-                return mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight;
-            }
+            return Math.min(0, mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight);
         }
         float stackTranslation = mNotificationStackScroller.getStackTranslation();
         float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR;
@@ -2191,7 +2185,7 @@
      *
      * @param x the x-coordinate the touch event
      */
-    private void updateVerticalPanelPosition(float x) {
+    protected void updateVerticalPanelPosition(float x) {
         if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) {
             resetVerticalPanelPosition();
             return;
@@ -2216,7 +2210,7 @@
         mQsContainer.setTranslationX(translation);
     }
 
-    private void updateStackHeight(float stackHeight) {
+    protected void updateStackHeight(float stackHeight) {
         mNotificationStackScroller.setStackHeight(stackHeight);
         updateKeyguardBottomAreaAlpha();
     }
@@ -2225,7 +2219,7 @@
         mBar.panelScrimMinFractionChanged(minFraction);
     }
 
-    public void clearNotificattonEffects() {
+    public void clearNotificationEffects() {
         mStatusBar.clearNotificationEffects();
     }
 
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 3dff37b..3bef19e 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -2346,62 +2346,75 @@
         ALOGD("nScriptGroupCreate, con(%p)", (RsContext)con);
     }
 
+    jlong id = 0;
+
+    RsScriptKernelID* kernelsPtr;
     jint kernelsLen = _env->GetArrayLength(_kernels);
     jlong *jKernelsPtr = _env->GetLongArrayElements(_kernels, nullptr);
+
+    RsScriptKernelID* srcPtr;
+    jint srcLen = _env->GetArrayLength(_src);
+    jlong *jSrcPtr = _env->GetLongArrayElements(_src, nullptr);
+
+    RsScriptKernelID* dstkPtr;
+    jint dstkLen = _env->GetArrayLength(_dstk);
+    jlong *jDstkPtr = _env->GetLongArrayElements(_dstk, nullptr);
+
+    RsScriptKernelID* dstfPtr;
+    jint dstfLen = _env->GetArrayLength(_dstf);
+    jlong *jDstfPtr = _env->GetLongArrayElements(_dstf, nullptr);
+
+    RsType* typesPtr;
+    jint typesLen = _env->GetArrayLength(_types);
+    jlong *jTypesPtr = _env->GetLongArrayElements(_types, nullptr);
+
     if (jKernelsPtr == nullptr) {
         ALOGE("Failed to get Java array elements: kernels");
-        return 0;
+        goto cleanup;
     }
-    RsScriptKernelID* kernelsPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * kernelsLen);
+    if (jSrcPtr == nullptr) {
+        ALOGE("Failed to get Java array elements: src");
+        goto cleanup;
+    }
+    if (jDstkPtr == nullptr) {
+        ALOGE("Failed to get Java array elements: dstk");
+        goto cleanup;
+    }
+    if (jDstfPtr == nullptr) {
+        ALOGE("Failed to get Java array elements: dstf");
+        goto cleanup;
+    }
+    if (jTypesPtr == nullptr) {
+        ALOGE("Failed to get Java array elements: types");
+        goto cleanup;
+    }
+
+    kernelsPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * kernelsLen);
     for(int i = 0; i < kernelsLen; ++i) {
         kernelsPtr[i] = (RsScriptKernelID)jKernelsPtr[i];
     }
 
-    jint srcLen = _env->GetArrayLength(_src);
-    jlong *jSrcPtr = _env->GetLongArrayElements(_src, nullptr);
-    if (jSrcPtr == nullptr) {
-        ALOGE("Failed to get Java array elements: src");
-        return 0;
-    }
-    RsScriptKernelID* srcPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * srcLen);
+    srcPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * srcLen);
     for(int i = 0; i < srcLen; ++i) {
         srcPtr[i] = (RsScriptKernelID)jSrcPtr[i];
     }
 
-    jint dstkLen = _env->GetArrayLength(_dstk);
-    jlong *jDstkPtr = _env->GetLongArrayElements(_dstk, nullptr);
-    if (jDstkPtr == nullptr) {
-        ALOGE("Failed to get Java array elements: dstk");
-        return 0;
-    }
-    RsScriptKernelID* dstkPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstkLen);
+    dstkPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstkLen);
     for(int i = 0; i < dstkLen; ++i) {
         dstkPtr[i] = (RsScriptKernelID)jDstkPtr[i];
     }
 
-    jint dstfLen = _env->GetArrayLength(_dstf);
-    jlong *jDstfPtr = _env->GetLongArrayElements(_dstf, nullptr);
-    if (jDstfPtr == nullptr) {
-        ALOGE("Failed to get Java array elements: dstf");
-        return 0;
-    }
-    RsScriptKernelID* dstfPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstfLen);
+    dstfPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstfLen);
     for(int i = 0; i < dstfLen; ++i) {
         dstfPtr[i] = (RsScriptKernelID)jDstfPtr[i];
     }
 
-    jint typesLen = _env->GetArrayLength(_types);
-    jlong *jTypesPtr = _env->GetLongArrayElements(_types, nullptr);
-    if (jTypesPtr == nullptr) {
-        ALOGE("Failed to get Java array elements: types");
-        return 0;
-    }
-    RsType* typesPtr = (RsType*) malloc(sizeof(RsType) * typesLen);
+    typesPtr = (RsType*) malloc(sizeof(RsType) * typesLen);
     for(int i = 0; i < typesLen; ++i) {
         typesPtr[i] = (RsType)jTypesPtr[i];
     }
 
-    jlong id = (jlong)(uintptr_t)rsScriptGroupCreate((RsContext)con,
+    id = (jlong)(uintptr_t)rsScriptGroupCreate((RsContext)con,
                                (RsScriptKernelID *)kernelsPtr, kernelsLen * sizeof(RsScriptKernelID),
                                (RsScriptKernelID *)srcPtr, srcLen * sizeof(RsScriptKernelID),
                                (RsScriptKernelID *)dstkPtr, dstkLen * sizeof(RsScriptKernelID),
@@ -2413,11 +2426,24 @@
     free(dstkPtr);
     free(dstfPtr);
     free(typesPtr);
-    _env->ReleaseLongArrayElements(_kernels, jKernelsPtr, 0);
-    _env->ReleaseLongArrayElements(_src, jSrcPtr, 0);
-    _env->ReleaseLongArrayElements(_dstk, jDstkPtr, 0);
-    _env->ReleaseLongArrayElements(_dstf, jDstfPtr, 0);
-    _env->ReleaseLongArrayElements(_types, jTypesPtr, 0);
+
+cleanup:
+    if (jKernelsPtr != nullptr) {
+        _env->ReleaseLongArrayElements(_kernels, jKernelsPtr, 0);
+    }
+    if (jSrcPtr != nullptr) {
+        _env->ReleaseLongArrayElements(_src, jSrcPtr, 0);
+    }
+    if (jDstkPtr != nullptr) {
+        _env->ReleaseLongArrayElements(_dstk, jDstkPtr, 0);
+    }
+    if (jDstfPtr != nullptr) {
+        _env->ReleaseLongArrayElements(_dstf, jDstfPtr, 0);
+    }
+    if (jTypesPtr != nullptr) {
+        _env->ReleaseLongArrayElements(_types, jTypesPtr, 0);
+    }
+
     return id;
 }
 
@@ -2662,45 +2688,61 @@
         ALOGD("nMeshCreate, con(%p)", (RsContext)con);
     }
 
+    jlong id = 0;
+
+    RsAllocation* vtxPtr;
     jint vtxLen = _env->GetArrayLength(_vtx);
     jlong *jVtxPtr = _env->GetLongArrayElements(_vtx, nullptr);
+
+    RsAllocation* idxPtr;
+    jint idxLen = _env->GetArrayLength(_idx);
+    jlong *jIdxPtr = _env->GetLongArrayElements(_idx, nullptr);
+
+    jint primLen = _env->GetArrayLength(_prim);
+    jint *primPtr = _env->GetIntArrayElements(_prim, nullptr);
+
     if (jVtxPtr == nullptr) {
         ALOGE("Failed to get Java array elements: vtx");
-        return 0;
+        goto cleanupMesh;
     }
-    RsAllocation* vtxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * vtxLen);
+    if (jIdxPtr == nullptr) {
+        ALOGE("Failed to get Java array elements: idx");
+        goto cleanupMesh;
+    }
+    if (primPtr == nullptr) {
+        ALOGE("Failed to get Java array elements: prim");
+        goto cleanupMesh;
+    }
+
+    vtxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * vtxLen);
     for(int i = 0; i < vtxLen; ++i) {
         vtxPtr[i] = (RsAllocation)(uintptr_t)jVtxPtr[i];
     }
 
-    jint idxLen = _env->GetArrayLength(_idx);
-    jlong *jIdxPtr = _env->GetLongArrayElements(_idx, nullptr);
-    if (jIdxPtr == nullptr) {
-        ALOGE("Failed to get Java array elements: idx");
-        return 0;
-    }
-    RsAllocation* idxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * idxLen);
+    idxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * idxLen);
     for(int i = 0; i < idxLen; ++i) {
         idxPtr[i] = (RsAllocation)(uintptr_t)jIdxPtr[i];
     }
 
-    jint primLen = _env->GetArrayLength(_prim);
-    jint *primPtr = _env->GetIntArrayElements(_prim, nullptr);
-    if (primPtr == nullptr) {
-        ALOGE("Failed to get Java array elements: prim");
-        return 0;
-    }
-
-    jlong id = (jlong)(uintptr_t)rsMeshCreate((RsContext)con,
-                               (RsAllocation *)vtxPtr, vtxLen,
-                               (RsAllocation *)idxPtr, idxLen,
-                               (uint32_t *)primPtr, primLen);
+    id = (jlong)(uintptr_t)rsMeshCreate((RsContext)con,
+                                        (RsAllocation *)vtxPtr, vtxLen,
+                                        (RsAllocation *)idxPtr, idxLen,
+                                        (uint32_t *)primPtr, primLen);
 
     free(vtxPtr);
     free(idxPtr);
-    _env->ReleaseLongArrayElements(_vtx, jVtxPtr, 0);
-    _env->ReleaseLongArrayElements(_idx, jIdxPtr, 0);
-    _env->ReleaseIntArrayElements(_prim, primPtr, 0);
+
+cleanupMesh:
+    if (jVtxPtr != nullptr) {
+        _env->ReleaseLongArrayElements(_vtx, jVtxPtr, 0);
+    }
+    if (jIdxPtr != nullptr) {
+        _env->ReleaseLongArrayElements(_idx, jIdxPtr, 0);
+    }
+    if (primPtr != nullptr) {
+        _env->ReleaseIntArrayElements(_prim, primPtr, 0);
+    }
+
     return id;
 }
 
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 499b706..6d0d9e9 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1672,6 +1672,7 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String args[]) {
+      mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
       if (mBluetoothBinder == null) {
         writer.println("Bluetooth Service not connected");
       } else {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 6716a47..574b9db 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1902,7 +1902,7 @@
             return false;
         }
 
-        cancelInitializingActivities();
+        mStackSupervisor.cancelInitializingActivities();
 
         // Find the first activity that is not finishing.
         final ActivityRecord next = topRunningActivityLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 662c51e..c143474 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -908,6 +908,15 @@
         }
     }
 
+    void cancelInitializingActivities() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                stacks.get(stackNdx).cancelInitializingActivities();
+            }
+        }
+    }
+
     void reportActivityVisibleLocked(ActivityRecord r) {
         sendWaitingVisibleReportLocked(r);
     }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index cca6fc5..1166ae1 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1397,7 +1397,7 @@
                 }
                 intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                         mStartActivity.launchedFromPackage);
-            } else if (!mStartActivity.intent.filterEquals(intentActivity.task.intent)) {
+            } else if (!mStartActivity.intent.filterEquals(intentActivity.intent)) {
                 // In this case we are launching the root activity of the task, but with a
                 // different intent. We should start a new instance on top.
                 mAddingToTask = true;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 484b0e9..12c70a3 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -23,11 +23,13 @@
 
 import android.app.Notification;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
+import android.os.Build;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
@@ -146,6 +148,22 @@
             importance = IMPORTANCE_DEFAULT;
         }
 
+        try {
+            final ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
+                    sbn.getPackageName(), 0);
+            if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.N) {
+                if (isNoisy) {
+                    if (importance >= IMPORTANCE_HIGH) {
+                        importance = IMPORTANCE_MAX;
+                    } else {
+                        importance = IMPORTANCE_HIGH;
+                    }
+                }
+            }
+        } catch (NameNotFoundException e) {
+            // oh well.
+        }
+
         if (n.fullScreenIntent != null) {
             importance = IMPORTANCE_MAX;
         }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c6613f5..8d75f60 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -346,8 +346,7 @@
                 String packageName = component.getPackageName();
                 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                         Uri.fromParts("package", packageName, null));
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
-                        Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                 intent.setSourceBounds(sourceBounds);
                 mContext.startActivityAsUser(intent, opts, user);
             } finally {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 76d6b28..117c663 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1749,16 +1749,6 @@
         }
     }
 
-    private boolean isPackageInstalled(String pkg, int userId) {
-        final ApplicationInfo info = mPm.getApplicationInfo(pkg,
-                PackageManager.GET_UNINSTALLED_PACKAGES,
-                userId);
-        if (info == null || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
-            return false;
-        }
-        return true;
-    }
-
     /**
      * Removes the app restrictions file for a specific package and user id, if it exists.
      */
@@ -2210,20 +2200,18 @@
             }
         }
 
-        if (isPackageInstalled(packageName, userId)) {
-            // Notify package of changes via an intent - only sent to explicitly registered receivers.
-            Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
-            changeIntent.setPackage(packageName);
-            changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            mContext.sendBroadcastAsUser(changeIntent, new UserHandle(userId));
-        }
+        // Notify package of changes via an intent - only sent to explicitly registered receivers.
+        Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+        changeIntent.setPackage(packageName);
+        changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(userId));
     }
 
     private int getUidForPackage(String packageName) {
         long ident = Binder.clearCallingIdentity();
         try {
             return mContext.getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_UNINSTALLED_PACKAGES).uid;
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES).uid;
         } catch (NameNotFoundException nnfe) {
             return -1;
         } finally {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 412a455..685403c 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -27,6 +27,8 @@
 
 import com.android.server.wm.DimLayer.DimLayerUser;
 
+import java.util.ArrayList;
+
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.view.WindowManager.DOCKED_BOTTOM;
@@ -77,7 +79,17 @@
     }
 
     void setResizing(boolean resizing) {
-        mResizing = resizing;
+        if (mResizing != resizing) {
+            mResizing = resizing;
+            resetDragResizingChangeReported();
+        }
+    }
+
+    private void resetDragResizingChangeReported() {
+        final WindowList windowList = mDisplayContent.getWindowList();
+        for (int i = windowList.size() - 1; i >= 0; i--) {
+            windowList.get(i).resetDragResizingChangeReported();
+        }
     }
 
     void setWindow(WindowState window) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4167ac4..fe55e80 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -536,7 +536,20 @@
     }
 
     void setDragResizing(boolean dragResizing) {
-        mDragResizing = dragResizing;
+        if (mDragResizing != dragResizing) {
+            mDragResizing = dragResizing;
+            resetDragResizingChangeReported();
+        }
+    }
+
+    void resetDragResizingChangeReported() {
+        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState win = windows.get(winNdx);
+                win.resetDragResizingChangeReported();
+            }
+        }
     }
 
     boolean isDragResizing() {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7244676..d169b34 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -919,6 +919,16 @@
         return mDragResizing;
     }
 
+    private void setDragResizingLocked(boolean resizing) {
+        if (mDragResizing == resizing) {
+            return;
+        }
+        mDragResizing = resizing;
+        for (int i = mTasks.size() - 1; i >= 0 ; i--) {
+            mTasks.get(i).resetDragResizingChangeReported();
+        }
+    }
+
     @Override  // AnimatesBounds
     public boolean setSize(Rect bounds) {
         synchronized (mService.mWindowMap) {
@@ -936,14 +946,14 @@
     @Override  // AnimatesBounds
     public void onAnimationStart() {
         synchronized (mService.mWindowMap) {
-            mDragResizing = true;
+            setDragResizingLocked(true);
         }
     }
 
     @Override  // AnimatesBounds
     public void onAnimationEnd() {
         synchronized (mService.mWindowMap) {
-            mDragResizing = false;
+            setDragResizingLocked(false);
             mService.requestTraversal();
         }
         if (mStackId == PINNED_STACK_ID) {
@@ -968,4 +978,4 @@
     public void getFullScreenBounds(Rect bounds) {
         getDisplayContent().getContentRect(bounds);
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c8f5dda..5cd14c9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8839,7 +8839,8 @@
                 Slog.v(TAG_WM, "Win " + w + " config changed: "
                         + mCurConfiguration);
             }
-            final boolean dragResizingChanged = w.isDragResizeChanged();
+            final boolean dragResizingChanged = w.isDragResizeChanged()
+                    && !w.isDragResizingChangeReported();
             if (localLOGV) Slog.v(TAG_WM, "Resizing " + w
                     + ": configChanged=" + configChanged
                     + " dragResizingChanged=" + dragResizingChanged
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 37c8a7e..bea333b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -161,6 +161,7 @@
     boolean mAttachedHidden;    // is our parent window hidden?
     boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
     boolean mDragResizing;
+    boolean mDragResizingChangeReported;
     int mResizeMode;
 
     RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -2103,6 +2104,7 @@
         mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                 reportDraw, newConfig, getBackdropFrame(frame),
                 isDragResizeChanged() /* forceRelayout */);
+        mDragResizingChangeReported = true;
     }
 
     public void registerFocusObserver(IWindowFocusObserver observer) {
@@ -2137,6 +2139,20 @@
         return mDragResizing != computeDragResizing();
     }
 
+    /**
+     * @return Whether we reported a drag resize change to the application or not already.
+     */
+    boolean isDragResizingChangeReported() {
+        return mDragResizingChangeReported;
+    }
+
+    /**
+     * Resets the state whether we reported a drag resize change to the app.
+     */
+    void resetDragResizingChangeReported() {
+        mDragResizingChangeReported = false;
+    }
+
     int getResizeMode() {
         return mResizeMode;
     }
@@ -2161,7 +2177,11 @@
     }
 
     void setDragResizing() {
-        mDragResizing = computeDragResizing();
+        final boolean resizing = computeDragResizing();
+        if (resizing == mDragResizing) {
+            return;
+        }
+        mDragResizing = resizing;
         mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing()
                 ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
                 : DRAG_RESIZE_MODE_FREEFORM;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 44be671..2373754 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -155,10 +155,6 @@
      * Do not place scans in the chip's scan history buffer
      */
     public static final int REPORT_EVENT_NO_BATCH = (1 << 2);
-    /**
-     * report full scan results and completion event to the context hub
-     */
-    public static final int REPORT_EVENT_CONTEXT_HUB = (1 << 3);
 
     /**
      * scan configuration parameters to be sent to {@link #startBackgroundScan}