Merge "Camera2: Unhide deferred surface configuration APIs"
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index f7f7c88..383cd01 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.AndroidException;
 import android.util.DisplayMetrics;
 import android.view.Display;
@@ -201,9 +202,11 @@
         try {
             if (density > 0) {
                 // TODO(multidisplay): For now Configuration only applies to main screen.
-                mWm.setForcedDisplayDensity(Display.DEFAULT_DISPLAY, density);
+                mWm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+                        UserHandle.USER_CURRENT);
             } else {
-                mWm.clearForcedDisplayDensity(Display.DEFAULT_DISPLAY);
+                mWm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+                        UserHandle.USER_CURRENT);
             }
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0ee0fc9..b05f3c2 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6406,21 +6406,6 @@
         public static final String WEB_ACTION_ENABLED = "web_action_enabled";
 
         /**
-         * The uptime when tasks were last persisted.  This is used to adjust the previous task
-         * active times to be relative to the current boot time.
-         * @hide
-         */
-        public static final String TASK_PERSISTER_LAST_WRITE_UPTIME = "task_persister_write_uptime";
-
-        /**
-         * Used by Overview to keep track of the last visible task's active time to determine what
-         * should tasks be visible.
-         * @hide
-         */
-        public static final String OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME =
-                "overview_last_visible_task_active_uptime";
-
-        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d2a3721..91d8c24 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -73,8 +73,8 @@
     void clearForcedDisplaySize(int displayId);
     int getInitialDisplayDensity(int displayId);
     int getBaseDisplayDensity(int displayId);
-    void setForcedDisplayDensity(int displayId, int density);
-    void clearForcedDisplayDensity(int displayId);
+    void setForcedDisplayDensityForUser(int displayId, int density, int userId);
+    void clearForcedDisplayDensityForUser(int displayId, int userId);
     void setForcedDisplayScalingMode(int displayId, int mode); // 0 = auto, 1 = disable
 
     void setOverscan(int displayId, int left, int top, int right, int bottom);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 3af8636..18e2118 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2233,7 +2233,7 @@
      */
     public void setText(CharSequence text) {
         enforceNotSealed();
-        mText = text;
+        mText = (text == null) ? null : text.subSequence(0, text.length());
     }
 
     /**
@@ -2250,7 +2250,7 @@
      */
     public void setError(CharSequence error) {
         enforceNotSealed();
-        mError = error;
+        mError = (error == null) ? null : error.subSequence(0, error.length());
     }
 
     /**
@@ -2285,7 +2285,8 @@
      */
     public void setContentDescription(CharSequence contentDescription) {
         enforceNotSealed();
-        mContentDescription = contentDescription;
+        mContentDescription = (contentDescription == null) ? null
+                : contentDescription.subSequence(0, contentDescription.length());
     }
 
     /**
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index f99690a..f2979bb 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -572,7 +572,8 @@
      */
     public void setBeforeText(CharSequence beforeText) {
         enforceNotSealed();
-        mBeforeText = beforeText;
+        mBeforeText = (beforeText == null) ? null
+                : beforeText.subSequence(0, beforeText.length());
     }
 
     /**
@@ -593,7 +594,8 @@
      */
     public void setContentDescription(CharSequence contentDescription) {
         enforceNotSealed();
-        mContentDescription = contentDescription;
+        mContentDescription = (contentDescription == null) ? null
+                : contentDescription.subSequence(0, contentDescription.length());
     }
 
     /**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index e45e413..4efcb09 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -460,7 +460,7 @@
                 // the view isn't yet added, so let's try not to crash.
                 if (mView.getParent() != null) {
                     if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
-                    mWM.removeView(mView);
+                    mWM.removeViewImmediate(mView);
                 }
 
                 mView = null;
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index a4b5a8e..cb2b019 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -145,7 +145,11 @@
                 if (itemType == TYPE_HEADER_SUGGESTED) {
                     textView.setText(R.string.language_picker_section_suggested);
                 } else {
-                    textView.setText(R.string.language_picker_section_all);
+                    if (mCountryMode) {
+                        textView.setText(R.string.region_picker_section_all);
+                    } else {
+                        textView.setText(R.string.language_picker_section_all);
+                    }
                 }
                 textView.setTextLocale(Locale.getDefault());
                 break;
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index a24aa31..8eb39e1 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -337,11 +337,15 @@
     return 0;
 }
 
-static void query_hub_for_apps(uint64_t appId, uint32_t hubHandle) {
+static void query_hub_for_apps(uint32_t hubHandle) {
     hub_message_t msg;
     query_apps_request_t queryMsg;
 
-    queryMsg.app_name.id = appId;
+    // TODO(b/30835598): When we're able to tell which request our
+    //     response matches, then we should allow this to be more
+    //     targetted, instead of always being every app in the
+    //     system.
+    queryMsg.app_name.id = ALL_APPS;
 
     msg.message_type = CONTEXT_HUB_QUERY_APPS;
     msg.message_len  = sizeof(queryMsg);
@@ -354,9 +358,9 @@
     }
 }
 
-static void sendQueryForApps(uint64_t appId) {
+static void sendQueryForApps() {
     for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
-        query_hub_for_apps(appId, i);
+        query_hub_for_apps(i);
     }
 }
 
@@ -386,9 +390,6 @@
 
 static jint add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle,
         jint appInstanceHandle, JNIEnv *env) {
-
-    ALOGI("Loading App");
-
     // Not checking if the apps are indeed distinct
     app_instance_info_s entry;
     assert(appInfo);
@@ -404,13 +405,14 @@
 
     db.appInstances[appInstanceHandle] = entry;
 
-    // Finally - let the service know of this app instance
+    // Finally - let the service know of this app instance, to populate
+    // the Java cache.
     env->CallIntMethod(db.jniInfo.jContextHubService,
                        db.jniInfo.contextHubServiceAddAppInstance,
                        hubHandle, entry.instanceId, entry.truncName,
                        entry.appInfo.version);
 
-    ALOGW("%s App 0x%" PRIx64 " on hub Handle %" PRId32
+    ALOGI("%s App 0x%" PRIx64 " on hub Handle %" PRId32
           " as appInstance %" PRId32, action, entry.truncName,
           entry.hubHandle, appInstanceHandle);
 
@@ -532,7 +534,7 @@
             }
         }
 
-        sendQueryForApps(ALL_APPS);
+        sendQueryForApps();
     } else {
         ALOGW("No Context Hub Module present");
     }
@@ -576,16 +578,63 @@
             return -1;
     }
 
-    int numApps = msgLen/sizeof(hub_app_info);
-    hub_app_info info;
+    int numApps = msgLen / sizeof(hub_app_info);
     const hub_app_info *unalignedInfoAddr = (const hub_app_info*)msg;
 
-    for (int i = 0; i < numApps; i++, unalignedInfoAddr++) {
-        memcpy(&info, unalignedInfoAddr, sizeof(info));
+    // We use this information to sync our JNI and Java caches of nanoapp info.
+    // We want to accomplish two things here:
+    // 1) Remove entries from our caches which are stale, and pertained to
+    //    apps no longer running on Context Hub.
+    // 2) Populate our caches with the latest information of all these apps.
+
+    // We make a couple of assumptions here:
+    // A) The JNI and Java caches are in sync with each other (this isn't
+    //    necessarily true; any failure of a single call into Java land to
+    //    update its cache will leave that cache in a bad state.  For NYC,
+    //    we're willing to tolerate this for now).
+    // B) The total number of apps is relatively small, so horribly inefficent
+    //    algorithms aren't too painful.
+    // C) We're going to call this relatively infrequently, so its inefficency
+    //    isn't a big impact.
+
+
+    // (1).  Looking for stale cache entries.  Yes, this is O(N^2).  See
+    // assumption (B).  Per assumption (A), it is sufficient to iterate
+    // over just the JNI cache.
+    auto end = db.appInstances.end();
+    for (auto current = db.appInstances.begin(); current != end; ) {
+        app_instance_info_s cache_entry = current->second;
+        // We perform our iteration here because if we call
+        // delete_app_instance() below, it will erase() this entry.
+        current++;
+        bool entryIsStale = true;
+        for (int i = 0; i < numApps; i++) {
+            // We use memcmp since this could be unaligned.
+            if (memcmp(&unalignedInfoAddr[i].app_name.id,
+                       &cache_entry.appInfo.app_name.id,
+                       sizeof(cache_entry.appInfo.app_name.id)) == 0) {
+                // We found a match; this entry is current.
+                entryIsStale = false;
+                break;
+            }
+        }
+        if (entryIsStale) {
+            delete_app_instance(cache_entry.instanceId, env);
+        }
+    }
+
+    // (2).  Update our caches with the latest.
+    for (int i = 0; i < numApps; i++) {
+        hub_app_info query_info;
+        memcpy(&query_info, &unalignedInfoAddr[i], sizeof(query_info));
         // We will only have one instance of the app
         // TODO : Change this logic once we support multiple instances of the same app
-        jint appInstance = get_app_instance_for_app_id(info.app_name.id);
-        add_app_instance(&info, hubHandle, appInstance, env);
+        jint appInstance = get_app_instance_for_app_id(query_info.app_name.id);
+        if (appInstance == -1) {
+            // This is a previously unknown app, let's allocate an "id" for it.
+            appInstance = generate_id();
+        }
+        add_app_instance(&query_info, hubHandle, appInstance, env);
     }
 
     return 0;
@@ -709,7 +758,12 @@
             ALOGW("Could not attach to JVM !");
             success = false;
         }
-        sendQueryForApps(info->appInfo.app_name.id);
+        // While we just called add_app_instance above, our info->appInfo was
+        // incomplete (for example, the 'version' is hardcoded to -1).  So we
+        // trigger an additional query to the CHRE, so we'll be able to get
+        // all the app "info", and have our JNI and Java caches with the
+        // full information.
+        sendQueryForApps();
     } else {
         ALOGW("Could not load the app successfully ! Unexpected failure");
         *appInstanceHandle = INVALID_APP_ID;
@@ -739,24 +793,6 @@
     return true;
 }
 
-static void invalidateNanoApps(uint32_t hubHandle) {
-    JNIEnv *env;
-
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
-        ALOGW("Could not attach to JVM !");
-        env = nullptr;
-    }
-
-    auto end = db.appInstances.end();
-    for (auto current = db.appInstances.begin(); current != end; ) {
-        app_instance_info_s info = current->second;
-        current++;
-        if (info.hubHandle == hubHandle) {
-             delete_app_instance(info.instanceId, env);
-        }
-    }
-}
-
 static int handle_os_message(uint32_t msgType, uint32_t hubHandle,
                              const uint8_t *msg, int msgLen) {
     int retVal = -1;
@@ -824,8 +860,7 @@
               ALOGW("Context Hub handle %d restarted", hubHandle);
               closeTxn();
               passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0);
-              invalidateNanoApps(hubHandle);
-              query_hub_for_apps(ALL_APPS, hubHandle);
+              query_hub_for_apps(hubHandle);
               retVal = 0;
           }
           break;
@@ -1157,7 +1192,8 @@
     if (retVal != 0) {
         ALOGD("Send Message failure - %d", retVal);
         if (msgType == CONTEXT_HUB_LOAD_APP) {
-            closeLoadTxn(false, nullptr);
+            jint ignored;
+            closeLoadTxn(false, &ignored);
         } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
             closeUnloadTxn(false);
         }
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 97833a0..7da0314 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -213,7 +213,9 @@
     sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
 
     status_t err = hardware::defaultServiceManager()->addService(
-                String16(reinterpret_cast<const char16_t *>(serviceName)),
+                String16(
+                    reinterpret_cast<const char16_t *>(serviceName),
+                    env->GetStringLength(serviceNameObj)),
                 binder,
                 kVersion);
 
@@ -245,12 +247,15 @@
 
     LOG(INFO) << "looking for service '"
               << String8(String16(
-                          reinterpret_cast<const char16_t *>(serviceName))).string()
+                          reinterpret_cast<const char16_t *>(serviceName),
+                          env->GetStringLength(serviceNameObj))).string()
               << "'";
 
     sp<hardware::IBinder> service =
         hardware::defaultServiceManager()->getService(
-                String16(reinterpret_cast<const char16_t *>(serviceName)),
+                String16(
+                    reinterpret_cast<const char16_t *>(serviceName),
+                    env->GetStringLength(serviceNameObj)),
                 kVersion);
 
     env->ReleaseStringCritical(serviceNameObj, serviceName);
diff --git a/core/res/res/color/hint_foreground_material_dark.xml b/core/res/res/color/hint_foreground_material_dark.xml
index 77883d9..5cc9559 100644
--- a/core/res/res/color/hint_foreground_material_dark.xml
+++ b/core/res/res/color/hint_foreground_material_dark.xml
@@ -15,6 +15,10 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:state_pressed="true"
+          android:alpha="@dimen/hint_pressed_alpha_material_dark"
+          android:color="@color/foreground_material_dark" />
     <item android:alpha="@dimen/hint_alpha_material_dark"
           android:color="@color/foreground_material_dark" />
 </selector>
diff --git a/core/res/res/color/hint_foreground_material_light.xml b/core/res/res/color/hint_foreground_material_light.xml
index 99168fd..f7465e0 100644
--- a/core/res/res/color/hint_foreground_material_light.xml
+++ b/core/res/res/color/hint_foreground_material_light.xml
@@ -15,6 +15,10 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true"
+          android:state_pressed="true"
+          android:alpha="@dimen/hint_pressed_alpha_material_light"
+          android:color="@color/foreground_material_light" />
     <item android:alpha="@dimen/hint_alpha_material_light"
           android:color="@color/foreground_material_light" />
 </selector>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index a864cf3..92426c6 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -61,7 +61,10 @@
     <color name="secondary_text_default_material_dark">#b3ffffff</color>
 
     <item name="hint_alpha_material_dark" format="float" type="dimen">0.50</item>
-    <item name="hint_alpha_material_light" format="float" type="dimen">0.54</item>
+    <item name="hint_alpha_material_light" format="float" type="dimen">0.38</item>
+
+    <item name="hint_pressed_alpha_material_dark" format="float" type="dimen">0.70</item>
+    <item name="hint_pressed_alpha_material_light" format="float" type="dimen">0.54</item>
 
     <item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
     <item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 62b9435..40b6047 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4367,6 +4367,9 @@
     <string name="language_picker_section_suggested">Suggested</string>
     <!-- List section subheader for the language picker, containing a list of all languages available [CHAR LIMIT=30] -->
     <string name="language_picker_section_all">All languages</string>
+    <!-- List section subheader for the region picker, containing a list of all regions supported for the selected language.
+    Warning: this is a more 'neutral' term for 'country', not for the sub-divisions of a country. [CHAR LIMIT=30] -->
+    <string name="region_picker_section_all">All regions</string>
 
     <!-- Menu item in the locale menu  [CHAR LIMIT=30] -->
     <string name="locale_search_menu">Search</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 96ab3162..647fc57 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2516,6 +2516,7 @@
   <java-symbol type="menu" name="language_selection_list" />
   <java-symbol type="string" name="country_selection_title" />
   <java-symbol type="string" name="language_picker_section_all" />
+  <java-symbol type="string" name="region_picker_section_all" />
   <java-symbol type="string" name="language_picker_section_suggested" />
   <java-symbol type="string" name="language_selection_title" />
   <java-symbol type="string" name="search_language_hint" />
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index dfc30c3..f3469b4 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -156,6 +156,16 @@
     "lang":"en"
   },
   {
+    "title":"GPU Debugger",
+    "summary":"Use the GPU Debugger to analyze and debug your OpenGL ES apps. Inspect the GPU state and understand what caused a specific rendering outcome.",
+    "url":"studio/debug/am-gpu-debugger.html",
+    "image":"images/tools/thumbnails/am-gpu-debugger_2-2_2x.png",
+    "type":"tools",
+    "keywords": ["android","performance","profiling","tools","monitor","debug"],
+    "tags": ["android","performance","profiling","tools","monitor","debug"],
+    "lang":"en"
+  },
+  {
     "title":"HPROF Viewer and Analyzer",
     "summary":"Use the Memory Monitor to dump the Java heap to an HPROF file. The HPROF Viewer displays classes, instances of each class, and a reference tree to help you track memory usage and find memory leaks.",
     "url":"studio/profile/am-hprof.html",
@@ -5453,6 +5463,12 @@
        "studio/profile/am-sysinfo.html"
     ]
   },
+"tools/help/gpu": {
+    "title": "",
+    "resources": [
+       "studio/debug/am-gpu-debugger.html"
+    ]
+  },
   "tools/help/shot": {
     "title": "",
     "resources": [
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index a99e668..af8fd4c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -23,6 +23,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.AsyncTask;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.MathUtils;
@@ -207,39 +208,41 @@
 
     /**
      * Asynchronously applies display density changes to the specified display.
+     * <p>
+     * The change will be applied to the user specified by the value of
+     * {@link UserHandle#myUserId()} at the time the method is called.
      *
      * @param displayId the identifier of the display to modify
      */
     public static void clearForcedDisplayDensity(final int displayId) {
-        AsyncTask.execute(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
-                    wm.clearForcedDisplayDensity(displayId);
-                } catch (RemoteException exc) {
-                    Log.w(LOG_TAG, "Unable to clear forced display density setting");
-                }
+        final int userId = UserHandle.myUserId();
+        AsyncTask.execute(() -> {
+            try {
+                final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+                wm.clearForcedDisplayDensityForUser(displayId, userId);
+            } catch (RemoteException exc) {
+                Log.w(LOG_TAG, "Unable to clear forced display density setting");
             }
         });
     }
 
     /**
      * Asynchronously applies display density changes to the specified display.
+     * <p>
+     * The change will be applied to the user specified by the value of
+     * {@link UserHandle#myUserId()} at the time the method is called.
      *
      * @param displayId the identifier of the display to modify
      * @param density the density to force for the specified display
      */
     public static void setForcedDisplayDensity(final int displayId, final int density) {
-        AsyncTask.execute(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
-                    wm.setForcedDisplayDensity(displayId, density);
-                } catch (RemoteException exc) {
-                    Log.w(LOG_TAG, "Unable to save forced display density setting");
-                }
+        final int userId = UserHandle.myUserId();
+        AsyncTask.execute(() -> {
+            try {
+                final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+                wm.setForcedDisplayDensityForUser(displayId, density, userId);
+            } catch (RemoteException exc) {
+                Log.w(LOG_TAG, "Unable to save forced display density setting");
             }
         });
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index a50b366..458672a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -28,6 +28,8 @@
 import android.content.res.TypedArray;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.support.v4.widget.DrawerLayout;
 import android.util.ArraySet;
@@ -73,6 +75,7 @@
     private FrameLayout mContentHeaderContainer;
     private DrawerLayout mDrawerLayout;
     private boolean mShowingMenu;
+    private UserManager mUserManager;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -110,6 +113,8 @@
                 onTileClicked(mDrawerAdapter.getTile(position));
             };
         });
+
+        mUserManager = UserManager.get(this);
         if (DEBUG_TIMING) Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
                 + " ms");
     }
@@ -257,6 +262,7 @@
             return true;
         }
         try {
+            updateUserHandlesIfNeeded(tile);
             int numUserHandles = tile.userHandle.size();
             if (numUserHandles > 1) {
                 ProfileSelectDialog.show(getFragmentManager(), tile);
@@ -278,6 +284,19 @@
         return true;
     }
 
+    private void updateUserHandlesIfNeeded(Tile tile) {
+        List<UserHandle> userHandles = tile.userHandle;
+
+        for (int i = userHandles.size()-1; i >= 0; i--) {
+            if (mUserManager.getUserInfo(userHandles.get(i).getIdentifier()) == null) {
+                if (DEBUG_TIMING) {
+                    Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier());
+                }
+                userHandles.remove(i);
+            }
+        }
+    }
+
     protected void onTileClicked(Tile tile) {
         if (openTile(tile)) {
             finish();
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index b9ae585..19ae295 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -49,7 +49,6 @@
         Key.QS_WORK_ADDED,
     })
     public @interface Key {
-        @Deprecated
         String OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME = "OverviewLastStackTaskActiveTime";
         String DEBUG_MODE_ENABLED = "debugModeEnabled";
         String HOTSPOT_TILE_LAST_USED = "HotspotTileLastUsed";
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a7d7df5..7207463 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -34,7 +34,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -47,7 +46,6 @@
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
-import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
@@ -252,19 +250,6 @@
             registerWithSystemUser();
         }
         putComponent(Recents.class, this);
