Merge "Remove onBeforeNotificationAdded call"
diff --git a/Android.bp b/Android.bp
index e0c3f96..8169d6d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -928,7 +928,8 @@
     "--hide RequiresPermission " +
     "--hide MissingPermission --hide BroadcastBehavior " +
     "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
-    "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo "
+    "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
+    "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*"
 
 // http://b/129765390 Rewrite links to "platform" or "technotes" folders
 // which are siblings (and thus outside of) {@docRoot}.
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index c036c77..041825c 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -1,9 +1,9 @@
 package com.android.server.usage;
 
+import android.annotation.UserIdInt;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.Context;
 import android.os.Looper;
 
@@ -33,6 +33,24 @@
         }
     }
 
+    /**
+     * Listener interface for notifications that an app's idle state changed.
+     */
+    abstract static class AppIdleStateChangeListener {
+
+        /** Callback to inform listeners that the idle state has changed to a new bucket. */
+        public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
+                boolean idle, int bucket, int reason);
+
+        /**
+         * Optional callback to inform the listener that the app has transitioned into
+         * an active state due to user interaction.
+         */
+        public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
+            // No-op by default
+        }
+    }
+
     void onBootPhase(int phase);
 
     void postCheckIdleStates(int userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index c3ffad6..a1734d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -37,7 +37,6 @@
 import android.app.job.JobWorkItem;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -103,6 +102,8 @@
 import com.android.server.job.controllers.TimeController;
 import com.android.server.job.restrictions.JobRestriction;
 import com.android.server.job.restrictions.ThermalStatusRestriction;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import libcore.util.EmptyArray;
 
@@ -1295,7 +1296,9 @@
         // Set up the app standby bucketing tracker
         mStandbyTracker = new StandbyTracker();
         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
-        mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
+
+        AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
+        appStandby.addListener(mStandbyTracker);
 
         // The job store needs to call back
         publishLocalService(JobSchedulerInternal.class, new LocalService());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 14dce84..cda5244 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -35,8 +35,6 @@
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.IUidObserver;
-import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -70,6 +68,8 @@
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobServiceContext;
 import com.android.server.job.StateControllerProto;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -574,9 +574,8 @@
         mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null);
 
         // Set up the app standby bucketing tracker
-        UsageStatsManagerInternal usageStats = LocalServices.getService(
-                UsageStatsManagerInternal.class);
-        usageStats.addAppIdleStateChangeListener(new StandbyTracker());
+        AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
+        appStandby.addListener(new StandbyTracker());
 
         try {
             ActivityManager.getService().registerUidObserver(mUidObserver,
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index ecc0459..bcd8be7 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -53,7 +53,6 @@
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -102,6 +101,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.usage.AppIdleHistory.AppUsageHistory;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import java.io.File;
 import java.io.PrintWriter;
diff --git a/api/system-current.txt b/api/system-current.txt
index 9573478..79650fc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4737,6 +4737,7 @@
     method @Deprecated public boolean hasNoInternetAccess();
     method @Deprecated public boolean isEphemeral();
     method @Deprecated public boolean isNoInternetAccessExpected();
+    field @Deprecated public boolean allowAutojoin;
     field @Deprecated public String creatorName;
     field @Deprecated public int creatorUid;
     field @Deprecated public String lastUpdateName;
@@ -4759,6 +4760,7 @@
 
   public class WifiManager {
     method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+    method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void allowAutojoin(int, boolean);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -5942,6 +5944,7 @@
     field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
     field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
     field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
+    field public static final String NAMESPACE_PERMISSIONS = "permissions";
     field public static final String NAMESPACE_PRIVACY = "privacy";
     field public static final String NAMESPACE_ROLLBACK = "rollback";
     field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
diff --git a/api/test-current.txt b/api/test-current.txt
index b3834bf..a060dfc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2373,6 +2373,7 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
     field public static final String NAMESPACE_AUTOFILL = "autofill";
     field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+    field public static final String NAMESPACE_PERMISSIONS = "permissions";
     field public static final String NAMESPACE_PRIVACY = "privacy";
     field public static final String NAMESPACE_ROLLBACK = "rollback";
     field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d5e41f0..8bca87e6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2822,12 +2822,12 @@
                             LongSparseArray.StringParcelling.class);
 
             return new OpFeatureEntry.Builder(source.readBoolean(),
-                    (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
-                    (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
-                    (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
-                    (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
-                    (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source),
-                    (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source));
+                    longSparseLongArrayParcelling.unparcel(source),
+                    longSparseLongArrayParcelling.unparcel(source),
+                    longSparseLongArrayParcelling.unparcel(source),
+                    longSparseLongArrayParcelling.unparcel(source),
+                    longSparseStringArrayParcelling.unparcel(source),
+                    longSparseStringArrayParcelling.unparcel(source));
         }
     }
 
diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java
deleted file mode 100644
index f072167..0000000
--- a/core/java/android/content/pm/VerificationParams.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents verification parameters used to verify packages to be installed.
- *
- * @deprecated callers should migrate to {@link PackageInstaller}.
- * @hide
- */
-@Deprecated
-public class VerificationParams implements Parcelable {
-    /** A constant used to indicate that a uid value is not present. */
-    public static final int NO_UID = -1;
-
-    /** What we print out first when toString() is called. */
-    private static final String TO_STRING_PREFIX = "VerificationParams{";
-
-    /** The location of the supplementary verification file. */
-    private final Uri mVerificationURI;
-
-    /** URI referencing where the package was downloaded from. */
-    private final Uri mOriginatingURI;
-
-    /** HTTP referrer URI associated with the originatingURI. */
-    private final Uri mReferrer;
-
-    /** UID of the application that the install request originated from. */
-    private final int mOriginatingUid;
-
-    /** UID of application requesting the install */
-    private int mInstallerUid;
-
-    /**
-     * Creates verification specifications for installing with application verification.
-     *
-     * @param verificationURI The location of the supplementary verification
-     *            file. This can be a 'file:' or a 'content:' URI. May be {@code null}.
-     * @param originatingURI URI referencing where the package was downloaded
-     *            from. May be {@code null}.
-     * @param referrer HTTP referrer URI associated with the originatingURI.
-     *            May be {@code null}.
-     * @param originatingUid UID of the application that the install request originated
-     *            from, or NO_UID if not present
-     */
-    public VerificationParams(Uri verificationURI, Uri originatingURI, Uri referrer,
-            int originatingUid) {
-        mVerificationURI = verificationURI;
-        mOriginatingURI = originatingURI;
-        mReferrer = referrer;
-        mOriginatingUid = originatingUid;
-        mInstallerUid = NO_UID;
-    }
-
-    public Uri getVerificationURI() {
-        return mVerificationURI;
-    }
-
-    public Uri getOriginatingURI() {
-        return mOriginatingURI;
-    }
-
-    public Uri getReferrer() {
-        return mReferrer;
-    }
-
-    /** return NO_UID if not available */
-    public int getOriginatingUid() {
-        return mOriginatingUid;
-    }
-
-    /** @return NO_UID when not set */
-    public int getInstallerUid() {
-        return mInstallerUid;
-    }
-
-    public void setInstallerUid(int uid) {
-        mInstallerUid = uid;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof VerificationParams)) {
-            return false;
-        }
-
-        final VerificationParams other = (VerificationParams) o;
-
-        if (mVerificationURI == null) {
-            if (other.mVerificationURI != null) {
-                return false;
-            }
-        } else if (!mVerificationURI.equals(other.mVerificationURI)) {
-            return false;
-        }
-
-        if (mOriginatingURI == null) {
-            if (other.mOriginatingURI != null) {
-                return false;
-            }
-        } else if (!mOriginatingURI.equals(other.mOriginatingURI)) {
-            return false;
-        }
-
-        if (mReferrer == null) {
-            if (other.mReferrer != null) {
-                return false;
-            }
-        } else if (!mReferrer.equals(other.mReferrer)) {
-            return false;
-        }
-
-        if (mOriginatingUid != other.mOriginatingUid) {
-            return false;
-        }
-
-        if (mInstallerUid != other.mInstallerUid) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 3;
-
-        hash += 5 * (mVerificationURI == null ? 1 : mVerificationURI.hashCode());
-        hash += 7 * (mOriginatingURI == null ? 1 : mOriginatingURI.hashCode());
-        hash += 11 * (mReferrer == null ? 1 : mReferrer.hashCode());
-        hash += 13 * mOriginatingUid;
-        hash += 17 * mInstallerUid;
-
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder(TO_STRING_PREFIX);
-
-        sb.append("mVerificationURI=");
-        sb.append(mVerificationURI.toString());
-        sb.append(",mOriginatingURI=");
-        sb.append(mOriginatingURI.toString());
-        sb.append(",mReferrer=");
-        sb.append(mReferrer.toString());
-        sb.append(",mOriginatingUid=");
-        sb.append(mOriginatingUid);
-        sb.append(",mInstallerUid=");
-        sb.append(mInstallerUid);
-        sb.append('}');
-
-        return sb.toString();
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mVerificationURI, 0);
-        dest.writeParcelable(mOriginatingURI, 0);
-        dest.writeParcelable(mReferrer, 0);
-        dest.writeInt(mOriginatingUid);
-        dest.writeInt(mInstallerUid);
-    }
-
-
-    private VerificationParams(Parcel source) {
-        mVerificationURI = source.readParcelable(Uri.class.getClassLoader());
-        mOriginatingURI = source.readParcelable(Uri.class.getClassLoader());
-        mReferrer = source.readParcelable(Uri.class.getClassLoader());
-        mOriginatingUid = source.readInt();
-        mInstallerUid = source.readInt();
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<VerificationParams> CREATOR =
-            new Parcelable.Creator<VerificationParams>() {
-        public VerificationParams createFromParcel(Parcel source) {
-                return new VerificationParams(source);
-        }
-
-        public VerificationParams[] newArray(int size) {
-            return new VerificationParams[size];
-        }
-    };
-}
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index 160376b..eb5d0cb 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -58,8 +58,8 @@
      */
     private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment";
     /**
-     * Activity Action: Show activity for managing the keyphrases for hotword detection.
-     * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase
+     * Intent Action: for managing the keyphrases for hotword detection.
+     * This needs to be defined by a service that supports enrolling users for hotword/keyphrase
      * detection.
      */
     public static final String ACTION_MANAGE_VOICE_KEYPHRASES =
@@ -101,7 +101,7 @@
         // Find the apps that supports enrollment for hotword keyhphrases,
         // Pick a privileged app and obtain the information about the supported keyphrases
         // from its metadata.
-        List<ResolveInfo> ris = pm.queryIntentActivities(
+        List<ResolveInfo> ris = pm.queryIntentServices(
                 new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
         if (ris == null || ris.isEmpty()) {
             // No application capable of enrolling for voice keyphrases is present.
@@ -116,11 +116,11 @@
         for (ResolveInfo ri : ris) {
             try {
                 ApplicationInfo ai = pm.getApplicationInfo(
-                        ri.activityInfo.packageName, PackageManager.GET_META_DATA);
+                        ri.serviceInfo.packageName, PackageManager.GET_META_DATA);
                 if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) {
                     // The application isn't privileged (/system/priv-app).
                     // The enrollment application needs to be a privileged system app.
-                    Slog.w(TAG, ai.packageName + "is not a privileged system app");
+                    Slog.w(TAG, ai.packageName + " is not a privileged system app");
                     continue;
                 }
                 if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) {
@@ -137,7 +137,7 @@
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 String error = "error parsing voice enrollment meta-data for "
-                        + ri.activityInfo.packageName;
+                        + ri.serviceInfo.packageName;
                 parseErrors.add(error + ": " + e);
                 Slog.w(TAG, error, e);
             }
@@ -290,7 +290,7 @@
     }
 
     /**
-     * Returns an intent to launch an activity that manages the given keyphrase
+     * Returns an intent to launch an service that manages the given keyphrase
      * for the locale.
      *
      * @param action The enrollment related action that this intent is supposed to perform.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 6d5fe53b..a92237b 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -55,6 +55,7 @@
     private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
     private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT";
     private static final String ENV_SYSTEM_EXT_ROOT = "SYSTEM_EXT_ROOT";
+    private static final String ENV_APEX_ROOT = "APEX_ROOT";
 
     /** {@hide} */
     public static final String DIR_ANDROID = "Android";
@@ -78,7 +79,9 @@
     private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
     private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
     private static final File DIR_SYSTEM_EXT_ROOT = getDirectory(ENV_SYSTEM_EXT_ROOT,
-                                                           "/system_ext");
+            "/system_ext");
+    private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
+            "/apex");
 
     @UnsupportedAppUsage
     private static UserEnvironment sCurrentUser;
@@ -248,6 +251,16 @@
     }
 
     /**
+     * Return root directory of the apex mount point, where all the apex modules are made available
+     * to the rest of the system.
+     *
+     * @hide
+     */
+    public static @NonNull File getApexDirectory() {
+        return DIR_APEX_ROOT;
+    }
+
+    /**
      * Return the system directory for a user. This is for use by system
      * services to store files relating to the user. This directory will be
      * automatically deleted when the user is removed.
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 77fd946..0e00d5e 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -104,16 +104,17 @@
      * Start DynamicSystem installation. This call may take an unbounded amount of time. The caller
      * may use another thread to call the getStartProgress() to get the progress.
      *
-     * @param systemSize system size in bytes
-     * @param userdataSize userdata size in bytes
+     * @param name The DSU partition name
+     * @param size Size of the DSU image in bytes
+     * @param readOnly True if the partition is read only, e.g. system.
      * @return {@code true} if the call succeeds. {@code false} either the device does not contain
      *     enough space or a DynamicSystem is currently in use where the {@link #isInUse} would be
      *     true.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
-    public Session startInstallation(long systemSize, long userdataSize) {
+    public Session startInstallation(String name, long size, boolean readOnly) {
         try {
-            if (mService.startInstallation(systemSize, userdataSize)) {
+            if (mService.startInstallation(name, size, readOnly)) {
                 return new Session();
             } else {
                 return null;
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index a6de170..75f6785 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -24,11 +24,12 @@
      * Start DynamicSystem installation. This call may take 60~90 seconds. The caller
      * may use another thread to call the getStartProgress() to get the progress.
      *
-     * @param systemSize system size in bytes
-     * @param userdataSize userdata size in bytes
+     * @param name The DSU partition name
+     * @param size Size of the DSU image in bytes
+     * @param readOnly True if this partition is readOnly
      * @return true if the call succeeds
      */
-    boolean startInstallation(long systemSize, long userdataSize);
+    boolean startInstallation(@utf8InCpp String name, long size, boolean readOnly);
 
     /**
      * Query the progress of the current installation operation. This can be called while
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index e456c8a..8b8afd5 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -338,6 +338,15 @@
     public static final String NAMESPACE_PRIVACY = "privacy";
 
     /**
+     * Permission related properties definitions.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final String NAMESPACE_PERMISSIONS = "permissions";
+
+    /**
      * Interface for accessing keys belonging to {@link #NAMESPACE_WINDOW_MANAGER}.
      * @hide
      */
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 94f6a50..cf56eae 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
-import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
@@ -446,7 +446,7 @@
 
     /**
      * Creates an intent to start the enrollment for the associated keyphrase.
-     * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+     * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
      * Starting re-enrollment is only valid if the keyphrase is un-enrolled,
      * i.e. {@link #STATE_KEYPHRASE_UNENROLLED},
      * otherwise {@link #createReEnrollIntent()} should be preferred.
@@ -468,7 +468,7 @@
 
     /**
      * Creates an intent to start the un-enrollment for the associated keyphrase.
-     * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+     * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
      * Starting re-enrollment is only valid if the keyphrase is already enrolled,
      * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
      *
@@ -489,7 +489,7 @@
 
     /**
      * Creates an intent to start the re-enrollment for the associated keyphrase.
-     * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+     * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
      * Starting re-enrollment is only valid if the keyphrase is already enrolled,
      * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
      *
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index e78b796..73e17a6 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -17,7 +17,6 @@
 package android.util;
 
 import android.os.Parcel;
-import android.os.Parcelable;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
@@ -25,6 +24,8 @@
 
 import libcore.util.EmptyArray;
 
+import java.util.Arrays;
+
 /**
  * SparseArray mapping longs to Objects.  Unlike a normal array of Objects,
  * there can be gaps in the indices.  It is intended to be more memory efficient
@@ -450,22 +451,25 @@
     /**
      * @hide
      */
