Merge "Check for null receiver in biometric AuthController"
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..9dcacaf 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);
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/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/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/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
new file mode 100644
index 0000000..0228dc9
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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 android.media.tv.tuner;
+
+/**
+ * Tuner is used to interact with tuner devices.
+ *
+ * @hide
+ */
+public final class Tuner implements AutoCloseable  {
+    private static final String TAG = "MediaTvTuner";
+    private static final boolean DEBUG = false;
+
+    static {
+        System.loadLibrary("media_tv_tuner");
+        nativeInit();
+    }
+
+    public Tuner() {
+        nativeSetup();
+    }
+
+    private long mNativeContext; // used by native jMediaTuner
+
+    @Override
+    public void close() {}
+
+    /**
+     * Native Initialization.
+     */
+    private static native void nativeInit();
+
+    /**
+     * Native setup.
+     */
+    private native void nativeSetup();
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index b4edabf..a596d89 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -123,6 +123,30 @@
     ],
 }
 
+cc_library_shared {
+    name: "libmedia_tv_tuner",
+    srcs: [
+        "android_media_tv_Tuner.cpp",
+    ],
+
+    shared_libs: [
+        "android.hardware.tv.tuner@1.0",
+        "libandroid_runtime",
+        "liblog",
+        "libutils",
+    ],
+
+    export_include_dirs: ["."],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
+
 subdirs = [
     "audioeffect",
     "soundpool",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
new file mode 100644
index 0000000..d499eee
--- /dev/null
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "TvTuner-JNI"
+#include <utils/Log.h>
+
+#include "android_media_tv_Tuner.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+using ::android::hardware::tv::tuner::V1_0::ITuner;
+
+struct fields_t {
+    jfieldID context;
+};
+
+static fields_t gFields;
+
+namespace android {
+
+sp<ITuner> JTuner::mTuner;
+
+JTuner::JTuner(JNIEnv *env, jobject thiz)
+    : mClass(NULL) {
+    jclass clazz = env->GetObjectClass(thiz);
+    CHECK(clazz != NULL);
+
+    mClass = (jclass)env->NewGlobalRef(clazz);
+    mObject = env->NewWeakGlobalRef(thiz);
+    if (mTuner == NULL) {
+        mTuner = getTunerService();
+    }
+}
+
+JTuner::~JTuner() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteGlobalRef(mClass);
+    mTuner = NULL;
+    mClass = NULL;
+    mObject = NULL;
+}
+
+sp<ITuner> JTuner::getTunerService() {
+    if (mTuner == nullptr) {
+        mTuner = ITuner::getService();
+
+        if (mTuner == nullptr) {
+            ALOGW("Failed to get tuner service.");
+        }
+    }
+    return mTuner;
+}
+
+}  // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
+    sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.context);
+
+    if (tuner != NULL) {
+        tuner->incStrong(thiz);
+    }
+    if (old != NULL) {
+        old->decStrong(thiz);
+    }
+    env->SetLongField(thiz, gFields.context, (jlong)tuner.get());
+
+    return old;
+}
+
+static sp<JTuner> getTuner(JNIEnv *env, jobject thiz) {
+    return (JTuner *)env->GetLongField(thiz, gFields.context);
+}
+
+static void android_media_tv_Tuner_native_init(JNIEnv *env) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
+    CHECK(clazz != NULL);
+
+    gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
+    CHECK(gFields.context != NULL);
+}
+
+static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
+    sp<JTuner> tuner = new JTuner(env, thiz);
+    setTuner(env,thiz, tuner);
+}
+
+static const JNINativeMethod gMethods[] = {
+    { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
+    { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
+};
+
+static int register_android_media_tv_Tuner(JNIEnv *env) {
+    return AndroidRuntime::registerNativeMethods(
+            env, "android/media/tv/tuner/Tuner", gMethods, NELEM(gMethods));
+}
+
+jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("ERROR: GetEnv failed\n");
+        return result;
+    }
+    assert(env != NULL);
+
+    if (register_android_media_tv_Tuner(env) != JNI_OK) {
+        ALOGE("ERROR: Tuner native registration failed\n");
+        return result;
+    }
+    return JNI_VERSION_1_4;
+}
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
new file mode 100644
index 0000000..e7d5924
--- /dev/null
+++ b/media/jni/android_media_tv_Tuner.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_TUNER_H_
+#define _ANDROID_MEDIA_TV_TUNER_H_
+
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <utils/RefBase.h>
+
+#include "jni.h"
+
+using ::android::hardware::tv::tuner::V1_0::ITuner;
+
+namespace android {
+
+struct JTuner : public RefBase {
+    JTuner(JNIEnv *env, jobject thiz);
+    sp<ITuner> getTunerService();
+protected:
+    virtual ~JTuner();
+
+private:
+    jclass mClass;
+    jweak mObject;
+    static sp<ITuner> mTuner;
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_TV_TUNER_H_
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/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/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/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/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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cd3343b..0fc1718 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7826,6 +7826,7 @@
                 R.array.config_allowedManagedServicesOnLowRamDevices)) {
             if (whitelisted.equals(pkg)) {
                 canUseManagedServices = true;
+                break;
             }
         }
 
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/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
      */