-
-        // Migrate the old stack active time if necessary, otherwise, it will already be managed
-        // when the tasks are loaded in the system. See TaskPersister.restoreTasksForUserLocked().
-        long lastVisibleTaskActiveTime = Prefs.getLong(mContext,
-                Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1);
-        if (lastVisibleTaskActiveTime != -1) {
-            long uptime = SystemClock.elapsedRealtime();
-            Settings.Secure.putLongForUser(mContext.getContentResolver(),
-                    Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME,
-                    uptime - Math.max(0, System.currentTimeMillis() - lastVisibleTaskActiveTime),
-                    processUser);
-            Prefs.remove(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 1e41870..7bdb1c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -20,7 +20,6 @@
 import android.app.ActivityOptions;
 import android.app.TaskStackBuilder;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -171,6 +170,13 @@
             if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 // When the screen turns off, dismiss Recents to Home
                 dismissRecentsToHomeIfVisible(false);
+            } else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
+                // For the time being, if the time changes, then invalidate the
+                // last-stack-active-time, this ensures that we will just show the last N tasks
+                // the next time that Recents loads, but prevents really old tasks from showing
+                // up if the task time is set forward.
+                Prefs.putLong(RecentsActivity.this, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
+                        0);
             }
         }
     };
@@ -316,6 +322,7 @@
         // Register the broadcast receiver to handle messages when the screen is turned off
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Intent.ACTION_TIME_CHANGED);
         registerReceiver(mSystemBroadcastReceiver, filter);
 
         getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
@@ -793,19 +800,14 @@
         EventBus.getDefault().dump(prefix, writer);
         Recents.getTaskLoader().dump(prefix, writer);
 
-        ContentResolver cr = getContentResolver();
-        long lastPersistUptime = Settings.Secure.getLong(cr,
-                Settings.Secure.TASK_PERSISTER_LAST_WRITE_UPTIME, 0);
-        long lastVisibleTaskActiveUptime = Settings.Secure.getLongForUser(cr,
-                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME,
-                SystemClock.elapsedRealtime(), Recents.getSystemServices().getCurrentUser());
-
         String id = Integer.toHexString(System.identityHashCode(this));
+        long lastStackActiveTime = Prefs.getLong(this,
+                Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1);
 
         writer.print(prefix); writer.print(TAG);
         writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
-        writer.print(" lastPersistUptime="); writer.print(lastPersistUptime);
-        writer.print(" lastVisibleTaskActiveUptime="); writer.print(lastVisibleTaskActiveUptime);
+        writer.print(" lastStackTaskActiveTime="); writer.print(lastStackActiveTime);
+        writer.print(" currentTime="); writer.print(System.currentTimeMillis());
         writer.print(" [0x"); writer.print(id); writer.print("]");
         writer.println();
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 64d9831..9d9e27389 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -60,7 +60,6 @@
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -76,7 +75,6 @@
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.os.BackgroundThread;
 import com.android.systemui.R;
@@ -201,9 +199,6 @@
      */
     private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
 
-    /** Test constructor */
-    @VisibleForTesting public SystemServicesProxy() {}
-
     /** Private constructor */
     private SystemServicesProxy(Context context) {
         mAccm = AccessibilityManager.getInstance(context);
@@ -305,7 +300,7 @@
                 rti.baseIntent = new Intent();
                 rti.baseIntent.setComponent(cn);
                 rti.description = description;
-                rti.firstActiveTime = rti.lastActiveTime = SystemClock.elapsedRealtime();
+                rti.firstActiveTime = rti.lastActiveTime = i;
                 if (i % 2 == 0) {
                     rti.taskDescription = new ActivityManager.TaskDescription(description,
                         Bitmap.createBitmap(mDummyIcon), null,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index ecd48e1..1278b73 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -24,15 +24,13 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -58,11 +56,6 @@
     private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
             6 /* hrs */;
 
-    @VisibleForTesting
-    public interface SystemTimeProvider {
-        public long getTime();
-    }
-
     /** The set of conditions to load tasks. */
     public static class Options {
         public int runningTaskId = -1;
@@ -74,46 +67,15 @@
         public int numVisibleTaskThumbnails = 0;
     }
 
-    private Context mContext;
-    @VisibleForTesting private SystemServicesProxy mSystemServicesProxy;
+    Context mContext;
 
-    private List<ActivityManager.RecentTaskInfo> mRawTasks;
-    private long mLastVisibileTaskActiveTime;
-    private TaskStack mStack;
-    private ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
-    private SystemTimeProvider mTimeProvider = new SystemTimeProvider() {
-        @Override
-        public long getTime() {
-            return SystemClock.elapsedRealtime();
-        }
-    };
+    List<ActivityManager.RecentTaskInfo> mRawTasks;
+    TaskStack mStack;
+    ArraySet<Integer> mCurrentQuietProfiles = new ArraySet<Integer>();
 
-    @VisibleForTesting
-    public RecentsTaskLoadPlan(Context context, SystemServicesProxy ssp) {
+    /** Package level ctor */
+    RecentsTaskLoadPlan(Context context) {
         mContext = context;
-        mSystemServicesProxy = ssp;
-    }
-
-    @VisibleForTesting
-    public void setInternals(List<ActivityManager.RecentTaskInfo> tasks,
-            final long currentTime, long lastVisibleTaskActiveTime) {
-        setInternals(tasks, MIN_NUM_TASKS, currentTime, lastVisibleTaskActiveTime,
-                SESSION_BEGIN_TIME);
-    }
-
-    @VisibleForTesting
-    public void setInternals(List<ActivityManager.RecentTaskInfo> tasks, int minNumTasks,
-            final long currentTime, long lastVisibleTaskActiveTime,  int sessionBeginTime) {
-        mRawTasks = tasks;
-        mLastVisibileTaskActiveTime = lastVisibleTaskActiveTime;
-        mTimeProvider = new SystemTimeProvider() {
-            @Override
-            public long getTime() {
-                return currentTime;
-            }
-        };
-        MIN_NUM_TASKS = minNumTasks;
-        SESSION_BEGIN_TIME = sessionBeginTime;
     }
 
     private void updateCurrentQuietProfilesCache(int currentUserId) {
@@ -141,13 +103,9 @@
     public synchronized void preloadRawTasks(boolean includeFrontMostExcludedTask) {
         int currentUserId = UserHandle.USER_CURRENT;
         updateCurrentQuietProfilesCache(currentUserId);
-        mRawTasks = mSystemServicesProxy.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
                 currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles);
-        mLastVisibileTaskActiveTime = RecentsDebugFlags.Static.EnableMockTasks
-                ? SystemClock.elapsedRealtime()
-                : Settings.Secure.getLongForUser(mContext.getContentResolver(),
-                        Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME,
-                                0, currentUserId);
 
         // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
         Collections.reverse(mRawTasks);
@@ -176,9 +134,12 @@
                 R.string.accessibility_recents_item_will_be_dismissed);
         String appInfoDescFormat = mContext.getString(
                 R.string.accessibility_recents_item_open_app_info);
-        boolean updatedLastVisibleTaskActiveTime = false;
-        long newLastVisibileTaskActiveTime = 0;
-        long currentTime = mTimeProvider.getTime();
+        long lastStackActiveTime = Prefs.getLong(mContext,
+                Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
+            lastStackActiveTime = 0;
+        }
+        long newLastStackActiveTime = -1;
         int taskCount = mRawTasks.size();
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
@@ -187,20 +148,19 @@
             Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
                     t.userId, t.firstActiveTime, t.lastActiveTime);
 
-            // Only show the task if it is freeform, or later than the last visible task active time
-            // and either recently used, or within the last five tasks
-            boolean isFreeformTask = mSystemServicesProxy.isFreeformStack(t.stackId);
-            boolean isRecentlyUsedTask = t.lastActiveTime >= (currentTime - SESSION_BEGIN_TIME);
-            boolean isMoreRecentThanLastVisible = t.lastActiveTime >= mLastVisibileTaskActiveTime;
-            boolean isStackTask = isFreeformTask || (isMoreRecentThanLastVisible &&
-                    (isRecentlyUsedTask || i >= (taskCount - MIN_NUM_TASKS)));
-            boolean isLaunchTarget = t.persistentId == runningTaskId;
+            // This task is only shown in the stack if it statisfies the historical time or min
+            // number of tasks constraints. Freeform tasks are also always shown.
+            boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
+            boolean isStackTask = isFreeformTask || !isHistoricalTask(t) ||
+                    (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS));
+            boolean isLaunchTarget = taskKey.id == runningTaskId;
 
-            // If this is the first task satisfying the stack constraints, update the baseline
-            // at which we show visible tasks
-            if (isStackTask && !updatedLastVisibleTaskActiveTime) {
-                newLastVisibileTaskActiveTime = t.lastActiveTime;
-                updatedLastVisibleTaskActiveTime = true;
+            // The last stack active time is the baseline for which we show visible tasks.  Since
+            // the system will store all the tasks, we don't want to show the tasks prior to the
+            // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy
+            // the other stack-task constraints.
+            if (isStackTask && newLastStackActiveTime < 0) {
+                newLastStackActiveTime = t.lastActiveTime;
             }
 
             // Load the title, icon, and color
@@ -228,12 +188,9 @@
             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
             affiliatedTasks.put(taskKey.id, taskKey);
         }
-        if (updatedLastVisibleTaskActiveTime &&
-                newLastVisibileTaskActiveTime != mLastVisibileTaskActiveTime) {
-            Settings.Secure.putLongForUser(mContext.getContentResolver(),
-                    Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME,
-                            newLastVisibileTaskActiveTime, UserHandle.USER_CURRENT);
-            mLastVisibileTaskActiveTime = newLastVisibileTaskActiveTime;
+        if (newLastStackActiveTime != -1) {
+            Prefs.putLong(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
+                    newLastStackActiveTime);
         }
 
         // Initialize the stacks
@@ -298,4 +255,11 @@
         }
         return false;
     }
+
+    /**
+     * Returns whether this task is too old to be shown.
+     */
+    private boolean isHistoricalTask(ActivityManager.RecentTaskInfo t) {
+        return t.lastActiveTime < (System.currentTimeMillis() - SESSION_BEGIN_TIME);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index e0eda37..ba31e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -30,7 +30,6 @@
 import android.util.Log;
 import android.util.LruCache;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -287,20 +286,6 @@
         }
     };
 
-    @VisibleForTesting
-    public RecentsTaskLoader() {
-        mActivityInfoCache = null;
-        mIconCache = null;
-        mThumbnailCache = null;
-        mActivityLabelCache = null;
-        mContentDescriptionCache = null;
-        mLoadQueue = null;
-        mLoader = null;
-
-        mMaxThumbnailCacheSize = 0;
-        mMaxIconCacheSize = 0;
-    }
-
     public RecentsTaskLoader(Context context) {
         Resources res = context.getResources();
         mDefaultTaskBarBackgroundColor =
@@ -347,8 +332,7 @@
 
     /** Creates a new plan for loading the recent tasks. */
     public RecentsTaskLoadPlan createLoadPlan(Context context) {
-        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context,
-                Recents.getSystemServices());
+        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
         return plan;
     }
 
@@ -471,8 +455,7 @@
     /**
      * Returns the cached task label if the task key is not expired, updating the cache if it is.
      */
-    @VisibleForTesting public String getAndUpdateActivityTitle(Task.TaskKey taskKey,
-            ActivityManager.TaskDescription td) {
+    String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Return the task description label if it exists
@@ -500,8 +483,7 @@
      * Returns the cached task content description if the task key is not expired, updating the
      * cache if it is.
      */
-    @VisibleForTesting public String getAndUpdateContentDescription(Task.TaskKey taskKey,
-            Resources res) {
+    String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) {
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Return the cached content description if it exists
@@ -525,8 +507,8 @@
     /**
      * Returns the cached task icon if the task key is not expired, updating the cache if it is.
      */
-    @VisibleForTesting public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
-            ActivityManager.TaskDescription td, Resources res, boolean loadIfNotCached) {
+    Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
+            Resources res, boolean loadIfNotCached) {
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Return the cached activity icon if it exists
@@ -560,8 +542,7 @@
     /**
      * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
      */
-    @VisibleForTesting public Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey,
-            boolean loadIfNotCached) {
+    Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Return the cached thumbnail if it exists
@@ -589,7 +570,7 @@
      * Returns the task's primary color if possible, defaulting to the default color if there is
      * no specified primary color.
      */
-    @VisibleForTesting public int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
+    int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
         if (td != null && td.getPrimaryColor() != 0) {
             return td.getPrimaryColor();
         }
@@ -599,7 +580,7 @@
     /**
      * Returns the task's background color if possible.
      */
-    @VisibleForTesting public int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
+    int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
         if (td != null && td.getBackgroundColor() != 0) {
             return td.getBackgroundColor();
         }
@@ -610,7 +591,7 @@
      * Returns the activity info for the given task key, retrieving one from the system if the
      * task key is expired.
      */
-    @VisibleForTesting public ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+    ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         ComponentName cn = taskKey.getComponent();
         ActivityInfo activityInfo = mActivityInfoCache.get(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 4191f52..86a0315 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -290,10 +290,7 @@
      */
     public boolean isFreeformTask() {
         SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp != null) {
-            return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId);
-        }
-        return false;
+        return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId);
     }
 
     /** Notifies the callback listeners that this task has been loaded */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a6536a8..75b9417 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -309,6 +309,7 @@
             mUsersAllowingPrivateNotifications.clear();
             mUsersAllowingNotifications.clear();
             // ... and refresh all the notifications
+            updateLockscreenNotificationSetting();
             updateNotifications();
         }
     };
@@ -714,7 +715,7 @@
                 mSettingsObserver);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