-    public static class StringParcelling implements com.android.internal.util.Parcelling {
+    public static class StringParcelling implements
+            com.android.internal.util.Parcelling<LongSparseArray<String>> {
         @Override
-        public void parcel(Object item, Parcel dest, int parcelFlags) {
-            if (item == null) {
+        public void parcel(LongSparseArray<String> array, Parcel dest, int parcelFlags) {
+            if (array == null) {
                 dest.writeInt(-1);
                 return;
             }
 
-            LongSparseArray<String> array = (LongSparseArray<String>) item;
-            dest.writeInt(array.mSize);
+            int size = array.mSize;
+
+            dest.writeInt(size);
             dest.writeLongArray(array.mKeys);
-            dest.writeStringArray((String[]) array.mValues);
+
+            dest.writeStringArray(Arrays.copyOfRange(array.mValues, 0, size, String[].class));
         }
 
         @Override
-        public Object unparcel(Parcel source) {
+        public LongSparseArray<String> unparcel(Parcel source) {
             int size = source.readInt();
             if (size == -1) {
                 return null;
@@ -490,49 +494,4 @@
             return array;
         }
     }
-
-    /**
-     * @hide
-     */
-    public static class Parcelling<T extends Parcelable> implements
-            com.android.internal.util.Parcelling {
-        @Override
-        public void parcel(Object item, Parcel dest, int parcelFlags) {
-            if (item == null) {
-                dest.writeInt(-1);
-                return;
-            }
-
-            LongSparseArray<T> array = (LongSparseArray<T>) item;
-            dest.writeInt(array.mSize);
-            dest.writeLongArray(array.mKeys);
-            dest.writeParcelableArray((T[]) array.mValues, parcelFlags);
-        }
-
-        @Override
-        public Object unparcel(Parcel source) {
-            int size = source.readInt();
-            if (size == -1) {
-                return null;
-            }
-
-            LongSparseArray<T> array = new LongSparseArray<>(0);
-            array.mSize = size;
-            array.mKeys = source.createLongArray();
-            array.mValues = source.readParcelableArray(null);
-
-            // Make sure array is sane
-            Preconditions.checkArgument(array.mKeys.length >= size);
-            Preconditions.checkArgument(array.mValues.length >= size);
-
-            if (size > 0) {
-                long last = array.mKeys[0];
-                for (int i = 1; i < size; i++) {
-                    Preconditions.checkArgument(last < array.mKeys[i]);
-                }
-            }
-
-            return array;
-        }
-    }
 }
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index 9ffd4f0..a0edd04 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -289,22 +289,22 @@
     /**
      * @hide
      */
-    public static class Parcelling implements com.android.internal.util.Parcelling {
+    public static class Parcelling implements
+            com.android.internal.util.Parcelling<LongSparseLongArray> {
         @Override
-        public void parcel(Object item, Parcel dest, int parcelFlags) {
-            if (item == null) {
+        public void parcel(LongSparseLongArray array, Parcel dest, int parcelFlags) {
+            if (array == null) {
                 dest.writeInt(-1);
                 return;
             }
 
-            LongSparseLongArray array = (LongSparseLongArray) item;
             dest.writeInt(array.mSize);
             dest.writeLongArray(array.mKeys);
             dest.writeLongArray(array.mValues);
         }
 
         @Override
-        public Object unparcel(Parcel source) {
+        public LongSparseLongArray unparcel(Parcel source) {
             int size = source.readInt();
             if (size == -1) {
                 return null;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 00c1e29..3b82f18 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2505,9 +2505,7 @@
                         ri.noResourceId = true;
                         ri.icon = 0;
                     }
-                    ResolveInfoPresentationGetter getter = makePresentationGetter(ri);
-                    mCallerTargets.add(new DisplayResolveInfo(ii, ri,
-                            getter.getLabel(), getter.getSubLabel(), ii));
+                    mCallerTargets.add(new DisplayResolveInfo(ii, ri, ii));
                 }
             }
         }
@@ -2606,7 +2604,22 @@
 
         @Override
         public void onListRebuilt() {
-            updateAlphabeticalList();
+            if (getDisplayList() == null || getDisplayList().isEmpty()) {
+                notifyDataSetChanged();
+            } else {
+                new AsyncTask<Void, Void, Void>() {
+                    @Override
+                    protected Void doInBackground(Void... voids) {
+                        updateAlphabeticalList();
+                        return null;
+                    }
+
+                    @Override
+                    protected void onPostExecute(Void aVoid) {
+                        notifyDataSetChanged();
+                    }
+                }.execute();
+            }
 
             // don't support direct share on low ram devices
             if (ActivityManager.isLowRamDeviceStatic()) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7f18ae3..74996e9 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1406,14 +1406,18 @@
 
     public final class DisplayResolveInfo implements TargetInfo {
         private final ResolveInfo mResolveInfo;
-        private final CharSequence mDisplayLabel;
+        private CharSequence mDisplayLabel;
         private Drawable mDisplayIcon;
         private Drawable mBadge;
-        private final CharSequence mExtendedInfo;
+        private CharSequence mExtendedInfo;
         private final Intent mResolvedIntent;
         private final List<Intent> mSourceIntents = new ArrayList<>();
         private boolean mIsSuspended;
 
+        public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, Intent pOrigIntent) {
+            this(originalIntent, pri, null /*mDisplayLabel*/, null /*mExtendedInfo*/, pOrigIntent);
+        }
+
         public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
                 CharSequence pInfo, Intent pOrigIntent) {
             mSourceIntents.add(originalIntent);
@@ -1448,9 +1452,26 @@
         }
 
         public CharSequence getDisplayLabel() {
+            if (mDisplayLabel == null) {
+                ResolveInfoPresentationGetter pg = makePresentationGetter(mResolveInfo);
+                mDisplayLabel = pg.getLabel();
+                mExtendedInfo = pg.getSubLabel();
+            }
             return mDisplayLabel;
         }
 
+        public boolean hasDisplayLabel() {
+            return mDisplayLabel != null;
+        }
+
+        public void setDisplayLabel(CharSequence displayLabel) {
+            mDisplayLabel = displayLabel;
+        }
+
+        public void setExtendedInfo(CharSequence extendedInfo) {
+            mExtendedInfo = extendedInfo;
+        }
+
         public Drawable getDisplayIcon() {
             return mDisplayIcon;
         }
@@ -1866,9 +1887,7 @@
                     final ResolveInfo ri = rci.getResolveInfoAt(0);
                     if (ri != null) {
                         mAllTargetsAreBrowsers &= ri.handleAllWebDataURI;
-
-                        ResolveInfoPresentationGetter pg = makePresentationGetter(ri);
-                        addResolveInfoWithAlternates(rci, pg.getSubLabel(), pg.getLabel());
+                        addResolveInfoWithAlternates(rci);
                     }
                 }
             }
@@ -1915,14 +1934,12 @@
             return mFilterLastUsed;
         }
 
-        private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
-                CharSequence extraInfo, CharSequence roLabel) {
+        private void addResolveInfoWithAlternates(ResolvedComponentInfo rci) {
             final int count = rci.getCount();
             final Intent intent = rci.getIntentAt(0);
             final ResolveInfo add = rci.getResolveInfoAt(0);
             final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
-            final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
-                    extraInfo, replaceIntent);
+            final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, replaceIntent);
             addResolveInfo(dri);
             if (replaceIntent == intent) {
                 // Only add alternates if we didn't get a specific replacement from
@@ -2054,20 +2071,11 @@
                 return;
             }
 
-            final CharSequence label = info.getDisplayLabel();
-            if (!TextUtils.equals(holder.text.getText(), label)) {
-                holder.text.setText(info.getDisplayLabel());
-            }
-
-            // Always show a subLabel for visual consistency across list items. Show an empty
-            // subLabel if the subLabel is the same as the label
-            CharSequence subLabel = info.getExtendedInfo();
-            if (TextUtils.equals(label, subLabel)) subLabel = null;
-
-            if (!TextUtils.equals(holder.text2.getText(), subLabel)
-                    && !TextUtils.isEmpty(subLabel)) {
-                holder.text2.setVisibility(View.VISIBLE);
-                holder.text2.setText(subLabel);
+            if (info instanceof DisplayResolveInfo
+                    && !((DisplayResolveInfo) info).hasDisplayLabel()) {
+                getLoadLabelTask((DisplayResolveInfo) info, holder).execute();
+            } else {
+                holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo());
             }
 
             if (info.isSuspended()) {
@@ -2085,6 +2093,9 @@
         }
     }
 
+    protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) {
+        return new LoadLabelTask(info, holder);
+    }
 
     @VisibleForTesting
     public static final class ResolvedComponentInfo {
@@ -2148,6 +2159,24 @@
             text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
             icon = (ImageView) view.findViewById(R.id.icon);
         }
+
+        public void bindLabel(CharSequence label, CharSequence subLabel) {
+            if (!TextUtils.equals(text.getText(), label)) {
+                text.setText(label);
+            }
+
+            // Always show a subLabel for visual consistency across list items. Show an empty
+            // subLabel if the subLabel is the same as the label
+            if (TextUtils.equals(label, subLabel)) {
+                subLabel = null;
+            }
+
+            if (!TextUtils.equals(text2.getText(), subLabel)
+                    && !TextUtils.isEmpty(subLabel)) {
+                text2.setVisibility(View.VISIBLE);
+                text2.setText(subLabel);
+            }
+        }
     }
 
     class ItemClickListener implements AdapterView.OnItemClickListener,
@@ -2200,6 +2229,34 @@
 
     }
 
+    protected class LoadLabelTask extends AsyncTask<Void, Void, CharSequence[]> {
+        private final DisplayResolveInfo mDisplayResolveInfo;
+        private final ViewHolder mHolder;
+
+        protected LoadLabelTask(DisplayResolveInfo dri, ViewHolder holder) {
+            mDisplayResolveInfo = dri;
+            mHolder = holder;
+        }
+
+
+        @Override
+        protected CharSequence[] doInBackground(Void... voids) {
+            ResolveInfoPresentationGetter pg =
+                    makePresentationGetter(mDisplayResolveInfo.mResolveInfo);
+            return new CharSequence[] {
+                    pg.getLabel(),
+                    pg.getSubLabel()
+            };
+        }
+
+        @Override
+        protected void onPostExecute(CharSequence[] result) {
+            mDisplayResolveInfo.setDisplayLabel(result[0]);
+            mDisplayResolveInfo.setExtendedInfo(result[1]);
+            mHolder.bindLabel(result[0], result[1]);
+        }
+    }
+
     class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
         protected final DisplayResolveInfo mDisplayResolveInfo;
         private final ResolveInfo mResolveInfo;
diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto
index 148bd7e..1e6ee3f 100644
--- a/core/proto/android/server/notificationhistory.proto
+++ b/core/proto/android/server/notificationhistory.proto
@@ -46,7 +46,7 @@
 
     // The uid of the package that posted the notification
     optional int32 uid = 7;
-    // The user id of the package that posted the notification
+    // The user id that the notification was posted to
     optional int32 user_id = 8;
     // The time at which the notification was posted
     optional int64 posted_time_ms = 9;
@@ -71,19 +71,19 @@
       optional ImageTypeEnum image_type = 1;
       optional string image_bitmap_filename = 2;
       optional int32 image_resource_id = 3;
-      optional bytes image_data = 4;
-      optional string image_uri = 5;
+      optional string image_resource_id_package = 4;
+      optional bytes image_data = 5;
+      optional int32 image_data_length = 6;
+      optional int32 image_data_offset = 7;
+      optional string image_uri = 8;
     }
   }
 
-  // The time the last entry was written
-  optional int64 end_time_ms = 1;
   // Pool of strings to save space
-  optional StringPool stringpool = 2;
+  optional StringPool string_pool = 1;
   // Versioning fields
-  optional int32 major_version = 3;
-  optional int32 minor_version = 4;
+  optional int32 major_version = 2;
 
   // List of historical notifications
-  repeated Notification notification = 5;
+  repeated Notification notification = 3;
 }
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 2e1de79..40c5a85 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -32,6 +32,7 @@
     optional UsbPortManagerProto port_manager = 3;
     optional UsbAlsaManagerProto alsa_manager = 4;
     optional UsbSettingsManagerProto settings_manager = 5;
+    optional UsbPermissionsManagerProto permissions_manager = 6;
 }
 
 message UsbDeviceManagerProto {
@@ -309,26 +310,12 @@
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional int32 user_id = 1;
-    repeated UsbSettingsDevicePermissionProto device_permissions = 2;
-    repeated UsbSettingsAccessoryPermissionProto accessory_permissions = 3;
+    reserved 2; // previously device_permissions, now unused
+    reserved 3; // previously accessory_permissions, now unused
     repeated UsbDeviceAttachedActivities device_attached_activities = 4;
     repeated UsbAccessoryAttachedActivities accessory_attached_activities = 5;
 }
 
-message UsbSettingsDevicePermissionProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional string device_name = 1;
-    repeated int32 uids = 2;
-}
-
-message UsbSettingsAccessoryPermissionProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional string accessory_description = 1;
-    repeated int32 uids = 2;
-}
-
 message UsbProfileGroupSettingsManagerProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -345,6 +332,63 @@
     optional UserPackageProto user_package = 2;
 }
 
+message UsbPermissionsManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    repeated UsbUserPermissionsManagerProto user_permissions = 1;
+}
+
+message UsbUserPermissionsManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 user_id = 1;
+
+    repeated UsbDevicePermissionProto device_permissions = 2;
+    repeated UsbAccessoryPermissionProto accessory_permissions = 3;
+
+    repeated UsbDevicePersistentPermissionProto device_persistent_permissions = 4;
+    repeated UsbAccessoryPersistentPermissionProto accessory_persistent_permissions = 5;
+}
+
+message UsbDevicePermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Name of device set by manufacturer
+    // All devices of the same model have the same name
+    optional string device_name = 1;
+    repeated int32 uids = 2;
+}
+
+message UsbAccessoryPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Description of accessory set by manufacturer
+    // All accessories of the same model have the same description
+    optional string accessory_description = 1;
+    repeated int32 uids = 2;
+}
+
+message UsbDevicePersistentPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbDeviceFilterProto device_filter = 1;
+    repeated UsbUidPermissionProto permission_values = 2;
+}
+
+message UsbAccessoryPersistentPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbAccessoryFilterProto accessory_filter = 1;
+    repeated UsbUidPermissionProto permission_values = 2;
+}
+
+message UsbUidPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 uid = 1;
+    optional bool is_granted = 2;
+}
+
 message UsbDeviceFilterProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
deleted file mode 100644
index f6527da..0000000
--- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.LargeTest;
-
-/**
- * Tests the android.content.pm.VerificationParams class
- *
- * To test run:
- * ./development/testrunner/runtest.py frameworks-core -c android.content.pm.VerificationParamsTest
- */
-@LargeTest
-public class VerificationParamsTest extends AndroidTestCase {
-
-    private final static String VERIFICATION_URI_STRING = "http://verification.uri/path";
-    private final static String ORIGINATING_URI_STRING = "http://originating.uri/path";
-    private final static String REFERRER_STRING = "http://referrer.uri/path";
-    private final static int INSTALLER_UID = 42;
-
-    private final static Uri VERIFICATION_URI = Uri.parse(VERIFICATION_URI_STRING);
-    private final static Uri ORIGINATING_URI = Uri.parse(ORIGINATING_URI_STRING);
-    private final static Uri REFERRER = Uri.parse(REFERRER_STRING);
-
-    private final static int ORIGINATING_UID = 10042;
-
-    public void testParcel() throws Exception {
-        VerificationParams expected = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        Parcel parcel = Parcel.obtain();
-        expected.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-
-        VerificationParams actual = VerificationParams.CREATOR.createFromParcel(parcel);
-
-        assertEquals(VERIFICATION_URI, actual.getVerificationURI());
-
-        assertEquals(ORIGINATING_URI, actual.getOriginatingURI());
-
-        assertEquals(REFERRER, actual.getReferrer());
-
-        assertEquals(ORIGINATING_UID, actual.getOriginatingUid());
-    }
-
-    public void testEquals_Success() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
-        assertEquals(params1, params2);
-    }
-
-    public void testEquals_VerificationUri_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse("http://a.different.uri/"), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
-        assertFalse(params1.equals(params2));
-    }
-
-    public void testEquals_OriginatingUri_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
-        assertFalse(params1.equals(params2));
-    }
-
-    public void testEquals_Referrer_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse("http://a.different.uri/"), ORIGINATING_UID);
-
-        assertFalse(params1.equals(params2));
-    }
-
-    public void testEquals_Originating_Uid_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), 12345);
-
-        assertFalse(params1.equals(params2));
-    }
-
-    public void testEquals_InstallerUid_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-        params2.setInstallerUid(INSTALLER_UID);
-
-        assertFalse(params1.equals(params2));
-    }
-
-    public void testHashCode_Success() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
-        assertEquals(params1.hashCode(), params2.hashCode());
-    }
-
-    public void testHashCode_VerificationUri_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(null, Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
-        assertFalse(params1.hashCode() == params2.hashCode());
-    }
-
-    public void testHashCode_OriginatingUri_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
-        assertFalse(params1.hashCode() == params2.hashCode());
-    }
-
-    public void testHashCode_Referrer_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), null,
-                ORIGINATING_UID);
-
-        assertFalse(params1.hashCode() == params2.hashCode());
-    }
-
-    public void testHashCode_Originating_Uid_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), 12345);
-
-        assertFalse(params1.hashCode() == params2.hashCode());
-    }
-
-    public void testHashCode_InstallerUid_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-        params2.setInstallerUid(INSTALLER_UID);
-
-        assertFalse(params1.hashCode() == params2.hashCode());
-    }
-}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 453bddd..0fa29bf 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -38,6 +38,7 @@
 import android.widget.RelativeLayout;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.Espresso;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -82,6 +83,7 @@
                 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        Espresso.registerIdlingResources(activity.getLabelIdlingResource());
         waitForIdle();
 
         assertThat(activity.getAdapter().getCount(), is(2));
@@ -214,6 +216,7 @@
                 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        Espresso.registerIdlingResources(activity.getLabelIdlingResource());
         waitForIdle();
 
         // The other entry is filtered to the last used slot
@@ -251,6 +254,7 @@
                 Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        Espresso.registerIdlingResources(activity.getLabelIdlingResource());
         waitForIdle();
 
         // The other entry is filtered to the other profile slot
@@ -296,6 +300,7 @@
                 .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        Espresso.registerIdlingResources(activity.getLabelIdlingResource());
         waitForIdle();
 
         // The other entry is filtered to the other profile slot
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 83f6bc2..9082543 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -21,6 +21,8 @@
 import android.app.usage.UsageStatsManager;
 import android.content.pm.PackageManager;
 
+import androidx.test.espresso.idling.CountingIdlingResource;
+
 import java.util.function.Function;
 
 /*
@@ -29,6 +31,12 @@
 public class ResolverWrapperActivity extends ResolverActivity {
     static final OverrideData sOverrides = new OverrideData();
     private UsageStatsManager mUsm;
+    private CountingIdlingResource mLabelIdlingResource =
+            new CountingIdlingResource("LoadLabelTask");
+
+    public CountingIdlingResource getLabelIdlingResource() {
+        return mLabelIdlingResource;
+    }
 
     ResolveListAdapter getAdapter() {
         return mAdapter;
@@ -64,6 +72,11 @@
         return super.getPackageManager();
     }
 
+    @Override
+    protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) {
+        return new LoadLabelWrapperTask(info, holder);
+    }
+
     /**
      * We cannot directly mock the activity created since instrumentation creates it.
      * <p>
@@ -83,4 +96,22 @@
             resolverListController = mock(ResolverListController.class);
         }
     }
+
+    class LoadLabelWrapperTask extends LoadLabelTask {
+
+        protected LoadLabelWrapperTask(DisplayResolveInfo dri, ViewHolder holder) {
+            super(dri, holder);
+        }
+
+        @Override
+        protected void onPreExecute() {
+            mLabelIdlingResource.increment();
+        }
+
+        @Override
+        protected void onPostExecute(CharSequence[] result) {
+            super.onPostExecute(result);
+            mLabelIdlingResource.decrement();
+        }
+    }
 }
\ No newline at end of file
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 09c546a..41ab670 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -6,6 +6,7 @@
         "android_media_SourceDefaultEffect.cpp",
         "android_media_StreamDefaultEffect.cpp",
         "android_media_Visualizer.cpp",
+        "Visualizer.cpp",
     ],
 
     shared_libs: [
@@ -14,10 +15,12 @@
         "libutils",
         "libandroid_runtime",
         "libnativehelper",
-        "libmedia",
         "libaudioclient",
+        "libaudioutils",
     ],
 
+    version_script: "exports.lds",
+
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
new file mode 100644
index 0000000..e48032a
--- /dev/null
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -0,0 +1,443 @@
+/*
+**
+** Copyright 2010, 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.
+*/
+
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Visualizer"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include <audio_utils/fixedfft.h>
+#include <utils/Thread.h>
+
+#include "Visualizer.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+Visualizer::Visualizer (const String16& opPackageName,
+         int32_t priority,
+         effect_callback_t cbf,
+         void* user,
+         audio_session_t sessionId)
+    :   AudioEffect(SL_IID_VISUALIZATION, opPackageName, NULL, priority, cbf, user, sessionId),
+        mCaptureRate(CAPTURE_RATE_DEF),
+        mCaptureSize(CAPTURE_SIZE_DEF),
+        mSampleRate(44100000),
+        mScalingMode(VISUALIZER_SCALING_MODE_NORMALIZED),
+        mMeasurementMode(MEASUREMENT_MODE_NONE),
+        mCaptureCallBack(NULL),
+        mCaptureCbkUser(NULL)
+{
+    initCaptureSize();
+}
+
+Visualizer::~Visualizer()
+{
+    ALOGV("Visualizer::~Visualizer()");
+    setEnabled(false);
+    setCaptureCallBack(NULL, NULL, 0, 0);
+}
+
+void Visualizer::release()
+{
+    ALOGV("Visualizer::release()");
+    setEnabled(false);
+    Mutex::Autolock _l(mCaptureLock);
+
+    mCaptureThread.clear();
+    mCaptureCallBack = NULL;
+    mCaptureCbkUser = NULL;
+    mCaptureFlags = 0;
+    mCaptureRate = 0;
+}
+
+status_t Visualizer::setEnabled(bool enabled)
+{
+    Mutex::Autolock _l(mCaptureLock);
+
+    sp<CaptureThread> t = mCaptureThread;
+    if (t != 0) {
+        if (enabled) {
+            if (t->exitPending()) {
+                if (t->requestExitAndWait() == WOULD_BLOCK) {
+                    ALOGE("Visualizer::enable() called from thread");
+                    return INVALID_OPERATION;
+                }
+            }
+        }
+        t->mLock.lock();
+    }
+
+    status_t status = AudioEffect::setEnabled(enabled);
+
+    if (t != 0) {
+        if (enabled && status == NO_ERROR) {
+            t->run("Visualizer");
+        } else {
+            t->requestExit();
+        }
+    }
+
+    if (t != 0) {
+        t->mLock.unlock();
+    }
+
+    return status;
+}
+
+status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags,
+        uint32_t rate)
+{
+    if (rate > CAPTURE_RATE_MAX) {
+        return BAD_VALUE;
+    }
+    Mutex::Autolock _l(mCaptureLock);
+
+    if (mEnabled) {
+        return INVALID_OPERATION;
+    }
+
+    if (mCaptureThread != 0) {
+        mCaptureLock.unlock();
+        mCaptureThread->requestExitAndWait();
+        mCaptureLock.lock();
+    }
+
+    mCaptureThread.clear();
+    mCaptureCallBack = cbk;
+    mCaptureCbkUser = user;
+    mCaptureFlags = flags;
+    mCaptureRate = rate;
+
+    if (cbk != NULL) {
+        mCaptureThread = new CaptureThread(this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
+    }
+    ALOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
+            rate, mCaptureThread.get(), mCaptureFlags);
+    return NO_ERROR;
+}
+
+status_t Visualizer::setCaptureSize(uint32_t size)
+{
+    if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
+        size < VISUALIZER_CAPTURE_SIZE_MIN ||
+        popcount(size) != 1) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock _l(mCaptureLock);
+    if (mEnabled) {
+        return INVALID_OPERATION;
+    }
+
+    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+    effect_param_t *p = (effect_param_t *)buf32;
+
+    p->psize = sizeof(uint32_t);
+    p->vsize = sizeof(uint32_t);
+    *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
+    *((int32_t *)p->data + 1)= size;
+    status_t status = setParameter(p);
+
+    ALOGV("setCaptureSize size %d  status %d p->status %d", size, status, p->status);
+
+    if (status == NO_ERROR) {
+        status = p->status;
+        if (status == NO_ERROR) {
+            mCaptureSize = size;
+        }
+    }
+
+    return status;
+}
+
+status_t Visualizer::setScalingMode(uint32_t mode) {
+    if ((mode != VISUALIZER_SCALING_MODE_NORMALIZED)
+            && (mode != VISUALIZER_SCALING_MODE_AS_PLAYED)) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock _l(mCaptureLock);
+
+    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+    effect_param_t *p = (effect_param_t *)buf32;
+
+    p->psize = sizeof(uint32_t);
+    p->vsize = sizeof(uint32_t);
+    *(int32_t *)p->data = VISUALIZER_PARAM_SCALING_MODE;
+    *((int32_t *)p->data + 1)= mode;
+    status_t status = setParameter(p);
+
+    ALOGV("setScalingMode mode %d  status %d p->status %d", mode, status, p->status);
+
+    if (status == NO_ERROR) {
+        status = p->status;
+        if (status == NO_ERROR) {
+            mScalingMode = mode;
+        }
+    }
+
+    return status;
+}
+
+status_t Visualizer::setMeasurementMode(uint32_t mode) {
+    if ((mode != MEASUREMENT_MODE_NONE)
+            //Note: needs to be handled as a mask when more measurement modes are added
+            && ((mode & MEASUREMENT_MODE_PEAK_RMS) != mode)) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock _l(mCaptureLock);
+
+    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+    effect_param_t *p = (effect_param_t *)buf32;
+
+    p->psize = sizeof(uint32_t);
+    p->vsize = sizeof(uint32_t);
+    *(int32_t *)p->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+    *((int32_t *)p->data + 1)= mode;
+    status_t status = setParameter(p);
+
+    ALOGV("setMeasurementMode mode %d  status %d p->status %d", mode, status, p->status);
+
+    if (status == NO_ERROR) {
+        status = p->status;
+        if (status == NO_ERROR) {
+            mMeasurementMode = mode;
+        }
+    }
+    return status;
+}
+
+status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements) {
+    if (mMeasurementMode == MEASUREMENT_MODE_NONE) {
+        ALOGE("Cannot retrieve int measurements, no measurement mode set");
+        return INVALID_OPERATION;
+    }
+    if (!(mMeasurementMode & type)) {
+        // measurement type has not been set on this Visualizer
+        ALOGE("Cannot retrieve int measurements, requested measurement mode 0x%x not set(0x%x)",
+                type, mMeasurementMode);
+        return INVALID_OPERATION;
+    }
+    // only peak+RMS measurement supported
+    if ((type != MEASUREMENT_MODE_PEAK_RMS)
+            // for peak+RMS measurement, the results are 2 int32_t values
+            || (number != 2)) {
+        ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
+                        number);
+        return BAD_VALUE;
+    }
+
+    status_t status = NO_ERROR;
+    if (mEnabled) {
+        uint32_t replySize = number * sizeof(int32_t);
+        status = command(VISUALIZER_CMD_MEASURE,
+                sizeof(uint32_t)  /*cmdSize*/,
+                &type /*cmdData*/,
+                &replySize, measurements);
+        ALOGV("getMeasurements() command returned %d", status);
+        if ((status == NO_ERROR) && (replySize == 0)) {
+            status = NOT_ENOUGH_DATA;
+        }
+    } else {
+        ALOGV("getMeasurements() disabled");
+        return INVALID_OPERATION;
+    }
+    return status;
+}
+
+status_t Visualizer::getWaveForm(uint8_t *waveform)
+{
+    if (waveform == NULL) {
+        return BAD_VALUE;
+    }
+    if (mCaptureSize == 0) {
+        return NO_INIT;
+    }
+
+    status_t status = NO_ERROR;
+    if (mEnabled) {
+        uint32_t replySize = mCaptureSize;
+        status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
+        ALOGV("getWaveForm() command returned %d", status);
+        if ((status == NO_ERROR) && (replySize == 0)) {
+            status = NOT_ENOUGH_DATA;
+        }
+    } else {
+        ALOGV("getWaveForm() disabled");
+        memset(waveform, 0x80, mCaptureSize);
+    }
+    return status;
+}
+
+status_t Visualizer::getFft(uint8_t *fft)
+{
+    if (fft == NULL) {
+        return BAD_VALUE;
+    }
+    if (mCaptureSize == 0) {
+        return NO_INIT;
+    }
+
+    status_t status = NO_ERROR;
+    if (mEnabled) {
+        uint8_t buf[mCaptureSize];
+        status = getWaveForm(buf);
+        if (status == NO_ERROR) {
+            status = doFft(fft, buf);
+        }
+    } else {
+        memset(fft, 0, mCaptureSize);
+    }
+    return status;
+}
+
+status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
+{
+    int32_t workspace[mCaptureSize >> 1];
+    int32_t nonzero = 0;
+
+    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+        workspace[i >> 1] =
+                ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
+        nonzero |= workspace[i >> 1];
+    }
+
+    if (nonzero) {
+        fixed_fft_real(mCaptureSize >> 1, workspace);
+    }
+
+    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+        short tmp = workspace[i >> 1] >> 21;
+        while (tmp > 127 || tmp < -128) tmp >>= 1;
+        fft[i] = tmp;
+        tmp = workspace[i >> 1];
+        tmp >>= 5;
+        while (tmp > 127 || tmp < -128) tmp >>= 1;
+        fft[i + 1] = tmp;
+    }
+
+    return NO_ERROR;
+}
+
+void Visualizer::periodicCapture()
+{
+    Mutex::Autolock _l(mCaptureLock);
+    ALOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
+            this, mCaptureCallBack, mCaptureFlags);
+    if (mCaptureCallBack != NULL &&
+        (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
+        mCaptureSize != 0) {
+        uint8_t waveform[mCaptureSize];
+        status_t status = getWaveForm(waveform);
+        if (status != NO_ERROR) {
+            return;
+        }
+        uint8_t fft[mCaptureSize];
+        if (mCaptureFlags & CAPTURE_FFT) {
+            status = doFft(fft, waveform);
+        }
+        if (status != NO_ERROR) {
+            return;
+        }
+        uint8_t *wavePtr = NULL;
+        uint8_t *fftPtr = NULL;
+        uint32_t waveSize = 0;
+        uint32_t fftSize = 0;
+        if (mCaptureFlags & CAPTURE_WAVEFORM) {
+            wavePtr = waveform;
+            waveSize = mCaptureSize;
+        }
+        if (mCaptureFlags & CAPTURE_FFT) {
+            fftPtr = fft;
+            fftSize = mCaptureSize;
+        }
+        mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
+    }
+}
+
+uint32_t Visualizer::initCaptureSize()
+{
+    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+    effect_param_t *p = (effect_param_t *)buf32;
+
+    p->psize = sizeof(uint32_t);
+    p->vsize = sizeof(uint32_t);
+    *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
+    status_t status = getParameter(p);
+
+    if (status == NO_ERROR) {
+        status = p->status;
+    }
+
+    uint32_t size = 0;
+    if (status == NO_ERROR) {
+        size = *((int32_t *)p->data + 1);
+    }
+    mCaptureSize = size;
+
+    ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
+
+    return size;
+}
+
+void Visualizer::controlStatusChanged(bool controlGranted) {
+    if (controlGranted) {
+        // this Visualizer instance regained control of the effect, reset the scaling mode
+        //   and capture size as has been cached through it.
+        ALOGV("controlStatusChanged(true) causes effect parameter reset:");
+        ALOGV("    scaling mode reset to %d", mScalingMode);
+        setScalingMode(mScalingMode);
+        ALOGV("    capture size reset to %d", mCaptureSize);
+        setCaptureSize(mCaptureSize);
+    }
+    AudioEffect::controlStatusChanged(controlGranted);
+}
+
+//-------------------------------------------------------------------------
+
+Visualizer::CaptureThread::CaptureThread(Visualizer* receiver, uint32_t captureRate,
+        bool bCanCallJava)
+    : Thread(bCanCallJava), mReceiver(receiver)
+{
+    mSleepTimeUs = 1000000000 / captureRate;
+    ALOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
+}
+
+bool Visualizer::CaptureThread::threadLoop()
+{
+    ALOGV("CaptureThread %p enter", this);
+    sp<Visualizer> receiver = mReceiver.promote();
+    if (receiver == NULL) {
+        return false;
+    }
+    while (!exitPending())
+    {
+        usleep(mSleepTimeUs);
+        receiver->periodicCapture();
+    }
+    ALOGV("CaptureThread %p exiting", this);
+    return false;
+}
+
+} // namespace android
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
new file mode 100644
index 0000000..8078e36
--- /dev/null
+++ b/media/jni/audioeffect/Visualizer.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_VISUALIZER_H
+#define ANDROID_MEDIA_VISUALIZER_H
+
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_visualizer.h>
+#include <utils/Thread.h>
+
+/**
+ * The Visualizer class enables application to retrieve part of the currently playing audio for
+ * visualization purpose. It is not an audio recording interface and only returns partial and low
+ * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
+ * of the visualizer requires the permission android.permission.RECORD_AUDIO.
+ * The audio session ID passed to the constructor indicates which audio content should be
+ * visualized:
+ * - If the session is 0, the audio output mix is visualized
+ * - If the session is not 0, the audio from a particular MediaPlayer or AudioTrack
+ *   using this audio session is visualized
+ * Two types of representation of audio content can be captured:
+ * - Waveform data: consecutive 8-bit (unsigned) mono samples by using the getWaveForm() method
+ * - Frequency data: 8-bit magnitude FFT by using the getFft() method
+ *
+ * The length of the capture can be retrieved or specified by calling respectively
+ * getCaptureSize() and setCaptureSize() methods. Note that the size of the FFT
+ * is half of the specified capture size but both sides of the spectrum are returned yielding in a
+ * number of bytes equal to the capture size. The capture size must be a power of 2 in the range
+ * returned by getMinCaptureSize() and getMaxCaptureSize().
+ * In addition to the polling capture mode, a callback mode is also available by installing a
+ * callback function by use of the setCaptureCallBack() method. The rate at which the callback
+ * is called as well as the type of data returned is specified.
+ * Before capturing data, the Visualizer must be enabled by calling the setEnabled() method.
+ * When data capture is not needed any more, the Visualizer should be disabled.
+ */
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class Visualizer: public AudioEffect {
+public:
+
+    enum callback_flags {
+        CAPTURE_WAVEFORM = 0x00000001,  // capture callback returns a PCM wave form
+        CAPTURE_FFT = 0x00000002,       // apture callback returns a frequency representation
+        CAPTURE_CALL_JAVA = 0x00000004  // the callback thread can call java
+    };
+
+
+    /* Constructor.
+     * See AudioEffect constructor for details on parameters.
+     */
+                        Visualizer(const String16& opPackageName,
+                                   int32_t priority = 0,
+                                   effect_callback_t cbf = NULL,
+                                   void* user = NULL,
+                                   audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX);
+
+                        ~Visualizer();
+
+    virtual status_t    setEnabled(bool enabled);
+
+    // maximum capture size in samples
+    static uint32_t getMaxCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MAX; }
+    // minimum capture size in samples
+    static uint32_t getMinCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MIN; }
+    // maximum capture rate in millihertz
+    static uint32_t getMaxCaptureRate() { return CAPTURE_RATE_MAX; }
+
+    // callback used to return periodic PCM or FFT captures to the application. Either one or both
+    // types of data are returned (PCM and FFT) according to flags indicated when installing the
+    // callback. When a type of data is not present, the corresponding size (waveformSize or
+    // fftSize) is 0.
+    typedef void (*capture_cbk_t)(void* user,
+                                    uint32_t waveformSize,
+                                    uint8_t *waveform,
+                                    uint32_t fftSize,
+                                    uint8_t *fft,
+                                    uint32_t samplingrate);
+
+    // install a callback to receive periodic captures. The capture rate is specified in milliHertz
+    // and the capture format is according to flags  (see callback_flags).
+    status_t setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate);
+
+    // set the capture size capture size must be a power of two in the range
+    // [VISUALIZER_CAPTURE_SIZE_MAX. VISUALIZER_CAPTURE_SIZE_MIN]
+    // must be called when the visualizer is not enabled
+    status_t setCaptureSize(uint32_t size);
+    uint32_t getCaptureSize() { return mCaptureSize; }
+
+    // returns the capture rate indicated when installing the callback
+    uint32_t getCaptureRate() { return mCaptureRate; }
+
+    // returns the sampling rate of the audio being captured
+    uint32_t getSamplingRate() { return mSampleRate; }
+
+    // set the way volume affects the captured data
+    // mode must one of VISUALIZER_SCALING_MODE_NORMALIZED,
+    //  VISUALIZER_SCALING_MODE_AS_PLAYED
+    status_t setScalingMode(uint32_t mode);
+    uint32_t getScalingMode() { return mScalingMode; }
+
+    // set which measurements are done on the audio buffers processed by the effect.
+    // valid measurements (mask): MEASUREMENT_MODE_PEAK_RMS
+    status_t setMeasurementMode(uint32_t mode);
+    uint32_t getMeasurementMode() { return mMeasurementMode; }
+
+    // return a set of int32_t measurements
+    status_t getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements);
+
+    // return a capture in PCM 8 bit unsigned format. The size of the capture is equal to
+    // getCaptureSize()
+    status_t getWaveForm(uint8_t *waveform);
+
+    // return a capture in FFT 8 bit signed format. The size of the capture is equal to
+    // getCaptureSize() but the length of the FFT is half of the size (both parts of the spectrum
+    // are returned
+    status_t getFft(uint8_t *fft);
+    void release();
+
+protected:
+    // from IEffectClient
+    virtual void controlStatusChanged(bool controlGranted);
+
+private:
+
+    static const uint32_t CAPTURE_RATE_MAX = 20000;
+    static const uint32_t CAPTURE_RATE_DEF = 10000;
+    static const uint32_t CAPTURE_SIZE_DEF = VISUALIZER_CAPTURE_SIZE_MAX;
+
+    /* internal class to handle the callback */
+    class CaptureThread : public Thread
+    {
+    public:
+        CaptureThread(Visualizer* visualizer, uint32_t captureRate, bool bCanCallJava = false);
+
+    private:
+        friend class Visualizer;
+        virtual bool        threadLoop();
+        wp<Visualizer> mReceiver;
+        Mutex       mLock;
+        uint32_t mSleepTimeUs;
+    };
+
+    status_t doFft(uint8_t *fft, uint8_t *waveform);
+    void periodicCapture();
+    uint32_t initCaptureSize();
+
+    Mutex mCaptureLock;
+    uint32_t mCaptureRate;
+    uint32_t mCaptureSize;
+    uint32_t mSampleRate;
+    uint32_t mScalingMode;
+    uint32_t mMeasurementMode;
+    capture_cbk_t mCaptureCallBack;
+    void *mCaptureCbkUser;
+    sp<CaptureThread> mCaptureThread;
+    uint32_t mCaptureFlags;
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_MEDIA_VISUALIZER_H
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 45de36e..1362433 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -24,7 +24,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
-#include "media/Visualizer.h"
+#include "Visualizer.h"
 
 #include <nativehelper/ScopedUtfChars.h>
 