-                mSettingsObserver,
+                mLockscreenSettingsObserver,
                 UserHandle.USER_ALL);
         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
             mContext.getContentResolver().registerContentObserver(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index d4f98c3..d94def9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -308,6 +308,7 @@
         super.setVisibility(visibility);
         if (visibility != View.VISIBLE) {
             mSystemIconsSuperContainer.animate().cancel();
+            mSystemIconsSuperContainer.setTranslationX(0);
             mMultiUserSwitch.animate().cancel();
             mMultiUserSwitch.setAlpha(1f);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index ebd4798..a07b695 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4664,44 +4664,47 @@
             final Runnable clickPendingViewRunnable = new Runnable() {
                 @Override
                 public void run() {
-                    if (mPendingWorkRemoteInputView != null) {
-                        final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
-                        ViewParent p = pendingWorkRemoteInputView.getParent();
-                        while (p != null) {
-                            if (p instanceof ExpandableNotificationRow) {
-                                final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
-                                ViewParent viewParent = row.getParent();
-                                if (viewParent instanceof NotificationStackScrollLayout) {
-                                    final NotificationStackScrollLayout scrollLayout =
-                                            (NotificationStackScrollLayout) viewParent;
-                                    row.makeActionsVisibile();
-                                    row.post(new Runnable() {
-                                        @Override
-                                        public void run() {
-                                            final Runnable finishScrollingCallback = new Runnable()
-                                            {
-                                                @Override
-                                                public void run() {
-                                                    mPendingWorkRemoteInputView.callOnClick();
-                                                    mPendingWorkRemoteInputView = null;
-                                                    scrollLayout.setFinishScrollingCallback(null);
-                                                }
-                                            };
-                                            if (scrollLayout.scrollTo(row)) {
-                                                // It scrolls! So call it when it's finished.
-                                                scrollLayout.setFinishScrollingCallback(
-                                                        finishScrollingCallback);
-                                            } else {
-                                                // It does not scroll, so call it now!
-                                                finishScrollingCallback.run();
-                                            }
-                                        }
-                                    });
-                                }
-                                break;
-                            }
-                            p = p.getParent();
+                    final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
+                    if (pendingWorkRemoteInputView == null) {
+                        return;
+                    }
+
+                    // Climb up the hierarchy until we get to the container for this row.
+                    ViewParent p = pendingWorkRemoteInputView.getParent();
+                    while (!(p instanceof ExpandableNotificationRow)) {
+                        if (p == null) {
+                            return;
                         }
+                        p = p.getParent();
+                    }
+
+                    final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
+                    ViewParent viewParent = row.getParent();
+                    if (viewParent instanceof NotificationStackScrollLayout) {
+                        final NotificationStackScrollLayout scrollLayout =
+                                (NotificationStackScrollLayout) viewParent;
+                        row.makeActionsVisibile();
+                        row.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                final Runnable finishScrollingCallback = new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        mPendingWorkRemoteInputView.callOnClick();
+                                        mPendingWorkRemoteInputView = null;
+                                        scrollLayout.setFinishScrollingCallback(null);
+                                    }
+                                };
+                                if (scrollLayout.scrollTo(row)) {
+                                    // It scrolls! So call it when it's finished.
+                                    scrollLayout.setFinishScrollingCallback(
+                                            finishScrollingCallback);
+                                } else {
+                                    // It does not scroll, so call it now!
+                                    finishScrollingCallback.run();
+                                }
+                            }
+                        });
                     }
                 }
             };
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTaskLoadPlanTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTaskLoadPlanTest.java
deleted file mode 100644
index 4280ff4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTaskLoadPlanTest.java
+++ /dev/null
@@ -1,344 +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.
- */
-
-package com.android.systemui.recents;
-
-import android.app.ActivityManager;
-import android.support.test.runner.AndroidJUnit4;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-
-import java.util.ArrayList;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-
-
-/**
- * Mock task loader that does not actually load any tasks.
- */
-class MockRecentsTaskNonLoader extends RecentsTaskLoader {
-    @Override
-    public String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
-        return "";
-    }
-
-    @Override
-    public String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) {
-        return "";
-    }
-
-    @Override
-    public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td, Resources res, boolean loadIfNotCached) {
-        return null;
-    }
-
-    @Override
-    public Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
-        return null;
-    }
-
-    @Override
-    public int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
-        return 0;
-    }
-
-    @Override
-    public int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
-        return 0;
-    }
-
-    @Override
-    public ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
-        return null;
-    }
-}
-
-/**
- * TODO(winsonc):
- * - add test to ensure excluded tasks are loaded at the front of the list
- * - add test to ensure the last visible task active time is migrated from absolute to uptime
- */
-@RunWith(AndroidJUnit4.class)
-public class RecentsTaskLoadPlanTest extends SysuiTestCase {
-    private static final String TAG = "RecentsTaskLoadPlanTest";
-
-    private MockRecentsTaskNonLoader mDummyLoader = new MockRecentsTaskNonLoader();
-    private SystemServicesProxy mDummySsp = new SystemServicesProxy();
-
-    @Test
-    public void testEmptyRecents() {
-        RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext, mDummySsp);
-        ArrayList<ActivityManager.RecentTaskInfo> tasks = new ArrayList<>();
-        loadPlan.setInternals(tasks, 0 /* current */, 0 /* lastVisibleTaskActive */);
-        loadPlan.preloadPlan(mDummyLoader, 0 /* runningTaskId */,
-                false /* includeFrontMostExcludedTask */);
-        assertFalse("Expected task to be empty", loadPlan.getTaskStack().getStackTaskCount() > 0);
-    }
-
-    @Test
-    public void testLessThanEqualMinTasks() {
-        RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext, mDummySsp);
-        ArrayList<ActivityManager.RecentTaskInfo> tasks = new ArrayList<>();
-        int minTasks = 3;
-
-        resetTaskInfoList(tasks,
-                createTaskInfo(0, 1),
-                createTaskInfo(1, 2),
-                createTaskInfo(2, 3));
-
-        // Ensure that all tasks are loaded if the tasks are within the session and after the last
-        // visible active time (all tasks are loaded because there are < minTasks number of tasks)
-        loadPlan.setInternals(tasks, minTasks, 0 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 1 /* current */, 0 /* lastVisibleTaskActive */,
-                0 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 1 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 3 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 3 /* current */, 1 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 50 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        // Ensure that only tasks are not loaded if are after the last visible active time, even if
-        // they are within the session
-        loadPlan.setInternals(tasks, minTasks, 50 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 50 /* current */, 1 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 50 /* current */, 2 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0);
-        assertTasksInStack(loadPlan.getTaskStack(), 1, 2);
-
-        loadPlan.setInternals(tasks, minTasks, 50 /* current */, 3 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1);
-        assertTasksInStack(loadPlan.getTaskStack(), 2);
-
-        loadPlan.setInternals(tasks, minTasks, 50 /* current */, 50 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1, 2);
-    }
-
-    @Test
-    public void testMoreThanMinTasks() {
-        RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext, mDummySsp);
-        ArrayList<ActivityManager.RecentTaskInfo> tasks = new ArrayList<>();
-        int minTasks = 3;
-
-        // Create all tasks within the session
-        resetTaskInfoList(tasks,
-                createTaskInfo(0, 1),
-                createTaskInfo(1, 50),
-                createTaskInfo(2, 100),
-                createTaskInfo(3, 101),
-                createTaskInfo(4, 102),
-                createTaskInfo(5, 103));
-
-        // Ensure that only the tasks that are within the window but after the last visible active
-        // time is loaded, or the minTasks number of tasks are loaded if there are less than that
-
-        // Session window shifts
-        loadPlan.setInternals(tasks, minTasks, 0 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 1 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 51 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 52 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0);
-        assertTasksInStack(loadPlan.getTaskStack(), 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 100 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0);
-        assertTasksInStack(loadPlan.getTaskStack(), 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 101 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1);
-        assertTasksInStack(loadPlan.getTaskStack(), 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 103 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1);
-        assertTasksInStack(loadPlan.getTaskStack(), 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1);
-        assertTasksInStack(loadPlan.getTaskStack(), 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 151 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1, 2);
-        assertTasksInStack(loadPlan.getTaskStack(), 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 200 /* current */, 0 /* lastVisibleTaskActive */,
-                50 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1, 2);
-        assertTasksInStack(loadPlan.getTaskStack(), 3, 4, 5);
-
-        // Last visible active time shifts (everything is in window)
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 0 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 1 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksInStack(loadPlan.getTaskStack(), 0, 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 2 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0);
-        assertTasksInStack(loadPlan.getTaskStack(), 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 50 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0);
-        assertTasksInStack(loadPlan.getTaskStack(), 1, 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 51 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1);
-        assertTasksInStack(loadPlan.getTaskStack(), 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 100 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1);
-        assertTasksInStack(loadPlan.getTaskStack(), 2, 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 101 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1, 2);
-        assertTasksInStack(loadPlan.getTaskStack(), 3, 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 102 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1, 2, 3);
-        assertTasksInStack(loadPlan.getTaskStack(), 4, 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 103 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1, 2, 3, 4);
-        assertTasksInStack(loadPlan.getTaskStack(), 5);
-
-        loadPlan.setInternals(tasks, minTasks, 150 /* current */, 104 /* lastVisibleTaskActive */,
-                150 /* sessionBegin */);
-        loadPlan.preloadPlan(mDummyLoader, 0, false);
-        assertTasksNotInStack(loadPlan.getTaskStack(), 0, 1, 2, 3, 4, 5);
-    }
-
-    private ActivityManager.RecentTaskInfo createTaskInfo(int taskId, long lastActiveTime) {
-        ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
-        info.id = info.persistentId = taskId;
-        info.lastActiveTime = lastActiveTime;
-        return info;
-    }
-
-    private void resetTaskInfoList(ArrayList<ActivityManager.RecentTaskInfo> tasks,
-            ActivityManager.RecentTaskInfo ... infos) {
-        tasks.clear();
-        for (ActivityManager.RecentTaskInfo info : infos) {
-            tasks.add(info);
-        }
-    }
-
-    private void assertTasksInStack(TaskStack stack, int... taskIds) {
-        for (int taskId : taskIds) {
-            assertNotNull("Expected task " + taskId + " in stack", stack.findTaskWithId(taskId));
-        }
-    }
-
-    private void assertTasksNotInStack(TaskStack stack, int... taskIds) {
-        for (int taskId : taskIds) {
-            assertNull("Expected task " + taskId + " not in stack", stack.findTaskWithId(taskId));
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 6cdabaa..43eb251 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import android.annotation.NonNull;
-import android.content.ContentResolver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Debug;
@@ -25,7 +24,6 @@
 import android.os.FileUtils;
 import android.os.Process;
 import android.os.SystemClock;
-import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -82,7 +80,7 @@
     private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt";
     static final String IMAGE_EXTENSION = ".png";
 
-    @VisibleForTesting static final String TAG_TASK = "task";
+    private static final String TAG_TASK = "task";
 
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
@@ -409,43 +407,18 @@
         return null;
     }
 
-    @VisibleForTesting
     List<TaskRecord> restoreTasksForUserLocked(final int userId) {
         final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
         ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
 
         File userTasksDir = getUserTasksDir(userId);
+
         File[] recentFiles = userTasksDir.listFiles();
         if (recentFiles == null) {
             Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir);
             return tasks;
         }
 
-        // Get the last persist uptime so we know how to adjust the first/last active times for each
-        // task
-        ContentResolver cr = mService.mContext.getContentResolver();
-        long lastPersistUptime = Settings.Secure.getLong(cr,
-                Settings.Secure.TASK_PERSISTER_LAST_WRITE_UPTIME, 0);
-        if (DEBUG) {
-            Slog.d(TaskPersister.TAG, "restoreTasksForUserLocked: lastPersistUptime=" +
-                    lastPersistUptime);
-        }
-
-        // Adjust the overview last visible task active time as we adjust the task active times when
-        // loading. See TaskRecord.restoreFromXml().  If we have not migrated yet, SystemUI will
-        // migrate the old value into the system setting.
-        if (lastPersistUptime > 0) {
-            long overviewLastActiveTime = Settings.Secure.getLongForUser(cr,
-                    Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, 0, userId);
-            if (DEBUG) {
-                Slog.d(TaskPersister.TAG, "restoreTasksForUserLocked: overviewLastActiveTime=" +
-                        overviewLastActiveTime + " lastPersistUptime=" + lastPersistUptime);
-            }
-            Settings.Secure.putLongForUser(cr,
-                    Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME,
-                    -lastPersistUptime + overviewLastActiveTime, userId);
-        }
-
         for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
             File taskFile = recentFiles[taskNdx];
             if (DEBUG) {
@@ -466,11 +439,15 @@
                     if (event == XmlPullParser.START_TAG) {
                         if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: START_TAG name=" + name);
                         if (TAG_TASK.equals(name)) {
-                            final TaskRecord task = TaskRecord.restoreFromXml(in, mService,
-                                    mStackSupervisor, lastPersistUptime);
+                            final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
                             if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: restored task="
                                     + task);
                             if (task != null) {
+                                // XXX Don't add to write queue... there is no reason to write
+                                // out the stuff we just read, if we don't write it we will
+                                // read the same thing again.
+                                // mWriteQueue.add(new TaskWriteQueueItem(task));
+
                                 final int taskId = task.taskId;
                                 if (mStackSupervisor.anyTaskForIdLocked(taskId,
                                         /* restoreFromRecents= */ false, 0) != null) {
@@ -486,12 +463,6 @@
                                     task.isPersistable = true;
                                     tasks.add(task);
                                     recoveredTaskIds.add(taskId);
-
-                                    // We've shifted the first and last active times, so we need to
-                                    // persist the new task data to disk otherwise they will not
-                                    // have the updated values.  This is only done once whenever
-                                    // the recents are first loaded for the user.
-                                    wakeup(task, false);
                                 }
                             } else {
                                 Slog.e(TAG, "restoreTasksForUserLocked: Unable to restore taskFile="
@@ -780,15 +751,6 @@
                         }
                     }
                 }
-
-                // Always update the task persister uptime when updating any tasks
-                if (DEBUG) {
-                    Slog.d(TAG, "LazyTaskWriter: Updating last write uptime=" +
-                            SystemClock.elapsedRealtime());
-                }
-                Settings.Secure.putLong(mService.mContext.getContentResolver(),
-                        Settings.Secure.TASK_PERSISTER_LAST_WRITE_UPTIME,
-                        SystemClock.elapsedRealtime());
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 206dd45..0745a85 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -39,14 +39,12 @@
 import android.os.Debug;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.util.XmlUtils;
 
@@ -153,10 +151,8 @@
     ComponentName realActivity; // The actual activity component that started the task.
     boolean realActivitySuspended; // True if the actual activity component that started the
                                    // task is suspended.
-    long firstActiveTime;   // First time this task was active, relative to boot time. This can be
-                            // negative if this task was last used prior to boot.
-    long lastActiveTime;    // Last time this task was active, relative to boot time. This can be
-                            // negative if this task was last used prior to boot.
+    long firstActiveTime;   // First time this task was active.
+    long lastActiveTime;    // Last time this task was active, including sleep.
     boolean inRecents;      // Actually in the recents list?
     boolean isAvailable;    // Is the activity available to be launched?
     boolean rootWasReset;   // True if the intent at the root of the task had
@@ -384,14 +380,14 @@
     }
 
     void touchActiveTime() {
-        lastActiveTime = SystemClock.elapsedRealtime();
+        lastActiveTime = System.currentTimeMillis();
         if (firstActiveTime == 0) {
             firstActiveTime = lastActiveTime;
         }
     }
 
     long getInactiveDuration() {
-        return SystemClock.elapsedRealtime() - lastActiveTime;
+        return System.currentTimeMillis() - lastActiveTime;
     }
 
     /** Sets the original intent, and the calling uid and package. */
@@ -462,9 +458,8 @@
             rootWasReset = true;
         }
         userId = UserHandle.getUserId(info.applicationInfo.uid);
-        mUserSetupComplete = mService != null &&
-                Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
-                        USER_SETUP_COMPLETE, 0, userId) != 0;
+        mUserSetupComplete = Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
+                USER_SETUP_COMPLETE, 0, userId) != 0;
         if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
             // If the activity itself has requested auto-remove, then just always do it.
             autoRemoveRecents = true;
@@ -1189,9 +1184,7 @@
         if (lastTaskDescription != null) {
             lastTaskDescription.saveToXml(out);
         }
-        if (mLastThumbnailInfo != null) {
-            mLastThumbnailInfo.saveToXml(out);
-        }
+        mLastThumbnailInfo.saveToXml(out);
         out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
         out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
         out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
@@ -1213,11 +1206,9 @@
             out.endTag(null, TAG_AFFINITYINTENT);
         }
 
-        if (intent != null) {
-            out.startTag(null, TAG_INTENT);
-            intent.saveToXml(out);
-            out.endTag(null, TAG_INTENT);
-        }
+        out.startTag(null, TAG_INTENT);
+        intent.saveToXml(out);
+        out.endTag(null, TAG_INTENT);
 
         final ArrayList<ActivityRecord> activities = mActivities;
         final int numActivities = activities.size();
@@ -1236,9 +1227,8 @@
         }
     }
 
-    static TaskRecord restoreFromXml(XmlPullParser in, ActivityManagerService service,
-            ActivityStackSupervisor stackSupervisor, long lastPersistUptime)
-                    throws IOException, XmlPullParserException {
+    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+            throws IOException, XmlPullParserException {
         Intent intent = null;
         Intent affinityIntent = null;
         ArrayList<ActivityRecord> activities = new ArrayList<>();
@@ -1351,31 +1341,6 @@
             }
         }
 
-        if (lastPersistUptime > 0) {
-            if (TaskPersister.DEBUG) {
-                Slog.d(TaskPersister.TAG, "TaskRecord: Adjust firstActiveTime=" + firstActiveTime +
-                        " lastPersistUptime=" + lastPersistUptime);
-                Slog.d(TaskPersister.TAG, "TaskRecord: Migrate lastActiveTime=" + lastActiveTime +
-                        " lastActiveTime=" + lastPersistUptime);
-            }
-            // The first and last task active times are relative to the last boot time, so offset
-            // them to be prior to the current boot time
-            firstActiveTime = -lastPersistUptime + firstActiveTime;
-            lastActiveTime = -lastPersistUptime + lastActiveTime;
-        } else {
-            // The first/last active times are still absolute clock times, so offset them to be
-            // relative to the current boot time
-            long currentTime = System.currentTimeMillis();
-            if (TaskPersister.DEBUG) {
-                Slog.d(TaskPersister.TAG, "TaskRecord: Migrate firstActiveTime=" + firstActiveTime +
-                                " currentTime=" + currentTime);
-                Slog.d(TaskPersister.TAG, "TaskRecord: Migrate lastActiveTime=" + lastActiveTime +
-                                " currentTime=" + currentTime);
-            }
-            firstActiveTime = -Math.max(0, currentTime - firstActiveTime);
-            lastActiveTime = -Math.max(0, currentTime - lastActiveTime);
-        }
-
         int event;
         while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
                 (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
@@ -1425,7 +1390,7 @@
                     + ": effectiveUid=" + effectiveUid);
         }
 
-        final TaskRecord task = new TaskRecord(service, taskId, intent,
+        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
                 affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
                 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
@@ -1820,7 +1785,7 @@
         pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
                 pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
                 pw.print(" isResizeable=" + isResizeable());
-                pw.print(" firstActiveTime=" + firstActiveTime);
+                pw.print(" firstActiveTime=" + lastActiveTime);
                 pw.print(" lastActiveTime=" + lastActiveTime);
                 pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0676cea..a1245a9 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -92,6 +92,7 @@
 
 import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -291,6 +292,7 @@
     private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
     private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
     private static final int MSG_POLICIES_CHANGED = 13;
+    private static final int MSG_SET_FIREWALL_RULES = 14;
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
@@ -711,6 +713,7 @@
             synchronized (mUidRulesFirstLock) {
                 updatePowerSaveWhitelistUL();
                 updateRulesForRestrictPowerUL();
+                updateRulesForAppIdleUL();
             }
         }
     };
@@ -2155,23 +2158,23 @@
     @Override
     public void setDeviceIdleMode(boolean enabled) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setDeviceIdleMode");
         try {
             synchronized (mUidRulesFirstLock) {
-                if (mDeviceIdleMode != enabled) {
-                    mDeviceIdleMode = enabled;
-                    if (mSystemReady) {
-                        // Device idle change means we need to rebuild rules for all
-                        // known apps, so do a global refresh.
-                        updateRulesForRestrictPowerUL();
-                    }
-                    if (enabled) {
-                        EventLogTags.writeDeviceIdleOnPhase("net");
-                    } else {
-                        EventLogTags.writeDeviceIdleOffPhase("net");
-                    }
+                if (mDeviceIdleMode == enabled) {
+                    return;
                 }
+                mDeviceIdleMode = enabled;
+                if (mSystemReady) {
+                    // Device idle change means we need to rebuild rules for all
+                    // known apps, so do a global refresh.
+                    updateRulesForRestrictPowerUL();
+                }
+            }
+            if (enabled) {
+                EventLogTags.writeDeviceIdleOnPhase("net");
+            } else {
+                EventLogTags.writeDeviceIdleOffPhase("net");
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -2583,10 +2586,10 @@
                     uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);
                 }
             }
-            setUidFirewallRules(chain, uidRules);
+            setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE);
+        } else {
+            setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE);
         }
-
-        enableFirewallChainUL(chain, enabled);
     }
 
     private boolean isWhitelistedBatterySaverUL(int uid) {
@@ -2630,7 +2633,7 @@
                 }
             }
 
-            setUidFirewallRules(FIREWALL_CHAIN_STANDBY, uidRules);
+            setUidFirewallRulesAsync(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
@@ -2660,6 +2663,7 @@
     private void updateRulesForGlobalChangeAL(boolean restrictedNetworksChanged) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForGlobalChangeAL");
         try {
+            updateRulesForAppIdleUL();
             updateRulesForRestrictPowerUL();
             updateRulesForRestrictBackgroundUL();
 
@@ -2673,11 +2677,11 @@
         }
     }
 
+    // TODO: rename / document to make it clear these are global (not app-specific) rules
     private void updateRulesForRestrictPowerUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
         try {
             updateRulesForDeviceIdleUL();
-            updateRulesForAppIdleUL();
             updateRulesForPowerSaveUL();
             updateRulesForAllAppsUL(TYPE_RESTRICT_POWER);
         } finally {
@@ -3235,6 +3239,18 @@
                     removeInterfaceQuota((String) msg.obj);
                     return true;
                 }
+                case MSG_SET_FIREWALL_RULES: {
+                    final int chain = msg.arg1;
+                    final int toggle = msg.arg2;
+                    final SparseIntArray uidRules = (SparseIntArray) msg.obj;
+                    if (uidRules != null) {
+                        setUidFirewallRules(chain, uidRules);
+                    }
+                    if (toggle != CHAIN_TOGGLE_NONE) {
+                        enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE);
+                    }
+                    return true;
+                }
                 default: {
                     return false;
                 }
@@ -3300,6 +3316,31 @@
         }
     }
 
+    private static final int CHAIN_TOGGLE_NONE = 0;
+    private static final int CHAIN_TOGGLE_ENABLE = 1;
+    private static final int CHAIN_TOGGLE_DISABLE = 2;
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, value = {
+            CHAIN_TOGGLE_NONE,
+            CHAIN_TOGGLE_ENABLE,
+            CHAIN_TOGGLE_DISABLE
+    })
+    public @interface ChainToggleType {
+    }
+
+    /**
+     * Calls {@link #setUidFirewallRules(int, SparseIntArray)} and
+     * {@link #enableFirewallChainUL(int, boolean)} asynchronously.
+     *
+     * @param chain firewall chain.
+     * @param uidRules new UID rules; if {@code null}, only toggles chain state.
+     * @param toggle whether the chain should be enabled, disabled, or not changed.
+     */
+    private void setUidFirewallRulesAsync(int chain, @Nullable SparseIntArray uidRules,
+            @ChainToggleType int toggle) {
+        mHandler.obtainMessage(MSG_SET_FIREWALL_RULES, chain, toggle, uidRules).sendToTarget();
+    }
+
     /**
      * Set uid rules on a particular firewall chain. This is going to synchronize the rules given
      * here to netd.  It will clean up dead rules and make sure the target chain only contains rules
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ced84f6..a2b86e3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2539,18 +2539,18 @@
 
     boolean setDeviceIdleModeInternal(boolean enabled) {
         synchronized (mLock) {
-            if (mDeviceIdleMode != enabled) {
-                mDeviceIdleMode = enabled;
-                updateWakeLockDisabledStatesLocked();
-                if (enabled) {
-                    EventLogTags.writeDeviceIdleOnPhase("power");
-                } else {
-                    EventLogTags.writeDeviceIdleOffPhase("power");
-                }
-                return true;
+            if (mDeviceIdleMode == enabled) {
+                return false;
             }
-            return false;
+            mDeviceIdleMode = enabled;
+            updateWakeLockDisabledStatesLocked();
         }
+        if (enabled) {
+            EventLogTags.writeDeviceIdleOnPhase("power");
+        } else {
+            EventLogTags.writeDeviceIdleOffPhase("power");
+        }
+        return true;
     }
 
     boolean setLightDeviceIdleModeInternal(boolean enabled) {
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 5ddf283..af68137 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -119,7 +118,7 @@
             int stackClip) {
         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
                 + ": " + anim + " wxh=" + width + "x" + height
-                + " isVisible=" + mAppToken.isVisible());
+                + " hasContentToDisplay=" + mAppToken.hasContentToDisplay());
         animation = anim;
         animating = false;
         if (!anim.isInitialized()) {
@@ -141,7 +140,7 @@
         }
         // Start out animation gone if window is gone, or visible if window is visible.
         transformation.clear();
-        transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
+        transformation.setAlpha(mAppToken.hasContentToDisplay() ? 1 : 0);
         hasTransformation = true;
         mStackClip = stackClip;
 
@@ -164,11 +163,11 @@
 
     public void setDummyAnimation() {
         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken
-                + " isVisible=" + mAppToken.isVisible());
+                + " hasContentToDisplay=" + mAppToken.hasContentToDisplay());
         animation = sDummyAnimation;
         hasTransformation = true;
         transformation.clear();
-        transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
+        transformation.setAlpha(mAppToken.hasContentToDisplay() ? 1 : 0);
     }
 
     void setNullAnimation() {
@@ -384,8 +383,8 @@
             return false;
         }
 
-        mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
-                "AppWindowToken", displayId);
+        mAppToken.setAppLayoutChanges(
+                WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM, "AppWindowToken", displayId);
 
         clearAnimation();
         animating = false;
@@ -398,8 +397,7 @@
             mService.moveInputMethodWindowsIfNeededLocked(true);
         }
 
-        if (DEBUG_ANIM) Slog.v(TAG,
-                "Animation done in " + mAppToken
+        if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken
                 + ": reportedVisible=" + mAppToken.reportedVisible);
 
         transformation.clear();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 47b5f3d..d7e7f81 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -359,6 +359,15 @@
     }
 
     @Override
+    boolean isVisible() {
+        if (hidden) {
+            // TODO: Should this be checking hiddenRequested instead of hidden?
+            return false;
+        }
+        return super.isVisible();
+    }
+
+    @Override
     void removeIfPossible() {
         mIsExiting = false;
         removeAllWindows();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e7a6747..3a3c017 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -331,7 +331,7 @@
      * Find the task whose outside touch area (for resizing) (x, y) falls within.
      * Returns null if the touch doesn't fall into a resizing area.
      */
-    Task findTaskForControlPoint(int x, int y) {
+    Task findTaskForResizePoint(int x, int y) {
         final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             TaskStack stack = mStacks.get(stackNdx);
@@ -498,7 +498,7 @@
         return false;
     }
 
-    void checkForDeferredActions() {
+    void onCompleteDeferredRemoval() {
         boolean animating = false;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mStacks.get(stackNdx);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index de8e5ac..0f5b042 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -538,15 +538,13 @@
         }
         final TaskStack fullscreenStack
                 = mService.mStackIdToStack.get(FULLSCREEN_WORKSPACE_STACK_ID);
-        final ArrayList<Task> homeStackTasks = homeStack.getTasks();
-        final Task topHomeStackTask = homeStackTasks.get(homeStackTasks.size() - 1);
         final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
         final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisible())
-                || (homeStackTasks.size() > 1 && topHomeStackTask != homeTask);
+                || (homeStack.hasMultipleTaskWithHomeTaskNotTop());
         // If the home task is an on-top launcher, we don't want to minimize the docked stack.
         // Instead we want everything underneath that was visible to remain visible.
         // See android.R.attr#onTopLauncher.
-        final boolean isOnTopLauncher = topHomeStackTask.isOnTopLauncher();
+        final boolean isOnTopLauncher = homeStack.topTaskIsOnTopLauncher();
         setMinimizedDockedStack(homeVisible && !homeBehind && !isOnTopLauncher, animate);
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 754a9a6..545a439 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -579,6 +579,16 @@
         return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
     }
 
+    boolean hasContentToDisplay() {
+        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+            final AppWindowToken appToken = mAppTokens.get(i);
+            if (appToken.hasContentToDisplay()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     boolean isVisible() {
         for (int i = mAppTokens.size() - 1; i >= 0; i--) {
             final AppWindowToken appToken = mAppTokens.get(i);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 21fc08b..cf31310 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -43,7 +43,6 @@
 import android.util.SparseArray;
 import android.view.DisplayInfo;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.animation.Animation;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
@@ -162,6 +161,14 @@
         return null;
     }
 
+    boolean hasMultipleTaskWithHomeTaskNotTop() {
+        return mTasks.size() > 1 && !mTasks.get(mTasks.size() - 1).isHomeTask();
+    }
+
+    boolean topTaskIsOnTopLauncher() {
+        return mTasks.get(mTasks.size() - 1).isOnTopLauncher();
+    }
+
     /**
      * Set the bounds of the stack and its containing tasks.
      * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
@@ -898,7 +905,7 @@
     void beginImeAdjustAnimation() {
         for (int j = mTasks.size() - 1; j >= 0; j--) {
             final Task task = mTasks.get(j);
-            if (task.isVisible()) {
+            if (task.hasContentToDisplay()) {
                 task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
                 task.setWaitingForDrawnIfResizingChanged();
             }
@@ -1201,10 +1208,8 @@
 
         for (int i = mTasks.size() - 1; i >= 0; i--) {
             final Task task = mTasks.get(i);
-            for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
-                if (!task.mAppTokens.get(j).hidden) {
-                    return true;
-                }
+            if (task.isVisible()) {
+                return true;
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 0310b97..dd9ba73 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -64,7 +64,7 @@
             case MotionEvent.ACTION_HOVER_MOVE: {
                 final int x = (int) motionEvent.getX();
                 final int y = (int) motionEvent.getY();
-                final Task task = mDisplayContent.findTaskForControlPoint(x, y);
+                final Task task = mDisplayContent.findTaskForResizePoint(x, y);
                 int iconType = TYPE_NOT_SPECIFIED;
                 if (task != null) {
                     task.getDimBounds(mTmpRect);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d37d0e1..fa05c0c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -193,6 +193,33 @@
         }
     }
 
+    /**
+     * Returns true if the container or one of its children as some content it can display or wants
+     * to display (e.g. app views or saved surface).
+     *
+     * NOTE: While this method will return true if the there is some content to display, it doesn't
+     * mean the container is visible. Use {@link #isVisible()} to determine if the container is
+     * visible.
+     */
+    boolean hasContentToDisplay() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            if (wc.hasContentToDisplay()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the container or one of its children is considered visible from the
+     * WindowManager perspective which usually means valid surface and some other internal state
+     * are true.
+     *
+     * NOTE: While this method will return true if the surface is visible, it doesn't mean the
+     * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
+     * the container has any content to display.
+     */
     boolean isVisible() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5c681d5..1cc7dad 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
@@ -6698,7 +6699,7 @@
     private void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
         int taskId = -1;
         synchronized (mWindowMap) {
-            final Task task = displayContent.findTaskForControlPoint(x, y);
+            final Task task = displayContent.findTaskForResizePoint(x, y);
             if (task != null) {
                 if (!startPositioningLocked(
                         task.getTopVisibleAppMainWindow(), true /*resize*/, x, y)) {
@@ -8119,7 +8120,7 @@
     }
 
     @Override
-    public void setForcedDisplayDensity(int displayId, int density) {
+    public void setForcedDisplayDensityForUser(int displayId, int density, int userId) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
                 PackageManager.PERMISSION_GRANTED) {
@@ -8129,16 +8130,20 @@
         if (displayId != Display.DEFAULT_DISPLAY) {
             throw new IllegalArgumentException("Can only set the default display");
         }
+
+        final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser",
+                null);
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized(mWindowMap) {
                 final DisplayContent displayContent = getDisplayContentLocked(displayId);
-                if (displayContent != null) {
+                if (displayContent != null && mCurrentUserId == targetUserId) {
                     setForcedDisplayDensityLocked(displayContent, density);
-                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                            Settings.Secure.DISPLAY_DENSITY_FORCED,
-                            Integer.toString(density), mCurrentUserId);
                 }
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.DISPLAY_DENSITY_FORCED,
+                        Integer.toString(density), targetUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -8146,7 +8151,7 @@
     }
 
     @Override
-    public void clearForcedDisplayDensity(int displayId) {
+    public void clearForcedDisplayDensityForUser(int displayId, int userId) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
                 PackageManager.PERMISSION_GRANTED) {
@@ -8156,16 +8161,20 @@
         if (displayId != Display.DEFAULT_DISPLAY) {
             throw new IllegalArgumentException("Can only set the default display");
         }
+
+        final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser",
+                null);
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized(mWindowMap) {
                 final DisplayContent displayContent = getDisplayContentLocked(displayId);
-                if (displayContent != null) {
+                if (displayContent != null && mCurrentUserId == callingUserId) {
                     setForcedDisplayDensityLocked(displayContent,
                             displayContent.mInitialDisplayDensity);
-                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                            Settings.Secure.DISPLAY_DENSITY_FORCED, "", mCurrentUserId);
                 }
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.DISPLAY_DENSITY_FORCED, "", callingUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -10039,7 +10048,7 @@
     public void setWillReplaceWindow(IBinder token, boolean animate) {
         synchronized (mWindowMap) {
             final AppWindowToken appWindowToken = findAppWindowToken(token);
-            if (appWindowToken == null || !appWindowToken.isVisible()) {
+            if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
                         + token);
                 return;
@@ -10063,7 +10072,7 @@
     public void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
         synchronized (mWindowMap) {
             final AppWindowToken appWindowToken = findAppWindowToken(token);
-            if (appWindowToken == null || !appWindowToken.isVisible()) {
+            if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
                         + token);
                 return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fd47f5b..fccd2d9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1181,10 +1181,8 @@
         }
     }
 
-    // TODO: Sigh...another is visible method...tried to consolidate with other isVisible methods
-    // below, but failed. Need to figure-out a good way to handle this long term...
     @Override
-    boolean isVisible() {
+    boolean hasContentToDisplay() {
         // If we're animating with a saved surface, we're already visible.
         // Return true so that the alpha doesn't get cleared.
         if (!mAppFreezing && isDrawnLw()
@@ -1193,6 +1191,17 @@
             return true;
         }
 
+        return super.hasContentToDisplay();
+    }
+
+    @Override
+    boolean isVisible() {
+        if ((mAppToken == null || !mAppToken.hiddenRequested) && isVisibleUnchecked()) {
+            // Is this window visible?  It is not visible if there is no surface, or we are in the
+            // process of running an exit animation that will remove the surface, or its app token
+            // has been hidden.
+            return true;
+        }
         return super.isVisible();
     }
 
@@ -1206,13 +1215,9 @@
                 && !mAnimatingExit && !mDestroying && (!mIsWallpaper || mWallpaperVisible);
     }
 
-    /**
-     * Is this window visible?  It is not visible if there is no surface, or we are in the process
-     * of running an exit animation that will remove the surface, or its app token has been hidden.
-     */
     @Override
     public boolean isVisibleLw() {
-        return (mAppToken == null || !mAppToken.hiddenRequested) && isVisibleUnchecked();
+        return isVisible();
     }
 
     /**
@@ -1238,7 +1243,8 @@
      * Is this window visible, ignoring its app token? It is not visible if there is no surface,
      * or we are in the process of running an exit animation that will remove the surface.
      */
-    public boolean isWinVisibleLw() {
+    // TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
+    boolean isWinVisibleLw() {
         return (mAppToken == null || !mAppToken.hiddenRequested || mAppToken.mAppAnimator.animating)
                 && isVisibleUnchecked();
     }
@@ -1287,7 +1293,7 @@
      * Like isOnScreen(), but ignores any force hiding of the window due
      * to the keyguard.
      */
-    boolean isOnScreenIgnoringKeyguard() {
+    private boolean isOnScreenIgnoringKeyguard() {
         if (!mHasSurface || mDestroying) {
             return false;
         }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 317bb35..22f39fb 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -560,7 +560,7 @@
 
         // Remove all deferred displays stacks, tasks, and activities.
         for (int displayNdx = mService.mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
-            mService.mDisplayContents.valueAt(displayNdx).checkForDeferredActions();
+            mService.mDisplayContents.valueAt(displayNdx).onCompleteDeferredRemoval();
         }
 
         if (updateInputWindowsNeeded) {
@@ -606,8 +606,6 @@
             final int displayId = displayContent.getDisplayId();
             final int dw = displayInfo.logicalWidth;
             final int dh = displayInfo.logicalHeight;
-            final int innerDw = displayInfo.appWidth;
-            final int innerDh = displayInfo.appHeight;
             final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
 
             // Reset for each display.
@@ -868,7 +866,7 @@
                     mPreferredModeId,
                     true /* inTraversal, must call performTraversalInTrans... below */);
 
-            mService.getDisplayContentLocked(displayId).stopDimmingIfNeeded();
+            displayContent.stopDimmingIfNeeded();
 
             if (updateAllDrawn) {
                 updateAllDrawnLocked(displayContent);
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 7571f79..984a484 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -16,36 +16,19 @@
 
 package com.android.server.am;
 
-import android.app.ActivityManager;
-import android.content.ContentResolver;
-import android.content.pm.ActivityInfo;
 import android.content.pm.UserInfo;
 import android.os.Environment;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.SparseBooleanArray;
-import android.util.Xml;
 
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
 import com.android.server.am.TaskPersister;
 
 import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
 import java.util.Random;
 
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 public class TaskPersisterTest extends AndroidTestCase {
     private static final String TEST_USER_NAME = "AM-Test-User";
 
@@ -86,140 +69,6 @@
                 taskIdsOnFile.equals(newTaskIdsOnFile));
     }
 
-    public void testActiveTimeMigration() {
-        // Simulate a migration scenario by setting the last write uptime to zero
-        ContentResolver cr = getContext().getContentResolver();
-        Settings.Secure.putLong(cr,
-                Settings.Secure.TASK_PERSISTER_LAST_WRITE_UPTIME, 0);
-
-        // Create a dummy task record with an absolute time 1s before now
-        long pastOffset = 1000;
-        long activeTime = System.currentTimeMillis() - pastOffset;
-        TaskRecord tr0 = createDummyTaskRecordWithActiveTime(activeTime, activeTime);
-
-        // Save and load the tasks with no last persist uptime (0)
-        String tr0XmlStr = serializeTaskRecordToXmlString(tr0);
-        TaskRecord xtr0 = unserializeTaskRecordFromXmlString(tr0XmlStr, 0);
-
-        // Ensure that the absolute time has been migrated to be relative to the current elapsed
-        // time
-        assertTrue("Expected firstActiveTime to be migrated from: " + tr0.firstActiveTime +
-                " instead found: " + xtr0.firstActiveTime,
-                        xtr0.firstActiveTime <= -pastOffset);
-        assertTrue("Expected lastActiveTime to be migrated from: " + tr0.lastActiveTime +
-                " instead found: " + xtr0.lastActiveTime,
-                        xtr0.lastActiveTime <= -pastOffset);
-
-        // Ensure that the last active uptime is not set so that SystemUI can migrate it itself
-        // assuming that the last persist time is zero
-        Settings.Secure.putLongForUser(cr,
-                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, 0, testUserId);
-        mTaskPersister.restoreTasksForUserLocked(testUserId);
-        long lastVisTaskActiveTime = Settings.Secure.getLongForUser(cr,
-                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, -1, testUserId);
-        assertTrue("Expected last visible task active time is zero", lastVisTaskActiveTime == 0);
-    }
-
-    public void testActiveTimeOffsets() {
-        // Simulate a normal boot scenario by setting the last write uptime
-        long lastWritePastOffset = 1000;
-        long lastVisActivePastOffset = 500;
-        ContentResolver cr = getContext().getContentResolver();
-        Settings.Secure.putLong(cr,
-                Settings.Secure.TASK_PERSISTER_LAST_WRITE_UPTIME, lastWritePastOffset);
-
-        // Create a dummy task record with an absolute time 1s before now
-        long activeTime = 250;
-        TaskRecord tr0 = createDummyTaskRecordWithActiveTime(activeTime, activeTime);
-
-        // Save and load the tasks with the last persist time
-        String tr0XmlStr = serializeTaskRecordToXmlString(tr0);
-        TaskRecord xtr0 = unserializeTaskRecordFromXmlString(tr0XmlStr, lastWritePastOffset);
-
-        // Ensure that the prior elapsed time has been offset to be relative to the current boot
-        // time
-        assertTrue("Expected firstActiveTime to be offset from: " + tr0.firstActiveTime +
-                " instead found: " + xtr0.firstActiveTime,
-                        xtr0.firstActiveTime <= (-lastWritePastOffset + activeTime));
-        assertTrue("Expected lastActiveTime to be offset from: " + tr0.lastActiveTime +
-                " instead found: " + xtr0.lastActiveTime,
-                        xtr0.lastActiveTime <= (-lastWritePastOffset + activeTime));
-
-        // Ensure that we update the last active uptime as well by simulating a restoreTasks call
-        Settings.Secure.putLongForUser(cr,
-                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, lastVisActivePastOffset,
-                        testUserId);
-        mTaskPersister.restoreTasksForUserLocked(testUserId);
-        long lastVisTaskActiveTime = Settings.Secure.getLongForUser(cr,
-                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, Long.MAX_VALUE,
-                        testUserId);
-        assertTrue("Expected last visible task active time to be offset", lastVisTaskActiveTime <=
-                (-lastWritePastOffset + lastVisActivePastOffset));
-    }
-
-    private TaskRecord createDummyTaskRecordWithActiveTime(long firstActiveTime,
-            long lastActiveTime) {
-        ActivityInfo info = createDummyActivityInfo();
-        ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
-        TaskRecord t = new TaskRecord(null, 0, info, null, td, null);
-        t.firstActiveTime = firstActiveTime;
-        t.lastActiveTime = lastActiveTime;
-        return t;
-    }
-
-    private ActivityInfo createDummyActivityInfo() {
-        ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = getContext().getApplicationInfo();
-        return info;
-    }
-
-    private String serializeTaskRecordToXmlString(TaskRecord tr) {
-        StringWriter stringWriter = new StringWriter();
-
-        try {
-            final XmlSerializer xmlSerializer = new FastXmlSerializer();
-            xmlSerializer.setOutput(stringWriter);
-
-            xmlSerializer.startDocument(null, true);
-            xmlSerializer.startTag(null, TaskPersister.TAG_TASK);
-            tr.saveToXml(xmlSerializer);
-            xmlSerializer.endTag(null, TaskPersister.TAG_TASK);
-            xmlSerializer.endDocument();
-            xmlSerializer.flush();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        return stringWriter.toString();
-    }
-
-    private TaskRecord unserializeTaskRecordFromXmlString(String xmlStr, long lastPersistUptime) {
-        StringReader reader = null;
-        TaskRecord task = null;
-        try {
-            reader = new StringReader(xmlStr);
-            final XmlPullParser in = Xml.newPullParser();
-            in.setInput(reader);
-
-            int event;
-            while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
-                    event != XmlPullParser.END_TAG) {
-                final String name = in.getName();
-                if (event == XmlPullParser.START_TAG) {
-                    if (TaskPersister.TAG_TASK.equals(name)) {
-                        task = TaskRecord.restoreFromXml(in, null, null, lastPersistUptime);
-                    }
-                }
-                XmlUtils.skipCurrentTag(in);
-            }
-        } catch (Exception e) {
-            return null;
-        } finally {
-            IoUtils.closeQuietly(reader);
-        }
-        return task;
-    }
-
     private int createUser(String name, int flags) {
         UserInfo user = mUserManager.createUser(name, flags);
         if (user == null) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index efd479f..39b74d8 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -137,7 +137,7 @@
     private final Context mContext;
     private final ContentResolver mContentResolver;
     @GuardedBy("mLock")
-    private UsbSettingsManager mCurrentSettings;
+    private UsbUserSettingsManager mCurrentUserSettings;
     private NotificationManager mNotificationManager;
     private final boolean mHasUsbAccessory;
     private boolean mUseUsbNotification;
@@ -150,6 +150,7 @@
     private String[] mAccessoryStrings;
     private UsbDebuggingManager mDebuggingManager;
     private final UsbAlsaManager mUsbAlsaManager;
+    private final UsbSettingsManager mSettingsManager;
     private Intent mBroadcastedIntent;
 
     private class AdbSettingsObserver extends ContentObserver {
@@ -192,9 +193,11 @@
         }
     };
 
-    public UsbDeviceManager(Context context, UsbAlsaManager alsaManager) {
+    public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
+            UsbSettingsManager settingsManager) {
         mContext = context;
         mUsbAlsaManager = alsaManager;
+        mSettingsManager = settingsManager;
         mContentResolver = context.getContentResolver();
         PackageManager pm = mContext.getPackageManager();
         mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
@@ -218,9 +221,9 @@
                 new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED));
     }
 
-    private UsbSettingsManager getCurrentSettings() {
+    private UsbUserSettingsManager getCurrentUserSettings() {
         synchronized (mLock) {
-            return mCurrentSettings;
+            return mCurrentUserSettings;
         }
     }
 
@@ -255,10 +258,10 @@
         mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
     }
 
-    public void setCurrentUser(int userId, UsbSettingsManager settings) {
+    public void setCurrentUser(int newCurrentUserId, UsbUserSettingsManager settings) {
         synchronized (mLock) {
-            mCurrentSettings = settings;
-            mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget();
+            mCurrentUserSettings = settings;
+            mHandler.obtainMessage(MSG_USER_SWITCHED, newCurrentUserId, 0).sendToTarget();
         }
     }
 
@@ -571,7 +574,7 @@
                     Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
                     // defer accessoryAttached if system is not ready
                     if (mBootCompleted) {
-                        getCurrentSettings().accessoryAttached(mCurrentAccessory);
+                        getCurrentUserSettings().accessoryAttached(mCurrentAccessory);
                     } // else handle in boot completed
                 } else {
                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -584,7 +587,7 @@
 
                 if (mCurrentAccessory != null) {
                     if (mBootCompleted) {
-                        getCurrentSettings().accessoryDetached(mCurrentAccessory);
+                        mSettingsManager.usbAccessoryRemoved(mCurrentAccessory);
                     }
                     mCurrentAccessory = null;
                     mAccessoryStrings = null;
@@ -764,7 +767,7 @@
                 case MSG_BOOT_COMPLETED:
                     mBootCompleted = true;
                     if (mCurrentAccessory != null) {
-                        getCurrentSettings().accessoryAttached(mCurrentAccessory);
+                        getCurrentUserSettings().accessoryAttached(mCurrentAccessory);
                     }
                     if (mDebuggingManager != null) {
                         mDebuggingManager.setAdbEnabled(mAdbEnabled);
@@ -947,7 +950,7 @@
     }
 
     /* opens the currently attached USB accessory */
-    public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+    public ParcelFileDescriptor openAccessory(UsbAccessory accessory, UsbUserSettingsManager settings) {
         UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
         if (currentAccessory == null) {
             throw new IllegalArgumentException("no accessory attached");
@@ -958,7 +961,7 @@
                     + currentAccessory;
             throw new IllegalArgumentException(error);
         }
-        getCurrentSettings().checkPermission(accessory);
+        settings.checkPermission(accessory);
         return nativeOpenAccessory();
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 1d850e1..84703c2 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -32,8 +32,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -62,18 +60,21 @@
     private ArrayList<UsbEndpoint> mNewEndpoints;
 
     private final UsbAlsaManager mUsbAlsaManager;
+    private final UsbSettingsManager mSettingsManager;
 
     @GuardedBy("mLock")
-    private UsbSettingsManager mCurrentSettings;
+    private UsbUserSettingsManager mCurrentUserSettings;
 
     @GuardedBy("mLock")
     private ComponentName mUsbDeviceConnectionHandler;
 
-    public UsbHostManager(Context context, UsbAlsaManager alsaManager) {
+    public UsbHostManager(Context context, UsbAlsaManager alsaManager,
+            UsbSettingsManager settingsManager) {
         mContext = context;
         mHostBlacklist = context.getResources().getStringArray(
                 com.android.internal.R.array.config_usbHostBlacklist);
         mUsbAlsaManager = alsaManager;
+        mSettingsManager = settingsManager;
         String deviceConnectionHandler = context.getResources().getString(
                 com.android.internal.R.string.config_UsbDeviceConnectionHandling_component);
         if (!TextUtils.isEmpty(deviceConnectionHandler)) {
@@ -82,15 +83,15 @@
         }
     }
 
-    public void setCurrentSettings(UsbSettingsManager settings) {
+    public void setCurrentUserSettings(UsbUserSettingsManager settings) {
         synchronized (mLock) {
-            mCurrentSettings = settings;
+            mCurrentUserSettings = settings;
         }
     }
 
-    private UsbSettingsManager getCurrentSettings() {
+    private UsbUserSettingsManager getCurrentUserSettings() {
         synchronized (mLock) {
-            return mCurrentSettings;
+            return mCurrentUserSettings;
         }
     }
 
@@ -247,11 +248,14 @@
                                 new UsbConfiguration[mNewConfigurations.size()]));
                 mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
                 Slog.d(TAG, "Added device " + mNewDevice);
+
+                // It is fine to call this only for the current user as all broadcasts are sent to
+                // all profiles of the user and the dialogs should only show once.
                 ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
                 if (usbDeviceConnectionHandler == null) {
-                    getCurrentSettings().deviceAttached(mNewDevice);
+                    getCurrentUserSettings().deviceAttached(mNewDevice);
                 } else {
-                    getCurrentSettings().deviceAttachedForFixedHandler(mNewDevice,
+                    getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
                             usbDeviceConnectionHandler);
                 }
                 mUsbAlsaManager.usbDeviceAdded(mNewDevice);
@@ -273,7 +277,8 @@
             UsbDevice device = mDevices.remove(deviceName);
             if (device != null) {
                 mUsbAlsaManager.usbDeviceRemoved(device);
-                getCurrentSettings().deviceDetached(device);
+                mSettingsManager.usbDeviceRemoved(device);
+                getCurrentUserSettings().usbDeviceRemoved(device);
             }
         }
     }
@@ -301,7 +306,7 @@
     }
 
     /* Opens the specified USB device */
-    public ParcelFileDescriptor openDevice(String deviceName) {
+    public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings) {
         synchronized (mLock) {
             if (isBlackListed(deviceName)) {
                 throw new SecurityException("USB device is on a restricted bus");
@@ -312,7 +317,7 @@
                 throw new IllegalArgumentException(
                         "device " + deviceName + " does not exist or is restricted");
             }
-            getCurrentSettings().checkPermission(device);
+            settings.checkPermission(device);
             return nativeOpenDevice(deviceName);
         }
     }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 81ac2dd..1f1fb3d 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usb;
 
+import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -36,7 +37,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
@@ -75,57 +75,65 @@
                 mUsbService.bootCompleted();
             }
         }
+
+        @Override
+        public void onSwitchUser(int newUserId) {
+            mUsbService.onSwitchUser(newUserId);
+        }
+
+        @Override
+        public void onStopUser(int userHandle) {
+            mUsbService.onStopUser(userHandle);
+        }
     }
 
     private static final String TAG = "UsbService";
 
     private final Context mContext;
+    private final UserManager mUserManager;
 
     private UsbDeviceManager mDeviceManager;
     private UsbHostManager mHostManager;
     private UsbPortManager mPortManager;
     private final UsbAlsaManager mAlsaManager;
 
+    private final UsbSettingsManager mSettingsManager;
+
+    /**
+     * The user id of the current user. There might be several profiles (with separate user ids)
+     * per user.
+     */
+    @GuardedBy("mLock")
+    private @UserIdInt int mCurrentUserId;
+
     private final Object mLock = new Object();
 
-    /** Map from {@link UserHandle} to {@link UsbSettingsManager} */
-    @GuardedBy("mLock")
-    private final SparseArray<UsbSettingsManager>
-            mSettingsByUser = new SparseArray<UsbSettingsManager>();
-
-    private UsbSettingsManager getSettingsForUser(int userId) {
-        synchronized (mLock) {
-            UsbSettingsManager settings = mSettingsByUser.get(userId);
-            if (settings == null) {
-                settings = new UsbSettingsManager(mContext, new UserHandle(userId));
-                mSettingsByUser.put(userId, settings);
-            }
-            return settings;
-        }
+    private UsbUserSettingsManager getSettingsForUser(@UserIdInt int userIdInt) {
+        return mSettingsManager.getSettingsForUser(userIdInt);
     }
 
     public UsbService(Context context) {
         mContext = context;
 
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mSettingsManager = new UsbSettingsManager(context);
         mAlsaManager = new UsbAlsaManager(context);
 
         final PackageManager pm = mContext.getPackageManager();
         if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
-            mHostManager = new UsbHostManager(context, mAlsaManager);
+            mHostManager = new UsbHostManager(context, mAlsaManager, mSettingsManager);
         }
         if (new File("/sys/class/android_usb").exists()) {
-            mDeviceManager = new UsbDeviceManager(context, mAlsaManager);
+            mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager);
         }
         if (mHostManager != null || mDeviceManager != null) {
             mPortManager = new UsbPortManager(context);
         }
 
-        setCurrentUser(UserHandle.USER_SYSTEM);
+        onSwitchUser(UserHandle.USER_SYSTEM);
 
         final IntentFilter filter = new IntentFilter();
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_USER_STOPPED);
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         mContext.registerReceiver(mReceiver, filter, null, null);
     }
@@ -133,15 +141,8 @@
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
             final String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                setCurrentUser(userId);
-            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
-                synchronized (mLock) {
-                    mSettingsByUser.remove(userId);
-                }
-            } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
+            if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
                     .equals(action)) {
                 if (mDeviceManager != null) {
                     mDeviceManager.updateUserRestrictions();
@@ -150,14 +151,35 @@
         }
     };
 
-    private void setCurrentUser(int userId) {
-        final UsbSettingsManager userSettings = getSettingsForUser(userId);
-        if (mHostManager != null) {
-            mHostManager.setCurrentSettings(userSettings);
+    /**
+     * Set new {@link #mCurrentUserId} and propagate it to other modules.
+     *
+     * @param newUserId The user id of the new current user.
+     */
+    private void onSwitchUser(@UserIdInt int newUserId) {
+        synchronized (mLock) {
+            mCurrentUserId = newUserId;
+
+            // The following two modules need to know about the current user. If they need to
+            // distinguish by profile of the user, the id has to be passed in the call to the
+            // module.
+            UsbUserSettingsManager userSettings = getSettingsForUser(newUserId);
+            if (mHostManager != null) {
+                mHostManager.setCurrentUserSettings(userSettings);
+            }
+            if (mDeviceManager != null) {
+                mDeviceManager.setCurrentUser(newUserId, userSettings);
+            }
         }
-        if (mDeviceManager != null) {
-            mDeviceManager.setCurrentUser(userId, userSettings);
-        }
+    }
+
+    /**
+     * Execute operations when a user is stopped.
+     *
+     * @param stoppedUserId The id of the used that is stopped
+     */
+    private void onStopUser(@UserIdInt int stoppedUserId) {
+        mSettingsManager.remove(stoppedUserId);
     }
 
     public void systemReady() {
@@ -188,14 +210,45 @@
         }
     }
 
+    /**
+     * Check if the calling user is in the same profile group as the {@link #mCurrentUserId
+     * current user}.
+     *
+     * @return Iff the caller is in the current user's profile group
+     */
+    private boolean isCallerInCurrentUserProfileGroupLocked() {
+        int userIdInt = UserHandle.getCallingUserId();
+
+        long ident = clearCallingIdentity();
+        try {
+            return mUserManager.isSameProfileGroup(userIdInt, mCurrentUserId);
+        } finally {
+            restoreCallingIdentity(ident);
+        }
+    }
+
     /* Opens the specified USB device (host mode) */
     @Override
     public ParcelFileDescriptor openDevice(String deviceName) {
+        ParcelFileDescriptor fd = null;
+
         if (mHostManager != null) {
-            return mHostManager.openDevice(deviceName);
-        } else {
-            return null;
+            synchronized (mLock) {
+                if (deviceName != null) {
+                    int userIdInt = UserHandle.getCallingUserId();
+                    boolean isCurrentUser = isCallerInCurrentUserProfileGroupLocked();
+
+                    if (isCurrentUser) {
+                        fd = mHostManager.openDevice(deviceName, getSettingsForUser(userIdInt));
+                    } else {
+                        Slog.w(TAG, "Cannot open " + deviceName + " for user " + userIdInt +
+                               " as user is not active.");
+                    }
+                }
+            }
         }
+
+        return fd;
     }
 
     /* returns the currently attached USB accessory (device mode) */
@@ -212,10 +265,21 @@
     @Override
     public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
         if (mDeviceManager != null) {
-            return mDeviceManager.openAccessory(accessory);
-        } else {
-            return null;
+            int userIdInt = UserHandle.getCallingUserId();
+
+            synchronized (mLock) {
+                boolean isCurrentUser = isCallerInCurrentUserProfileGroupLocked();
+
+                if (isCurrentUser) {
+                    return mDeviceManager.openAccessory(accessory, getSettingsForUser(userIdInt));
+                } else {
+                    Slog.w(TAG, "Cannot open " + accessory + " for user " + userIdInt +
+                            " as user is not active.");
+                }
+            }
         }
+
+        return null;
     }
 
     @Override
@@ -388,8 +452,15 @@
     @Override
     public void setUsbDeviceConnectionHandler(ComponentName usbDeviceConnectionHandler) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-        if (mHostManager != null) {
-            mHostManager.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler);
+        synchronized (mLock) {
+            if (mCurrentUserId == UserHandle.getCallingUserId()) {
+                if (mHostManager != null) {
+                    mHostManager.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler);
+                }
+            } else {
+                throw new IllegalArgumentException("Only the current user can register a usb " +
+                        "connection handler");
+            }
         }
     }
 
@@ -414,16 +485,7 @@
                 }
                 mAlsaManager.dump(pw);
 