diff --git a/media/jni/audioeffect/exports.lds b/media/jni/audioeffect/exports.lds
new file mode 100644
index 0000000..70491f4
--- /dev/null
+++ b/media/jni/audioeffect/exports.lds
@@ -0,0 +1,6 @@
+{
+    global:
+        *;
+    local:
+        *android10Visualizer*;
+};
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 1cf48c5..0f964fd 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -31,14 +31,15 @@
         android:paddingStart="@*android:dimen/car_padding_5"
         android:paddingEnd="@*android:dimen/car_padding_5">
 
-        <com.android.systemui.navigationbar.car.CarNavigationButton
+        <com.android.systemui.navigationbar.car.CarFacetButton
             android:id="@+id/home"
             android:layout_width="@*android:dimen/car_touch_target_size"
             android:layout_height="match_parent"
             android:background="?android:attr/selectableItemBackground"
-            android:src="@drawable/car_ic_overview"
+            systemui:icon="@drawable/car_ic_overview"
             systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-        />
+            systemui:selectedIcon="@drawable/car_ic_overview_selected"
+            systemui:useMoreIcon="false"/>
     </LinearLayout>
 </com.android.systemui.navigationbar.car.CarNavigationBarView>
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 6fba1d5..63bc66a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -25,7 +25,6 @@
 import android.os.ServiceManager;
 import android.view.Display;
 import android.view.Gravity;
-import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
@@ -37,8 +36,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.car.hvac.TemperatureView;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -52,15 +49,13 @@
 /** Navigation bars customized for the automotive use case. */
 public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks {
 
-    private final NavigationBarViewFactory mNavigationBarViewFactory;
+    private final CarNavigationBarController mCarNavigationBarController;
     private final WindowManager mWindowManager;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListener;
     private final Handler mMainHandler;
     private final Lazy<KeyguardStateController> mKeyguardStateController;
-    private final Lazy<CarFacetButtonController> mFacetButtonController;
     private final Lazy<NavigationBarController> mNavigationBarController;
-    private final Lazy<HvacController> mHvacController;
 
     private IStatusBarService mBarService;
     private CommandQueue mCommandQueue;
@@ -82,33 +77,23 @@
     // it's open.
     private boolean mDeviceIsSetUpForUser = true;
 
-    // Configuration values for if nav bars should be shown.
-    private boolean mShowBottom;
-    private boolean mShowLeft;
-    private boolean mShowRight;
-
-
     @Inject
     public CarNavigationBar(Context context,
-            NavigationBarViewFactory navigationBarViewFactory,
+            CarNavigationBarController carNavigationBarController,
             WindowManager windowManager,
             DeviceProvisionedController deviceProvisionedController,
             Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListener,
             @MainHandler Handler mainHandler,
             Lazy<KeyguardStateController> keyguardStateController,
-            Lazy<CarFacetButtonController> facetButtonController,
-            Lazy<NavigationBarController> navigationBarController,
-            Lazy<HvacController> hvacController) {
+            Lazy<NavigationBarController> navigationBarController) {
         super(context);
-        mNavigationBarViewFactory = navigationBarViewFactory;
+        mCarNavigationBarController = carNavigationBarController;
         mWindowManager = windowManager;
         mDeviceProvisionedController = deviceProvisionedController;
         mFacetButtonTaskStackListener = facetButtonTaskStackListener;
         mMainHandler = mainHandler;
         mKeyguardStateController = keyguardStateController;
-        mFacetButtonController = facetButtonController;
         mNavigationBarController = navigationBarController;
-        mHvacController = hvacController;
     }
 
     @Override
@@ -118,11 +103,6 @@
                 com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
         mBottomNavBarVisible = false;
 
-        // Read configuration.
-        mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
-        mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
-        mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
-
         // Get bar service.
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -157,7 +137,7 @@
         mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
         mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListener.get());
 