-                synchronized (mLock) {
-                    for (int i = 0; i < mSettingsByUser.size(); i++) {
-                        final int userId = mSettingsByUser.keyAt(i);
-                        final UsbSettingsManager settings = mSettingsByUser.valueAt(i);
-                        pw.println("Settings for user " + userId + ":");
-                        pw.increaseIndent();
-                        settings.dump(pw);
-                        pw.decreaseIndent();
-                    }
-                }
+                mSettingsManager.dump(pw);
             } else if (args.length == 4 && "set-port-roles".equals(args[0])) {
                 final String portId = args[1];
                 final int powerRole;
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 6b9acf2..78f8477 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -1,1278 +1,139 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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
+ *  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
+ *       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.
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
  */
 
 package com.android.server.usb;
 
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.res.XmlResourceParser;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
-import android.os.Binder;
-import android.os.Environment;
-import android.os.Process;
 import android.os.UserHandle;
-import android.util.AtomicFile;
-import android.util.Log;
 import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.Xml;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.XmlUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import libcore.io.IoUtils;
-
+/**
+ * Maintains all {@link UsbUserSettingsManager} for all users.
+ */
 class UsbSettingsManager {
-    private static final String TAG = "UsbSettingsManager";
+    private static final String LOG_TAG = UsbSettingsManager.class.getSimpleName();
     private static final boolean DEBUG = false;
 
-    /** Legacy settings file, before multi-user */
-    private static final File sSingleUserSettingsFile = new File(
-            "/data/system/usb_device_manager.xml");
+    /** Context to be used by this module */
+    private final @NonNull Context mContext;
 
-    private final UserHandle mUser;
-    private final AtomicFile mSettingsFile;
-    private final boolean mDisablePermissionDialogs;
+    /** Map from user id to {@link UsbUserSettingsManager} for the user */
+    @GuardedBy("mSettingsByUser")
+    private final SparseArray<UsbUserSettingsManager> mSettingsByUser = new SparseArray<>();
 
-    private final Context mContext;
-    private final Context mUserContext;
-    private final PackageManager mPackageManager;
-
-    // Temporary mapping USB device name to list of UIDs with permissions for the device
-    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
-            new HashMap<String, SparseBooleanArray>();
-    // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
-    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
-            new HashMap<UsbAccessory, SparseBooleanArray>();
-    // Maps DeviceFilter to user preferred application package
-    private final HashMap<DeviceFilter, String> mDevicePreferenceMap =
-            new HashMap<DeviceFilter, String>();
-    // Maps AccessoryFilter to user preferred application package
-    private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
-            new HashMap<AccessoryFilter, String>();
-
-    private final Object mLock = new Object();
-
-    // This class is used to describe a USB device.
-    // When used in HashMaps all values must be specified,
-    // but wildcards can be used for any of the fields in
-    // the package meta-data.
-    private static class DeviceFilter {
-        // USB Vendor ID (or -1 for unspecified)
-        public final int mVendorId;
-        // USB Product ID (or -1 for unspecified)
-        public final int mProductId;
-        // USB device or interface class (or -1 for unspecified)
-        public final int mClass;
-        // USB device subclass (or -1 for unspecified)
-        public final int mSubclass;
-        // USB device protocol (or -1 for unspecified)
-        public final int mProtocol;
-        // USB device manufacturer name string (or null for unspecified)
-        public final String mManufacturerName;
-        // USB device product name string (or null for unspecified)
-        public final String mProductName;
-        // USB device serial number string (or null for unspecified)
-        public final String mSerialNumber;
-
-        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
-                            String manufacturer, String product, String serialnum) {
-            mVendorId = vid;
-            mProductId = pid;
-            mClass = clasz;
-            mSubclass = subclass;
-            mProtocol = protocol;
-            mManufacturerName = manufacturer;
-            mProductName = product;
-            mSerialNumber = serialnum;
-        }
-
-        public DeviceFilter(UsbDevice device) {
-            mVendorId = device.getVendorId();
-            mProductId = device.getProductId();
-            mClass = device.getDeviceClass();
-            mSubclass = device.getDeviceSubclass();
-            mProtocol = device.getDeviceProtocol();
-            mManufacturerName = device.getManufacturerName();
-            mProductName = device.getProductName();
-            mSerialNumber = device.getSerialNumber();
-        }
-
-        public static DeviceFilter read(XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            int vendorId = -1;
-            int productId = -1;
-            int deviceClass = -1;
-            int deviceSubclass = -1;
-            int deviceProtocol = -1;
-            String manufacturerName = null;
-            String productName = null;
-            String serialNumber = null;
-
-            int count = parser.getAttributeCount();
-            for (int i = 0; i < count; i++) {
-                String name = parser.getAttributeName(i);
-                String value = parser.getAttributeValue(i);
-                // Attribute values are ints or strings
-                if ("manufacturer-name".equals(name)) {
-                    manufacturerName = value;
-                } else if ("product-name".equals(name)) {
-                    productName = value;
-                } else if ("serial-number".equals(name)) {
-                    serialNumber = value;
-                } else {
-                    int intValue = -1;
-                    int radix = 10;
-                    if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
-                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
-                        // allow hex values starting with 0x or 0X
-                        radix = 16;
-                        value = value.substring(2);
-                    }
-                    try {
-                        intValue = Integer.parseInt(value, radix);
-                    } catch (NumberFormatException e) {
-                        Slog.e(TAG, "invalid number for field " + name, e);
-                        continue;
-                    }
-                    if ("vendor-id".equals(name)) {
-                        vendorId = intValue;
-                    } else if ("product-id".equals(name)) {
-                        productId = intValue;
-                    } else if ("class".equals(name)) {
-                        deviceClass = intValue;
-                    } else if ("subclass".equals(name)) {
-                        deviceSubclass = intValue;
-                    } else if ("protocol".equals(name)) {
-                        deviceProtocol = intValue;
-                    }
-                }
-            }
-            return new DeviceFilter(vendorId, productId,
-                    deviceClass, deviceSubclass, deviceProtocol,
-                    manufacturerName, productName, serialNumber);
-        }
-
-        public void write(XmlSerializer serializer) throws IOException {
-            serializer.startTag(null, "usb-device");
-            if (mVendorId != -1) {
-                serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
-            }
-            if (mProductId != -1) {
-                serializer.attribute(null, "product-id", Integer.toString(mProductId));
-            }
-            if (mClass != -1) {
-                serializer.attribute(null, "class", Integer.toString(mClass));
-            }
-            if (mSubclass != -1) {
-                serializer.attribute(null, "subclass", Integer.toString(mSubclass));
-            }
-            if (mProtocol != -1) {
-                serializer.attribute(null, "protocol", Integer.toString(mProtocol));
-            }
-            if (mManufacturerName != null) {
-                serializer.attribute(null, "manufacturer-name", mManufacturerName);
-            }
-            if (mProductName != null) {
-                serializer.attribute(null, "product-name", mProductName);
-            }
-            if (mSerialNumber != null) {
-                serializer.attribute(null, "serial-number", mSerialNumber);
-            }
-            serializer.endTag(null, "usb-device");
-        }
-
-        private boolean matches(int clasz, int subclass, int protocol) {
-            return ((mClass == -1 || clasz == mClass) &&
-                    (mSubclass == -1 || subclass == mSubclass) &&
-                    (mProtocol == -1 || protocol == mProtocol));
-        }
-
-        public boolean matches(UsbDevice device) {
-            if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
-            if (mProductId != -1 && device.getProductId() != mProductId) return false;
-            if (mManufacturerName != null && device.getManufacturerName() == null) return false;
-            if (mProductName != null && device.getProductName() == null) return false;
-            if (mSerialNumber != null && device.getSerialNumber() == null) return false;
-            if (mManufacturerName != null && device.getManufacturerName() != null &&
-                !mManufacturerName.equals(device.getManufacturerName())) return false;
-            if (mProductName != null && device.getProductName() != null &&
-                !mProductName.equals(device.getProductName())) return false;
-            if (mSerialNumber != null && device.getSerialNumber() != null &&
-                !mSerialNumber.equals(device.getSerialNumber())) return false;
-
-            // check device class/subclass/protocol
-            if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
-                    device.getDeviceProtocol())) return true;
-
-            // if device doesn't match, check the interfaces
-            int count = device.getInterfaceCount();
-            for (int i = 0; i < count; i++) {
-                UsbInterface intf = device.getInterface(i);
-                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
-                        intf.getInterfaceProtocol())) return true;
-            }
-
-            return false;
-        }
-
-        public boolean matches(DeviceFilter f) {
-            if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
-            if (mProductId != -1 && f.mProductId != mProductId) return false;
-            if (f.mManufacturerName != null && mManufacturerName == null) return false;
-            if (f.mProductName != null && mProductName == null) return false;
-            if (f.mSerialNumber != null && mSerialNumber == null) return false;
-            if (mManufacturerName != null && f.mManufacturerName != null &&
-                !mManufacturerName.equals(f.mManufacturerName)) return false;
-            if (mProductName != null && f.mProductName != null &&
-                !mProductName.equals(f.mProductName)) return false;
-            if (mSerialNumber != null && f.mSerialNumber != null &&
-                !mSerialNumber.equals(f.mSerialNumber)) return false;
-
-            // check device class/subclass/protocol
-            return matches(f.mClass, f.mSubclass, f.mProtocol);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            // can't compare if we have wildcard strings
-            if (mVendorId == -1 || mProductId == -1 ||
-                    mClass == -1 || mSubclass == -1 || mProtocol == -1) {
-                return false;
-            }
-            if (obj instanceof DeviceFilter) {
-                DeviceFilter filter = (DeviceFilter)obj;
-
-                if (filter.mVendorId != mVendorId ||
-                        filter.mProductId != mProductId ||
-                        filter.mClass != mClass ||
-                        filter.mSubclass != mSubclass ||
-                        filter.mProtocol != mProtocol) {
-                    return(false);
-                }
-                if ((filter.mManufacturerName != null &&
-                        mManufacturerName == null) ||
-                    (filter.mManufacturerName == null &&
-                        mManufacturerName != null) ||
-                    (filter.mProductName != null &&
-                        mProductName == null)  ||
-                    (filter.mProductName == null &&
-                        mProductName != null) ||
-                    (filter.mSerialNumber != null &&
-                        mSerialNumber == null)  ||
-                    (filter.mSerialNumber == null &&
-                        mSerialNumber != null)) {
-                    return(false);
-                }
-                if  ((filter.mManufacturerName != null &&
-                        mManufacturerName != null &&
-                        !mManufacturerName.equals(filter.mManufacturerName)) ||
-                     (filter.mProductName != null &&
-                        mProductName != null &&
-                        !mProductName.equals(filter.mProductName)) ||
-                     (filter.mSerialNumber != null &&
-                        mSerialNumber != null &&
-                        !mSerialNumber.equals(filter.mSerialNumber))) {
-                    return(false);
-                }
-                return(true);
-            }
-            if (obj instanceof UsbDevice) {
-                UsbDevice device = (UsbDevice)obj;
-                if (device.getVendorId() != mVendorId ||
-                        device.getProductId() != mProductId ||
-                        device.getDeviceClass() != mClass ||
-                        device.getDeviceSubclass() != mSubclass ||
-                        device.getDeviceProtocol() != mProtocol) {
-                    return(false);
-                }
-                if ((mManufacturerName != null && device.getManufacturerName() == null) ||
-                        (mManufacturerName == null && device.getManufacturerName() != null) ||
-                        (mProductName != null && device.getProductName() == null) ||
-                        (mProductName == null && device.getProductName() != null) ||
-                        (mSerialNumber != null && device.getSerialNumber() == null) ||
-                        (mSerialNumber == null && device.getSerialNumber() != null)) {
-                    return(false);
-                }
-                if ((device.getManufacturerName() != null &&
-                        !mManufacturerName.equals(device.getManufacturerName())) ||
-                        (device.getProductName() != null &&
-                            !mProductName.equals(device.getProductName())) ||
-                        (device.getSerialNumber() != null &&
-                            !mSerialNumber.equals(device.getSerialNumber()))) {
-                    return(false);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return (((mVendorId << 16) | mProductId) ^
-                    ((mClass << 16) | (mSubclass << 8) | mProtocol));
-        }
-
-        @Override
-        public String toString() {
-            return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
-                    ",mClass=" + mClass + ",mSubclass=" + mSubclass +
-                    ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
-                    ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
-                    "]";
-        }
-    }
-
-    // This class is used to describe a USB accessory.
-    // When used in HashMaps all values must be specified,
-    // but wildcards can be used for any of the fields in
-    // the package meta-data.
-    private static class AccessoryFilter {
-        // USB accessory manufacturer (or null for unspecified)
-        public final String mManufacturer;
-        // USB accessory model (or null for unspecified)
-        public final String mModel;
-        // USB accessory version (or null for unspecified)
-        public final String mVersion;
-
-        public AccessoryFilter(String manufacturer, String model, String version) {
-            mManufacturer = manufacturer;
-            mModel = model;
-            mVersion = version;
-        }
-
-        public AccessoryFilter(UsbAccessory accessory) {
-            mManufacturer = accessory.getManufacturer();
-            mModel = accessory.getModel();
-            mVersion = accessory.getVersion();
-        }
-
-        public static AccessoryFilter read(XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            String manufacturer = null;
-            String model = null;
-            String version = null;
-
-            int count = parser.getAttributeCount();
-            for (int i = 0; i < count; i++) {
-                String name = parser.getAttributeName(i);
-                String value = parser.getAttributeValue(i);
-
-                if ("manufacturer".equals(name)) {
-                    manufacturer = value;
-                } else if ("model".equals(name)) {
-                    model = value;
-                } else if ("version".equals(name)) {
-                    version = value;
-                }
-             }
-             return new AccessoryFilter(manufacturer, model, version);
-        }
-
-        public void write(XmlSerializer serializer)throws IOException {
-            serializer.startTag(null, "usb-accessory");
-            if (mManufacturer != null) {
-                serializer.attribute(null, "manufacturer", mManufacturer);
-            }
-            if (mModel != null) {
-                serializer.attribute(null, "model", mModel);
-            }
-            if (mVersion != null) {
-                serializer.attribute(null, "version", mVersion);
-            }
-            serializer.endTag(null, "usb-accessory");
-        }
-
-        public boolean matches(UsbAccessory acc) {
-            if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
-            if (mModel != null && !acc.getModel().equals(mModel)) return false;
-            if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
-            return true;
-        }
-
-        public boolean matches(AccessoryFilter f) {
-            if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false;
-            if (mModel != null && !f.mModel.equals(mModel)) return false;
-            if (mVersion != null && !f.mVersion.equals(mVersion)) return false;
-            return true;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            // can't compare if we have wildcard strings
-            if (mManufacturer == null || mModel == null || mVersion == null) {
-                return false;
-            }
-            if (obj instanceof AccessoryFilter) {
-                AccessoryFilter filter = (AccessoryFilter)obj;
-                return (mManufacturer.equals(filter.mManufacturer) &&
-                        mModel.equals(filter.mModel) &&
-                        mVersion.equals(filter.mVersion));
-            }
-            if (obj instanceof UsbAccessory) {
-                UsbAccessory accessory = (UsbAccessory)obj;
-                return (mManufacturer.equals(accessory.getManufacturer()) &&
-                        mModel.equals(accessory.getModel()) &&
-                        mVersion.equals(accessory.getVersion()));
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
-                    (mModel == null ? 0 : mModel.hashCode()) ^
-                    (mVersion == null ? 0 : mVersion.hashCode()));
-        }
-
-        @Override
-        public String toString() {
-            return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
-                                "\", mModel=\"" + mModel +
-                                "\", mVersion=\"" + mVersion + "\"]";
-        }
-    }
-
-    private class MyPackageMonitor extends PackageMonitor {
-        @Override
-        public void onPackageAdded(String packageName, int uid) {
-            handlePackageUpdate(packageName);
-        }
-
-        @Override
-        public boolean onPackageChanged(String packageName, int uid, String[] components) {
-            handlePackageUpdate(packageName);
-            return false;
-        }
-
-        @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            clearDefaults(packageName);
-        }
-    }
-
-    MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
-
-    private final MtpNotificationManager mMtpNotificationManager;
-
-    public UsbSettingsManager(Context context, UserHandle user) {
-        if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
-
-        try {
-            mUserContext = context.createPackageContextAsUser("android", 0, user);
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException("Missing android package");
-        }
-
+    public UsbSettingsManager(@NonNull Context context) {
         mContext = context;
-        mPackageManager = mUserContext.getPackageManager();
-
-        mUser = user;
-        mSettingsFile = new AtomicFile(new File(
-                Environment.getUserSystemDirectory(user.getIdentifier()),
-                "usb_device_manager.xml"));
-
-        mDisablePermissionDialogs = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
-
-        synchronized (mLock) {
-            if (UserHandle.SYSTEM.equals(user)) {
-                upgradeSingleUserLocked();
-            }
-            readSettingsLocked();
-        }
-
-        mPackageMonitor.register(mUserContext, null, true);
-        mMtpNotificationManager = new MtpNotificationManager(
-                context,
-                new MtpNotificationManager.OnOpenInAppListener() {
-                    @Override
-                    public void onOpenInApp(UsbDevice device) {
-                        resolveActivity(createDeviceAttachedIntent(device), device);
-                    }
-                });
-    }
-
-    private void readPreference(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        String packageName = null;
-        int count = parser.getAttributeCount();
-        for (int i = 0; i < count; i++) {
-            if ("package".equals(parser.getAttributeName(i))) {
-                packageName = parser.getAttributeValue(i);
-                break;
-            }
-        }
-        XmlUtils.nextElement(parser);
-        if ("usb-device".equals(parser.getName())) {
-            DeviceFilter filter = DeviceFilter.read(parser);
-            mDevicePreferenceMap.put(filter, packageName);
-        } else if ("usb-accessory".equals(parser.getName())) {
-            AccessoryFilter filter = AccessoryFilter.read(parser);
-            mAccessoryPreferenceMap.put(filter, packageName);
-        }
-        XmlUtils.nextElement(parser);
     }
 
     /**
-     * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
-     * Should only by called by owner.
+     * Get the {@link UsbUserSettingsManager} for a user.
+     *
+     * @param userId The id of the user
+     *
+     * @return The settings for the user
      */
-    private void upgradeSingleUserLocked() {
-        if (sSingleUserSettingsFile.exists()) {
-            mDevicePreferenceMap.clear();
-            mAccessoryPreferenceMap.clear();
-
-            FileInputStream fis = null;
-            try {
-                fis = new FileInputStream(sSingleUserSettingsFile);
-                XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(fis, StandardCharsets.UTF_8.name());
-
-                XmlUtils.nextElement(parser);
-                while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                    final String tagName = parser.getName();
-                    if ("preference".equals(tagName)) {
-                        readPreference(parser);
-                    } else {
-                        XmlUtils.nextElement(parser);
-                    }
-                }
-            } catch (IOException e) {
-                Log.wtf(TAG, "Failed to read single-user settings", e);
-            } catch (XmlPullParserException e) {
-                Log.wtf(TAG, "Failed to read single-user settings", e);
-            } finally {
-                IoUtils.closeQuietly(fis);
+    @NonNull UsbUserSettingsManager getSettingsForUser(@UserIdInt int userId) {
+        synchronized (mSettingsByUser) {
+            UsbUserSettingsManager settings = mSettingsByUser.get(userId);
+            if (settings == null) {
+                settings = new UsbUserSettingsManager(mContext, new UserHandle(userId));
+                mSettingsByUser.put(userId, settings);
             }
-
-            writeSettingsLocked();
-
-            // Success or failure, we delete single-user file
-            sSingleUserSettingsFile.delete();
+            return settings;
         }
     }
 
-    private void readSettingsLocked() {
-        if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
+    /**
+     * Remove the settings for a user.
+     *
+     * @param userIdToRemove The user o remove
+     */
+    void remove(@UserIdInt int userIdToRemove) {
+        synchronized (mSettingsByUser) {
+            mSettingsByUser.remove(userIdToRemove);
+        }
+    }
 
-        mDevicePreferenceMap.clear();
-        mAccessoryPreferenceMap.clear();
-
-        FileInputStream stream = null;
-        try {
-            stream = mSettingsFile.openRead();
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(stream, StandardCharsets.UTF_8.name());
-
-            XmlUtils.nextElement(parser);
-            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                String tagName = parser.getName();
-                if ("preference".equals(tagName)) {
-                    readPreference(parser);
-                } else {
-                    XmlUtils.nextElement(parser);
+    /**
+     * Dump all settings of all users.
+     *
+     * @param pw The writer to dump to
+     */
+    void dump(@NonNull IndentingPrintWriter pw) {
+        synchronized (mSettingsByUser) {
+            for (int i = 0; i < mSettingsByUser.size(); i++) {
+                final int userId = mSettingsByUser.keyAt(i);
+                final UsbUserSettingsManager settings = mSettingsByUser.valueAt(i);
+                pw.println("Settings for user " + userId + ":");
+                pw.increaseIndent();
+                try {
+                    settings.dump(pw);
+                } finally {
+                    pw.decreaseIndent();
                 }
             }
-        } catch (FileNotFoundException e) {
-            if (DEBUG) Slog.d(TAG, "settings file not found");
-        } catch (Exception e) {
-            Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
-            mSettingsFile.delete();
-        } finally {
-            IoUtils.closeQuietly(stream);
         }
     }
 
-    private void writeSettingsLocked() {
-        if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
-
-        FileOutputStream fos = null;
-        try {
-            fos = mSettingsFile.startWrite();
-
-            FastXmlSerializer serializer = new FastXmlSerializer();
-            serializer.setOutput(fos, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            serializer.startTag(null, "settings");
-
-            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
-                serializer.startTag(null, "preference");
-                serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
-                filter.write(serializer);
-                serializer.endTag(null, "preference");
-            }
-
-            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
-                serializer.startTag(null, "preference");
-                serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
-                filter.write(serializer);
-                serializer.endTag(null, "preference");
-            }
-
-            serializer.endTag(null, "settings");
-            serializer.endDocument();
-
-            mSettingsFile.finishWrite(fos);
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to write settings", e);
-            if (fos != null) {
-                mSettingsFile.failWrite(fos);
+    /**
+     * Remove temporary access permission and broadcast that a device was removed.
+     *
+     * @param device The device that is removed
+     */
+    void usbDeviceRemoved(@NonNull UsbDevice device) {
+        synchronized (mSettingsByUser) {
+            for (int i = 0; i < mSettingsByUser.size(); i++) {
+                // clear temporary permissions for the device
+                mSettingsByUser.valueAt(i).removeDevicePermissions(device);
             }
         }
-    }
-
-    // Checks to see if a package matches a device or accessory.
-    // Only one of device and accessory should be non-null.
-    private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
-            UsbDevice device, UsbAccessory accessory) {
-        ActivityInfo ai = info.activityInfo;
-
-        XmlResourceParser parser = null;
-        try {
-            parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
-            if (parser == null) {
-                Slog.w(TAG, "no meta-data for " + info);
-                return false;
-            }
-
-            XmlUtils.nextElement(parser);
-            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                String tagName = parser.getName();
-                if (device != null && "usb-device".equals(tagName)) {
-                    DeviceFilter filter = DeviceFilter.read(parser);
-                    if (filter.matches(device)) {
-                        return true;
-                    }
-                }
-                else if (accessory != null && "usb-accessory".equals(tagName)) {
-                    AccessoryFilter filter = AccessoryFilter.read(parser);
-                    if (filter.matches(accessory)) {
-                        return true;
-                    }
-                }
-                XmlUtils.nextElement(parser);
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to load component info " + info.toString(), e);
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return false;
-    }
-
-    private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
-        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
-        List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
-                PackageManager.GET_META_DATA);
-        int count = resolveInfos.size();
-        for (int i = 0; i < count; i++) {
-            ResolveInfo resolveInfo = resolveInfos.get(i);
-            if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
-                matches.add(resolveInfo);
-            }
-        }
-        return matches;
-    }
-
-    private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
-            UsbAccessory accessory, Intent intent) {
-        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
-        List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
-                PackageManager.GET_META_DATA);
-        int count = resolveInfos.size();
-        for (int i = 0; i < count; i++) {
-            ResolveInfo resolveInfo = resolveInfos.get(i);
-            if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
-                matches.add(resolveInfo);
-            }
-        }
-        return matches;
-    }
-
-    public void deviceAttached(UsbDevice device) {
-        final Intent intent = createDeviceAttachedIntent(device);
-
-        // Send broadcast to running activity with registered intent
-        mUserContext.sendBroadcast(intent);
-
-        if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
-            // Show notification if the device is MTP storage.
-            mMtpNotificationManager.showNotification(device);
-        } else {
-            resolveActivity(intent, device);
-        }
-    }
-
-    private void resolveActivity(Intent intent, UsbDevice device) {
-        ArrayList<ResolveInfo> matches;
-        String defaultPackage;
-        synchronized (mLock) {
-            matches = getDeviceMatchesLocked(device, intent);
-            // Launch our default activity directly, if we have one.
-            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
-            defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
-        }
-
-        // Start activity with registered intent
-        resolveActivity(intent, matches, defaultPackage, device, null);
-    }
-
-    public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
-        final Intent intent = createDeviceAttachedIntent(device);
-
-        // Send broadcast to running activity with registered intent
-        mUserContext.sendBroadcast(intent);
-
-        ApplicationInfo appInfo;
-        try {
-            appInfo = mPackageManager.getApplicationInfo(component.getPackageName(), 0);
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "Default USB handling package not found: " + component.getPackageName());
-            return;
-        }
-
-        grantDevicePermission(device, appInfo.uid);
-
-        Intent activityIntent = new Intent(intent);
-        activityIntent.setComponent(component);
-        try {
-            mUserContext.startActivityAsUser(activityIntent, mUser);
-        } catch (ActivityNotFoundException e) {
-            Slog.e(TAG, "unable to start activity " + activityIntent);
-        }
-    }
-
-    public void deviceDetached(UsbDevice device) {
-        // clear temporary permissions for the device
-        mDevicePermissionMap.remove(device.getDeviceName());
 
         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
 
-        mMtpNotificationManager.hideNotification(device.getDeviceId());
-    }
-
-    public void accessoryAttached(UsbAccessory accessory) {
-        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
-        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        ArrayList<ResolveInfo> matches;
-        String defaultPackage;
-        synchronized (mLock) {
-            matches = getAccessoryMatchesLocked(accessory, intent);
-            // Launch our default activity directly, if we have one.
-            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
-            defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "usbDeviceRemoved, sending " + intent);
         }