-        mHvacController.get().connectToCarService();
+        mCarNavigationBarController.connectToHvac();
     }
 
     private void restartNavBarsIfNecessary() {
@@ -175,8 +155,7 @@
     private void restartNavBars() {
         // remove and reattach all hvac components such that we don't keep a reference to unused
         // ui elements
-        mHvacController.get().removeAllComponents();
-        mFacetButtonController.get().removeAll();
+        mCarNavigationBarController.removeAllFromHvac();
 
         if (mNavigationBarWindow != null) {
             mNavigationBarWindow.removeAllViews();
@@ -199,10 +178,6 @@
         if (mKeyguardStateController.get().isShowing()) {
             updateNavBarForKeyguardContent();
         }
-
-        // CarFacetButtonController was reset therefore we need to re-add the status bar elements
-        // to the controller.
-        // TODO(hseog): Add facet buttons in status bar to controller.
     }
 
     private void createNavigationBar(RegisterStatusBarResult result) {
@@ -224,54 +199,30 @@
     }
 
     private void buildNavBarWindows() {
-        if (mShowBottom) {
-            mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow();
-        }
-
-        if (mShowLeft) {
-            mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow();
-        }
-
-        if (mShowRight) {
-            mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow();
-        }
+        mNavigationBarWindow = mCarNavigationBarController.getBottomWindow();
+        mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow();
+        mRightNavigationBarWindow = mCarNavigationBarController.getRightWindow();
     }
 
     private void buildNavBarContent() {
-        if (mShowBottom) {
-            mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser);
+        mNavigationBarView = mCarNavigationBarController.getBottomBar(mDeviceIsSetUpForUser);
+        if (mNavigationBarView != null) {
             mNavigationBarWindow.addView(mNavigationBarView);
-            addTemperatureViewToController(mNavigationBarView);
         }
 
-        if (mShowLeft) {
-            mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
+        mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
+        if (mLeftNavigationBarView != null) {
             mLeftNavigationBarWindow.addView(mLeftNavigationBarView);
-            addTemperatureViewToController(mLeftNavigationBarView);
         }
 
-        if (mShowRight) {
-            mRightNavigationBarView = mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser);
+        mRightNavigationBarView = mCarNavigationBarController.getRightBar(mDeviceIsSetUpForUser);
+        if (mRightNavigationBarView != null) {
             mRightNavigationBarWindow.addView(mRightNavigationBarView);
-            // Add ability to toggle notification center.
-            addTemperatureViewToController(mRightNavigationBarView);
-            // Add ability to close notification center on touch.
-        }
-    }
-
-    private void addTemperatureViewToController(View v) {
-        if (v instanceof TemperatureView) {
-            mHvacController.get().addHvacTextView((TemperatureView) v);
-        } else if (v instanceof ViewGroup) {
-            ViewGroup viewGroup = (ViewGroup) v;
-            for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                addTemperatureViewToController(viewGroup.getChildAt(i));
-            }
         }
     }
 
     private void attachNavBarWindows() {
-        if (mShowBottom && !mBottomNavBarVisible) {
+        if (mNavigationBarWindow != null && !mBottomNavBarVisible) {
             mBottomNavBarVisible = true;
 
             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -287,7 +238,7 @@
             mWindowManager.addView(mNavigationBarWindow, lp);
         }
 
-        if (mShowLeft) {
+        if (mLeftNavigationBarWindow != null) {
             int width = mContext.getResources().getDimensionPixelSize(
                     R.dimen.car_left_navigation_bar_width);
             WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
@@ -304,7 +255,7 @@
             leftlp.gravity = Gravity.LEFT;
             mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
         }
-        if (mShowRight) {
+        if (mRightNavigationBarWindow != null) {
             int width = mContext.getResources().getDimensionPixelSize(
                     R.dimen.car_right_navigation_bar_width);
             WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
@@ -340,23 +291,7 @@
         }
 
         boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
-        showBottomNavBarWindow(isKeyboardVisible);
-    }
-
-    private void showBottomNavBarWindow(boolean isKeyboardVisible) {
-        if (!mShowBottom) {
-            return;
-        }
-
-        // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
-        // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
-        if (isKeyboardVisible ^ mBottomNavBarVisible) {
-            return;
-        }
-
-        mNavigationBarViewFactory.getBottomWindow().setVisibility(
-                isKeyboardVisible ? View.GONE : View.VISIBLE);
-        mBottomNavBarVisible = !isKeyboardVisible;
+        mCarNavigationBarController.setBottomWindowVisibility(!isKeyboardVisible);
     }
 
     private void updateNavBarForKeyguardContent() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
new file mode 100644
index 0000000..f59f886
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2019 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.navigationbar.car;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.car.hvac.HvacController;
+import com.android.systemui.statusbar.car.hvac.TemperatureView;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/** A single class which controls the navigation bar views. */
+@Singleton
+public class CarNavigationBarController {
+
+    private final Context mContext;
+    private final NavigationBarViewFactory mNavigationBarViewFactory;
+    private final Lazy<HvacController> mHvacControllerLazy;
+
+    private boolean mShowBottom;
+    private boolean mShowLeft;
+    private boolean mShowRight;
+
+    private View.OnTouchListener mTopBarTouchListener;
+    private View.OnTouchListener mBottomBarTouchListener;
+    private View.OnTouchListener mLeftBarTouchListener;
+    private View.OnTouchListener mRightBarTouchListener;
+    private NotificationsShadeController mNotificationsShadeController;
+
+    private CarNavigationBarView mTopView;
+    private CarNavigationBarView mBottomView;
+    private CarNavigationBarView mLeftView;
+    private CarNavigationBarView mRightView;
+
+    @Inject
+    public CarNavigationBarController(Context context,
+            NavigationBarViewFactory navigationBarViewFactory,
+            Lazy<HvacController> hvacControllerLazy) {
+        mContext = context;
+        mNavigationBarViewFactory = navigationBarViewFactory;
+        mHvacControllerLazy = hvacControllerLazy;
+
+        // Read configuration.
+        mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
+        mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
+        mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
+    }
+
+    /** Connect to hvac service. */
+    public void connectToHvac() {
+        mHvacControllerLazy.get().connectToCarService();
+    }
+
+    /** Clean up hvac. */
+    public void removeAllFromHvac() {
+        mHvacControllerLazy.get().removeAllComponents();
+    }
+
+    /** Gets the bottom window if configured to do so. */
+    @Nullable
+    public ViewGroup getBottomWindow() {
+        return mShowBottom ? mNavigationBarViewFactory.getBottomWindow() : null;
+    }
+
+    /** Gets the left window if configured to do so. */
+    @Nullable
+    public ViewGroup getLeftWindow() {
+        return mShowLeft ? mNavigationBarViewFactory.getLeftWindow() : null;
+    }
+
+    /** Gets the right window if configured to do so. */
+    @Nullable
+    public ViewGroup getRightWindow() {
+        return mShowRight ? mNavigationBarViewFactory.getRightWindow() : null;
+    }
+
+    /** Toggles the bottom nav bar visibility. */
+    public boolean setBottomWindowVisibility(boolean isVisible) {
+        return setWindowVisibility(getBottomWindow(), isVisible);
+    }
+
+    /** Toggles the left nav bar visibility. */
+    public boolean setLeftWindowVisibility(boolean isVisible) {
+        return setWindowVisibility(getLeftWindow(), isVisible);
+    }
+
+    /** Toggles the right nav bar visibility. */
+    public boolean setRightWindowVisibility(boolean isVisible) {
+        return setWindowVisibility(getRightWindow(), isVisible);
+    }
+
+    private boolean setWindowVisibility(ViewGroup window, boolean isVisible) {
+        if (window == null) {
+            return false;
+        }
+
+        int newVisibility = isVisible ? View.VISIBLE : View.GONE;
+        if (window.getVisibility() == newVisibility) {
+            return false;
+        }
+
+        window.setVisibility(newVisibility);
+        return true;
+    }
+
+    /** Gets the top navigation bar with the appropriate listeners set. */
+    @NonNull
+    public CarNavigationBarView getTopBar(boolean isSetUp) {
+        mTopView = mNavigationBarViewFactory.getTopBar(isSetUp);
+        mTopView.setStatusBarWindowTouchListener(mTopBarTouchListener);
+        mTopView.setNotificationsPanelController(mNotificationsShadeController);
+        addTemperatureViewToController(mTopView);
+        return mTopView;
+    }
+
+    /** Gets the bottom navigation bar with the appropriate listeners set. */
+    @Nullable
+    public CarNavigationBarView getBottomBar(boolean isSetUp) {
+        if (!mShowBottom) {
+            return null;
+        }
+
+        mBottomView = mNavigationBarViewFactory.getBottomBar(isSetUp);
+        mBottomView.setStatusBarWindowTouchListener(mBottomBarTouchListener);
+        mBottomView.setNotificationsPanelController(mNotificationsShadeController);
+        addTemperatureViewToController(mBottomView);
+        return mBottomView;
+    }
+
+    /** Gets the left navigation bar with the appropriate listeners set. */
+    @Nullable
+    public CarNavigationBarView getLeftBar(boolean isSetUp) {
+        if (!mShowLeft) {
+            return null;
+        }
+
+        mLeftView = mNavigationBarViewFactory.getLeftBar(isSetUp);
+        mLeftView.setStatusBarWindowTouchListener(mLeftBarTouchListener);
+        mLeftView.setNotificationsPanelController(mNotificationsShadeController);
+        addTemperatureViewToController(mLeftView);
+        return mLeftView;
+    }
+
+    /** Gets the right navigation bar with the appropriate listeners set. */
+    @Nullable
+    public CarNavigationBarView getRightBar(boolean isSetUp) {
+        if (!mShowRight) {
+            return null;
+        }
+
+        mRightView = mNavigationBarViewFactory.getRightBar(isSetUp);
+        mRightView.setStatusBarWindowTouchListener(mRightBarTouchListener);
+        mRightView.setNotificationsPanelController(mNotificationsShadeController);
+        addTemperatureViewToController(mRightView);
+        return mRightView;
+    }
+
+    /** Sets a touch listener for the top navigation bar. */
+    public void registerTopBarTouchListener(View.OnTouchListener listener) {
+        mTopBarTouchListener = listener;
+        if (mTopView != null) {
+            mTopView.setStatusBarWindowTouchListener(mTopBarTouchListener);
+        }
+    }
+
+    /** Sets a touch listener for the bottom navigation bar. */
+    public void registerBottomBarTouchListener(View.OnTouchListener listener) {
+        mBottomBarTouchListener = listener;
+        if (mBottomView != null) {
+            mBottomView.setStatusBarWindowTouchListener(mBottomBarTouchListener);
+        }
+    }
+
+    /** Sets a touch listener for the left navigation bar. */
+    public void registerLeftBarTouchListener(View.OnTouchListener listener) {
+        mLeftBarTouchListener = listener;
+        if (mLeftView != null) {
+            mLeftView.setStatusBarWindowTouchListener(mLeftBarTouchListener);
+        }
+    }
+
+    /** Sets a touch listener for the right navigation bar. */
+    public void registerRightBarTouchListener(View.OnTouchListener listener) {
+        mRightBarTouchListener = listener;
+        if (mRightView != null) {
+            mRightView.setStatusBarWindowTouchListener(mRightBarTouchListener);
+        }
+    }
+
+    /** Sets a notification controller which toggles the notification panel. */
+    public void registerNotificationController(
+            NotificationsShadeController notificationsShadeController) {
+        mNotificationsShadeController = notificationsShadeController;
+        if (mTopView != null) {
+            mTopView.setNotificationsPanelController(mNotificationsShadeController);
+        }
+        if (mBottomView != null) {
+            mBottomView.setNotificationsPanelController(mNotificationsShadeController);
+        }
+        if (mLeftView != null) {
+            mLeftView.setNotificationsPanelController(mNotificationsShadeController);
+        }
+        if (mRightView != null) {
+            mRightView.setNotificationsPanelController(mNotificationsShadeController);
+        }
+    }
+
+    /** Interface for controlling the notifications shade. */
+    public interface NotificationsShadeController {
+        /** Toggles the visibility of the notifications shade. */
+        void togglePanel();
+    }
+
+    private void addTemperatureViewToController(View v) {
+        if (v instanceof TemperatureView) {
+            mHvacControllerLazy.get().addHvacTextView((TemperatureView) v);
+        } else if (v instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) v;
+            for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                addTemperatureViewToController(viewGroup.getChildAt(i));
+            }
+        }
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
index afb6954..24f8b74 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
@@ -24,7 +24,6 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.car.CarStatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 
 /**
@@ -36,7 +35,7 @@
 public class CarNavigationBarView extends LinearLayout {
     private View mNavButtons;
     private CarNavigationButton mNotificationsButton;
-    private CarStatusBar mCarStatusBar;
+    private CarNavigationBarController.NotificationsShadeController mNotificationsShadeController;
     private Context mContext;
     private View mLockScreenButtons;
     // used to wire in open/close gestures for notifications
@@ -82,8 +81,9 @@
         return super.onInterceptTouchEvent(ev);
     }
 
-    public void setStatusBar(CarStatusBar carStatusBar) {
-        mCarStatusBar = carStatusBar;
+    public void setNotificationsPanelController(
+            CarNavigationBarController.NotificationsShadeController controller) {
+        mNotificationsShadeController = controller;
     }
 
     /**
@@ -104,7 +104,9 @@
     }
 
     protected void onNotificationsClick(View v) {
-        mCarStatusBar.togglePanel();
+        if (mNotificationsShadeController != null) {
+            mNotificationsShadeController.togglePanel();
+        }
     }
 
     /**
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
index 707d80f..40823ab 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
@@ -85,6 +85,7 @@
         super.onFinishInflate();
         setScaleType(ImageView.ScaleType.CENTER);
         setAlpha(mUnselectedAlpha);
+        setImageResource(mIconResourceId);
         try {
             if (mIntent != null) {
                 final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 52aaf4f..aebb62d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -76,8 +76,8 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.navigationbar.car.CarFacetButtonController;
+import com.android.systemui.navigationbar.car.CarNavigationBarController;
 import com.android.systemui.navigationbar.car.CarNavigationBarView;
-import com.android.systemui.navigationbar.car.NavigationBarViewFactory;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.car.CarQSFragment;
@@ -93,8 +93,6 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.car.hvac.TemperatureView;
 import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NewNotifPipeline;
@@ -174,14 +172,10 @@
     private CarNavigationBarView mRightNavigationBarView;
 
     private final Object mQueueLock = new Object();
-    private boolean mShowLeft;
-    private boolean mShowRight;
-    private boolean mShowBottom;
-    private final NavigationBarViewFactory mNavigationBarViewFactory;
+    private final CarNavigationBarController mCarNavigationBarController;
     private CarFacetButtonController mCarFacetButtonController;
     private DeviceProvisionedController mDeviceProvisionedController;
     private boolean mDeviceIsSetUpForUser = true;
-    private HvacController mHvacController;
     private DrivingStateHelper mDrivingStateHelper;
     private PowerManagerHelper mPowerManagerHelper;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -309,7 +303,7 @@
             DozeScrimController dozeScrimController,
 
             /* Car Settings injected components. */
-            NavigationBarViewFactory navigationBarViewFactory) {
+            CarNavigationBarController carNavigationBarController) {
         super(
                 context,
                 featureFlags,
@@ -375,7 +369,7 @@
                 powerManager,
                 dozeScrimController);
         mScrimController = scrimController;
-        mNavigationBarViewFactory = navigationBarViewFactory;
+        mCarNavigationBarController = carNavigationBarController;
     }
 
     @Override
@@ -390,10 +384,6 @@
         mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
         mScreenLifecycle.addObserver(mScreenObserver);
 
-        // Need to initialize HVAC controller before calling super.start - before system bars are
-        // created.
-        mHvacController = new HvacController(mContext);
-
         super.start();
 
         mNotificationPanel.setScrollingEnabled(true);
@@ -407,8 +397,6 @@
         createBatteryController();
         mCarBatteryController.startListening();
 
-        mHvacController.connectToCarService();
-
         mDeviceProvisionedController.addCallback(
                 new DeviceProvisionedController.DeviceProvisionedListener() {
                     @Override
@@ -445,9 +433,6 @@
      * before and after the device is provisioned. . Also for change of density and font size.
      */
     private void restartNavBars() {
-        // remove and reattach all hvac components such that we don't keep a reference to unused
-        // ui elements
-        mHvacController.removeAllComponents();
         mCarFacetButtonController.removeAll();
 
         if (mNavigationBarWindow != null) {
@@ -466,17 +451,6 @@
         mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
     }
 
-    private void addTemperatureViewToController(View v) {
-        if (v instanceof TemperatureView) {
-            mHvacController.addHvacTextView((TemperatureView) v);
-        } else if (v instanceof ViewGroup) {
-            ViewGroup viewGroup = (ViewGroup) v;
-            for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                addTemperatureViewToController(viewGroup.getChildAt(i));
-            }
-        }
-    }
-
     /**
      * Allows for showing or hiding just the navigation bars. This is indented to be used when
      * the full screen user selector is shown.
@@ -897,10 +871,6 @@
 
     @Override
     protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
-        mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
-        mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
-        mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
-
         buildNavBarWindows();
         buildNavBarContent();
     }
@@ -908,50 +878,36 @@
     private void buildNavBarContent() {
         buildTopBar();
 
-        if (mShowBottom) {
-            mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser);
-            mNavigationBarView.setStatusBar(this);
-            mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
-        }
+        mNavigationBarView = mCarNavigationBarController.getBottomBar(mDeviceIsSetUpForUser);
+        mCarNavigationBarController.registerBottomBarTouchListener(
+                mNavBarNotificationTouchListener);
 
-        if (mShowLeft) {
-            mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
-            mLeftNavigationBarView.setStatusBar(this);
-            mLeftNavigationBarView.setStatusBarWindowTouchListener(
-                    mNavBarNotificationTouchListener);
-        }
+        mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
+        mCarNavigationBarController.registerLeftBarTouchListener(
+                mNavBarNotificationTouchListener);
 
-        if (mShowRight) {
-            mRightNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
-            mRightNavigationBarView.setStatusBar(this);
-            mRightNavigationBarView.setStatusBarWindowTouchListener(
-                    mNavBarNotificationTouchListener);
-        }
+        mRightNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
+        mCarNavigationBarController.registerRightBarTouchListener(
+                mNavBarNotificationTouchListener);
+
+        mCarNavigationBarController.registerNotificationController(() -> togglePanel());
     }
 
     private void buildNavBarWindows() {
         mTopNavigationBarContainer = mStatusBarWindow
                 .findViewById(R.id.car_top_navigation_bar_container);
 
-        if (mShowBottom) {
-            mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow();
-        }
-        if (mShowLeft) {
-            mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow();
-        }
-        if (mShowRight) {
-            mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow();
-        }
+        mNavigationBarWindow = mCarNavigationBarController.getBottomWindow();
+        mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow();
+        mRightNavigationBarWindow = mCarNavigationBarController.getRightWindow();
     }
 
     private void buildTopBar() {
         mTopNavigationBarContainer.removeAllViews();
-        mTopNavigationBarView = mNavigationBarViewFactory.getTopBar(mDeviceIsSetUpForUser);
+        mTopNavigationBarView = mCarNavigationBarController.getTopBar(mDeviceIsSetUpForUser);
+        mCarNavigationBarController.registerTopBarTouchListener(
+                mTopNavBarNotificationTouchListener);
         mTopNavigationBarContainer.addView(mTopNavigationBarView);
-
-        mTopNavigationBarView.setStatusBar(this);
-        addTemperatureViewToController(mTopNavigationBarView);
-        mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
     }
 
     @Override
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index cf286bd..738c425 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -99,11 +99,13 @@
             // init input stream before calling startInstallation(), which takes 90 seconds.
             initInputStream();
 
-            Thread thread = new Thread(() -> {
-                mInstallationSession =
-                        mDynSystem.startInstallation(mSystemSize, mUserdataSize);
-            });
-
+            Thread thread =
+                    new Thread(
+                            () -> {
+                                mDynSystem.startInstallation("userdata", mUserdataSize, false);
+                                mInstallationSession =
+                                        mDynSystem.startInstallation("system", mSystemSize, true);
+                            });
 
             thread.start();
 
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
new file mode 100644
index 0000000..2054129
--- /dev/null
+++ b/packages/SettingsProvider/OWNERS
@@ -0,0 +1,3 @@
+hackbod@google.com
+svetoslavganov@google.com
+moltmann@google.com
diff --git a/packages/SystemUI/res/layout/home_controls.xml b/packages/SystemUI/res/layout/home_controls.xml
index bb971c2..b9a6a48 100644
--- a/packages/SystemUI/res/layout/home_controls.xml
+++ b/packages/SystemUI/res/layout/home_controls.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/home_controls_layout"
     android:layout_width="match_parent"
@@ -8,6 +8,5 @@
     android:visibility="gone"
     android:padding="8dp"
     android:layout_margin="5dp"
-    android:background="?android:attr/colorBackgroundFloating"
-    android:orientation="vertical">
-</LinearLayout>
+    android:background="?android:attr/colorBackgroundFloating">
+</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b758731..446ed25 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -93,8 +93,11 @@
                         Log.w(TAG, "Evicting client due to: " + topPackage);
                         mCurrentDialog.dismissWithoutCallback(true /* animate */);
                         mCurrentDialog = null;
-                        mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
-                        mReceiver = null;
+                        if (mReceiver != null) {
+                            mReceiver.onDialogDismissed(
+                                    BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+                            mReceiver = null;
+                        }
                     }
                 }
             } catch (RemoteException e) {
@@ -105,6 +108,10 @@
 
     @Override
     public void onTryAgainPressed() {
+        if (mReceiver == null) {
+            Log.e(TAG, "onTryAgainPressed: Receiver is null");
+            return;
+        }
         try {
             mReceiver.onTryAgainPressed();
         } catch (RemoteException e) {
@@ -114,6 +121,10 @@
 
     @Override
     public void onDeviceCredentialPressed() {
+        if (mReceiver == null) {
+            Log.e(TAG, "onDeviceCredentialPressed: Receiver is null");
+            return;
+        }
         try {
             mReceiver.onDeviceCredentialPressed();
         } catch (RemoteException e) {
@@ -161,7 +172,7 @@
 
     private void sendResultAndCleanUp(@DismissedReason int reason) {
         if (mReceiver == null) {
-            Log.e(TAG, "Receiver is null");
+            Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
             return;
         }
         try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index af418f6..6949640 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -33,6 +33,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -50,7 +51,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.MediaTransferManager;
 
 /**
  * Single media player for carousel in QSPanel
@@ -101,6 +101,12 @@
         mToken = token;
         mController = new MediaController(mContext, token);
         MediaMetadata mMediaMetadata = mController.getMetadata();
+
+        if (mMediaMetadata == null) {
+            Log.e(TAG, "Media metadata was null");
+            return;
+        }
+
         Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
 
         // Album art
@@ -151,18 +157,17 @@
         // Album name
         TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text);
         String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
-        if (!albumString.isEmpty()) {
+        if (TextUtils.isEmpty(albumString)) {
+            albumName.setVisibility(View.GONE);
+            separator.setVisibility(View.GONE);
+        } else {
             albumName.setText(albumString);
             albumName.setTextColor(iconColor);
             albumName.setVisibility(View.VISIBLE);
             separator.setVisibility(View.VISIBLE);
-        } else {
-            albumName.setVisibility(View.GONE);
-            separator.setVisibility(View.GONE);
         }
 
         // Transfer chip
-        MediaTransferManager mediaTransferManager = new MediaTransferManager(mContext);
         View transferBackgroundView = headerView.findViewById(
                 com.android.internal.R.id.media_seamless);
         LinearLayout viewLayout = (LinearLayout) transferBackgroundView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 2060059..b48814b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -166,6 +166,7 @@
             mMediaCarousel = new LinearLayout(mContext);
             mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL);
             mediaScrollView.addView(mMediaCarousel, lpCarousel);
+            mediaScrollView.setVisibility(View.GONE);
         } else {
             mMediaCarousel = null;
         }
@@ -239,6 +240,7 @@
             } else {
                 mMediaCarousel.addView(player.getView(), lp); // add at end
             }
+            mMediaPlayers.add(player);
         } else if (player.isPlaying()) {
             // move it to the front
             mMediaCarousel.removeView(player.getView());
@@ -248,7 +250,10 @@
         Log.d(TAG, "setting player session");
         player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
                 notif.getNotification());
-        mMediaPlayers.add(player);
+
+        if (mMediaPlayers.size() > 0) {
+            ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
+        }
     }
 
     protected View getMediaPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index ae66cd5..3ec71ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -84,6 +84,11 @@
         mController = new MediaController(mContext, token);
         MediaMetadata mMediaMetadata = mController.getMetadata();
 
+        if (mMediaMetadata == null) {
+            Log.e(TAG, "Media metadata was null");
+            return;
+        }
+
         // Album art
         addAlbumArtBackground(mMediaMetadata, bgColor);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 1c8e451..9a33c8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -145,7 +145,7 @@
                 return mBluetoothTileProvider.get();
             case "controls":
                 if (Settings.System.getInt(mHost.getContext().getContentResolver(),
-                        "qs_controls_tile_enabled", 0) == 1) {
+                        "npv_plugin_flag", 0) == 3) {
                     return mControlsTileProvider.get();
                 } else return null;
             case "cell":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
index 0a59618..39ae66e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
@@ -22,11 +22,11 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
 
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.NPVPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -44,7 +44,7 @@
     private ControlsDetailAdapter mDetailAdapter;
     private final ActivityStarter mActivityStarter;
     private PluginManager mPluginManager;
-    private HomeControlsPlugin mPlugin;
+    private NPVPlugin mPlugin;
     private Intent mHomeAppIntent;
 
     @Inject
@@ -81,7 +81,7 @@
     public void setDetailListening(boolean listening) {
         if (mPlugin == null) return;
 
-        mPlugin.setVisible(listening);
+        mPlugin.setListening(listening);
     }
 
     @Override
@@ -142,7 +142,7 @@
 
     private class ControlsDetailAdapter implements DetailAdapter {
         private View mDetailView;
-        protected LinearLayout mHomeControlsLayout;
+        protected FrameLayout mHomeControlsLayout;
 
         public CharSequence getTitle() {
             return "Controls";
@@ -157,24 +157,30 @@
         }
 
         public View createDetailView(Context context, View convertView, final ViewGroup parent) {
-            mHomeControlsLayout = (LinearLayout) LayoutInflater.from(context).inflate(
-                R.layout.home_controls, parent, false);
+            if (convertView != null) return convertView;
+
+            mHomeControlsLayout = (FrameLayout) LayoutInflater.from(context).inflate(
+                    R.layout.home_controls, parent, false);
             mHomeControlsLayout.setVisibility(View.VISIBLE);
+            parent.addView(mHomeControlsLayout);
+
             mPluginManager.addPluginListener(
-                    new PluginListener<HomeControlsPlugin>() {
+                    new PluginListener<NPVPlugin>() {
                         @Override
-                        public void onPluginConnected(HomeControlsPlugin plugin,
+                        public void onPluginConnected(NPVPlugin plugin,
                                                       Context pluginContext) {
                             mPlugin = plugin;
-                            mPlugin.sendParentGroup(mHomeControlsLayout);
-                            mPlugin.setVisible(true);
+                            mPlugin.attachToRoot(mHomeControlsLayout);
+                            mPlugin.setListening(true);
                         }
 
                         @Override
-                        public void onPluginDisconnected(HomeControlsPlugin plugin) {
+                        public void onPluginDisconnected(NPVPlugin plugin) {
+                            mPlugin.setListening(false);
+                            mHomeControlsLayout.removeAllViews();
 
                         }
-                    }, HomeControlsPlugin.class, false);
+                    }, NPVPlugin.class, false);
             return mHomeControlsLayout;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index f284f73..53fbe5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -155,7 +156,8 @@
                                     focusedStack.configuration.windowConfiguration
                                             .getWindowingMode();
                             if (windowingMode == WINDOWING_MODE_FULLSCREEN
-                                    || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+                                    || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                                    || windowingMode == WINDOWING_MODE_FREEFORM) {
                                 checkAndPostForStack(focusedStack, notifs, noMan, pm);
                             }
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index bd3d848..ffcbc40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -47,10 +47,12 @@
 import java.lang.annotation.RetentionPolicy;
 
 import javax.inject.Inject;
+import javax.inject.Singleton;
 
 /**
  * Controller which coordinates all the biometric unlocking actions with the UI.
  */
+@Singleton
 public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
 
     private static final String TAG = "BiometricUnlockCtrl";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index dd200da..7f31f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -77,9 +77,12 @@
                 ExpandableView child = mCallback.getChildAtRawPosition(x, y);
                 mTouchingHeadsUpView = false;
                 if (child instanceof ExpandableNotificationRow) {
-                    mPickedChild = (ExpandableNotificationRow) child;
+                    ExpandableNotificationRow pickedChild = (ExpandableNotificationRow) child;
                     mTouchingHeadsUpView = !mCallback.isExpanded()
-                            && mPickedChild.isHeadsUp() && mPickedChild.isPinned();
+                            && pickedChild.isHeadsUp() && pickedChild.isPinned();
+                    if (mTouchingHeadsUpView) {
+                        mPickedChild = pickedChild;
+                    }
                 } else if (child == null && !mCallback.isExpanded()) {
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 30fe68a..e00cfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -55,7 +55,6 @@
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -243,7 +242,7 @@
     private View mQsNavbarScrim;
     protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
-    protected LinearLayout mHomeControlsLayout;
+    protected FrameLayout mHomeControlsLayout;
     private boolean mAnimateNextPositionUpdate;
 
     private int mTrackingPointer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
index d6d0a36..9b685f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
@@ -30,6 +30,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.widget.ImageView;
@@ -45,7 +46,10 @@
     private static final String TAG = "AudioRecordingDisclosureBar";
     private static final boolean DEBUG = false;
 
-    private static final String LAYOUT_PARAMS_TITLE = "AudioRecordingDisclosureBar";
+    // This title is used to test the microphone disclosure indicator in
+    // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest
+    private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
+
     private static final int ANIM_DURATION_MS = 150;
 
     private final Context mContext;
@@ -70,6 +74,7 @@
     }
 
     private void createView() {
+        //TODO(b/142228704): this is to be re-implemented once proper design is completed
         mView = View.inflate(mContext,
                 R.layout.tv_status_bar_audio_recording, null);
         mAppsInfoContainer = mView.findViewById(R.id.container);
@@ -88,7 +93,7 @@
                 Context.WINDOW_SERVICE);
         windowManager.addView(mView, layoutParams);
 
-        // Set invisible first util it gains its actual size and we are able to hide it by moving
+        // Set invisible first until it gains its actual size and we are able to hide it by moving
         // off the screen
         mView.setVisibility(View.INVISIBLE);
         mView.getViewTreeObserver().addOnGlobalLayoutListener(
@@ -98,16 +103,18 @@
                         // Now that we get the height, we can move the bar off ("below") the screen
                         final int height = mView.getHeight();
                         mView.setTranslationY(height);
-                        // ... and make it visible
-                        mView.setVisibility(View.VISIBLE);
                         // Remove the observer
                         mView.getViewTreeObserver()
                                 .removeOnGlobalLayoutListener(this);
+                        // Now, that the view has been measured, and the translation was set to
+                        // move it off the screen, we change the visibility to GONE
+                        mView.setVisibility(View.GONE);
                     }
                 });
     }
 
     private void showAudioRecordingDisclosureBar() {
+        mView.setVisibility(View.VISIBLE);
         mView.animate()
                 .translationY(0f)
                 .setDuration(ANIM_DURATION_MS)
@@ -138,9 +145,10 @@
     }
 
     private void hideAudioRecordingDisclosureBar() {
-        mView.animate()
-                .translationY(mView.getHeight())
+        final ViewPropertyAnimator animator = mView.animate();
+        animator.translationY(mView.getHeight())
                 .setDuration(ANIM_DURATION_MS)
+                .withEndAction(() -> mView.setVisibility(View.GONE))
                 .start();
     }
 
@@ -156,7 +164,7 @@
         public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
             if (DEBUG) {
                 Log.d(TAG,
-                        "OP_RECORD_AUDIO active change, active" + active + ", app=" + packageName);
+                        "OP_RECORD_AUDIO active change, active=" + active + ", app=" + packageName);
             }
 
             if (mExemptApps.contains(packageName)) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index b089b74..85d818a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -389,6 +389,20 @@
         verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
     }
 
+    @Test
+    public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() {
+        showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+        mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+        mAuthController.onTryAgainPressed();
+    }
+
+    @Test
+    public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() {
+        showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+        mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+        mAuthController.onDeviceCredentialPressed();
+    }
+
     // Helpers
 
     private void showDialog(int authenticators, int biometricModality) {
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index dc88fd4..ca69c18 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -24,6 +24,8 @@
     ],
     static_libs: [
         "androidx.annotation_annotation",
+        "netd_aidl_interface-java",
+        "networkstack-aidl-interfaces-java",
         "tethering-client",
     ],
     manifest: "AndroidManifestBase.xml",
@@ -68,5 +70,10 @@
     name: "tethering-services-srcs",
     srcs: [
         "src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
+        "src/android/net/dhcp/DhcpServerCallbacks.java",
+        "src/android/net/dhcp/DhcpServingParamsParcelExt.java",
+        "src/android/net/ip/IpServer.java",
+        "src/android/net/ip/RouterAdvertisementDaemon.java",
+        "src/android/net/util/InterfaceSet.java",
     ],
 }
diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpServerCallbacks.java
rename to packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
rename to packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
diff --git a/services/net/java/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
similarity index 94%
rename from services/net/java/android/net/ip/IpServer.java
rename to packages/Tethering/src/android/net/ip/IpServer.java
index 3d79bba..ff3d7bc 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -16,7 +16,7 @@
 
 package android.net.ip;
 
-import static android.net.NetworkUtils.numericToInetAddress;
+import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.util.NetworkConstants.FF;
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
@@ -77,6 +77,7 @@
     public static final int STATE_TETHERED    = 2;
     public static final int STATE_LOCAL_ONLY  = 3;
 
+    /** Get string name of |state|.*/
     public static String getStateString(int state) {
         switch (state) {
             case STATE_UNAVAILABLE: return "UNAVAILABLE";
@@ -103,15 +104,16 @@
     // TODO: have this configurable
     private static final int DHCP_LEASE_TIME_SECS = 3600;
 
-    private final static String TAG = "IpServer";
-    private final static boolean DBG = false;
-    private final static boolean VDBG = false;
-    private static final Class[] messageClasses = {
+    private static final String TAG = "IpServer";
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+    private static final Class[] sMessageClasses = {
             IpServer.class
     };
     private static final SparseArray<String> sMagicDecoderRing =
-            MessageUtils.findMessageNames(messageClasses);
+            MessageUtils.findMessageNames(sMessageClasses);
 
+    /** IpServer callback. */
     public static class Callback {
         /**
          * Notify that |who| has changed its tethering state.
@@ -131,11 +133,14 @@
         public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
     }
 
+    /** Capture IpServer dependencies, for injection. */
     public static class Dependencies {
+        /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
         }
 
+        /** Get |ifName|'s interface information.*/
         public InterfaceParams getInterfaceParams(String ifName) {
             return InterfaceParams.getByName(ifName);
         }
@@ -244,25 +249,51 @@
         setInitialState(mInitialState);
     }
 
-    public String interfaceName() { return mIfaceName; }
-
-    public int interfaceType() { return mInterfaceType; }
-
-    public int lastError() { return mLastError; }
-
-    public int servingMode() { return mServingMode; }
-
-    public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); }
-
-    public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
-
-    public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
+    /** Interface name which IpServer served.*/
+    public String interfaceName() {
+        return mIfaceName;
+    }
 
     /**
-     * Internals.
+     * Tethering downstream type. It would be one of ConnectivityManager#TETHERING_*.
      */
+    public int interfaceType() {
+        return mInterfaceType;
+    }
 
-    private boolean startIPv4() { return configureIPv4(true); }
+    /** Last error from this IpServer. */
+    public int lastError() {
+        return mLastError;
+    }
+
+    /** Serving mode is the current state of IpServer state machine. */
+    public int servingMode() {
+        return mServingMode;
+    }
+
+    /** The properties of the network link which IpServer is serving. */
+    public LinkProperties linkProperties() {
+        return new LinkProperties(mLinkProperties);
+    }
+
+    /** Stop this IpServer. After this is called this IpServer should not be used any more. */
+    public void stop() {
+        sendMessage(CMD_INTERFACE_DOWN);
+    }
+
+    /**
+     * Tethering is canceled. IpServer state machine will be available and wait for
+     * next tethering request.
+     */
+    public void unwanted() {
+        sendMessage(CMD_TETHER_UNREQUESTED);
+    }
+
+    /** Internals. */
+
+    private boolean startIPv4() {
+        return configureIPv4(true);
+    }
 
     /**
      * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
@@ -410,7 +441,7 @@
             prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
         } else {
             // BT configures the interface elsewhere: only start DHCP.
-            final Inet4Address srvAddr = (Inet4Address) numericToInetAddress(BLUETOOTH_IFACE_ADDR);
+            final Inet4Address srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
             return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
         }
 
@@ -422,7 +453,7 @@
                 return false;
             }
 
-            InetAddress addr = numericToInetAddress(ipAsString);
+            InetAddress addr = parseNumericAddress(ipAsString);
             linkAddr = new LinkAddress(addr, prefixLen);
             ifcg.setLinkAddress(linkAddr);
             if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
@@ -473,7 +504,7 @@
 
     private String getRandomWifiIPv4Address() {
         try {
-            byte[] bytes = numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
+            byte[] bytes = parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress();
             bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
             return InetAddress.getByAddress(bytes).getHostAddress();
         } catch (Exception e) {
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
similarity index 88%
rename from services/net/java/android/net/ip/RouterAdvertisementDaemon.java
rename to packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 59aea21..4147413 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -119,6 +119,7 @@
     private volatile MulticastTransmitter mMulticastTransmitter;
     private volatile UnicastResponder mUnicastResponder;
 
+    /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
     public static class RaParams {
         // Tethered traffic will have the hop limit properly decremented.
         // Consequently, set the hoplimit greater by one than the upstream
@@ -150,10 +151,12 @@
             dnses = (HashSet) other.dnses.clone();
         }
 
-        // Returns the subset of RA parameters that become deprecated when
-        // moving from announcing oldRa to announcing newRa.
-        //
-        // Currently only tracks differences in |prefixes| and |dnses|.
+        /**
+         * Returns the subset of RA parameters that become deprecated when
+         * moving from announcing oldRa to announcing newRa.
+         *
+         * Currently only tracks differences in |prefixes| and |dnses|.
+         */
         public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
             RaParams newlyDeprecated = new RaParams();
 
@@ -179,7 +182,9 @@
         private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
         private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
 
-        Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
+        Set<IpPrefix> getPrefixes() {
+            return mPrefixes.keySet();
+        }
 
         void putPrefixes(Set<IpPrefix> prefixes) {
             for (IpPrefix ipp : prefixes) {
@@ -193,7 +198,9 @@
             }
         }
 
-        Set<Inet6Address> getDnses() { return mDnses.keySet(); }
+        Set<Inet6Address> getDnses() {
+            return mDnses.keySet();
+        }
 
         void putDnses(Set<Inet6Address> dnses) {
             for (Inet6Address dns : dnses) {
@@ -207,7 +214,9 @@
             }
         }
 
-        boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
+        boolean isEmpty() {
+            return mPrefixes.isEmpty() && mDnses.isEmpty();
+        }
 
         private boolean decrementCounters() {
             boolean removed = decrementCounter(mPrefixes);
@@ -219,7 +228,7 @@
             boolean removed = false;
 
             for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
-                 it.hasNext();) {
+                    it.hasNext();) {
                 Map.Entry<T, Integer> kv = it.next();
                 if (kv.getValue() == 0) {
                     it.remove();
@@ -240,6 +249,7 @@
         mDeprecatedInfoTracker = new DeprecatedInfoTracker();
     }
 
+    /** Build new RA.*/
     public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
         synchronized (mLock) {
             if (deprecatedParams != null) {
@@ -260,6 +270,7 @@
         maybeNotifyMulticastTransmitter();
     }
 
+    /** Start router advertisement daemon. */
     public boolean start() {
         if (!createSocket()) {
             return false;
@@ -274,6 +285,7 @@
         return true;
     }
 
+    /** Stop router advertisement daemon. */
     public void stop() {
         closeSocket();
         // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
@@ -362,8 +374,12 @@
         }
     }
 
-    private static byte asByte(int value) { return (byte) value; }
-    private static short asShort(int value) { return (short) value; }
+    private static byte asByte(int value) {
+        return (byte) value;
+    }
+    private static short asShort(int value) {
+        return (short) value;
+    }
 
     private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
         /**
@@ -384,14 +400,14 @@
             +-+-+-+-+-+-+-+-+-+-+-+-
         */
         ra.put(ICMPV6_ND_ROUTER_ADVERT)
-          .put(asByte(0))
-          .putShort(asShort(0))
-          .put(hopLimit)
-          // RFC 4191 "high" preference, iff. advertising a default route.
-          .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
-          .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
-          .putInt(0)
-          .putInt(0);
+                .put(asByte(0))
+                .putShort(asShort(0))
+                .put(hopLimit)
+                // RFC 4191 "high" preference, iff. advertising a default route.
+                .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
+                .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
+                .putInt(0)
+                .putInt(0);
     }
 
     private static void putSlla(ByteBuffer ra, byte[] slla) {
@@ -408,11 +424,12 @@
             // Only IEEE 802.3 6-byte addresses are supported.
             return;
         }
-        final byte ND_OPTION_SLLA = 1;
-        final byte SLLA_NUM_8OCTETS = 1;
-        ra.put(ND_OPTION_SLLA)
-          .put(SLLA_NUM_8OCTETS)
-          .put(slla);
+
+        final byte nd_option_slla = 1;
+        final byte slla_num_8octets = 1;
+        ra.put(nd_option_slla)
+            .put(slla_num_8octets)
+            .put(slla);
     }
 
     private static void putExpandedFlagsOption(ByteBuffer ra) {
@@ -428,13 +445,13 @@
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
          */
 
-        final byte ND_OPTION_EFO = 26;
-        final byte EFO_NUM_8OCTETS = 1;
+        final byte nd_option__efo = 26;
+        final byte efo_num_8octets = 1;
 
-        ra.put(ND_OPTION_EFO)
-          .put(EFO_NUM_8OCTETS)
-          .putShort(asShort(0))
-          .putInt(0);
+        ra.put(nd_option__efo)
+            .put(efo_num_8octets)
+            .putShort(asShort(0))
+            .putInt(0);
     }
 
     private static void putMtu(ByteBuffer ra, int mtu) {
@@ -449,12 +466,12 @@
             |                              MTU                              |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */
-        final byte ND_OPTION_MTU = 5;
-        final byte MTU_NUM_8OCTETS = 1;
-        ra.put(ND_OPTION_MTU)
-          .put(MTU_NUM_8OCTETS)
-          .putShort(asShort(0))
-          .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
+        final byte nd_option_mtu = 5;
+        final byte mtu_num_8octs = 1;
+        ra.put(nd_option_mtu)
+            .put(mtu_num_8octs)
+            .putShort(asShort(0))
+            .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
     }
 
     private static void putPio(ByteBuffer ra, IpPrefix ipp,
@@ -486,22 +503,22 @@
         if (prefixLength != 64) {
             return;
         }
-        final byte ND_OPTION_PIO = 3;
-        final byte PIO_NUM_8OCTETS = 4;
+        final byte nd_option_pio = 3;
+        final byte pio_num_8octets = 4;
 
         if (validTime < 0) validTime = 0;
         if (preferredTime < 0) preferredTime = 0;
         if (preferredTime > validTime) preferredTime = validTime;
 
         final byte[] addr = ipp.getAddress().getAddress();
-        ra.put(ND_OPTION_PIO)
-          .put(PIO_NUM_8OCTETS)
-          .put(asByte(prefixLength))
-          .put(asByte(0xc0)) /* L & A set */
-          .putInt(validTime)
-          .putInt(preferredTime)
-          .putInt(0)
-          .put(addr);
+        ra.put(nd_option_pio)
+            .put(pio_num_8octets)
+            .put(asByte(prefixLength))
+            .put(asByte(0xc0)) /* L & A set */
+            .putInt(validTime)
+            .putInt(preferredTime)
+            .putInt(0)
+            .put(addr);
     }
 
     private static void putRio(ByteBuffer ra, IpPrefix ipp) {
@@ -524,16 +541,16 @@
         if (prefixLength > 64) {
             return;
         }
-        final byte ND_OPTION_RIO = 24;
-        final byte RIO_NUM_8OCTETS = asByte(
+        final byte nd_option_rio = 24;
+        final byte rio_num_8octets = asByte(
                 (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
 
         final byte[] addr = ipp.getAddress().getAddress();
-        ra.put(ND_OPTION_RIO)
-          .put(RIO_NUM_8OCTETS)
-          .put(asByte(prefixLength))
-          .put(asByte(0x18))
-          .putInt(DEFAULT_LIFETIME);
+        ra.put(nd_option_rio)
+            .put(rio_num_8octets)
+            .put(asByte(prefixLength))
+            .put(asByte(0x18))
+            .putInt(DEFAULT_LIFETIME);
 
         // Rely upon an IpPrefix's address being properly zeroed.
         if (prefixLength > 0) {
@@ -566,12 +583,12 @@
         }
         if (filteredDnses.isEmpty()) return;
 
-        final byte ND_OPTION_RDNSS = 25;
-        final byte RDNSS_NUM_8OCTETS = asByte(dnses.size() * 2 + 1);
-        ra.put(ND_OPTION_RDNSS)
-          .put(RDNSS_NUM_8OCTETS)
-          .putShort(asShort(0))
-          .putInt(lifetime);
+        final byte nd_option_rdnss = 25;
+        final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1);
+        ra.put(nd_option_rdnss)
+            .put(rdnss_num_8octets)
+            .putShort(asShort(0))
+            .putInt(lifetime);
 
         for (Inet6Address dns : filteredDnses) {
             // NOTE: If the full of list DNS servers doesn't fit in the packet,
@@ -585,7 +602,7 @@
     }
 
     private boolean createSocket() {
-        final int SEND_TIMEOUT_MS = 300;
+        final int send_timout_ms = 300;
 
         final int oldTag = TrafficStats.getAndSetThreadStatsTag(
                 TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR);
@@ -593,7 +610,7 @@
             mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
             // Setting SNDTIMEO is purely for defensive purposes.
             Os.setsockoptTimeval(
-                    mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
+                    mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
             Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
             NetworkUtils.protectFromVpn(mSocket);
             NetworkUtils.setupRaSocket(mSocket, mInterface.index);
@@ -611,7 +628,7 @@
         if (mSocket != null) {
             try {
                 IoBridge.closeAndSignalBlockedThreads(mSocket);
-            } catch (IOException ignored) {}
+            } catch (IOException ignored) { }
         }
         mSocket = null;
     }
@@ -627,9 +644,9 @@
         }
 
         final InetAddress destip = dest.getAddress();
-        return (destip instanceof Inet6Address) &&
-                destip.isLinkLocalAddress() &&
-               (((Inet6Address) destip).getScopeId() == mInterface.index);
+        return (destip instanceof Inet6Address)
+               && destip.isLinkLocalAddress()
+               && (((Inet6Address) destip).getScopeId() == mInterface.index);
     }
 
     private void maybeSendRA(InetSocketAddress dest) {
@@ -654,11 +671,11 @@
     }
 
     private final class UnicastResponder extends Thread {
-        private final InetSocketAddress solicitor = new InetSocketAddress();
+        private final InetSocketAddress mSolicitor = new InetSocketAddress();
         // The recycled buffer for receiving Router Solicitations from clients.
         // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
         // This is fine since currently only byte 0 is examined anyway.
-        private final byte mSolication[] = new byte[IPV6_MIN_MTU];
+        private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
 
         @Override
         public void run() {
@@ -666,9 +683,9 @@
                 try {
                     // Blocking receive.
                     final int rval = Os.recvfrom(
-                            mSocket, mSolication, 0, mSolication.length, 0, solicitor);
+                            mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
                     // Do the least possible amount of validation.
-                    if (rval < 1 || mSolication[0] != ICMPV6_ND_ROUTER_SOLICIT) {
+                    if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) {
                         continue;
                     }
                 } catch (ErrnoException | SocketException e) {
@@ -678,7 +695,7 @@
                     continue;
                 }
 
-                maybeSendRA(solicitor);
+                maybeSendRA(mSolicitor);
             }
         }
     }
diff --git a/services/net/java/android/net/util/InterfaceSet.java b/packages/Tethering/src/android/net/util/InterfaceSet.java
similarity index 95%
rename from services/net/java/android/net/util/InterfaceSet.java
rename to packages/Tethering/src/android/net/util/InterfaceSet.java
index 9f26fa1..7589787 100644
--- a/services/net/java/android/net/util/InterfaceSet.java
+++ b/packages/Tethering/src/android/net/util/InterfaceSet.java
@@ -47,6 +47,6 @@
     public boolean equals(Object obj) {
         return obj != null
                 && obj instanceof InterfaceSet
-                && ifnames.equals(((InterfaceSet)obj).ifnames);
+                && ifnames.equals(((InterfaceSet) obj).ifnames);
     }
 }
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 089bbd3..da62107 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -43,5 +43,8 @@
     name: "tethering-tests-src",
     srcs: [
         "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
+        "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
+        "src/android/net/ip/IpServerTest.java",
+        "src/android/net/util/InterfaceSetTest.java",
     ],
 }
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
similarity index 100%
rename from tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
rename to packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
similarity index 99%
rename from tests/net/java/android/net/ip/IpServerTest.java
rename to packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index b6ccebb..4358cd6 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -183,7 +183,7 @@
     @Test
     public void shouldDoNothingUntilRequested() throws Exception {
         initStateMachine(TETHERING_BLUETOOTH);
-        final int [] NOOP_COMMANDS = {
+        final int [] noOp_commands = {
             IpServer.CMD_TETHER_UNREQUESTED,
             IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
             IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
@@ -192,7 +192,7 @@
             IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
             IpServer.CMD_TETHER_CONNECTION_CHANGED
         };
-        for (int command : NOOP_COMMANDS) {
+        for (int command : noOp_commands) {
             // None of these commands should trigger us to request action from
             // the rest of the system.
             dispatchCommand(command);
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
similarity index 100%
rename from tests/net/java/android/net/util/InterfaceSetTest.java
rename to packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ca8e11a..3067beb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -80,7 +80,6 @@
         ":vold_aidl",
         ":gsiservice_aidl",
         ":platform-compat-config",
-        ":tethering-services-srcs",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/policy/EventLogTags.logtags",
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
similarity index 91%
rename from core/java/android/app/usage/UsageStatsManagerInternal.java
rename to services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 024afe2..6641b5b 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -23,6 +23,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
+
 import java.util.List;
 import java.util.Set;
 
@@ -153,35 +155,6 @@
      */
     public abstract int[] getIdleUidsForUser(@UserIdInt int userId);
 
-    /**
-     * Sets up a listener for changes to packages being accessed.
-     * @param listener A listener within the system process.
-     */
-    public abstract void addAppIdleStateChangeListener(
-            AppIdleStateChangeListener listener);
-
-    /**
-     * Removes a listener that was previously added for package usage state changes.
-     * @param listener The listener within the system process to remove.
-     */
-    public abstract void removeAppIdleStateChangeListener(
-            AppIdleStateChangeListener listener);
-
-    public static abstract class AppIdleStateChangeListener {
-
-        /** Callback to inform listeners that the idle state has changed to a new bucket. */
-        public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
-                boolean idle, int bucket, int reason);
-
-        /**
-         * Optional callback to inform the listener that the app has transitioned into
-         * an active state due to user interaction.
-         */
-        public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
-            // No-op by default
-        }
-    }
-
     /**  Backup/Restore API */
     public abstract byte[] getBackupPayload(@UserIdInt int userId, String key);
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index b41e95f..ff0044f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -99,6 +99,8 @@
 import com.android.internal.util.LocalLog;
 import com.android.internal.util.StatLogger;
 import com.android.server.AppStateTracker.Listener;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
@@ -1599,7 +1601,9 @@
                         LocalServices.getService(DeviceIdleInternal.class);
                 mUsageStatsManagerInternal =
                         LocalServices.getService(UsageStatsManagerInternal.class);
-                mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
+                AppStandbyInternal appStandbyInternal =
+                        LocalServices.getService(AppStandbyInternal.class);
+                appStandbyInternal.addListener(new AppStandbyTracker());
 
                 mAppStateTracker = LocalServices.getService(AppStateTracker.class);
                 mAppStateTracker.addListener(mForceAppStandbyListener);
@@ -4468,7 +4472,7 @@
      * Tracking of app assignments to standby buckets
      */
     private final class AppStandbyTracker extends
-            UsageStatsManagerInternal.AppIdleStateChangeListener {
+            AppIdleStateChangeListener {
         @Override
         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
                 boolean idle, int bucket, int reason) {
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index da760b6..5eff2c5 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -24,7 +24,6 @@
 import android.app.IUidObserver;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -58,6 +57,8 @@
 import com.android.internal.util.StatLogger;
 import com.android.server.AppStateTrackerProto.ExemptedPackage;
 import com.android.server.AppStateTrackerProto.RunAnyInBackgroundRestrictedPackages;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -89,7 +90,7 @@
     IAppOpsService mAppOpsService;
     PowerManagerInternal mPowerManagerInternal;
     StandbyTracker mStandbyTracker;
-    UsageStatsManagerInternal mUsageStatsManagerInternal;
+    AppStandbyInternal mAppStandbyInternal;
 
     private final MyHandler mHandler;
 
@@ -420,8 +421,7 @@
             mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
             mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
             mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
-            mUsageStatsManagerInternal = Preconditions.checkNotNull(
-                    injectUsageStatsManagerInternal());
+            mAppStandbyInternal = Preconditions.checkNotNull(injectAppStandbyInternal());
 
             mFlagsObserver = new FeatureFlagsObserver();
             mFlagsObserver.register();
@@ -429,7 +429,7 @@
             mForceAllAppStandbyForSmallBattery =
                     mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
             mStandbyTracker = new StandbyTracker();
-            mUsageStatsManagerInternal.addAppIdleStateChangeListener(mStandbyTracker);
+            mAppStandbyInternal.addListener(mStandbyTracker);
 
             try {
                 mIActivityManager.registerUidObserver(new UidObserver(),
@@ -494,8 +494,8 @@
     }
 
     @VisibleForTesting
-    UsageStatsManagerInternal injectUsageStatsManagerInternal() {
-        return LocalServices.getService(UsageStatsManagerInternal.class);
+    AppStandbyInternal injectAppStandbyInternal() {
+        return LocalServices.getService(AppStandbyInternal.class);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 18009e1..190e6cf 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -115,7 +115,8 @@
     }
 
     @Override
-    public boolean startInstallation(long systemSize, long userdataSize) throws RemoteException {
+    public boolean startInstallation(String name, long size, boolean readOnly)
+            throws RemoteException {
         // priority from high to low: sysprop -> sdcard -> /data
         String path = SystemProperties.get("os.aot.path");
         if (path.isEmpty()) {
@@ -137,11 +138,18 @@
             }
             Slog.i(TAG, "startInstallation -> " + path);
         }
+        IGsiService service = getGsiService();
         GsiInstallParams installParams = new GsiInstallParams();
         installParams.installDir = path;
-        installParams.gsiSize = systemSize;
-        installParams.userdataSize = userdataSize;
-        return getGsiService().beginGsiInstall(installParams) == 0;
+        installParams.name = name;
+        installParams.size = size;
+        installParams.wipe = readOnly;
+        installParams.readOnly = readOnly;
+        if (service.beginGsiInstall(installParams) != 0) {
+            Slog.i(TAG, "Failed to install " + name);
+            return false;
+        }
+        return true;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 087c84f..67d3589 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -23,7 +23,6 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
 import android.content.BroadcastReceiver;
@@ -300,7 +299,8 @@
     @GuardedBy("mLock")
     @VisibleForTesting
     protected UserState getOrCreateCurrentUserStateLocked() {
-        return getOrCreateUserStateLocked(ActivityManager.getCurrentUser());
+        // Doesn't need to cache the states of different users.
+        return getOrCreateUserStateLocked(0);
     }
 
     @GuardedBy("mLock")
@@ -318,7 +318,8 @@
     @Nullable
     @VisibleForTesting
     protected UserState peekCurrentUserStateLocked() {
-        return peekUserStateLocked(ActivityManager.getCurrentUser());
+        // Doesn't need to cache the states of different users.
+        return peekUserStateLocked(0);
     }
 
     @GuardedBy("mLock")
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
similarity index 100%
rename from core/java/com/android/server/backup/SystemBackupAgent.java
rename to services/core/java/com/android/server/backup/SystemBackupAgent.java
diff --git a/core/java/com/android/server/backup/UsageStatsBackupHelper.java b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
similarity index 100%
rename from core/java/com/android/server/backup/UsageStatsBackupHelper.java
rename to services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 09be474..3b14d50 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -229,6 +229,8 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import libcore.io.IoUtils;
 
@@ -396,6 +398,7 @@
     private NetworkStatsManagerInternal mNetworkStats;
     private final INetworkManagementService mNetworkManager;
     private UsageStatsManagerInternal mUsageStats;
+    private AppStandbyInternal mAppStandby;
     private final Clock mClock;
     private final UserManager mUserManager;
     private final CarrierConfigManager mCarrierConfigManager;
@@ -734,6 +737,7 @@
             }
 
             mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+            mAppStandby = LocalServices.getService(AppStandbyInternal.class);
             mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
 
             synchronized (mUidRulesFirstLock) {
@@ -868,7 +872,7 @@
             mContext.getSystemService(ConnectivityManager.class).registerNetworkCallback(
                     new NetworkRequest.Builder().build(), mNetworkCallback);
 
-            mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
+            mAppStandby.addListener(new NetPolicyAppIdleStateChangeListener());
 
             // Listen for subscriber changes
             mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
@@ -4375,9 +4379,7 @@
         return newUidRules;
     }
 
-    private class AppIdleStateChangeListener
-            extends UsageStatsManagerInternal.AppIdleStateChangeListener {
-
+    private class NetPolicyAppIdleStateChangeListener extends AppIdleStateChangeListener {
         @Override
         public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
                 int reason) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index fd44cbb..12e8069 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,6 +32,7 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.sysprop.ApexProperties;
@@ -47,6 +48,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -64,9 +66,9 @@
     static final int MATCH_FACTORY_PACKAGE = 1 << 1;
 
     /**
-     * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerNoOp} depending
-     * on whenever this device supports APEX, i.e. {@link ApexProperties#updatable()} evaluates to
-     * {@code true}.
+     * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
+     * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
+     * evaluates to {@code true}.
      */
     static ApexManager create(Context systemContext) {
         if (ApexProperties.updatable().orElse(false)) {
@@ -77,10 +79,28 @@
                 throw new IllegalStateException("Required service apexservice not available");
             }
         } else {
-            return new ApexManagerNoOp();
+            return new ApexManagerFlattenedApex();
         }
     }
 
+    /**
+     * Minimal information about APEX mount points and the original APEX package they refer to.
+     */
+    static class ActiveApexInfo {
+        public final File apexDirectory;
+        public final File preinstalledApexPath;
+
+        private ActiveApexInfo(File apexDirectory, File preinstalledApexPath) {
+            this.apexDirectory = apexDirectory;
+            this.preinstalledApexPath = preinstalledApexPath;
+        }
+    }
+
+    /**
+     * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
+     */
+    abstract List<ActiveApexInfo> getActiveApexInfos();
+
     abstract void systemReady();
 
     /**
@@ -259,6 +279,22 @@
         }
 
         @Override
+        List<ActiveApexInfo> getActiveApexInfos() {
+            try {
+                return Arrays.stream(mApexService.getActivePackages())
+                        .map(apexInfo -> new ActiveApexInfo(
+                                new File(
+                                Environment.getApexDirectory() + File.separator
+                                        + apexInfo.moduleName),
+                                new File(apexInfo.modulePath))).collect(
+                                Collectors.toList());
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
+            }
+            return Collections.emptyList();
+        }
+
+        @Override
         void systemReady() {
             mContext.registerReceiver(new BroadcastReceiver() {
                 @Override
@@ -549,7 +585,40 @@
      * An implementation of {@link ApexManager} that should be used in case device does not support
      * updating APEX packages.
      */
-    private static final class ApexManagerNoOp extends ApexManager {
+    private static final class ApexManagerFlattenedApex extends ApexManager {
+
+        @Override
+        List<ActiveApexInfo> getActiveApexInfos() {
+            // There is no apexd running in case of flattened apex
+            // We look up the /apex directory and identify the active APEX modules from there.
+            // As "preinstalled" path, we just report /system since in the case of flattened APEX
+            // the /apex directory is just a symlink to /system/apex.
+            List<ActiveApexInfo> result = new ArrayList<>();
+            File apexDir = Environment.getApexDirectory();
+            // In flattened configuration, init special-case the art directory and bind-mounts
+            // com.android.art.{release|debug} to com.android.art. At the time of writing, these
+            // directories are copied from the kArtApexDirNames variable in
+            // system/core/init/mount_namespace.cpp.
+            String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
+            if (apexDir.isDirectory()) {
+                File[] files = apexDir.listFiles();
+                // listFiles might be null if system server doesn't have permission to read
+                // a directory.
+                if (files != null) {
+                    for (File file : files) {
+                        if (file.isDirectory() && !file.getName().contains("@")) {
+                            for (String skipDir : skipDirs) {
+                                if (file.getName().equals(skipDir)) {
+                                    continue;
+                                }
+                            }
+                            result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
+                        }
+                    }
+                }
+            }
+            return result;
+        }
 
         @Override
         void systemReady() {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 1d3d24c..259200b 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -67,6 +67,15 @@
             codeRoot = Environment.getSystemExtDirectory();
         } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
             codeRoot = Environment.getOdmDirectory();
+        } else if (FileUtils.contains(Environment.getApexDirectory(), codePath)) {
+            String fullPath = codePath.getAbsolutePath();
+            String[] parts = fullPath.split(File.separator);
+            if (parts.length > 2) {
+                codeRoot = new File(parts[1] + File.separator + parts[2]);
+            } else {
+                Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath);
+                codeRoot = Environment.getApexDirectory();
+            }
         } else {
             // Unrecognized code path; take its top real segment as the apk root:
             // e.g. /something/app/blah.apk => /something
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b5fa80d1..74a85d5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -370,6 +370,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -762,6 +763,8 @@
                     new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT,
                             true /* hasPriv */, true /* hasOverlays */)));
 
+    private final List<SystemPartition> mDirsToScanAsSystem;
+
     /**
      * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
      *
@@ -2552,6 +2555,16 @@
         mApexManager = ApexManager.create(mContext);
         mAppsFilter = mInjector.getAppsFilter();
 
+        mDirsToScanAsSystem = new ArrayList<>();
+        mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
+        mDirsToScanAsSystem.addAll(mApexManager.getActiveApexInfos().stream()
+                .map(ai -> resolveApexToSystemPartition(ai))
+                .filter(Objects::nonNull).collect(Collectors.toList()));
+        Slog.d(TAG,
+                "Directories scanned as system partitions: [" + mDirsToScanAsSystem.stream().map(
+                        d -> (d.folder.getAbsolutePath() + ":" + d.scanFlag))
+                        .collect(Collectors.joining(",")) + "]");
+
         // CHECKSTYLE:OFF IndentationCheck
         synchronized (mInstallLock) {
         // writer
@@ -2684,8 +2697,8 @@
             // reside in the right directory.
             final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
             final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
-            for (int i = SYSTEM_PARTITIONS.size() - 1; i >= 0; i--) {
-                final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+            for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
+                final SystemPartition partition = mDirsToScanAsSystem.get(i);
                 if (partition.overlayFolder == null) {
                     continue;
                 }
@@ -2699,8 +2712,8 @@
                 throw new IllegalStateException(
                         "Failed to load frameworks package; check log for warnings");
             }
-            for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
-                final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+            for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+                final SystemPartition partition = mDirsToScanAsSystem.get(i);
                 if (partition.privAppFolder != null) {
                     scanDirTracedLI(partition.privAppFolder, systemParseFlags,
                             systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
@@ -2878,8 +2891,8 @@
 
                         @ParseFlags int reparseFlags = 0;
                         @ScanFlags int rescanFlags = 0;
-                        for (int i1 = 0, size = SYSTEM_PARTITIONS.size(); i1 < size; i1++) {
-                            SystemPartition partition = SYSTEM_PARTITIONS.get(i1);
+                        for (int i1 = 0, size = mDirsToScanAsSystem.size(); i1 < size; i1++) {
+                            SystemPartition partition = mDirsToScanAsSystem.get(i1);
                             if (partition.containsPrivApp(scanFile)) {
                                 reparseFlags = systemParseFlags;
                                 rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
@@ -17778,6 +17791,7 @@
     }
 
     static boolean locationIsPrivileged(String path) {
+        // TODO(dariofreni): include APEX partitions when they will support priv apps.
         for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
             SystemPartition partition = SYSTEM_PARTITIONS.get(i);
             if (partition.containsPrivPath(path)) {
@@ -17787,6 +17801,18 @@
         return false;
     }
 
+    private static @Nullable SystemPartition resolveApexToSystemPartition(
+            ApexManager.ActiveApexInfo apexInfo) {
+        for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+            SystemPartition sp = SYSTEM_PARTITIONS.get(i);
+            if (apexInfo.preinstalledApexPath.getAbsolutePath().startsWith(
+                    sp.folder.getAbsolutePath())) {
+                return new SystemPartition(apexInfo.apexDirectory, sp.scanFlag,
+                        false /* hasPriv */, false /* hasOverlays */);
+            }
+        }
+        return null;
+    }
 
     /*
      * Tries to delete system package.
@@ -17897,8 +17923,8 @@
                 | PackageParser.PARSE_MUST_BE_APK
                 | PackageParser.PARSE_IS_SYSTEM_DIR;
         @ScanFlags int scanFlags = SCAN_AS_SYSTEM;
-        for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
-            SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+        for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+            SystemPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.containsPath(codePathString)) {
                 scanFlags |= partition.scanFlag;
                 if (partition.containsPrivPath(codePathString)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 59996cc..5a1d552 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -62,4 +62,6 @@
             String packageName, boolean hasGrant) {
         return false;
     }
+
+    public void setLocationEnabled(ComponentName who, boolean locationEnabled) {}
 }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 6a871aa..08cdbfc 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,6 +1,9 @@
 java_library_static {
     name: "services.net",
-    srcs: ["java/**/*.java"],
+    srcs: [
+        ":tethering-services-srcs",
+        "java/**/*.java",
+    ],
     static_libs: [
         "dnsresolver_aidl_interface-V2-java",
         "netd_aidl_interface-java",
@@ -23,6 +26,12 @@
     name: "services-tethering-shared-srcs",
     srcs: [
         ":framework-annotations",
+        "java/android/net/ConnectivityModuleConnector.java",
+        "java/android/net/NetworkStackClient.java",
+        "java/android/net/ip/InterfaceController.java",
+        "java/android/net/util/InterfaceParams.java",
+        "java/android/net/util/NetdService.java",
+        "java/android/net/util/NetworkConstants.java",
         "java/android/net/util/SharedLog.java"
     ],
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 7b7b8e6..9e7b805 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -85,6 +85,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.usage.AppStandbyInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -108,7 +109,7 @@
 
     private long mAppStandbyWindow;
     private AlarmManagerService mService;
-    private UsageStatsManagerInternal.AppIdleStateChangeListener mAppStandbyListener;
+    private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
     private AlarmManagerService.ChargingReceiver mChargingReceiver;
     @Mock
     private ContentResolver mMockResolver;
@@ -119,6 +120,8 @@
     @Mock
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
     @Mock
+    private AppStandbyInternal mAppStandbyInternal;
+    @Mock
     private AppStateTracker mAppStateTracker;
     @Mock
     private AlarmManagerService.ClockReceiver mClockReceiver;
@@ -257,6 +260,8 @@
         doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
         doReturn(null)
                 .when(() -> LocalServices.getService(DeviceIdleInternal.class));
+        doReturn(mAppStandbyInternal).when(
+                () -> LocalServices.getService(AppStandbyInternal.class));
         doReturn(mUsageStatsManagerInternal).when(
                 () -> LocalServices.getService(UsageStatsManagerInternal.class));
         when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
@@ -289,9 +294,9 @@
         assertEquals(0, mService.mConstants.MIN_FUTURITY);
         assertEquals(0, mService.mConstants.MIN_INTERVAL);
         mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
-        ArgumentCaptor<UsageStatsManagerInternal.AppIdleStateChangeListener> captor =
-                ArgumentCaptor.forClass(UsageStatsManagerInternal.AppIdleStateChangeListener.class);
-        verify(mUsageStatsManagerInternal).addAppIdleStateChangeListener(captor.capture());
+        ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
+                ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
+        verify(mAppStandbyInternal).addListener(captor.capture());
         mAppStandbyListener = captor.getValue();
 
         ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor =
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 80d1129..1f4656a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -45,7 +45,6 @@
 import android.app.IUidObserver;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -70,6 +69,8 @@
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.server.AppStateTracker.Listener;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -128,8 +129,8 @@
         }
 
         @Override
-        UsageStatsManagerInternal injectUsageStatsManagerInternal() {
-            return mMockUsageStatsManagerInternal;
+        AppStandbyInternal injectAppStandbyInternal() {
+            return mMockAppStandbyInternal;
         }
 
         @Override
@@ -175,7 +176,7 @@
     private PowerManagerInternal mMockPowerManagerInternal;
 
     @Mock
-    private UsageStatsManagerInternal mMockUsageStatsManagerInternal;
+    private AppStandbyInternal mMockAppStandbyInternal;
 
     private MockContentResolver mMockContentResolver;
 
@@ -271,7 +272,7 @@
 
         verify(mMockContext).registerReceiver(
                 receiverCaptor.capture(), any(IntentFilter.class));
-        verify(mMockUsageStatsManagerInternal).addAppIdleStateChangeListener(
+        verify(mMockAppStandbyInternal).addListener(
                 appIdleStateChangeListenerCaptor.capture());
 
         mIUidObserver = uidObserverArgumentCaptor.getValue();
diff --git a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
index 4a9dd97..0605d9e 100644
--- a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
@@ -36,7 +36,7 @@
     public void test1() {
         assertTrue("dynamic_system service available", mService != null);
         try {
-            mService.startInstallation(1 << 20, 8 << 30);
+            mService.startInstallation("userdata", 8L << 30, false);
             fail("DynamicSystemService did not throw SecurityException as expected");
         } catch (SecurityException e) {
             // expected
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 5d03e151..6a80568 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -86,6 +86,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -180,8 +181,8 @@
         }
     }
 
-    private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
-            new UsageStatsManagerInternal.AppIdleStateChangeListener() {
+    private AppIdleStateChangeListener mStandbyChangeListener =
+            new AppIdleStateChangeListener() {
                 @Override
                 public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
                         int bucket, int reason) {
@@ -253,6 +254,7 @@
                 null, mHandler);
 
         publishLocalService(UsageStatsManagerInternal.class, new LocalService());
+        publishLocalService(AppStandbyInternal.class, mAppStandby);
         publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
     }
 
@@ -2029,17 +2031,6 @@
         }
 
         @Override
-        public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
-            mAppStandby.addListener(listener);
-        }
-
-        @Override
-        public void removeAppIdleStateChangeListener(
-                AppIdleStateChangeListener listener) {
-            mAppStandby.removeListener(listener);
-        }
-
-        @Override
         public byte[] getBackupPayload(int user, String key) {
             synchronized (mLock) {
                 if (!mUserUnlockedStates.get(user)) {
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
index ef9ee73..1e46f98 100644
--- a/services/usb/java/com/android/server/usb/UsbPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -20,14 +20,20 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.usb.UsbSettingsManagerProto;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.util.List;
 
 class UsbPermissionManager {
     private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
@@ -112,4 +118,18 @@
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
+    void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+        long token = dump.start(idName, id);
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        synchronized (mPermissionsByUser) {
+            List<UserInfo> users = userManager.getUsers();
+            int numUsers = users.size();
+            for (int i = 0; i < numUsers; i++) {
+                getPermissionsForUser(users.get(i).id).dump(dump, "user_permissions",
+                        UsbSettingsManagerProto.USER_SETTINGS);
+            }
+        }
+        dump.end(token);
+    }
+
 }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index ce6f592..0493637 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -740,6 +740,8 @@
 
                 mSettingsManager.dump(dump, "settings_manager",
                         UsbServiceDumpProto.SETTINGS_MANAGER);
+                mPermissionManager.dump(dump, "permissions_manager",
+                        UsbServiceDumpProto.PERMISSIONS_MANAGER);
                 dump.flush();
             } else if ("set-port-roles".equals(args[0]) && args.length == 4) {
                 final String portId = args[1];
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 0cb64a3..e700f19 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -36,9 +36,12 @@
 import android.os.Environment;
 import android.os.Process;
 import android.os.UserHandle;
-import android.service.usb.UsbSettingsAccessoryPermissionProto;
-import android.service.usb.UsbSettingsDevicePermissionProto;
-import android.service.usb.UsbUserSettingsManagerProto;
+import android.service.usb.UsbAccessoryPermissionProto;
+import android.service.usb.UsbAccessoryPersistentPermissionProto;
+import android.service.usb.UsbDevicePermissionProto;
+import android.service.usb.UsbDevicePersistentPermissionProto;
+import android.service.usb.UsbUidPermissionProto;
+import android.service.usb.UsbUserPermissionsManagerProto;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -261,7 +264,7 @@
             }
 
             if (isChanged) {
-                scheduleWritePermissionLocked();
+                scheduleWritePermissionsLocked();
             }
         }
     }
@@ -288,7 +291,7 @@
             }
 
             if (isChanged) {
-                scheduleWritePermissionLocked();
+                scheduleWritePermissionsLocked();
             }
         }
     }