-
-        resolveActivity(intent, matches, defaultPackage, null, accessory);
-    }
-
-    public void accessoryDetached(UsbAccessory accessory) {
-        // clear temporary permissions for the accessory
-        mAccessoryPermissionMap.remove(accessory);
-
-        Intent intent = new Intent(
-                UsbManager.ACTION_USB_ACCESSORY_DETACHED);
-        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
-            String defaultPackage, UsbDevice device, UsbAccessory accessory) {
-        int count = matches.size();
-
-        // don't show the resolver activity if there are no choices available
-        if (count == 0) {
-            if (accessory != null) {
-                String uri = accessory.getUri();
-                if (uri != null && uri.length() > 0) {
-                    // display URI to user
-                    // start UsbResolverActivity so user can choose an activity
-                    Intent dialogIntent = new Intent();
-                    dialogIntent.setClassName("com.android.systemui",
-                            "com.android.systemui.usb.UsbAccessoryUriActivity");
-                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                    dialogIntent.putExtra("uri", uri);
-                    try {
-                        mUserContext.startActivityAsUser(dialogIntent, mUser);
-                    } catch (ActivityNotFoundException e) {
-                        Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
-                    }
-                }
-            }
-
-            // do nothing
-            return;
-        }
-
-        ResolveInfo defaultRI = null;
-        if (count == 1 && defaultPackage == null) {
-            // Check to see if our single choice is on the system partition.
-            // If so, treat it as our default without calling UsbResolverActivity
-            ResolveInfo rInfo = matches.get(0);
-            if (rInfo.activityInfo != null &&
-                    rInfo.activityInfo.applicationInfo != null &&
-                    (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                defaultRI = rInfo;
-            }
-
-            if (mDisablePermissionDialogs) {
-                // bypass dialog and launch the only matching activity
-                rInfo = matches.get(0);
-                if (rInfo.activityInfo != null) {
-                    defaultPackage = rInfo.activityInfo.packageName;
-                }
+    /**
+     * Remove temporary access permission and broadcast that a accessory was removed.
+     *
+     * @param accessory The accessory that is removed
+     */
+    void usbAccessoryRemoved(@NonNull UsbAccessory accessory) {
+        synchronized (mSettingsByUser) {
+            for (int i = 0; i < mSettingsByUser.size(); i++) {
+                // clear temporary permissions for the accessory
+                mSettingsByUser.valueAt(i).removeAccessoryPermissions(accessory);
             }
         }
 
-        if (defaultRI == null && defaultPackage != null) {
-            // look for default activity
-            for (int i = 0; i < count; i++) {
-                ResolveInfo rInfo = matches.get(i);
-                if (rInfo.activityInfo != null &&
-                        defaultPackage.equals(rInfo.activityInfo.packageName)) {
-                    defaultRI = rInfo;
-                    break;
-                }
-            }
-        }
-
-        if (defaultRI != null) {
-            // grant permission for default activity
-            if (device != null) {
-                grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
-            } else if (accessory != null) {
-                grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid);
-            }
-
-            // start default activity directly
-            try {
-                intent.setComponent(
-                        new ComponentName(defaultRI.activityInfo.packageName,
-                                defaultRI.activityInfo.name));
-                mUserContext.startActivityAsUser(intent, mUser);
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "startActivity failed", e);
-            }
-        } else {
-            Intent resolverIntent = new Intent();
-            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            if (count == 1) {
-                // start UsbConfirmActivity if there is only one choice
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbConfirmActivity");
-                resolverIntent.putExtra("rinfo", matches.get(0));
-
-                if (device != null) {
-                    resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
-                } else {
-                    resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                }
-            } else {
-                // start UsbResolverActivity so user can choose an activity
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbResolverActivity");
-                resolverIntent.putParcelableArrayListExtra("rlist", matches);
-                resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
-            }
-            try {
-                mUserContext.startActivityAsUser(resolverIntent, mUser);
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "unable to start activity " + resolverIntent);
-            }
-        }
-    }
-
-    private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
-        boolean changed = false;
-        for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
-            if (filter.matches(test)) {
-                mDevicePreferenceMap.remove(test);
-                changed = true;
-            }
-        }
-        return changed;
-    }
-
-    private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) {
-        boolean changed = false;
-        for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) {
-            if (filter.matches(test)) {
-                mAccessoryPreferenceMap.remove(test);
-                changed = true;
-            }
-        }
-        return changed;
-    }
-
-    private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
-            String metaDataName) {
-        XmlResourceParser parser = null;
-        boolean changed = false;
-
-        try {
-            parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
-            if (parser == null) return false;
-
-            XmlUtils.nextElement(parser);
-            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                String tagName = parser.getName();
-                if ("usb-device".equals(tagName)) {
-                    DeviceFilter filter = DeviceFilter.read(parser);
-                    if (clearCompatibleMatchesLocked(packageName, filter)) {
-                        changed = true;
-                    }
-                }
-                else if ("usb-accessory".equals(tagName)) {
-                    AccessoryFilter filter = AccessoryFilter.read(parser);
-                    if (clearCompatibleMatchesLocked(packageName, filter)) {
-                        changed = true;
-                    }
-                }
-                XmlUtils.nextElement(parser);
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return changed;
-    }
-
-    // Check to see if the package supports any USB devices or accessories.
-    // If so, clear any non-matching preferences for matching devices/accessories.
-    private void handlePackageUpdate(String packageName) {
-        synchronized (mLock) {
-            PackageInfo info;
-            boolean changed = false;
-
-            try {
-                info = mPackageManager.getPackageInfo(packageName,
-                        PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
-            } catch (NameNotFoundException e) {
-                Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
-                return;
-            }
-
-            ActivityInfo[] activities = info.activities;
-            if (activities == null) return;
-            for (int i = 0; i < activities.length; i++) {
-                // check for meta-data, both for devices and accessories
-                if (handlePackageUpdateLocked(packageName, activities[i],
-                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
-                    changed = true;
-                }
-                if (handlePackageUpdateLocked(packageName, activities[i],
-                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
-                    changed = true;
-                }
-            }
-
-            if (changed) {
-                writeSettingsLocked();
-            }
-        }
-    }
-
-    public boolean hasPermission(UsbDevice device) {
-        synchronized (mLock) {
-            int uid = Binder.getCallingUid();
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
-            if (uidList == null) {
-                return false;
-            }
-            return uidList.get(uid);
-        }
-    }
-
-    public boolean hasPermission(UsbAccessory accessory) {
-        synchronized (mLock) {
-            int uid = Binder.getCallingUid();
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                return false;
-            }
-            return uidList.get(uid);
-        }
-    }
-
-    public void checkPermission(UsbDevice device) {
-        if (!hasPermission(device)) {
-            throw new SecurityException("User has not given permission to device " + device);
-        }
-    }
-
-    public void checkPermission(UsbAccessory accessory) {
-        if (!hasPermission(accessory)) {
-            throw new SecurityException("User has not given permission to accessory " + accessory);
-        }
-    }
-
-    private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
-        final int uid = Binder.getCallingUid();
-
-        // compare uid with packageName to foil apps pretending to be someone else
-        try {
-            ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
-            if (aInfo.uid != uid) {
-                throw new IllegalArgumentException("package " + packageName +
-                        " does not match caller's uid " + uid);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalArgumentException("package " + packageName + " not found");
-        }
-
-        long identity = Binder.clearCallingIdentity();
-        intent.setClassName("com.android.systemui",
-                "com.android.systemui.usb.UsbPermissionActivity");
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(Intent.EXTRA_INTENT, pi);
-        intent.putExtra("package", packageName);
-        intent.putExtra(Intent.EXTRA_UID, uid);
-        try {
-            mUserContext.startActivityAsUser(intent, mUser);
-        } catch (ActivityNotFoundException e) {
-            Slog.e(TAG, "unable to start UsbPermissionActivity");
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
-      Intent intent = new Intent();
-
-        // respond immediately if permission has already been granted
-      if (hasPermission(device)) {
-            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
-            try {
-                pi.send(mUserContext, 0, intent);
-            } catch (PendingIntent.CanceledException e) {
-                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
-            }
-            return;
-        }
-
-        // start UsbPermissionActivity so user can choose an activity
-        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        requestPermissionDialog(intent, packageName, pi);
-    }
-
-    public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
-        Intent intent = new Intent();
-
-        // respond immediately if permission has already been granted
-        if (hasPermission(accessory)) {
-            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
-            try {
-                pi.send(mUserContext, 0, intent);
-            } catch (PendingIntent.CanceledException e) {
-                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
-            }
-            return;
-        }
-
+        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        requestPermissionDialog(intent, packageName, pi);
-    }
-
-    public void setDevicePackage(UsbDevice device, String packageName) {
-        DeviceFilter filter = new DeviceFilter(device);
-        boolean changed = false;
-        synchronized (mLock) {
-            if (packageName == null) {
-                changed = (mDevicePreferenceMap.remove(filter) != null);
-            } else {
-                changed = !packageName.equals(mDevicePreferenceMap.get(filter));
-                if (changed) {
-                    mDevicePreferenceMap.put(filter, packageName);
-                }
-            }
-            if (changed) {
-                writeSettingsLocked();
-            }
-        }
-    }
-
-    public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
-        AccessoryFilter filter = new AccessoryFilter(accessory);
-        boolean changed = false;
-        synchronized (mLock) {
-            if (packageName == null) {
-                changed = (mAccessoryPreferenceMap.remove(filter) != null);
-            } else {
-                changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
-                if (changed) {
-                    mAccessoryPreferenceMap.put(filter, packageName);
-                }
-            }
-            if (changed) {
-                writeSettingsLocked();
-            }
-        }
-    }
-
-    public void grantDevicePermission(UsbDevice device, int uid) {
-        synchronized (mLock) {
-            String deviceName = device.getDeviceName();
-            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mDevicePermissionMap.put(deviceName, uidList);
-            }
-            uidList.put(uid, true);
-        }
-    }
-
-    public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
-        synchronized (mLock) {
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mAccessoryPermissionMap.put(accessory, uidList);
-            }
-            uidList.put(uid, true);
-        }
-    }
-
-    public boolean hasDefaults(String packageName) {
-        synchronized (mLock) {
-            if (mDevicePreferenceMap.values().contains(packageName)) return true;
-            if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
-            return false;
-        }
-    }
-
-    public void clearDefaults(String packageName) {
-        synchronized (mLock) {
-            if (clearPackageDefaultsLocked(packageName)) {
-                writeSettingsLocked();
-            }
-        }
-    }
-
-    private boolean clearPackageDefaultsLocked(String packageName) {
-        boolean cleared = false;
-        synchronized (mLock) {
-            if (mDevicePreferenceMap.containsValue(packageName)) {
-                // make a copy of the key set to avoid ConcurrentModificationException
-                Object[] keys = mDevicePreferenceMap.keySet().toArray();
-                for (int i = 0; i < keys.length; i++) {
-                    Object key = keys[i];
-                    if (packageName.equals(mDevicePreferenceMap.get(key))) {
-                        mDevicePreferenceMap.remove(key);
-                        cleared = true;
-                    }
-                }
-            }
-            if (mAccessoryPreferenceMap.containsValue(packageName)) {
-                // make a copy of the key set to avoid ConcurrentModificationException
-                Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
-                for (int i = 0; i < keys.length; i++) {
-                    Object key = keys[i];
-                    if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
-                        mAccessoryPreferenceMap.remove(key);
-                        cleared = true;
-                    }
-                }
-            }
-            return cleared;
-        }
-    }
-
-    public void dump(IndentingPrintWriter pw) {
-        synchronized (mLock) {
-            pw.println("Device permissions:");
-            for (String deviceName : mDevicePermissionMap.keySet()) {
-                pw.print("  " + deviceName + ": ");
-                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    pw.print(Integer.toString(uidList.keyAt(i)) + " ");
-                }
-                pw.println();
-            }
-            pw.println("Accessory permissions:");
-            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
-                pw.print("  " + accessory + ": ");
-                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    pw.print(Integer.toString(uidList.keyAt(i)) + " ");
-                }
-                pw.println();
-            }
-            pw.println("Device preferences:");
-            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
-                pw.println("  " + filter + ": " + mDevicePreferenceMap.get(filter));
-            }
-            pw.println("Accessory preferences:");
-            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
-                pw.println("  " + filter + ": " + mAccessoryPreferenceMap.get(filter));
-            }
-        }
-    }
-
-    private static Intent createDeviceAttachedIntent(UsbDevice device) {
-        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
-        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return intent;
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
new file mode 100644
index 0000000..5fd265a
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -0,0 +1,1288 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.XmlResourceParser;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.Xml;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import libcore.io.IoUtils;
+
+class UsbUserSettingsManager {
+    private static final String TAG = "UsbUserSettingsManager";
+    private static final boolean DEBUG = false;
+
+    /** Legacy settings file, before multi-user */
+    private static final File sSingleUserSettingsFile = new File(
+            "/data/system/usb_device_manager.xml");
+
+    private final UserHandle mUser;
+    private final AtomicFile mSettingsFile;
+    private final boolean mDisablePermissionDialogs;
+
+    private final Context mContext;
+    private final Context mUserContext;
+    private final PackageManager mPackageManager;
+
+    // Temporary mapping USB device name to list of UIDs with permissions for the device
+    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
+            new HashMap<String, SparseBooleanArray>();
+    // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
+    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+            new HashMap<UsbAccessory, SparseBooleanArray>();
+    // Maps DeviceFilter to user preferred application package
+    private final HashMap<DeviceFilter, String> mDevicePreferenceMap =
+            new HashMap<DeviceFilter, String>();
+    // Maps AccessoryFilter to user preferred application package
+    private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
+            new HashMap<AccessoryFilter, String>();
+
+    private final Object mLock = new Object();
+
+    // This class is used to describe a USB device.
+    // When used in HashMaps all values must be specified,
+    // but wildcards can be used for any of the fields in
+    // the package meta-data.
+    private static class DeviceFilter {
+        // USB Vendor ID (or -1 for unspecified)
+        public final int mVendorId;
+        // USB Product ID (or -1 for unspecified)
+        public final int mProductId;
+        // USB device or interface class (or -1 for unspecified)
+        public final int mClass;
+        // USB device subclass (or -1 for unspecified)
+        public final int mSubclass;
+        // USB device protocol (or -1 for unspecified)
+        public final int mProtocol;
+        // USB device manufacturer name string (or null for unspecified)
+        public final String mManufacturerName;
+        // USB device product name string (or null for unspecified)
+        public final String mProductName;
+        // USB device serial number string (or null for unspecified)
+        public final String mSerialNumber;
+
+        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
+                            String manufacturer, String product, String serialnum) {
+            mVendorId = vid;
+            mProductId = pid;
+            mClass = clasz;
+            mSubclass = subclass;
+            mProtocol = protocol;
+            mManufacturerName = manufacturer;
+            mProductName = product;
+            mSerialNumber = serialnum;
+        }
+
+        public DeviceFilter(UsbDevice device) {
+            mVendorId = device.getVendorId();
+            mProductId = device.getProductId();
+            mClass = device.getDeviceClass();
+            mSubclass = device.getDeviceSubclass();
+            mProtocol = device.getDeviceProtocol();
+            mManufacturerName = device.getManufacturerName();
+            mProductName = device.getProductName();
+            mSerialNumber = device.getSerialNumber();
+        }
+
+        public static DeviceFilter read(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            int vendorId = -1;
+            int productId = -1;
+            int deviceClass = -1;
+            int deviceSubclass = -1;
+            int deviceProtocol = -1;
+            String manufacturerName = null;
+            String productName = null;
+            String serialNumber = null;
+
+            int count = parser.getAttributeCount();
+            for (int i = 0; i < count; i++) {
+                String name = parser.getAttributeName(i);
+                String value = parser.getAttributeValue(i);
+                // Attribute values are ints or strings
+                if ("manufacturer-name".equals(name)) {
+                    manufacturerName = value;
+                } else if ("product-name".equals(name)) {
+                    productName = value;
+                } else if ("serial-number".equals(name)) {
+                    serialNumber = value;
+                } else {
+                    int intValue = -1;
+                    int radix = 10;
+                    if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
+                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
+                        // allow hex values starting with 0x or 0X
+                        radix = 16;
+                        value = value.substring(2);
+                    }
+                    try {
+                        intValue = Integer.parseInt(value, radix);
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG, "invalid number for field " + name, e);
+                        continue;
+                    }
+                    if ("vendor-id".equals(name)) {
+                        vendorId = intValue;
+                    } else if ("product-id".equals(name)) {
+                        productId = intValue;
+                    } else if ("class".equals(name)) {
+                        deviceClass = intValue;
+                    } else if ("subclass".equals(name)) {
+                        deviceSubclass = intValue;
+                    } else if ("protocol".equals(name)) {
+                        deviceProtocol = intValue;
+                    }
+                }
+            }
+            return new DeviceFilter(vendorId, productId,
+                    deviceClass, deviceSubclass, deviceProtocol,
+                    manufacturerName, productName, serialNumber);
+        }
+
+        public void write(XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, "usb-device");
+            if (mVendorId != -1) {
+                serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
+            }
+            if (mProductId != -1) {
+                serializer.attribute(null, "product-id", Integer.toString(mProductId));
+            }
+            if (mClass != -1) {
+                serializer.attribute(null, "class", Integer.toString(mClass));
+            }
+            if (mSubclass != -1) {
+                serializer.attribute(null, "subclass", Integer.toString(mSubclass));
+            }
+            if (mProtocol != -1) {
+                serializer.attribute(null, "protocol", Integer.toString(mProtocol));
+            }
+            if (mManufacturerName != null) {
+                serializer.attribute(null, "manufacturer-name", mManufacturerName);
+            }
+            if (mProductName != null) {
+                serializer.attribute(null, "product-name", mProductName);
+            }
+            if (mSerialNumber != null) {
+                serializer.attribute(null, "serial-number", mSerialNumber);
+            }
+            serializer.endTag(null, "usb-device");
+        }
+
+        private boolean matches(int clasz, int subclass, int protocol) {
+            return ((mClass == -1 || clasz == mClass) &&
+                    (mSubclass == -1 || subclass == mSubclass) &&
+                    (mProtocol == -1 || protocol == mProtocol));
+        }
+
+        public boolean matches(UsbDevice device) {
+            if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
+            if (mProductId != -1 && device.getProductId() != mProductId) return false;
+            if (mManufacturerName != null && device.getManufacturerName() == null) return false;
+            if (mProductName != null && device.getProductName() == null) return false;
+            if (mSerialNumber != null && device.getSerialNumber() == null) return false;
+            if (mManufacturerName != null && device.getManufacturerName() != null &&
+                !mManufacturerName.equals(device.getManufacturerName())) return false;
+            if (mProductName != null && device.getProductName() != null &&
+                !mProductName.equals(device.getProductName())) return false;
+            if (mSerialNumber != null && device.getSerialNumber() != null &&
+                !mSerialNumber.equals(device.getSerialNumber())) return false;
+
+            // check device class/subclass/protocol
+            if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
+                    device.getDeviceProtocol())) return true;
+
+            // if device doesn't match, check the interfaces
+            int count = device.getInterfaceCount();
+            for (int i = 0; i < count; i++) {
+                UsbInterface intf = device.getInterface(i);
+                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
+                        intf.getInterfaceProtocol())) return true;
+            }
+
+            return false;
+        }
+
+        public boolean matches(DeviceFilter f) {
+            if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
+            if (mProductId != -1 && f.mProductId != mProductId) return false;
+            if (f.mManufacturerName != null && mManufacturerName == null) return false;
+            if (f.mProductName != null && mProductName == null) return false;
+            if (f.mSerialNumber != null && mSerialNumber == null) return false;
+            if (mManufacturerName != null && f.mManufacturerName != null &&
+                !mManufacturerName.equals(f.mManufacturerName)) return false;
+            if (mProductName != null && f.mProductName != null &&
+                !mProductName.equals(f.mProductName)) return false;
+            if (mSerialNumber != null && f.mSerialNumber != null &&
+                !mSerialNumber.equals(f.mSerialNumber)) return false;
+
+            // check device class/subclass/protocol
+            return matches(f.mClass, f.mSubclass, f.mProtocol);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            // can't compare if we have wildcard strings
+            if (mVendorId == -1 || mProductId == -1 ||
+                    mClass == -1 || mSubclass == -1 || mProtocol == -1) {
+                return false;
+            }
+            if (obj instanceof DeviceFilter) {
+                DeviceFilter filter = (DeviceFilter)obj;
+
+                if (filter.mVendorId != mVendorId ||
+                        filter.mProductId != mProductId ||
+                        filter.mClass != mClass ||
+                        filter.mSubclass != mSubclass ||
+                        filter.mProtocol != mProtocol) {
+                    return(false);
+                }
+                if ((filter.mManufacturerName != null &&
+                        mManufacturerName == null) ||
+                    (filter.mManufacturerName == null &&
+                        mManufacturerName != null) ||
+                    (filter.mProductName != null &&
+                        mProductName == null)  ||
+                    (filter.mProductName == null &&
+                        mProductName != null) ||
+                    (filter.mSerialNumber != null &&
+                        mSerialNumber == null)  ||
+                    (filter.mSerialNumber == null &&
+                        mSerialNumber != null)) {
+                    return(false);
+                }
+                if  ((filter.mManufacturerName != null &&
+                        mManufacturerName != null &&
+                        !mManufacturerName.equals(filter.mManufacturerName)) ||
+                     (filter.mProductName != null &&
+                        mProductName != null &&
+                        !mProductName.equals(filter.mProductName)) ||
+                     (filter.mSerialNumber != null &&
+                        mSerialNumber != null &&
+                        !mSerialNumber.equals(filter.mSerialNumber))) {
+                    return(false);
+                }
+                return(true);
+            }
+            if (obj instanceof UsbDevice) {
+                UsbDevice device = (UsbDevice)obj;
+                if (device.getVendorId() != mVendorId ||
+                        device.getProductId() != mProductId ||
+                        device.getDeviceClass() != mClass ||
+                        device.getDeviceSubclass() != mSubclass ||
+                        device.getDeviceProtocol() != mProtocol) {
+                    return(false);
+                }
+                if ((mManufacturerName != null && device.getManufacturerName() == null) ||
+                        (mManufacturerName == null && device.getManufacturerName() != null) ||
+                        (mProductName != null && device.getProductName() == null) ||
+                        (mProductName == null && device.getProductName() != null) ||
+                        (mSerialNumber != null && device.getSerialNumber() == null) ||
+                        (mSerialNumber == null && device.getSerialNumber() != null)) {
+                    return(false);
+                }
+                if ((device.getManufacturerName() != null &&
+                        !mManufacturerName.equals(device.getManufacturerName())) ||
+                        (device.getProductName() != null &&
+                            !mProductName.equals(device.getProductName())) ||
+                        (device.getSerialNumber() != null &&
+                            !mSerialNumber.equals(device.getSerialNumber()))) {
+                    return(false);
+                }
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return (((mVendorId << 16) | mProductId) ^
+                    ((mClass << 16) | (mSubclass << 8) | mProtocol));
+        }
+
+        @Override
+        public String toString() {
+            return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
+                    ",mClass=" + mClass + ",mSubclass=" + mSubclass +
+                    ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
+                    ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
+                    "]";
+        }
+    }
+
+    // This class is used to describe a USB accessory.
+    // When used in HashMaps all values must be specified,
+    // but wildcards can be used for any of the fields in
+    // the package meta-data.
+    private static class AccessoryFilter {
+        // USB accessory manufacturer (or null for unspecified)
+        public final String mManufacturer;
+        // USB accessory model (or null for unspecified)
+        public final String mModel;
+        // USB accessory version (or null for unspecified)
+        public final String mVersion;
+
+        public AccessoryFilter(String manufacturer, String model, String version) {
+            mManufacturer = manufacturer;
+            mModel = model;
+            mVersion = version;
+        }
+
+        public AccessoryFilter(UsbAccessory accessory) {
+            mManufacturer = accessory.getManufacturer();
+            mModel = accessory.getModel();
+            mVersion = accessory.getVersion();
+        }
+
+        public static AccessoryFilter read(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            String manufacturer = null;
+            String model = null;
+            String version = null;
+
+            int count = parser.getAttributeCount();
+            for (int i = 0; i < count; i++) {
+                String name = parser.getAttributeName(i);
+                String value = parser.getAttributeValue(i);
+
+                if ("manufacturer".equals(name)) {
+                    manufacturer = value;
+                } else if ("model".equals(name)) {
+                    model = value;
+                } else if ("version".equals(name)) {
+                    version = value;
+                }
+             }
+             return new AccessoryFilter(manufacturer, model, version);
+        }
+
+        public void write(XmlSerializer serializer)throws IOException {
+            serializer.startTag(null, "usb-accessory");
+            if (mManufacturer != null) {
+                serializer.attribute(null, "manufacturer", mManufacturer);
+            }
+            if (mModel != null) {
+                serializer.attribute(null, "model", mModel);
+            }
+            if (mVersion != null) {
+                serializer.attribute(null, "version", mVersion);
+            }
+            serializer.endTag(null, "usb-accessory");
+        }
+
+        public boolean matches(UsbAccessory acc) {
+            if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
+            if (mModel != null && !acc.getModel().equals(mModel)) return false;
+            if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
+            return true;
+        }
+
+        public boolean matches(AccessoryFilter f) {
+            if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false;
+            if (mModel != null && !f.mModel.equals(mModel)) return false;
+            if (mVersion != null && !f.mVersion.equals(mVersion)) return false;
+            return true;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            // can't compare if we have wildcard strings
+            if (mManufacturer == null || mModel == null || mVersion == null) {
+                return false;
+            }
+            if (obj instanceof AccessoryFilter) {
+                AccessoryFilter filter = (AccessoryFilter)obj;
+                return (mManufacturer.equals(filter.mManufacturer) &&
+                        mModel.equals(filter.mModel) &&
+                        mVersion.equals(filter.mVersion));
+            }
+            if (obj instanceof UsbAccessory) {
+                UsbAccessory accessory = (UsbAccessory)obj;
+                return (mManufacturer.equals(accessory.getManufacturer()) &&
+                        mModel.equals(accessory.getModel()) &&
+                        mVersion.equals(accessory.getVersion()));
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
+                    (mModel == null ? 0 : mModel.hashCode()) ^
+                    (mVersion == null ? 0 : mVersion.hashCode()));
+        }
+
+        @Override
+        public String toString() {
+            return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
+                                "\", mModel=\"" + mModel +
+                                "\", mVersion=\"" + mVersion + "\"]";
+        }
+    }
+
+    private class MyPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+            handlePackageUpdate(packageName);
+        }
+
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            handlePackageUpdate(packageName);
+            return false;
+        }
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            clearDefaults(packageName);
+        }
+    }
+
+    MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+
+    private final MtpNotificationManager mMtpNotificationManager;
+
+    public UsbUserSettingsManager(Context context, UserHandle user) {
+        if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
+
+        try {
+            mUserContext = context.createPackageContextAsUser("android", 0, user);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException("Missing android package");
+        }
+
+        mContext = context;
+        mPackageManager = mUserContext.getPackageManager();
+
+        mUser = user;
+        mSettingsFile = new AtomicFile(new File(
+                Environment.getUserSystemDirectory(user.getIdentifier()),
+                "usb_device_manager.xml"));
+
+        mDisablePermissionDialogs = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+
+        synchronized (mLock) {
+            if (UserHandle.SYSTEM.equals(user)) {
+                upgradeSingleUserLocked();
+            }
+            readSettingsLocked();
+        }
+
+        mPackageMonitor.register(mUserContext, null, true);
+        mMtpNotificationManager = new MtpNotificationManager(
+                context,
+                new MtpNotificationManager.OnOpenInAppListener() {
+                    @Override
+                    public void onOpenInApp(UsbDevice device) {
+                        resolveActivity(createDeviceAttachedIntent(device), device);
+                    }
+                });
+    }
+
+    private void readPreference(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        String packageName = null;
+        int count = parser.getAttributeCount();
+        for (int i = 0; i < count; i++) {
+            if ("package".equals(parser.getAttributeName(i))) {
+                packageName = parser.getAttributeValue(i);
+                break;
+            }
+        }
+        XmlUtils.nextElement(parser);
+        if ("usb-device".equals(parser.getName())) {
+            DeviceFilter filter = DeviceFilter.read(parser);
+            mDevicePreferenceMap.put(filter, packageName);
+        } else if ("usb-accessory".equals(parser.getName())) {
+            AccessoryFilter filter = AccessoryFilter.read(parser);
+            mAccessoryPreferenceMap.put(filter, packageName);
+        }
+        XmlUtils.nextElement(parser);
+    }
+
+    /**
+     * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
+     * Should only by called by owner.
+     */
+    private void upgradeSingleUserLocked() {
+        if (sSingleUserSettingsFile.exists()) {
+            mDevicePreferenceMap.clear();
+            mAccessoryPreferenceMap.clear();
+
+            FileInputStream fis = null;
+            try {
+                fis = new FileInputStream(sSingleUserSettingsFile);
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(fis, StandardCharsets.UTF_8.name());
+
+                XmlUtils.nextElement(parser);
+                while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                    final String tagName = parser.getName();
+                    if ("preference".equals(tagName)) {
+                        readPreference(parser);
+                    } else {
+                        XmlUtils.nextElement(parser);
+                    }
+                }
+            } catch (IOException e) {
+                Log.wtf(TAG, "Failed to read single-user settings", e);
+            } catch (XmlPullParserException e) {
+                Log.wtf(TAG, "Failed to read single-user settings", e);
+            } finally {
+                IoUtils.closeQuietly(fis);
+            }
+
+            writeSettingsLocked();
+
+            // Success or failure, we delete single-user file
+            sSingleUserSettingsFile.delete();
+        }
+    }
+
+    private void readSettingsLocked() {
+        if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
+
+        mDevicePreferenceMap.clear();
+        mAccessoryPreferenceMap.clear();
+
+        FileInputStream stream = null;
+        try {
+            stream = mSettingsFile.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if ("preference".equals(tagName)) {
+                    readPreference(parser);
+                } else {
+                    XmlUtils.nextElement(parser);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            if (DEBUG) Slog.d(TAG, "settings file not found");
+        } catch (Exception e) {
+            Slog.e(TAG, "error reading settings file, deleting to start fresh", e);
+            mSettingsFile.delete();
+        } finally {
+            IoUtils.closeQuietly(stream);
+        }
+    }
+
+    private void writeSettingsLocked() {
+        if (DEBUG) Slog.v(TAG, "writeSettingsLocked()");
+
+        FileOutputStream fos = null;
+        try {
+            fos = mSettingsFile.startWrite();
+
+            FastXmlSerializer serializer = new FastXmlSerializer();
+            serializer.setOutput(fos, StandardCharsets.UTF_8.name());
+            serializer.startDocument(null, true);
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.startTag(null, "settings");
+
+            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
+                serializer.startTag(null, "preference");
+                serializer.attribute(null, "package", mDevicePreferenceMap.get(filter));
+                filter.write(serializer);
+                serializer.endTag(null, "preference");
+            }
+
+            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
+                serializer.startTag(null, "preference");
+                serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
+                filter.write(serializer);
+                serializer.endTag(null, "preference");
+            }
+
+            serializer.endTag(null, "settings");
+            serializer.endDocument();
+
+            mSettingsFile.finishWrite(fos);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write settings", e);
+            if (fos != null) {
+                mSettingsFile.failWrite(fos);
+            }
+        }
+    }
+
+    // Checks to see if a package matches a device or accessory.
+    // Only one of device and accessory should be non-null.
+    private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
+            UsbDevice device, UsbAccessory accessory) {
+        ActivityInfo ai = info.activityInfo;
+
+        XmlResourceParser parser = null;
+        try {
+            parser = ai.loadXmlMetaData(mPackageManager, metaDataName);
+            if (parser == null) {
+                Slog.w(TAG, "no meta-data for " + info);
+                return false;
+            }
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if (device != null && "usb-device".equals(tagName)) {
+                    DeviceFilter filter = DeviceFilter.read(parser);
+                    if (filter.matches(device)) {
+                        return true;
+                    }
+                }
+                else if (accessory != null && "usb-accessory".equals(tagName)) {
+                    AccessoryFilter filter = AccessoryFilter.read(parser);
+                    if (filter.matches(accessory)) {
+                        return true;
+                    }
+                }
+                XmlUtils.nextElement(parser);
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to load component info " + info.toString(), e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return false;
+    }
+
+    private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) {
+        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
+        List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
+                PackageManager.GET_META_DATA);
+        int count = resolveInfos.size();
+        for (int i = 0; i < count; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+            if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) {
+                matches.add(resolveInfo);
+            }
+        }
+        return matches;
+    }
+
+    private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
+            UsbAccessory accessory, Intent intent) {
+        ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
+        List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent,
+                PackageManager.GET_META_DATA);
+        int count = resolveInfos.size();
+        for (int i = 0; i < count; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+            if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) {
+                matches.add(resolveInfo);
+            }
+        }
+        return matches;
+    }
+
+    public void deviceAttached(UsbDevice device) {
+        final Intent intent = createDeviceAttachedIntent(device);
+
+        // Send broadcast to running activities with registered intent
+        mUserContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+
+        if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
+            // Show notification if the device is MTP storage.
+            mMtpNotificationManager.showNotification(device);
+        } else {
+            resolveActivity(intent, device);
+        }
+    }
+
+    private void resolveActivity(Intent intent, UsbDevice device) {
+        ArrayList<ResolveInfo> matches;
+        String defaultPackage;
+        synchronized (mLock) {
+            matches = getDeviceMatchesLocked(device, intent);
+            // Launch our default activity directly, if we have one.
+            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
+            defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
+        }
+
+        // Start activity with registered intent
+        resolveActivity(intent, matches, defaultPackage, device, null);
+    }
+
+    public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
+        final Intent intent = createDeviceAttachedIntent(device);
+
+        // Send broadcast to running activity with registered intent
+        mUserContext.sendBroadcast(intent);
+
+        ApplicationInfo appInfo;
+        try {
+            appInfo = mPackageManager.getApplicationInfo(component.getPackageName(), 0);
+        } catch (NameNotFoundException e) {
+            Slog.e(TAG, "Default USB handling package not found: " + component.getPackageName());
+            return;
+        }
+
+        grantDevicePermission(device, appInfo.uid);
+
+        Intent activityIntent = new Intent(intent);
+        activityIntent.setComponent(component);
+        try {
+            mUserContext.startActivityAsUser(activityIntent, mUser);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(TAG, "unable to start activity " + activityIntent);
+        }
+    }
+
+    /**
+     * Remove all access permission for a device.
+     *
+     * @param device The device the permissions are for
+     */
+    void removeDevicePermissions(@NonNull UsbDevice device) {
+        synchronized (mLock) {
+            mDevicePermissionMap.remove(device.getDeviceName());
+        }
+    }
+
+    /**
+     * Remove notifications for a usb device.
+     *
+     * @param device The device the notifications are for.
+     */
+    void usbDeviceRemoved(@NonNull UsbDevice device) {
+        mMtpNotificationManager.hideNotification(device.getDeviceId());
+    }
+
+    public void accessoryAttached(UsbAccessory accessory) {
+        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        ArrayList<ResolveInfo> matches;
+        String defaultPackage;
+        synchronized (mLock) {
+            matches = getAccessoryMatchesLocked(accessory, intent);
+            // Launch our default activity directly, if we have one.
+            // Otherwise we will start the UsbResolverActivity to allow the user to choose.
+            defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
+        }
+
+        resolveActivity(intent, matches, defaultPackage, null, accessory);
+    }
+
+    /**
+     * Remove all access permission for a accessory.
+     *
+     * @param accessory The accessory the permissions are for
+     */
+    void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
+        synchronized (mLock) {
+            mAccessoryPermissionMap.remove(accessory);
+        }
+    }
+
+    private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
+            String defaultPackage, UsbDevice device, UsbAccessory accessory) {
+        int count = matches.size();
+
+        // don't show the resolver activity if there are no choices available
+        if (count == 0) {
+            if (accessory != null) {
+                String uri = accessory.getUri();
+                if (uri != null && uri.length() > 0) {
+                    // display URI to user
+                    // start UsbResolverActivity so user can choose an activity
+                    Intent dialogIntent = new Intent();
+                    dialogIntent.setClassName("com.android.systemui",
+                            "com.android.systemui.usb.UsbAccessoryUriActivity");
+                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+                    dialogIntent.putExtra("uri", uri);
+                    try {
+                        mUserContext.startActivityAsUser(dialogIntent, mUser);
+                    } catch (ActivityNotFoundException e) {
+                        Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
+                    }
+                }
+            }
+
+            // do nothing
+            return;
+        }
+
+        ResolveInfo defaultRI = null;
+        if (count == 1 && defaultPackage == null) {
+            // Check to see if our single choice is on the system partition.
+            // If so, treat it as our default without calling UsbResolverActivity
+            ResolveInfo rInfo = matches.get(0);
+            if (rInfo.activityInfo != null &&
+                    rInfo.activityInfo.applicationInfo != null &&
+                    (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                defaultRI = rInfo;
+            }
+
+            if (mDisablePermissionDialogs) {
+                // bypass dialog and launch the only matching activity
+                rInfo = matches.get(0);
+                if (rInfo.activityInfo != null) {
+                    defaultPackage = rInfo.activityInfo.packageName;
+                }
+            }
+        }
+
+        if (defaultRI == null && defaultPackage != null) {
+            // look for default activity
+            for (int i = 0; i < count; i++) {
+                ResolveInfo rInfo = matches.get(i);
+                if (rInfo.activityInfo != null &&
+                        defaultPackage.equals(rInfo.activityInfo.packageName)) {
+                    defaultRI = rInfo;
+                    break;
+                }
+            }
+        }
+
+        if (defaultRI != null) {
+            // grant permission for default activity
+            if (device != null) {
+                grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
+            } else if (accessory != null) {
+                grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid);
+            }
+
+            // start default activity directly
+            try {
+                intent.setComponent(
+                        new ComponentName(defaultRI.activityInfo.packageName,
+                                defaultRI.activityInfo.name));
+                mUserContext.startActivityAsUser(intent, mUser);
+            } catch (ActivityNotFoundException e) {
+                Slog.e(TAG, "startActivity failed", e);
+            }
+        } else {
+            Intent resolverIntent = new Intent();
+            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            if (count == 1) {
+                // start UsbConfirmActivity if there is only one choice
+                resolverIntent.setClassName("com.android.systemui",
+                        "com.android.systemui.usb.UsbConfirmActivity");
+                resolverIntent.putExtra("rinfo", matches.get(0));
+
+                if (device != null) {
+                    resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
+                } else {
+                    resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+                }
+            } else {
+                // start UsbResolverActivity so user can choose an activity
+                resolverIntent.setClassName("com.android.systemui",
+                        "com.android.systemui.usb.UsbResolverActivity");
+                resolverIntent.putParcelableArrayListExtra("rlist", matches);
+                resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+            }
+            try {
+                mUserContext.startActivityAsUser(resolverIntent, mUser);
+            } catch (ActivityNotFoundException e) {
+                Slog.e(TAG, "unable to start activity " + resolverIntent);
+            }
+        }
+    }
+
+    private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
+        boolean changed = false;
+        for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
+            if (filter.matches(test)) {
+                mDevicePreferenceMap.remove(test);
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) {
+        boolean changed = false;
+        for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) {
+            if (filter.matches(test)) {
+                mAccessoryPreferenceMap.remove(test);
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo,
+            String metaDataName) {
+        XmlResourceParser parser = null;
+        boolean changed = false;
+
+        try {
+            parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName);
+            if (parser == null) return false;
+
+            XmlUtils.nextElement(parser);
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if ("usb-device".equals(tagName)) {
+                    DeviceFilter filter = DeviceFilter.read(parser);
+                    if (clearCompatibleMatchesLocked(packageName, filter)) {
+                        changed = true;
+                    }
+                }
+                else if ("usb-accessory".equals(tagName)) {
+                    AccessoryFilter filter = AccessoryFilter.read(parser);
+                    if (clearCompatibleMatchesLocked(packageName, filter)) {
+                        changed = true;
+                    }
+                }
+                XmlUtils.nextElement(parser);
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return changed;
+    }
+
+    // Check to see if the package supports any USB devices or accessories.
+    // If so, clear any non-matching preferences for matching devices/accessories.
+    private void handlePackageUpdate(String packageName) {
+        synchronized (mLock) {
+            PackageInfo info;
+            boolean changed = false;
+
+            try {
+                info = mPackageManager.getPackageInfo(packageName,
+                        PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+            } catch (NameNotFoundException e) {
+                Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
+                return;
+            }
+
+            ActivityInfo[] activities = info.activities;
+            if (activities == null) return;
+            for (int i = 0; i < activities.length; i++) {
+                // check for meta-data, both for devices and accessories
+                if (handlePackageUpdateLocked(packageName, activities[i],
+                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+                    changed = true;
+                }
+                if (handlePackageUpdateLocked(packageName, activities[i],
+                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+                    changed = true;
+                }
+            }
+
+            if (changed) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    public boolean hasPermission(UsbDevice device) {
+        synchronized (mLock) {
+            int uid = Binder.getCallingUid();
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    public boolean hasPermission(UsbAccessory accessory) {
+        synchronized (mLock) {
+            int uid = Binder.getCallingUid();
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    public void checkPermission(UsbDevice device) {
+        if (!hasPermission(device)) {
+            throw new SecurityException("User has not given permission to device " + device);
+        }
+    }
+
+    public void checkPermission(UsbAccessory accessory) {
+        if (!hasPermission(accessory)) {
+            throw new SecurityException("User has not given permission to accessory " + accessory);
+        }
+    }
+
+    private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
+        final int uid = Binder.getCallingUid();
+
+        // compare uid with packageName to foil apps pretending to be someone else
+        try {
+            ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
+            if (aInfo.uid != uid) {
+                throw new IllegalArgumentException("package " + packageName +
+                        " does not match caller's uid " + uid);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("package " + packageName + " not found");
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        intent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbPermissionActivity");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(Intent.EXTRA_INTENT, pi);
+        intent.putExtra("package", packageName);
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        try {
+            mUserContext.startActivityAsUser(intent, mUser);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(TAG, "unable to start UsbPermissionActivity");
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
+      Intent intent = new Intent();
+
+        // respond immediately if permission has already been granted
+      if (hasPermission(device)) {
+            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+            try {
+                pi.send(mUserContext, 0, intent);
+            } catch (PendingIntent.CanceledException e) {
+                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
+            }
+            return;
+        }
+
+        // start UsbPermissionActivity so user can choose an activity
+        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        requestPermissionDialog(intent, packageName, pi);
+    }
+
+    public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
+        Intent intent = new Intent();
+
+        // respond immediately if permission has already been granted
+        if (hasPermission(accessory)) {
+            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+            try {
+                pi.send(mUserContext, 0, intent);
+            } catch (PendingIntent.CanceledException e) {
+                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
+            }
+            return;
+        }
+
+        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        requestPermissionDialog(intent, packageName, pi);
+    }
+
+    public void setDevicePackage(UsbDevice device, String packageName) {
+        DeviceFilter filter = new DeviceFilter(device);
+        boolean changed = false;
+        synchronized (mLock) {
+            if (packageName == null) {
+                changed = (mDevicePreferenceMap.remove(filter) != null);
+            } else {
+                changed = !packageName.equals(mDevicePreferenceMap.get(filter));
+                if (changed) {
+                    mDevicePreferenceMap.put(filter, packageName);
+                }
+            }
+            if (changed) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
+        AccessoryFilter filter = new AccessoryFilter(accessory);
+        boolean changed = false;
+        synchronized (mLock) {
+            if (packageName == null) {
+                changed = (mAccessoryPreferenceMap.remove(filter) != null);
+            } else {
+                changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
+                if (changed) {
+                    mAccessoryPreferenceMap.put(filter, packageName);
+                }
+            }
+            if (changed) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    public void grantDevicePermission(UsbDevice device, int uid) {
+        synchronized (mLock) {
+            String deviceName = device.getDeviceName();
+            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mDevicePermissionMap.put(deviceName, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
+        synchronized (mLock) {
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mAccessoryPermissionMap.put(accessory, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    public boolean hasDefaults(String packageName) {
+        synchronized (mLock) {
+            if (mDevicePreferenceMap.values().contains(packageName)) return true;
+            if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
+            return false;
+        }
+    }
+
+    public void clearDefaults(String packageName) {
+        synchronized (mLock) {
+            if (clearPackageDefaultsLocked(packageName)) {
+                writeSettingsLocked();
+            }
+        }
+    }
+
+    private boolean clearPackageDefaultsLocked(String packageName) {
+        boolean cleared = false;
+        synchronized (mLock) {
+            if (mDevicePreferenceMap.containsValue(packageName)) {
+                // make a copy of the key set to avoid ConcurrentModificationException
+                Object[] keys = mDevicePreferenceMap.keySet().toArray();
+                for (int i = 0; i < keys.length; i++) {
+                    Object key = keys[i];
+                    if (packageName.equals(mDevicePreferenceMap.get(key))) {
+                        mDevicePreferenceMap.remove(key);
+                        cleared = true;
+                    }
+                }
+            }
+            if (mAccessoryPreferenceMap.containsValue(packageName)) {
+                // make a copy of the key set to avoid ConcurrentModificationException
+                Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
+                for (int i = 0; i < keys.length; i++) {
+                    Object key = keys[i];
+                    if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
+                        mAccessoryPreferenceMap.remove(key);
+                        cleared = true;
+                    }
+                }
+            }
+            return cleared;
+        }
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("Device permissions:");
+            for (String deviceName : mDevicePermissionMap.keySet()) {
+                pw.print("  " + deviceName + ": ");
+                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    pw.print(Integer.toString(uidList.keyAt(i)) + " ");
+                }
+                pw.println();
+            }
+            pw.println("Accessory permissions:");
+            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+                pw.print("  " + accessory + ": ");
+                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    pw.print(Integer.toString(uidList.keyAt(i)) + " ");
+                }
+                pw.println();
+            }
+            pw.println("Device preferences:");
+            for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
+                pw.println("  " + filter + ": " + mDevicePreferenceMap.get(filter));
+            }
+            pw.println("Accessory preferences:");
+            for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
+                pw.println("  " + filter + ": " + mAccessoryPreferenceMap.get(filter));
+            }
+        }
+    }
+
+    private static Intent createDeviceAttachedIntent(UsbDevice device) {
+        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e27ab52..871aae8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -577,6 +577,15 @@
     public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
 
     /**
+     * Determines whether High Definition audio property is displayed in the dialer UI.
+     * If {@code false}, remove the HD audio property from the connection so that HD audio related
+     * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
+     * @hide
+     */
+    public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL =
+            "display_hd_audio_property_bool";
+
+    /**
      * Determines whether video conference calls are supported by a carrier.  When {@code true},
      * video calls can be merged into conference calls, {@code false} otherwiwse.
      * <p>
@@ -1057,6 +1066,7 @@
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
         sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
         sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 8b165bd..6ca13d6 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -96,7 +96,7 @@
     }
 
     @Override
-    public void clearForcedDisplayDensity(int displayId) throws RemoteException {
+    public void clearForcedDisplayDensityForUser(int displayId, int userId) throws RemoteException {
         // TODO Auto-generated method stub
     }
 
@@ -397,7 +397,8 @@
     }
 
     @Override
-    public void setForcedDisplayDensity(int displayId, int density) throws RemoteException {
+    public void setForcedDisplayDensityForUser(int displayId, int density, int userId)
+            throws RemoteException {
         // TODO Auto-generated method stub
     }