@@ -370,7 +373,7 @@
     }
 
     @GuardedBy("mLock")
-    private void scheduleWritePermissionLocked() {
+    private void scheduleWritePermissionsLocked() {
         if (mIsCopyPermissionsScheduled) {
             return;
         }
@@ -393,15 +396,18 @@
                 devices = new DeviceFilter[numDevices];
                 uidsForDevices = new int[numDevices][];
                 grantedValuesForDevices = new boolean[numDevices][];
-                for (int i = 0; i < numDevices; i++) {
-                    devices[i] = new DeviceFilter(mDevicePersistentPermissionMap.keyAt(i));
-                    SparseBooleanArray permissions = mDevicePersistentPermissionMap.valueAt(i);
+                for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) {
+                    devices[deviceIdx] =
+                            new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx));
+                    SparseBooleanArray permissions =
+                            mDevicePersistentPermissionMap.valueAt(deviceIdx);
                     int numPermissions = permissions.size();
-                    uidsForDevices[i] = new int[numPermissions];
-                    grantedValuesForDevices[i] = new boolean[numPermissions];
-                    for (int j = 0; j < numPermissions; j++) {
-                        uidsForDevices[i][j] = permissions.keyAt(j);
-                        grantedValuesForDevices[i][j] = permissions.valueAt(j);
+                    uidsForDevices[deviceIdx] = new int[numPermissions];
+                    grantedValuesForDevices[deviceIdx] = new boolean[numPermissions];
+                    for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
+                        uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx);
+                        grantedValuesForDevices[deviceIdx][permissionIdx] =
+                                permissions.valueAt(permissionIdx);
                     }
                 }
 
@@ -409,16 +415,19 @@
                 accessories = new AccessoryFilter[numAccessories];
                 uidsForAccessories = new int[numAccessories][];
                 grantedValuesForAccessories = new boolean[numAccessories][];
-                for (int i = 0; i < numAccessories; i++) {
-                    accessories[i] =
-                            new AccessoryFilter(mAccessoryPersistentPermissionMap.keyAt(i));
-                    SparseBooleanArray permissions = mAccessoryPersistentPermissionMap.valueAt(i);
+                for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) {
+                    accessories[accessoryIdx] = new AccessoryFilter(
+                                    mAccessoryPersistentPermissionMap.keyAt(accessoryIdx));
+                    SparseBooleanArray permissions =
+                            mAccessoryPersistentPermissionMap.valueAt(accessoryIdx);
                     int numPermissions = permissions.size();
-                    uidsForAccessories[i] = new int[numPermissions];
-                    grantedValuesForAccessories[i] = new boolean[numPermissions];
-                    for (int j = 0; j < numPermissions; j++) {
-                        uidsForAccessories[i][j] = permissions.keyAt(j);
-                        grantedValuesForAccessories[i][j] = permissions.valueAt(j);
+                    uidsForAccessories[accessoryIdx] = new int[numPermissions];
+                    grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions];
+                    for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
+                        uidsForAccessories[accessoryIdx][permissionIdx] =
+                                permissions.keyAt(permissionIdx);
+                        grantedValuesForAccessories[accessoryIdx][permissionIdx] =
+                                permissions.valueAt(permissionIdx);
                     }
                 }
                 mIsCopyPermissionsScheduled = false;
@@ -477,22 +486,22 @@
      * Creates UI dialog to request permission for the given package to access the device
      * or accessory.
      *
-     * @param device The USB device attached
-     * @param accessory The USB accessory attached
+     * @param device       The USB device attached
+     * @param accessory    The USB accessory attached
      * @param canBeDefault Whether the calling pacakge can set as default handler
-     * of the USB device or accessory
-     * @param packageName The package name of the calling package
-     * @param uid The uid of the calling package
-     * @param userContext The context to start the UI dialog
-     * @param pi PendingIntent for returning result
+     *                     of the USB device or accessory
+     * @param packageName  The package name of the calling package
+     * @param uid          The uid of the calling package
+     * @param userContext  The context to start the UI dialog
+     * @param pi           PendingIntent for returning result
      */
     void requestPermissionDialog(@Nullable UsbDevice device,
-                                 @Nullable UsbAccessory accessory,
-                                 boolean canBeDefault,
-                                 @NonNull String packageName,
-                                 int uid,
-                                 @NonNull Context userContext,
-                                 @NonNull PendingIntent pi) {
+            @Nullable UsbAccessory accessory,
+            boolean canBeDefault,
+            @NonNull String packageName,
+            int uid,
+            @NonNull Context userContext,
+            @NonNull PendingIntent pi) {
         long identity = Binder.clearCallingIdentity();
         Intent intent = new Intent();
         if (device != null) {
@@ -517,48 +526,96 @@
         }
     }
 
-    void dump(@NonNull DualDumpOutputStream dump) {
+    void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+        long token = dump.start(idName, id);
         synchronized (mLock) {
-            for (String deviceName : mDevicePermissionMap.keySet()) {
+            dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier());
+            int numMappings = mDevicePermissionMap.size();
+            for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+                String deviceName = mDevicePermissionMap.keyAt(mappingsIdx);
                 long devicePermissionToken = dump.start("device_permissions",
-                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+                        UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS);
 
-                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+                dump.write("device_name", UsbDevicePermissionProto.DEVICE_NAME, deviceName);
 
-                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
+                SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx);
+                int numUids = uidList.size();
+                for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
+                    dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx));
                 }
 
                 dump.end(devicePermissionToken);
             }
 
-            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+            numMappings = mAccessoryPermissionMap.size();
+            for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) {
+                UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx);
                 long accessoryPermissionToken = dump.start("accessory_permissions",
-                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+                        UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS);
 
                 dump.write("accessory_description",
-                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+                        UsbAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
                         accessory.getDescription());
 
-                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
+                SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx);
+                int numUids = uidList.size();
+                for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
+                    dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx));
                 }
 
                 dump.end(accessoryPermissionToken);
             }
+
+            numMappings = mDevicePersistentPermissionMap.size();
+            for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+                DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx);
+                long devicePermissionToken = dump.start("device_persistent_permissions",
+                        UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS);
+                filter.dump(dump, "device",
+                        UsbDevicePersistentPermissionProto.DEVICE_FILTER);
+                SparseBooleanArray permissions =
+                        mDevicePersistentPermissionMap.valueAt(mappingsIdx);
+                int numPermissions = permissions.size();
+                for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
+                    long uidPermissionToken = dump.start("uid_permission",
+                            UsbDevicePersistentPermissionProto.PERMISSION_VALUES);
+                    dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
+                    dump.write("is_granted",
+                            UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
+                    dump.end(uidPermissionToken);
+                }
+                dump.end(devicePermissionToken);
+            }
+
+            numMappings = mAccessoryPersistentPermissionMap.size();
+            for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+                AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx);
+                long accessoryPermissionToken = dump.start("accessory_persistent_permissions",
+                        UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS);
+                filter.dump(dump, "accessory",
+                        UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER);
+                SparseBooleanArray permissions =
+                        mAccessoryPersistentPermissionMap.valueAt(mappingsIdx);
+                int numPermissions = permissions.size();
+                for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
+                    long uidPermissionToken = dump.start("uid_permission",
+                            UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES);
+                    dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
+                    dump.write("is_granted",
+                            UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
+                    dump.end(uidPermissionToken);
+                }
+                dump.end(accessoryPermissionToken);
+            }
         }
+        dump.end(token);
     }
 
     /**
      * Check for camera permission of the calling process.
      *
      * @param packageName Package name of the caller.
-     * @param uid Linux uid of the calling process.
-     *
+     * @param uid         Linux uid of the calling process.
      * @return True in case camera permission is available, False otherwise.
      */
     private boolean isCameraPermissionGranted(String packageName, int uid) {
@@ -677,7 +734,7 @@
      *
      * @param device The device that needs to get scanned
      * @return True in case a VIDEO device or interface is present,
-     *         False otherwise.
+     * False otherwise.
      */
     private boolean isCameraDevicePresent(UsbDevice device) {
         if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
diff --git a/tests/Compatibility/Android.bp b/tests/Compatibility/Android.bp
index 4ca406e..7dc44fa 100644
--- a/tests/Compatibility/Android.bp
+++ b/tests/Compatibility/Android.bp
@@ -19,4 +19,7 @@
     srcs: ["src/**/*.java"],
     platform_apis: true,
     certificate: "platform",
+    test_suites: [
+        "csuite"
+    ],
 }
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 4ce4406..c08f9b0 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -95,7 +95,7 @@
     ],
 }
 
-cc_library_shared {
+cc_library {
     name: "libstatslog",
     host_supported: true,
     generated_sources: ["statslog.cpp"],
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1337739..19be132 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -94,6 +94,8 @@
 
     boolean disableNetwork(int netId, String packageName);
 
+    void allowAutojoin(int netId, boolean choice);
+
     boolean startScan(String packageName);
 
     List<ScanResult> getScanResults(String callingPackage);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 2afb14a..3bedddc 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -735,6 +735,14 @@
      */
     public int userApproved = USER_UNSPECIFIED;
 
+    /**
+     * @hide
+     * Auto-join is allowed by user for this network.
+     * Default true.
+     */
+    @SystemApi
+    public boolean allowAutojoin = true;
+
     /** The Below RSSI thresholds are used to configure AutoJoin
      *  - GOOD/LOW/BAD thresholds are used so as to calculate link score
      *  - UNWANTED_SOFT are used by the blacklisting logic so as to handle
@@ -2022,6 +2030,7 @@
         if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier);
         sbuf.append(" lcuid=" + lastConnectUid);
         sbuf.append(" userApproved=" + userApprovedAsString(userApproved));
+        sbuf.append(" allowAutojoin=" + allowAutojoin);
         sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
         sbuf.append(" ");
 
@@ -2421,6 +2430,7 @@
             numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
             numAssociation = source.numAssociation;
             userApproved = source.userApproved;
+            allowAutojoin = source.allowAutojoin;
             numNoInternetAccessReports = source.numNoInternetAccessReports;
             noInternetAccessExpected = source.noInternetAccessExpected;
             creationTime = source.creationTime;
@@ -2496,6 +2506,7 @@
         dest.writeInt(numScorerOverrideAndSwitchedNetwork);
         dest.writeInt(numAssociation);
         dest.writeInt(userApproved);
+        dest.writeBoolean(allowAutojoin);
         dest.writeInt(numNoInternetAccessReports);
         dest.writeInt(noInternetAccessExpected ? 1 : 0);
         dest.writeInt(shared ? 1 : 0);
@@ -2571,6 +2582,7 @@
                 config.numScorerOverrideAndSwitchedNetwork = in.readInt();
                 config.numAssociation = in.readInt();
                 config.userApproved = in.readInt();
+                config.allowAutojoin = in.readBoolean();
                 config.numNoInternetAccessReports = in.readInt();
                 config.noInternetAccessExpected = in.readInt() != 0;
                 config.shared = in.readInt() != 0;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 11c2131..f626b0c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3869,6 +3869,29 @@
     }
 
     /**
+     * Sets the user choice for allowing auto-join to a network.
+     * The updated choice will be made available through the updated config supplied by the
+     * CONFIGURED_NETWORKS_CHANGED broadcast.
+     *
+     * @param netId the id of the network to allow/disallow autojoin for.
+     * @param choice true to allow autojoin, false to disallow autojoin
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void allowAutojoin(int netId, boolean choice) {
+        try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.allowAutojoin(netId, choice);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Disable ephemeral Network
      *
      * @param SSID, in the format of WifiConfiguration's SSID.
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 94fb5ae..5e6c107 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -168,6 +168,11 @@
     }
 
     @Override
+    public void allowAutojoin(int netId, boolean choice) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean startScan(String packageName) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f8a0c8f..881dcbb 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1489,6 +1489,16 @@
     }
 
     /**
+     * Test behavior of {@link WifiManager#allowAutojoin(int, boolean)}
+     * @throws Exception
+     */
+    @Test
+    public void testAllowAutojoin() throws Exception {
+        mWifiManager.allowAutojoin(1, true);
+        verify(mWifiService).allowAutojoin(eq(1), eq(true));
+    }
+
+    /**
      * Test behavior of {@link WifiManager#disconnect()}
      * @throws Exception
      */