Merge "Move app overlay permission to app level"
diff --git a/api/current.txt b/api/current.txt
index 4fa007a3f..8791232 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38347,6 +38347,11 @@
     field public static final java.lang.String VALUE = "value";
   }
 
+  public static final class Settings.Panel {
+    field public static final java.lang.String ACTION_INTERNET_CONNECTIVITY = "android.settings.panel.action.INTERNET_CONNECTIVITY";
+    field public static final java.lang.String ACTION_VOLUME = "android.settings.panel.action.VOLUME";
+  }
+
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     ctor public Settings.Secure();
     method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
@@ -41235,10 +41240,12 @@
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
     method public int getState();
+    method public java.lang.CharSequence getSubtitle();
     method public void setContentDescription(java.lang.CharSequence);
     method public void setIcon(android.graphics.drawable.Icon);
     method public void setLabel(java.lang.CharSequence);
     method public void setState(int);
+    method public void setSubtitle(java.lang.CharSequence);
     method public void updateTile();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
diff --git a/api/system-current.txt b/api/system-current.txt
index dfcae63..4278003 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1471,6 +1471,8 @@
 
   public final class BrightnessConfiguration implements android.os.Parcelable {
     method public int describeContents();
+    method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
+    method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
     method public android.util.Pair<float[], float[]> getCurve();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1478,10 +1480,22 @@
 
   public static class BrightnessConfiguration.Builder {
     ctor public BrightnessConfiguration.Builder(float[], float[]);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
     method public android.hardware.display.BrightnessConfiguration build();
+    method public int getMaxCorrectionsByCategory();
+    method public int getMaxCorrectionsByPackageName();
     method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
   }
 
+  public final class BrightnessCorrection implements android.os.Parcelable {
+    method public float apply(float);
+    method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
+  }
+
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
     method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -2520,7 +2534,37 @@
   }
 
   public class UsbManager {
+    method public java.util.List<android.hardware.usb.UsbPort> getPorts();
     method public void grantPermission(android.hardware.usb.UsbDevice, java.lang.String);
+    field public static final java.lang.String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
+  }
+
+  public final class UsbPort {
+    method public android.hardware.usb.UsbPortStatus getStatus();
+    method public void setRoles(int, int);
+  }
+
+  public final class UsbPortStatus implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCurrentDataRole();
+    method public int getCurrentMode();
+    method public int getCurrentPowerRole();
+    method public int getSupportedRoleCombinations();
+    method public boolean isConnected();
+    method public boolean isRoleCombinationSupported(int, int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
+    field public static final int DATA_ROLE_DEVICE = 2; // 0x2
+    field public static final int DATA_ROLE_HOST = 1; // 0x1
+    field public static final int DATA_ROLE_NONE = 0; // 0x0
+    field public static final int MODE_AUDIO_ACCESSORY = 4; // 0x4
+    field public static final int MODE_DEBUG_ACCESSORY = 8; // 0x8
+    field public static final int MODE_DFP = 2; // 0x2
+    field public static final int MODE_NONE = 0; // 0x0
+    field public static final int MODE_UFP = 1; // 0x1
+    field public static final int POWER_ROLE_NONE = 0; // 0x0
+    field public static final int POWER_ROLE_SINK = 2; // 0x2
+    field public static final int POWER_ROLE_SOURCE = 1; // 0x1
   }
 
 }
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 04173b2..51a7316 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -466,23 +466,12 @@
         name.assign(args[1].c_str(), args[1].size());
         good = true;
     } else if (argCount == 3) {
-        // If it's a userdebug or eng build, then the shell user can
-        // impersonate other uids.
-        if (mEngBuild) {
-            const char* s = args[1].c_str();
-            if (*s != '\0') {
-                char* end = NULL;
-                uid = strtol(s, &end, 0);
-                if (*end == '\0') {
-                    name.assign(args[2].c_str(), args[2].size());
-                    good = true;
-                }
-            }
-        } else {
-            dprintf(out,
-                    "The metrics can only be dumped for other UIDs on eng or userdebug "
-                    "builds.\n");
+        good = getUidFromArgs(args, 1, uid);
+        if (!good) {
+            dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+                         "other UIDs on eng or userdebug builds.\n");
         }
+        name.assign(args[2].c_str(), args[2].size());
     }
     if (!good) {
         print_cmd_help(out);
@@ -518,23 +507,12 @@
                 name.assign(args[2].c_str(), args[2].size());
                 good = true;
             } else if (argCount == 4) {
-                // If it's a userdebug or eng build, then the shell user can
-                // impersonate other uids.
-                if (mEngBuild) {
-                    const char* s = args[2].c_str();
-                    if (*s != '\0') {
-                        char* end = NULL;
-                        uid = strtol(s, &end, 0);
-                        if (*end == '\0') {
-                            name.assign(args[3].c_str(), args[3].size());
-                            good = true;
-                        }
-                    }
-                } else {
-                    dprintf(err,
-                            "The config can only be set for other UIDs on eng or userdebug "
-                            "builds.\n");
+                good = getUidFromArgs(args, 2, uid);
+                if (!good) {
+                    dprintf(err, "Invalid UID. Note that the config can only be set for "
+                                 "other UIDs on eng or userdebug builds.\n");
                 }
+                name.assign(args[3].c_str(), args[3].size());
             } else if (argCount == 2 && args[1] == "remove") {
                 good = true;
             }
@@ -612,23 +590,12 @@
             name.assign(args[1].c_str(), args[1].size());
             good = true;
         } else if (argCount == 3) {
-            // If it's a userdebug or eng build, then the shell user can
-            // impersonate other uids.
-            if (mEngBuild) {
-                const char* s = args[1].c_str();
-                if (*s != '\0') {
-                    char* end = NULL;
-                    uid = strtol(s, &end, 0);
-                    if (*end == '\0') {
-                        name.assign(args[2].c_str(), args[2].size());
-                        good = true;
-                    }
-                }
-            } else {
-                dprintf(out,
-                        "The metrics can only be dumped for other UIDs on eng or userdebug "
-                        "builds.\n");
+            good = getUidFromArgs(args, 1, uid);
+            if (!good) {
+                dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
+                             "other UIDs on eng or userdebug builds.\n");
             }
+            name.assign(args[2].c_str(), args[2].size());
         }
         if (good) {
             vector<uint8_t> data;
@@ -714,18 +681,14 @@
         state = atoi(args[2].c_str());
         good = true;
     } else if (argCount == 4) {
-        uid = atoi(args[1].c_str());
-        // If it's a userdebug or eng build, then the shell user can impersonate other uids.
-        // Otherwise, the uid must match the actual caller's uid.
-        if (mEngBuild || (uid >= 0 && (uid_t)uid == IPCThreadState::self()->getCallingUid())) {
-            label = atoi(args[2].c_str());
-            state = atoi(args[3].c_str());
-            good = true;
-        } else {
+        good = getUidFromArgs(args, 1, uid);
+        if (!good) {
             dprintf(out,
-                    "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
-                    "on eng or userdebug builds.\n");
+                    "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be "
+                    "done for other UIDs on eng or userdebug builds.\n");
         }
+        label = atoi(args[2].c_str());
+        state = atoi(args[3].c_str());
     }
     if (good) {
         dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
@@ -792,6 +755,28 @@
     }
 }
 
+bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
+    const char* s = args[uidArgIndex].c_str();
+    if (*s == '\0') {
+        return false;
+    }
+    char* endc = NULL;
+    int64_t longUid = strtol(s, &endc, 0);
+    if (*endc != '\0') {
+        return false;
+    }
+    int32_t goodUid = static_cast<int32_t>(longUid);
+    if (longUid < 0 || static_cast<uint64_t>(longUid) != static_cast<uid_t>(goodUid)) {
+        return false;  // It was not of uid_t type.
+    }
+    uid = goodUid;
+
+    int32_t callingUid = IPCThreadState::self()->getCallingUid();
+    return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
+            || (callingUid == goodUid) // Anyone can 'impersonate' themselves.
+            || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
+}
+
 Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                       const vector<String16>& version_string,
                                       const vector<String16>& app,
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index cd4d601..135a3c9 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -291,6 +291,15 @@
     status_t cmd_print_logs(int outFd, const Vector<String8>& args);
 
     /**
+     * Writes the value of args[uidArgIndex] into uid.
+     * Returns whether the uid is reasonable (type uid_t) and whether
+     * 1. it is equal to the calling uid, or
+     * 2. the device is mEngBuild, or
+     * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
+     */
+    bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid);
+
+    /**
      * Adds a configuration after checking permissions and obtaining UID from binder call.
      */
     bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
@@ -340,6 +349,7 @@
     FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
+    FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
     FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
     FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
     FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index a7b4136..560fb9f 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -58,6 +58,45 @@
             service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
+TEST(StatsServiceTest, TestGetUidFromArgs) {
+    Vector<String8> args;
+    args.push(String8("-1"));
+    args.push(String8("0"));
+    args.push(String8("1"));
+    args.push(String8("9999999999999999999999999999999999"));
+    args.push(String8("a1"));
+    args.push(String8(""));
+
+    int32_t uid;
+
+    StatsService service(nullptr);
+    service.mEngBuild = true;
+
+    // "-1"
+    EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+
+    // "0"
+    EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+    EXPECT_EQ(0, uid);
+
+    // "1"
+    EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+    EXPECT_EQ(1, uid);
+
+    // "999999999999999999"
+    EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
+
+    // "a1"
+    EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+
+    // ""
+    EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+
+    // For a non-userdebug, uid "1" cannot be impersonated.
+    service.mEngBuild = false;
+    EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9590782..a782ced 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1857,12 +1857,17 @@
      * Recover a list of active notifications: ones that have been posted by the calling app that
      * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app.
      *
-     * Each notification is embedded in a {@link StatusBarNotification} object, including the
+     * <p><Each notification is embedded in a {@link StatusBarNotification} object, including the
      * original <code>tag</code> and <code>id</code> supplied to
      * {@link #notify(String, int, Notification) notify()}
      * (via {@link StatusBarNotification#getTag() getTag()} and
      * {@link StatusBarNotification#getId() getId()}) as well as a copy of the original
      * {@link Notification} object (via {@link StatusBarNotification#getNotification()}).
+     * </p>
+     * <p>From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as an
+     * app's notification delegate via
+     * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+     * </p>
      *
      * @return An array of {@link StatusBarNotification}.
      */
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e08d405..adedff3 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -67,7 +67,9 @@
         }
         // Legacy api - getDefaultAdapter does not take in the context
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mAdapter.setContext(context);
+        if (mAdapter != null) {
+            mAdapter.setContext(context);
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 7e52ca3..be054297 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -19,26 +19,54 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.content.pm.ApplicationInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 
 /** @hide */
 @SystemApi
 @TestApi
 public final class BrightnessConfiguration implements Parcelable {
+    private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+    private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
+    private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections";
+    private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction";
+    private static final String ATTR_LUX = "lux";
+    private static final String ATTR_NITS = "nits";
+    private static final String ATTR_DESCRIPTION = "description";
+    private static final String ATTR_PACKAGE_NAME = "package-name";
+    private static final String ATTR_CATEGORY = "category";
+
     private final float[] mLux;
     private final float[] mNits;
+    private final Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+    private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
     private final String mDescription;
 
-    private BrightnessConfiguration(float[] lux, float[] nits, String description) {
+    private BrightnessConfiguration(float[] lux, float[] nits,
+            Map<String, BrightnessCorrection> correctionsByPackageName,
+            Map<Integer, BrightnessCorrection> correctionsByCategory, String description) {
         mLux = lux;
         mNits = nits;
+        mCorrectionsByPackageName = correctionsByPackageName;
+        mCorrectionsByCategory = correctionsByCategory;
         mDescription = description;
     }
 
@@ -56,6 +84,38 @@
     }
 
     /**
+     * Returns a brightness correction by app, or null.
+     *
+     * @param packageName
+     *      The app's package name.
+     *
+     * @return The matching brightness correction, or null.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+        return mCorrectionsByPackageName.get(packageName);
+    }
+
+    /**
+     * Returns a brightness correction by app category, or null.
+     *
+     * @param category
+     *      The app category.
+     *
+     * @return The matching brightness correction, or null.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public BrightnessCorrection getCorrectionByCategory(int category) {
+        return mCorrectionsByCategory.get(category);
+    }
+
+    /**
      * Returns description string.
      * @hide
      */
@@ -67,6 +127,20 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeFloatArray(mLux);
         dest.writeFloatArray(mNits);
+        dest.writeInt(mCorrectionsByPackageName.size());
+        for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+            final String packageName = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            dest.writeString(packageName);
+            correction.writeToParcel(dest, flags);
+        }
+        dest.writeInt(mCorrectionsByCategory.size());
+        for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+            final int category = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            dest.writeInt(category);
+            correction.writeToParcel(dest, flags);
+        }
         dest.writeString(mDescription);
     }
 
@@ -85,7 +159,14 @@
             }
             sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
         }
-        sb.append("], '");
+        sb.append("], {");
+        for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+            sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", ");
+        }
+        for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+            sb.append(entry.getKey() + ": " + entry.getValue() + ", ");
+        }
+        sb.append("}, '");
         if (mDescription != null) {
             sb.append(mDescription);
         }
@@ -98,6 +179,8 @@
         int result = 1;
         result = result * 31 + Arrays.hashCode(mLux);
         result = result * 31 + Arrays.hashCode(mNits);
+        result = result * 31 + mCorrectionsByPackageName.hashCode();
+        result = result * 31 + mCorrectionsByCategory.hashCode();
         if (mDescription != null) {
             result = result * 31 + mDescription.hashCode();
         }
@@ -114,6 +197,8 @@
         }
         final BrightnessConfiguration other = (BrightnessConfiguration) o;
         return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
+                && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName)
+                && mCorrectionsByCategory.equals(other.mCorrectionsByCategory)
                 && Objects.equals(mDescription, other.mDescription);
     }
 
@@ -123,7 +208,25 @@
             float[] lux = in.createFloatArray();
             float[] nits = in.createFloatArray();
             Builder builder = new Builder(lux, nits);
-            builder.setDescription(in.readString());
+
+            int n = in.readInt();
+            for (int i = 0; i < n; i++) {
+                final String packageName = in.readString();
+                final BrightnessCorrection correction =
+                        BrightnessCorrection.CREATOR.createFromParcel(in);
+                builder.addCorrectionByPackageName(packageName, correction);
+            }
+
+            n = in.readInt();
+            for (int i = 0; i < n; i++) {
+                final int category = in.readInt();
+                final BrightnessCorrection correction =
+                        BrightnessCorrection.CREATOR.createFromParcel(in);
+                builder.addCorrectionByCategory(category, correction);
+            }
+
+            final String description = in.readString();
+            builder.setDescription(description);
             return builder.build();
         }
 
@@ -133,11 +236,146 @@
     };
 
     /**
+     * Writes the configuration to an XML serializer.
+     *
+     * @param serializer
+     *      The XML serializer.
+     *
+     * @hide
+     */
+    public void saveToXml(XmlSerializer serializer) throws IOException {
+        serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+        if (mDescription != null) {
+            serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
+        }
+        for (int i = 0; i < mLux.length; i++) {
+            serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+            serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
+            serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
+            serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+        }
+        serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+        serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+        for (Map.Entry<String, BrightnessCorrection> entry :
+                mCorrectionsByPackageName.entrySet()) {
+            final String packageName = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+            correction.saveToXml(serializer);
+            serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+        }
+        for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+            final int category = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+            serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
+            correction.saveToXml(serializer);
+            serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+        }
+        serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+    }
+
+    /**
+     * Read a configuration from an XML parser.
+     *
+     * @param parser
+     *      The XML parser.
+     *
+     * @throws IOException
+     *      The parser failed to read the XML file.
+     * @throws XmlPullParserException
+     *      The parser failed to parse the XML file.
+     *
+     * @hide
+     */
+    public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        String description = null;
+        List<Float> luxList = new ArrayList<>();
+        List<Float> nitsList = new ArrayList<>();
+        Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
+        Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>();
+        final int configDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, configDepth)) {
+            if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+                description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+                final int curveDepth = parser.getDepth();
+                while (XmlUtils.nextElementWithin(parser, curveDepth)) {
+                    if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+                        continue;
+                    }
+                    final float lux = loadFloatFromXml(parser, ATTR_LUX);
+                    final float nits = loadFloatFromXml(parser, ATTR_NITS);
+                    luxList.add(lux);
+                    nitsList.add(nits);
+                }
+            }
+            if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) {
+                final int correctionsDepth = parser.getDepth();
+                while (XmlUtils.nextElementWithin(parser, correctionsDepth)) {
+                    if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) {
+                        continue;
+                    }
+                    final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+                    final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
+                    BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
+                    if (packageName != null) {
+                        correctionsByPackageName.put(packageName, correction);
+                    } else if (categoryText != null) {
+                        try {
+                            final int category = Integer.parseInt(categoryText);
+                            correctionsByCategory.put(category, correction);
+                        } catch (NullPointerException | NumberFormatException e) {
+                            continue;
+                        }
+                    }
+                }
+            }
+        }
+        final int n = luxList.size();
+        float[] lux = new float[n];
+        float[] nits = new float[n];
+        for (int i = 0; i < n; i++) {
+            lux[i] = luxList.get(i);
+            nits[i] = nitsList.get(i);
+        }
+        final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux,
+                nits);
+        builder.setDescription(description);
+        for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) {
+            final String packageName = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            builder.addCorrectionByPackageName(packageName, correction);
+        }
+        for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) {
+            final int category = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            builder.addCorrectionByCategory(category, correction);
+        }
+        return builder.build();
+    }
+
+    private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+        final String string = parser.getAttributeValue(null, attribute);
+        try {
+            return Float.parseFloat(string);
+        } catch (NullPointerException | NumberFormatException e) {
+            return Float.NaN;
+        }
+    }
+
+    /**
      * A builder class for {@link BrightnessConfiguration}s.
      */
     public static class Builder {
+        private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20;
+        private static final int MAX_CORRECTIONS_BY_CATEGORY = 20;
+
         private float[] mCurveLux;
         private float[] mCurveNits;
+        private Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+        private Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
         private String mDescription;
 
         /**
@@ -169,6 +407,88 @@
             checkMonotonic(nits, false /*strictly increasing*/, "nits");
             mCurveLux = lux;
             mCurveNits = nits;
+            mCorrectionsByPackageName = new HashMap<>();
+            mCorrectionsByCategory = new HashMap<>();
+        }
+
+        /**
+         * Returns the maximum number of corrections by package name allowed.
+         *
+         * @return The maximum number of corrections by package name allowed.
+         *
+         * @hide
+         */
+        @SystemApi
+        public int getMaxCorrectionsByPackageName() {
+            return MAX_CORRECTIONS_BY_PACKAGE_NAME;
+        }
+
+        /**
+         * Returns the maximum number of corrections by category allowed.
+         *
+         * @return The maximum number of corrections by category allowed.
+         *
+         * @hide
+         */
+        @SystemApi
+        public int getMaxCorrectionsByCategory() {
+            return MAX_CORRECTIONS_BY_CATEGORY;
+        }
+
+        /**
+         * Add a brightness correction by app package name.
+         * This correction is applied whenever an app with this package name has the top activity
+         * of the focused stack.
+         *
+         * @param packageName
+         *      The app's package name.
+         * @param correction
+         *      The brightness correction.
+         *
+         * @return The builder.
+         *
+         * @throws IllegalArgumentExceptions
+         *      Maximum number of corrections by package name exceeded (see
+         *      {@link #getMaxCorrectionsByPackageName}).
+         *
+         * @hide
+         */
+        @SystemApi
+        public Builder addCorrectionByPackageName(String packageName,
+                BrightnessCorrection correction) {
+            if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
+                throw new IllegalArgumentException("Too many corrections by package name");
+            }
+            mCorrectionsByPackageName.put(packageName, correction);
+            return this;
+        }
+
+        /**
+         * Add a brightness correction by app category.
+         * This correction is applied whenever an app with this category has the top activity of
+         * the focused stack, and only if a correction by package name has not been applied.
+         *
+         * @param category
+         *      The {@link android.content.pm.ApplicationInfo#category app category}.
+         * @param correction
+         *      The brightness correction.
+         *
+         * @return The builder.
+         *
+         * @throws IllegalArgumentException
+         *      Maximum number of corrections by category exceeded (see
+         *      {@link #getMaxCorrectionsByCategory}).
+         *
+         * @hide
+         */
+        @SystemApi
+        public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
+                BrightnessCorrection correction) {
+            if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
+                throw new IllegalArgumentException("Too many corrections by category");
+            }
+            mCorrectionsByCategory.put(category, correction);
+            return this;
         }
 
         /**
@@ -191,7 +511,8 @@
             if (mCurveLux == null || mCurveNits == null) {
                 throw new IllegalStateException("A curve must be set!");
             }
-            return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
+            return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName,
+                    mCorrectionsByCategory, mDescription);
         }
 
         private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/display/BrightnessCorrection.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/hardware/display/BrightnessCorrection.aidl
index b7a7920..3abe29c 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/display/BrightnessCorrection.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.hardware.display;
 
-parcelable UsbPort;
+parcelable BrightnessCorrection;
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
new file mode 100644
index 0000000..c4e0e3b
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 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.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.MathUtils;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
+ * actual correction scheme.
+ * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
+ * name and category) to corrections that need to be applied to the brightness within that context.
+ * Corrections are currently done by the app that has the top activity of the focused stack, either
+ * by its package name, or (if its package name is not mapped to any correction) by its category.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BrightnessCorrection implements Parcelable {
+
+    private static final int SCALE_AND_TRANSLATE_LOG = 1;
+
+    private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
+
+    private BrightnessCorrectionImplementation mImplementation;
+
+    // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
+    // make this class abstract and use composition instead of inheritence.
+    private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
+        mImplementation = implementation;
+    }
+
+    /**
+     * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
+     * {@code exp(scale * log(brightness) + translate)}.
+     *
+     * @param scale
+     *      How much to scale the log brightness.
+     * @param translate
+     *      How much to translate the log brightness.
+     *
+     * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
+     * {@code exp(scale * log(brightness) + translate)}.
+     *
+     * @throws IllegalArgumentException
+     *      - scale or translate are NaN.
+     */
+    @NonNull
+    public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
+        BrightnessCorrectionImplementation implementation =
+                new ScaleAndTranslateLog(scale, translate);
+        return new BrightnessCorrection(implementation);
+    }
+
+    /**
+     * Applies the brightness correction to a given brightness.
+     *
+     * @param brightness
+     *      The brightness.
+     *
+     * @return The corrected brightness.
+     */
+    public float apply(float brightness) {
+        return mImplementation.apply(brightness);
+    }
+
+    /**
+     * Returns a string representation.
+     *
+     * @return A string representation.
+     */
+    public String toString() {
+        return mImplementation.toString();
+    }
+
+    public static final Creator<BrightnessCorrection> CREATOR =
+            new Creator<BrightnessCorrection>() {
+                public BrightnessCorrection createFromParcel(Parcel in) {
+                    final int type = in.readInt();
+                    switch (type) {
+                        case SCALE_AND_TRANSLATE_LOG:
+                            return ScaleAndTranslateLog.readFromParcel(in);
+                    }
+                    return null;
+                }
+
+                public BrightnessCorrection[] newArray(int size) {
+                    return new BrightnessCorrection[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mImplementation.writeToParcel(dest);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes the correction to an XML serializer.
+     *
+     * @param serializer
+     *      The XML serializer.
+     *
+     * @hide
+     */
+    public void saveToXml(XmlSerializer serializer) throws IOException {
+        mImplementation.saveToXml(serializer);
+    }
+
+    /**
+     * Read a correction from an XML parser.
+     *
+     * @param parser
+     *      The XML parser.
+     *
+     * @throws IOException
+     *      The parser failed to read the XML file.
+     * @throws XmlPullParserException
+     *      The parser failed to parse the XML file.
+     *
+     * @hide
+     */
+    public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+            XmlPullParserException {
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
+                return ScaleAndTranslateLog.loadFromXml(parser);
+            }
+        }
+        return null;
+    }
+
+    private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+        final String string = parser.getAttributeValue(null, attribute);
+        try {
+            return Float.parseFloat(string);
+        } catch (NullPointerException | NumberFormatException e) {
+            return Float.NaN;
+        }
+    }
+
+    private interface BrightnessCorrectionImplementation {
+        float apply(float brightness);
+        String toString();
+        void writeToParcel(Parcel dest);
+        void saveToXml(XmlSerializer serializer) throws IOException;
+        // Package-private static methods:
+        // static BrightnessCorrection readFromParcel(Parcel in);
+        // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+        //      XmlPullParserException;
+    }
+
+    /**
+     * A BrightnessCorrection that given {@code brightness}, corrects it to be
+     * {@code exp(scale * log(brightness) + translate)}.
+     */
+    private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
+        private static final float MIN_SCALE = 0.5f;
+        private static final float MAX_SCALE = 2.0f;
+        private static final float MIN_TRANSLATE = -0.6f;
+        private static final float MAX_TRANSLATE = 0.7f;
+
+        private static final String ATTR_SCALE = "scale";
+        private static final String ATTR_TRANSLATE = "translate";
+
+        private final float mScale;
+        private final float mTranslate;
+
+        ScaleAndTranslateLog(float scale, float translate) {
+            if (Float.isNaN(scale) || Float.isNaN(translate)) {
+                throw new IllegalArgumentException("scale and translate must be numbers");
+            }
+            mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+            mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
+        }
+
+        @Override
+        public float apply(float brightness) {
+            return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
+        }
+
+        @Override
+        public String toString() {
+            return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest) {
+            dest.writeInt(SCALE_AND_TRANSLATE_LOG);
+            dest.writeFloat(mScale);
+            dest.writeFloat(mTranslate);
+        }
+
+        @Override
+        public void saveToXml(XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+            serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
+            serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
+            serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+        }
+
+        static BrightnessCorrection readFromParcel(Parcel in) {
+            float scale = in.readFloat();
+            float translate = in.readFloat();
+            return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+        }
+
+        static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+                XmlPullParserException {
+            final float scale = loadFloatFromXml(parser, ATTR_SCALE);
+            final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
+            return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+        }
+    }
+}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index f4e776c..edc3f94 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -20,7 +20,7 @@
 import android.content.ComponentName;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbPort;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbPortStatus;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -112,7 +112,7 @@
     ParcelFileDescriptor getControlFd(long function);
 
     /* Gets the list of USB ports. */
-    UsbPort[] getPorts();
+    List<ParcelableUsbPort> getPorts();
 
     /* Gets the status of the specified USB port. */
     UsbPortStatus getPortStatus(in String portId);
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
similarity index 87%
rename from core/java/android/hardware/usb/UsbPort.aidl
rename to core/java/android/hardware/usb/ParcelableUsbPort.aidl
index b7a7920..4431551 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * Copyright (C) 2018, 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.
@@ -16,4 +16,4 @@
 
 package android.hardware.usb;
 
-parcelable UsbPort;
+parcelable ParcelableUsbPort;
diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java
new file mode 100644
index 0000000..7f7ba96
--- /dev/null
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 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.hardware.usb;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * A parcelable wrapper to send UsbPorts over binders.
+ *
+ * @hide
+ */
+@Immutable
+public final class ParcelableUsbPort implements Parcelable {
+    private final @NonNull String mId;
+    private final int mSupportedModes;
+
+    private ParcelableUsbPort(@NonNull String id, int supportedModes) {
+        mId = id;
+        mSupportedModes = supportedModes;
+    }
+
+    /**
+     * Create the parcelable version of a {@link UsbPort}.
+     *
+     * @param port The port to create a parcealable version of
+     *
+     * @return The parcelable version of the port
+     */
+    public static @NonNull ParcelableUsbPort of(@NonNull UsbPort port) {
+        return new ParcelableUsbPort(port.getId(), port.getSupportedModes());
+    }
+
+    /**
+     * Create a {@link UsbPort} from this object.
+     *
+     * @param usbManager A link to the usbManager in the current context
+     *
+     * @return The UsbPort for this object
+     */
+    public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) {
+        return new UsbPort(usbManager, mId, mSupportedModes);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeInt(mSupportedModes);
+    }
+
+    public static final Creator<ParcelableUsbPort> CREATOR =
+            new Creator<ParcelableUsbPort>() {
+                @Override
+                public ParcelableUsbPort createFromParcel(Parcel in) {
+                    String id = in.readString();
+                    int supportedModes = in.readInt();
+                    return new ParcelableUsbPort(id, supportedModes);
+                }
+
+                @Override
+                public ParcelableUsbPort[] newArray(int size) {
+                    return new ParcelableUsbPort[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 4111941..6014478 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
 package android.hardware.usb;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -39,9 +40,10 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.StringJoiner;
 
@@ -97,15 +99,11 @@
      * Broadcast Action: A broadcast for USB port changes.
      *
      * This intent is sent when a USB port is added, removed, or changes state.
-     * <ul>
-     * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
-     * for the port.
-     * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
-     * for the port, or null if the port has been removed
-     * </ul>
      *
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
     public static final String ACTION_USB_PORT_CHANGED =
             "android.hardware.usb.action.USB_PORT_CHANGED";
 
@@ -796,34 +794,44 @@
      * device class (which supports all types of ports despite its name).
      * </p>
      *
-     * @return The list of USB ports, or null if none.
+     * @return The list of USB ports
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public UsbPort[] getPorts() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @NonNull List<UsbPort> getPorts() {
         if (mService == null) {
-            return null;
+            return Collections.emptyList();
         }
+
+        List<ParcelableUsbPort> parcelablePorts;
         try {
-            return mService.getPorts();
+            parcelablePorts = mService.getPorts();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        if (parcelablePorts == null) {
+            return Collections.emptyList();
+        } else {
+            int numPorts = parcelablePorts.size();
+
+            ArrayList<UsbPort> ports = new ArrayList<>(numPorts);
+            for (int i = 0; i < numPorts; i++) {
+                ports.add(parcelablePorts.get(i).getUsbPort(this));
+            }
+
+            return ports;
+        }
     }
 
     /**
-     * Gets the status of the specified USB port.
-     *
-     * @param port The port to query.
-     * @return The status of the specified USB port, or null if unknown.
+     * Should only be called by {@link UsbPort#getStatus}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public UsbPortStatus getPortStatus(UsbPort port) {
-        Preconditions.checkNotNull(port, "port must not be null");
-
+    UsbPortStatus getPortStatus(UsbPort port) {
         try {
             return mService.getPortStatus(port.getId());
         } catch (RemoteException e) {
@@ -832,29 +840,11 @@
     }
 
     /**
-     * Sets the desired role combination of the port.
-     * <p>
-     * The supported role combinations depend on what is connected to the port and may be
-     * determined by consulting
-     * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
-     * </p><p>
-     * Note: This function is asynchronous and may fail silently without applying
-     * the requested changes.  If this function does cause a status change to occur then
-     * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
-     * </p>
-     *
-     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
-     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
-     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * Should only be called by {@link UsbPort#setRoles}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
-        Preconditions.checkNotNull(port, "port must not be null");
-        UsbPort.checkRoles(powerRole, dataRole);
-
+    void setPortRoles(UsbPort port, int powerRole, int dataRole) {
         Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName());
         try {
             mService.setPortRoles(port.getId(), powerRole, dataRole);
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index afdb202..37154e4 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,104 +16,53 @@
 
 package android.hardware.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.hardware.usb.V1_0.Constants;
-import android.os.Parcel;
-import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
 
 /**
  * Represents a physical USB port and describes its characteristics.
- * <p>
- * This object is immutable.
- * </p>
  *
  * @hide
  */
-public final class UsbPort implements Parcelable {
+@SystemApi
+public final class UsbPort {
     private final String mId;
     private final int mSupportedModes;
-
-    public static final int MODE_NONE = Constants.PortMode.NONE;
-    /**
-     * Mode bit: This USB port can act as a downstream facing port (host).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_DFP = Constants.PortMode.DFP;
-
-    /**
-     * Mode bit: This USB port can act as an upstream facing port (device).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_UFP = Constants.PortMode.UFP;
-
-    /**
-     * Mode bit: This USB port can act either as an downstream facing port (host) or as
-     * an upstream facing port (device).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
-     * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_DUAL = Constants.PortMode.DRP;
-
-    /**
-     * Mode bit: This USB port can support USB Type-C Audio accessory.
-     */
-    public static final int MODE_AUDIO_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
-
-    /**
-     * Mode bit: This USB port can support USB Type-C debug accessory.
-     */
-    public static final int MODE_DEBUG_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
-
-    /**
-     * Power role: This USB port does not have a power role.
-     */
-    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
-
-    /**
-     * Power role: This USB port can act as a source (provide power).
-     */
-    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
-
-    /**
-     * Power role: This USB port can act as a sink (receive power).
-     */
-    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
-
-    /**
-     * Power role: This USB port does not have a data role.
-     */
-    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
-
-    /**
-     * Data role: This USB port can act as a host (access data services).
-     */
-    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
-
-    /**
-     * Data role: This USB port can act as a device (offer data services).
-     */
-    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+    private final UsbManager mUsbManager;
 
     private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
+
     /**
      * Points to the first power role in the IUsb HAL.
      */
     private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
 
     /** @hide */
-    public UsbPort(String id, int supportedModes) {
+    public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes) {
+        Preconditions.checkNotNull(id);
+        Preconditions.checkFlagsArgument(supportedModes,
+                MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY);
+
+        mUsbManager = usbManager;
         mId = id;
         mSupportedModes = supportedModes;
     }
@@ -122,6 +71,8 @@
      * Gets the unique id of the port.
      *
      * @return The unique id of the port; not intended for display.
+     *
+     * @hide
      */
     public String getId() {
         return mId;
@@ -133,23 +84,62 @@
      * The actual mode of the port may vary depending on what is plugged into it.
      * </p>
      *
-     * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
-     * {@link #MODE_DUAL}.
+     * @return The supported modes: one of {@link UsbPortStatus#MODE_DFP},
+     * {@link UsbPortStatus#MODE_UFP}, or {@link UsbPortStatus#MODE_DUAL}.
+     *
+     * @hide
      */
     public int getSupportedModes() {
         return mSupportedModes;
     }
 
     /**
+     * Gets the status of this USB port.
+     *
+     * @return The status of the this port, or {@code null} if port is unknown.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @Nullable UsbPortStatus getStatus() {
+        return mUsbManager.getPortStatus(this);
+    }
+
+    /**
+     * Sets the desired role combination of the port.
+     * <p>
+     * The supported role combinations depend on what is connected to the port and may be
+     * determined by consulting
+     * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
+     * </p><p>
+     * Note: This function is asynchronous and may fail silently without applying
+     * the requested changes.  If this function does cause a status change to occur then
+     * a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
+     * </p>
+     *
+     * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE} or
+     *                  {@link UsbPortStatus#POWER_ROLE_SINK}, or
+     *                  {@link UsbPortStatus#POWER_ROLE_NONE} if no power role.
+     * @param dataRole The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST} or
+     *                 {@link UsbPortStatus#DATA_ROLE_DEVICE}, or
+     *                 {@link UsbPortStatus#DATA_ROLE_NONE} if no data role.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public void setRoles(@UsbPortStatus.UsbPowerRole int powerRole,
+            @UsbPortStatus.UsbDataRole int dataRole) {
+        UsbPort.checkRoles(powerRole, dataRole);
+
+        mUsbManager.setPortRoles(this, powerRole, dataRole);
+    }
+
+    /**
      * Combines one power and one data role together into a unique value with
      * exactly one bit set.  This can be used to efficiently determine whether
      * a combination of roles is supported by testing whether that bit is present
      * in a bit-field.
      *
-     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
-     *                  or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole  The desired data role: {@link UsbPort#DATA_ROLE_HOST}
-     *                  or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE}
+     *                  or {@link UsbPortStatus#POWER_ROLE_SINK}, or 0 if no power role.
+     * @param dataRole  The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST}
+     *                  or {@link UsbPortStatus#DATA_ROLE_DEVICE}, or 0 if no data role.
      * @hide
      */
     public static int combineRolesAsBit(int powerRole, int dataRole) {
@@ -276,30 +266,4 @@
     public String toString() {
         return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
     }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mId);
-        dest.writeInt(mSupportedModes);
-    }
-
-    public static final Parcelable.Creator<UsbPort> CREATOR =
-            new Parcelable.Creator<UsbPort>() {
-                @Override
-                public UsbPort createFromParcel(Parcel in) {
-                    String id = in.readString();
-                    int supportedModes = in.readInt();
-                    return new UsbPort(id, supportedModes);
-                }
-
-                @Override
-                public UsbPort[] newArray(int size) {
-                    return new UsbPort[size];
-                }
-            };
 }
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 2cd8209..d30201a 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -16,27 +16,134 @@
 
 package android.hardware.usb;
 
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.hardware.usb.V1_0.Constants;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Describes the status of a USB port.
- * <p>
- * This object is immutable.
- * </p>
  *
  * @hide
  */
+@Immutable
+@SystemApi
 public final class UsbPortStatus implements Parcelable {
     private final int mCurrentMode;
-    private final int mCurrentPowerRole;
-    private final int mCurrentDataRole;
+    private final @UsbPowerRole int mCurrentPowerRole;
+    private final @UsbDataRole int mCurrentDataRole;
     private final int mSupportedRoleCombinations;
 
+    /**
+     * Power role: This USB port does not have a power role.
+     */
+    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+
+    /**
+     * Power role: This USB port can act as a source (provide power).
+     */
+    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+
+    /**
+     * Power role: This USB port can act as a sink (receive power).
+     */
+    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+
+    @IntDef(prefix = { "POWER_ROLE_" }, value = {
+            POWER_ROLE_NONE,
+            POWER_ROLE_SOURCE,
+            POWER_ROLE_SINK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbPowerRole{}
+
+    /**
+     * Power role: This USB port does not have a data role.
+     */
+    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+
+    /**
+     * Data role: This USB port can act as a host (access data services).
+     */
+    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+
+    /**
+     * Data role: This USB port can act as a device (offer data services).
+     */
+    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+
+    @IntDef(prefix = { "DATA_ROLE_" }, value = {
+            DATA_ROLE_NONE,
+            DATA_ROLE_HOST,
+            DATA_ROLE_DEVICE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbDataRole{}
+
+    /**
+     * There is currently nothing connected to this USB port.
+     */
+    public static final int MODE_NONE = Constants.PortMode.NONE;
+
+    /**
+     * This USB port can act as a downstream facing port (host).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+     * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+     */
+    public static final int MODE_DFP = Constants.PortMode.DFP;
+
+    /**
+     * This USB port can act as an upstream facing port (device).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
+     * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+     */
+    public static final int MODE_UFP = Constants.PortMode.UFP;
+
+    /**
+     * This USB port can act either as an downstream facing port (host) or as
+     * an upstream facing port (device).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+     * {@link #DATA_ROLE_HOST} combination of roles and the {@link #POWER_ROLE_SINK} and
+     * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+     *
+     * @hide
+     */
+    public static final int MODE_DUAL = Constants.PortMode.DRP;
+
+    /**
+     * This USB port can support USB Type-C Audio accessory.
+     */
+    public static final int MODE_AUDIO_ACCESSORY =
+            android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+
+    /**
+     * This USB port can support USB Type-C debug accessory.
+     */
+    public static final int MODE_DEBUG_ACCESSORY =
+            android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+
+    @IntDef(prefix = { "MODE_" }, flag = true, value = {
+            MODE_NONE,
+            MODE_DFP,
+            MODE_UFP,
+            MODE_AUDIO_ACCESSORY,
+            MODE_DEBUG_ACCESSORY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbPortMode{}
+
     /** @hide */
-    public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
-            int supportedRoleCombinations) {
+    public UsbPortStatus(int currentMode, @UsbPowerRole int currentPowerRole,
+            @UsbDataRole int currentDataRole, int supportedRoleCombinations) {
         mCurrentMode = currentMode;
         mCurrentPowerRole = currentPowerRole;
         mCurrentDataRole = currentDataRole;
@@ -46,9 +153,8 @@
     /**
      * Returns true if there is anything connected to the port.
      *
-     * @return True if there is anything connected to the port.
+     * @return {@code true} iff there is anything connected to the port.
      */
-    @UnsupportedAppUsage
     public boolean isConnected() {
         return mCurrentMode != 0;
     }
@@ -56,33 +162,31 @@
     /**
      * Gets the current mode of the port.
      *
-     * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
-     * or 0 if nothing is connected.
+     * @return The current mode: {@link #MODE_DFP}, {@link #MODE_UFP},
+     * {@link #MODE_AUDIO_ACCESSORY}, {@link #MODE_DEBUG_ACCESSORY}, or {@link {@link #MODE_NONE} if
+     * nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentMode() {
+    public @UsbPortMode int getCurrentMode() {
         return mCurrentMode;
     }
 
     /**
      * Gets the current power role of the port.
      *
-     * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
-     * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
+     * @return The current power role: {@link #POWER_ROLE_SOURCE}, {@link #POWER_ROLE_SINK}, or
+     * {@link #POWER_ROLE_NONE} if nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentPowerRole() {
+    public @UsbPowerRole int getCurrentPowerRole() {
         return mCurrentPowerRole;
     }
 
     /**
      * Gets the current data role of the port.
      *
-     * @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
-     * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
+     * @return The current data role: {@link #DATA_ROLE_HOST}, {@link #DATA_ROLE_DEVICE}, or
+     * {@link #DATA_ROLE_NONE} if nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentDataRole() {
+    public @UsbDataRole int getCurrentDataRole() {
         return mCurrentDataRole;
     }
 
@@ -90,19 +194,20 @@
      * Returns true if the specified power and data role combination is supported
      * given what is currently connected to the port.
      *
-     * @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE}
-     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST}
-     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * @param powerRole The power role to check: {@link #POWER_ROLE_SOURCE}  or
+     *                  {@link #POWER_ROLE_SINK}, or {@link #POWER_ROLE_NONE} if no power role.
+     * @param dataRole  The data role to check: either {@link #DATA_ROLE_HOST} or
+     *                  {@link #DATA_ROLE_DEVICE}, or {@link #DATA_ROLE_NONE} if no data role.
      */
-    @UnsupportedAppUsage
-    public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
+    public boolean isRoleCombinationSupported(@UsbPowerRole int powerRole,
+            @UsbDataRole int dataRole) {
         return (mSupportedRoleCombinations &
                 UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Get the supported role combinations.
+     */
     public int getSupportedRoleCombinations() {
         return mSupportedRoleCombinations;
     }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8a5f43d..c41a56c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1467,7 +1467,7 @@
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
         }
-        if (0 != mNetworkCapabilities) {
+        if (0 != mUnwantedNetworkCapabilities) {
             sb.append(" Unwanted: ");
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 124d7b1..8a0d916 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -17,8 +17,10 @@
 package android.os;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.opengl.EGL14;
@@ -57,9 +59,9 @@
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
-    private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
     private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
+    private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -276,6 +278,27 @@
     }
 
     /**
+     * Get the ANGLE package name.
+     */
+    private String getAnglePackageName(Context context) {
+        Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID);
+
+        List<ResolveInfo> resolveInfos = context.getPackageManager()
+                .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
+        if (resolveInfos.size() != 1) {
+            Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+                    + resolveInfos.size());
+            for (ResolveInfo resolveInfo : resolveInfos) {
+                Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+            }
+            return "";
+        }
+
+        // Must be exactly 1 ANGLE PKG found to get here.
+        return resolveInfos.get(0).activityInfo.packageName;
+    }
+
+    /**
      * Pass ANGLE details down to trigger enable logic
      */
     private void setupAngle(Context context, Bundle bundle, String packageName) {
@@ -286,12 +309,18 @@
                     + "set to: '" + devOptIn + "'");
         }
 
+        String anglePkgName = getAnglePackageName(context);
+        if (anglePkgName.isEmpty()) {
+            Log.e(TAG, "Failed to find ANGLE package.");
+            return;
+        }
+
         ApplicationInfo angleInfo;
         try {
-            angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
+            angleInfo = context.getPackageManager().getApplicationInfo(anglePkgName,
                 PackageManager.MATCH_SYSTEM_ONLY);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
+            Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed");
             return;
         }
 
@@ -351,7 +380,7 @@
                 angleAssets =
                     context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
             } catch (PackageManager.NameNotFoundException e) {
-                Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
+                Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "'");
                 return;
             }
 
@@ -360,7 +389,7 @@
                 assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
             } catch (IOException e) {
                 Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
-                           + "'" + ANGLE_PACKAGE_NAME + "'");
+                           + "'" + anglePkgName + "'");
                 return;
             }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 299db73..40f1946 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14260,6 +14260,44 @@
         }
     }
 
+    /**
+     * <p>
+     *     A Settings panel is floating UI that contains a fixed subset of settings to address a
+     *     particular user problem. For example, the
+     *     {@link #ACTION_INTERNET_CONNECTIVITY Internet Panel} surfaces settings related to
+     *     connecting to the internet.
+     * <p>
+     *     Settings panels appear above the calling app to address the problem without
+     *     the user needing to open Settings and thus leave their current screen.
+     */
+    public static final class Panel {
+        private Panel() {
+        }
+
+        /**
+         * Activity Action: Show a settings dialog containing settings to enable internet
+         * connection.
+         * <p>
+         * Input: Nothing.
+         * <p>
+         * Output: Nothing.
+         */
+        @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_INTERNET_CONNECTIVITY =
+                "android.settings.panel.action.INTERNET_CONNECTIVITY";
+
+        /**
+         * Activity Action: Show a settings dialog containing all volume streams.
+         * <p>
+         * Input: Nothing.
+         * <p>
+         * Output: Nothing.
+         */
+        @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_VOLUME =
+                "android.settings.panel.action.VOLUME";
+    }
+
     private static final String[] PM_WRITE_SETTINGS = {
         android.Manifest.permission.WRITE_SETTINGS
     };
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 4b81a72..6b569cf 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -15,6 +15,7 @@
  */
 package android.service.quicksettings;
 
+import android.annotation.Nullable;
 import android.graphics.drawable.Icon;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -62,6 +63,7 @@
     private IBinder mToken;
     private Icon mIcon;
     private CharSequence mLabel;
+    private CharSequence mSubtitle;
     private CharSequence mContentDescription;
     // Default to active until clients of the new API can update.
     private int mState = STATE_ACTIVE;
@@ -152,6 +154,22 @@
     }
 
     /**
+     * Gets the current subtitle for the tile.
+     */
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Set the subtitle for the tile. Will be displayed as the secondary label.
+     * @param subtitle the subtitle to show.
+     */
+    public void setSubtitle(@Nullable CharSequence subtitle) {
+        this.mSubtitle = subtitle;
+    }
+
+    /**
      * Gets the current content description for the tile.
      */
     public CharSequence getContentDescription() {
@@ -195,6 +213,7 @@
         }
         dest.writeInt(mState);
         TextUtils.writeToParcel(mLabel, dest, flags);
+        TextUtils.writeToParcel(mSubtitle, dest, flags);
         TextUtils.writeToParcel(mContentDescription, dest, flags);
     }
 
@@ -206,6 +225,7 @@
         }
         mState = source.readInt();
         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
     }
 
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index cac2265..240c2e7 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.usb;
 
-import static android.hardware.usb.UsbPort.MODE_AUDIO_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DEBUG_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DFP;
-import static android.hardware.usb.UsbPort.MODE_DUAL;
-import static android.hardware.usb.UsbPort.MODE_NONE;
-import static android.hardware.usb.UsbPort.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
 
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
diff --git a/core/res/res/values-night/themes_permission_controller.xml b/core/res/res/values-night/themes_permission_controller.xml
index 0ad2bdc..a071927 100644
--- a/core/res/res/values-night/themes_permission_controller.xml
+++ b/core/res/res/values-night/themes_permission_controller.xml
@@ -28,8 +28,5 @@
     <style name="Theme.DeviceDefault.PermissionGrant"
            parent="@style/Theme.DeviceDefault.Dialog">
         <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-        <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
-        <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
-        <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
     </style>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dd0b1ee..7688c8d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2615,6 +2615,11 @@
     <!-- Package name for default network scorer app; overridden by product overlays. -->
     <string name="config_defaultNetworkScorerPackageName"></string>
 
+    <!-- Feature flag to enable memory efficient task snapshots that are used in recents optimized
+         for low memory devices and replace the app transition starting window with the splash
+         screen. -->
+    <bool name="config_lowRamTaskSnapshotsAndRecents">false</bool>
+
     <!-- Determines whether recent tasks are provided to the user. Default device has recents
          property. If this is false, then the following recents config flags are ignored. -->
     <bool name="config_hasRecents">true</bool>
@@ -3492,8 +3497,6 @@
 
     <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
     <string name="config_headlineFontFamily" translatable="false"></string>
-    <!-- Name of a font family to use for headlines. Defaults to sans-serif-light -->
-    <string name="config_headlineFontFamilyLight" translatable="false">sans-serif-light</string>
     <!-- Allows setting custom fontFeatureSettings on specific text. -->
     <string name="config_headlineFontFeatureSettings" translatable="false"></string>
 
@@ -3560,8 +3563,6 @@
     <string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
     <!-- Name of a font family to use for body text. -->
     <string name="config_bodyFontFamily" translatable="false">sans-serif</string>
-    <!-- Name of a font family to use for light body text. -->
-    <string name="config_bodyFontFamilyLight" translatable="false">sans-serif-light</string>
     <!-- Name of a font family to use for medium body text. -->
     <string name="config_bodyFontFamilyMedium" translatable="false">sans-serif-medium</string>
 
diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
index e6e0de3..5a9d3e6 100644
--- a/core/res/res/values/styles_permission_controller.xml
+++ b/core/res/res/values/styles_permission_controller.xml
@@ -25,15 +25,16 @@
     </style>
 
     <style name="PermissionGrantTitleIcon">
-        <item name="layout_width">36dp</item>
-        <item name="layout_height">36dp</item>
+        <item name="layout_width">24dp</item>
+        <item name="layout_height">24dp</item>
+        <item name="layout_marginBottom">12dp</item>
         <item name="tint">?attr/colorAccent</item>
         <item name="scaleType">fitCenter</item>
     </style>
 
     <style name="PermissionGrantTitleMessage"
            parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingStart">22dp</item>
+        <item name="gravity">center</item>
         <item name="textSize">20sp</item>
         <item name="textColor">?attr/textColorPrimary</item>
     </style>
@@ -46,56 +47,22 @@
     </style>
 
     <style name="PermissionGrantDescription">
-        <item name="layout_marginTop">20dp</item>
         <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginBottom">16dp</item>
         <item name="layout_marginEnd">24dp</item>
     </style>
 
     <style name="PermissionGrantContent">
-        <item name="layout_marginStart">16dp</item>
+        <item name="layout_marginStart">24dp</item>
         <item name="layout_marginEnd">24dp</item>
     </style>
 
-    <style name="PermissionGrantRadioGroup">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginTop">-4dp</item>
-    </style>
-
-    <style name="PermissionGrantRadioButton"
-           parent="@style/Widget.DeviceDefault.CompoundButton.RadioButton">
-        <item name="paddingStart">16dp</item>
-        <item name="paddingTop">8dp</item>
-        <item name="paddingBottom">8dp</item>
-        <item name="textSize">16sp</item>
-    </style>
-
     <style name="PermissionGrantDetailMessage"
            parent="@style/TextAppearance.DeviceDefault">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginBottom">4dp</item>
+        <item name="layout_marginTop">18dp</item>
         <item name="textColor">?attr/textColorPrimary</item>
         <item name="textSize">16sp</item>
     </style>
 
-    <style name="PermissionGrantDetailMessageSpace">
-        <item name="layout_height">8dp</item>
-    </style>
-
-    <style name="PermissionGrantCheckbox"
-           parent="@style/Widget.DeviceDefault.CompoundButton.CheckBox">
-        <item name="paddingStart">16dp</item>
-        <item name="textColor">?attr/textColorSecondary</item>
-        <item name="buttonTint">?attr/textColorSecondary</item>
-        <item name="textSize">16sp</item>
-    </style>
-
-    <style name="PermissionGrantButtonBar">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">16dp</item>
-        <item name="layout_marginBottom">4dp</item>
-    </style>
-
     <!-- styles for the permission review screen. -->
     <style name="PermissionReviewDescription">
         <item name="layout_marginTop">20dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 87fdc1f..c3d5f63 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
 /* Copyright 2012, The Android Open Source Project
 **
@@ -330,6 +329,7 @@
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
+  <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="string" name="config_recentsComponentName" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
@@ -3328,7 +3328,6 @@
   <java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
   <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
   <java-symbol type="string" name="config_headlineFontFamily" />
-  <java-symbol type="string" name="config_headlineFontFamilyLight" />
   <java-symbol type="string" name="config_headlineFontFamilyMedium" />
 
   <java-symbol type="drawable" name="stat_sys_vitals" />
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
index 369cee3..205c4eb 100644
--- a/core/res/res/values/themes_permission_controller.xml
+++ b/core/res/res/values/themes_permission_controller.xml
@@ -28,9 +28,6 @@
     <style name="Theme.DeviceDefault.PermissionGrant"
            parent="@style/Theme.DeviceDefault.Light.Dialog">
         <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-        <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
-        <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
-        <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
     </style>
 
     <!-- themes for the permission review dialog. -->
@@ -39,6 +36,5 @@
         <item name="windowActionBar">false</item>
         <item name="windowNoTitle">true</item>
         <item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
-        <item name="buttonBarStyle">@style/PermissionReviewButtonBar</item>
     </style>
 </resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
index 8656781..c7b2dd1 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -20,7 +20,7 @@
 LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
 LOCAL_SDK_VERSION := current
 LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_USE_AAPT2 := true
 LOCAL_AAPT_FLAGS := --no-resource-removal
 include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
index 06077a7..8b5fe99 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
@@ -21,7 +21,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
         android:label="Update Overlay Test"/>
 </manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index 86a8679..fef6320 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -19,9 +19,10 @@
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 98af3eb..eeaefc5 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -167,6 +167,7 @@
         },
     },
     data: ["tests/data/**/*.apk"],
+    test_suites: ["device-tests"],
 }
 
 cc_benchmark {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c20c720..5a26780 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -623,7 +623,7 @@
               }
 
               // Add the pairing of overlayable properties to resource ids to the package
-              OverlayableInfo overlayable_info;
+              OverlayableInfo overlayable_info{};
               overlayable_info.policy_flags = policy_header->policy_flags;
               loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
               break;
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 74d6605..9b6ad38 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -69,6 +69,7 @@
         ],
     },
     resource_dirs: [
+        "res-keyguard",
         "res",
     ],
 
@@ -80,4 +81,5 @@
         "com.android.keyguard",
     ],
 
+    annotation_processors: ["dagger2-compiler-2.19"],
 }
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
new file mode 100644
index 0000000..f3a2f0f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_backspace.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
new file mode 100644
index 0000000..ef0aac2
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/ic_done.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.2l-3.5,-3.5a0.984,0.984 0,0 0,-1.4 0,0.984 0.984,0 0,0 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7a0.984,0.984 0,0 0,0 -1.4,0.984 0.984,0 0,0 -1.4,0L9,16.2z"/>
+</vector>
diff --git a/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
new file mode 100644
index 0000000..b428931
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/drawable/keyguard_button_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/car_button_radius"/>
+            <solid android:color="#131315"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/car_button_radius"/>
+            <solid android:color="@color/button_background"/>
+        </shape>
+    </item>
+</selector>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
new file mode 100644
index 0000000..b115a1f
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pattern_view.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, 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.
+*/
+-->
+
+<!-- Car customizations
+     - Added title "Enter your Pattern" at the top
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyguard_pattern_view"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingHorizontal="@dimen/car_margin">
+
+    <FrameLayout
+        android:layout_height="match_parent"
+        android:layout_width="0dp"
+        android:layout_weight="1">
+
+        <com.android.internal.widget.LockPatternView
+            android:id="@+id/lockPatternView"
+            android:layout_width="@dimen/keyguard_pattern_dimension"
+            android:layout_height="@dimen/keyguard_pattern_dimension"
+            android:layout_gravity="center"/>
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/container"
+        android:layout_height="match_parent"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:gravity="center_vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/car_padding_2"
+            android:gravity="center"
+            android:textColor="@android:color/white"
+            android:textSize="@dimen/car_body1_size"
+            android:text="@string/keyguard_enter_your_pattern" />
+
+        <include layout="@layout/keyguard_message_area" />
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+
+        <include layout="@layout/keyguard_eca"
+            android:id="@+id/keyguard_selector_fade_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_gravity="bottom|center_horizontal"
+            android:gravity="center_horizontal"
+            android:visibility="gone" />
+    </LinearLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
new file mode 100644
index 0000000..ed88c62
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout-land/keyguard_pin_view.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+
+<!-- Car customizations
+     - Added title "Enter your PIN" under the entry field
+     - Put backspace and enter buttons in row 4
+     - PIN pad is on start side while entry field and title are on the end side
+     -  Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_pin_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:paddingHorizontal="@dimen/car_margin">
+
+    <FrameLayout
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="match_parent">
+
+        <GridLayout
+            android:id="@+id/container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:columnCount="3">
+
+            <include layout="@layout/num_pad_keys"/>
+        </GridLayout>
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <com.android.keyguard.PasswordTextView
+            android:id="@+id/pinEntry"
+            android:layout_width="@dimen/keyguard_security_width"
+            android:layout_height="@dimen/pin_entry_height"
+            android:gravity="center"
+            app:scaledTextSize="@integer/password_text_view_scale"
+            android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+        <View
+            android:id="@+id/divider"
+            android:layout_width="@dimen/keyguard_security_width"
+            android:layout_height="@dimen/divider_height"
+            android:background="@android:color/white" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/car_padding_2"
+            android:gravity="center"
+            android:textColor="@android:color/white"
+            android:textSize="@dimen/car_body1_size"
+            android:text="@string/keyguard_enter_your_pin" />
+
+        <include layout="@layout/keyguard_message_area" />
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+    </LinearLayout>
+
+    <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+         keyguard to crash. Instead put them down here where they are out of the way and set their
+         visibility to gone. -->
+    <com.android.keyguard.AlphaOptimizedRelativeLayout
+        android:id="@+id/row0"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row1"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row2"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row3"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row4"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+
+    <include layout="@layout/keyguard_eca"
+         android:id="@+id/keyguard_selector_fade_container"
+         android:layout_width="0dp"
+         android:layout_height="0dp"
+         android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
new file mode 100644
index 0000000..062f7bd
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 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
+  -->
+
+<!-- Car customizations
+     Car has solid black background instead of a transparent one
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/black"
+    android:fitsSystemWindows="true">
+
+    <include
+        style="@style/BouncerSecurityContainer"
+        layout="@layout/keyguard_host_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</FrameLayout>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
new file mode 100644
index 0000000..c230414
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_message_area.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, 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.
+*/
+-->
+
+<com.android.keyguard.KeyguardMessageArea
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    style="@style/Keyguard.TextView"
+    android:id="@+id/keyguard_message_area"
+    android:singleLine="true"
+    android:ellipsize="marquee"
+    android:focusable="true"
+    android:layout_marginBottom="@dimen/car_padding_4"
+    android:textSize="@dimen/car_body2_size" />
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
new file mode 100644
index 0000000..c7eda38
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_num_pad_key.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 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
+  -->
+
+<!-- Car customizations
+     The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+     Removing it will cause the keyguard to crash. Hide them instead
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <TextView
+        android:id="@+id/digit_text"
+        style="@style/Widget.TextView.NumPadKey"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+    <!-- The mnemonics is not shown for car but KeyguardPinView references the resource id in code.
+         Removing it will cause the keyguard to crash. Hide them instead -->
+    <TextView
+        android:id="@+id/klondike_text"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone"
+        />
+</merge>
+
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
new file mode 100644
index 0000000..e701fdb
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, 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.
+*/
+-->
+
+<!-- Car customizations
+     - Added title "Enter your Password" below the password field
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPasswordView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_password_view"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+    androidprv:layout_maxHeight="@dimen/keyguard_security_height"
+    android:gravity="center">
+
+    <include layout="@layout/keyguard_message_area" />
+
+    <!-- Password entry field -->
+    <LinearLayout
+         android:layout_height="wrap_content"
+         android:layout_width="wrap_content"
+         android:layout_gravity="center_horizontal"
+         android:orientation="vertical"
+         android:theme="?attr/passwordStyle">
+
+        <EditText
+            android:id="@+id/passwordEntry"
+            android:layout_width="@dimen/password_field_width"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:singleLine="true"
+            android:textStyle="normal"
+            android:inputType="textPassword"
+            android:textSize="@dimen/car_body1_size"
+            android:textColor="?attr/wallpaperTextColor"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:imeOptions="flagForceAscii|actionDone"
+            android:maxLength="@integer/password_text_view_scale"
+         />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/car_padding_2"
+            android:gravity="center"
+            android:textColor="@android:color/white"
+            android:textSize="@dimen/car_body1_size"
+            android:text="@string/keyguard_enter_your_password" />
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+
+        <ImageView android:id="@+id/switch_ime_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="12dp"
+            android:src="@drawable/ic_lockscreen_ime"
+            android:contentDescription="@string/accessibility_ime_switch_button"
+            android:clickable="true"
+            android:padding="8dp"
+            android:tint="@color/background_protected"
+            android:layout_gravity="end|center_vertical"
+            android:background="?android:attr/selectableItemBackground"
+            android:visibility="gone"
+        />
+    </LinearLayout>
+
+    <include layout="@layout/keyguard_eca"
+        android:id="@+id/keyguard_selector_fade_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        android:orientation="vertical"
+        android:layout_gravity="bottom|center_horizontal"
+        android:gravity="center_horizontal"
+        android:visibility="gone"
+    />
+
+</com.android.keyguard.KeyguardPasswordView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
new file mode 100644
index 0000000..00333a8
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, 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.
+*/
+-->
+
+<!-- Car customizations
+     - Added title "Enter your Pattern" at the top
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPatternView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_pattern_view"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+    android:gravity="center_horizontal">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:id="@+id/container"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:orientation="vertical"
+            android:layout_gravity="center">
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/car_padding_2"
+                android:gravity="center"
+                android:textColor="@android:color/white"
+                android:textSize="@dimen/car_body1_size"
+                android:text="@string/keyguard_enter_your_pattern" />
+
+            <include layout="@layout/keyguard_message_area" />
+
+            <com.android.internal.widget.LockPatternView
+                android:id="@+id/lockPatternView"
+                android:layout_width="@dimen/keyguard_pattern_dimension"
+                android:layout_height="@dimen/keyguard_pattern_dimension"
+                android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+                android:layout_gravity="center_horizontal"
+                android:gravity="center" />
+
+            <Button
+                android:id="@+id/cancel_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                style="@style/KeyguardButton"
+                android:text="@string/cancel"/>
+
+          <include layout="@layout/keyguard_eca"
+              android:id="@+id/keyguard_selector_fade_container"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:orientation="vertical"
+              android:layout_gravity="bottom|center_horizontal"
+              android:gravity="center_horizontal"
+              android:visibility="gone" />
+        </LinearLayout>
+    </FrameLayout>
+
+</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..1662251
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+
+<!-- Car customizations
+     - Added title "Enter your PIN" under the entry field
+     - Put backspace and enter buttons in row 4
+     - Hid the emergency call at the bottom
+-->
+
+<com.android.keyguard.KeyguardPINView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/keyguard_pin_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="@dimen/num_pad_margin_left"
+        android:layout_marginRight="@dimen/num_pad_margin_right"
+        android:orientation="vertical"
+        android:gravity="center">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <com.android.keyguard.PasswordTextView
+                android:id="@+id/pinEntry"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/pin_entry_height"
+                android:gravity="center"
+                app:scaledTextSize="@integer/password_text_view_scale"
+                android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+            <View
+                android:id="@+id/divider"
+                android:layout_width="@dimen/keyguard_security_width"
+                android:layout_height="@dimen/divider_height"
+                android:background="@android:color/white" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/car_padding_2"
+                android:gravity="center"
+                android:textColor="@android:color/white"
+                android:textSize="@dimen/car_body1_size"
+                android:text="@string/keyguard_enter_your_pin" />
+
+            <include layout="@layout/keyguard_message_area" />
+
+        </LinearLayout>
+
+        <GridLayout
+            android:id="@+id/container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="@dimen/pin_pattern_pad_margin_vertical"
+            android:columnCount="3">
+
+            <include layout="@layout/num_pad_keys"/>
+        </GridLayout>
+
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            style="@style/KeyguardButton"
+            android:text="@string/cancel"/>
+
+    </LinearLayout>
+
+    <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+         keyguard to crash. Instead put them down here where they are out of the way and set their
+         visibility to gone. -->
+    <com.android.keyguard.AlphaOptimizedRelativeLayout
+        android:id="@+id/row0"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row1"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row2"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row3"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+    <LinearLayout
+        android:id="@+id/row4"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+
+    <include
+        layout="@layout/keyguard_eca"
+        android:id="@+id/keyguard_selector_fade_container"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
new file mode 100644
index 0000000..8306cb4
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/layout/num_pad_keys.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 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
+  -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:app="http://schemas.android.com/apk/res-auto">
+    <!-- Row 1 -->
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key1"
+        style="@style/NumPadKeyButton"
+        app:digit="1" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key2"
+        style="@style/NumPadKeyButton.MiddleColumn"
+        app:digit="2" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key3"
+        style="@style/NumPadKeyButton"
+        app:digit="3" />
+
+    <!-- Row 2 -->
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key4"
+        style="@style/NumPadKeyButton"
+        app:digit="4" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key5"
+        style="@style/NumPadKeyButton.MiddleColumn"
+        app:digit="5" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key6"
+        style="@style/NumPadKeyButton"
+        app:digit="6" />
+
+    <!-- Row 3 -->
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key7"
+        style="@style/NumPadKeyButton"
+        app:digit="7" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key8"
+        style="@style/NumPadKeyButton.MiddleColumn"
+        app:digit="8" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key9"
+        style="@style/NumPadKeyButton"
+        app:digit="9" />
+
+    <!-- Row 4 -->
+    <ImageButton
+        android:id="@+id/delete_button"
+        style="@style/NumPadKeyButton.LastRow"
+        android:gravity="center_vertical"
+        android:src="@drawable/ic_backspace"
+        android:clickable="true"
+        android:tint="@android:color/white"
+        android:background="@drawable/ripple_drawable"
+        android:contentDescription="@string/keyboardview_keycode_delete" />
+    <com.android.keyguard.NumPadKey
+        android:id="@+id/key0"
+        style="@style/NumPadKeyButton.LastRow.MiddleColumn"
+        app:digit="0" />
+    <ImageButton
+        android:id="@+id/key_enter"
+        style="@style/NumPadKeyButton.LastRow"
+        android:src="@drawable/ic_done"
+        android:tint="@android:color/white"
+        android:background="@drawable/ripple_drawable"
+        android:contentDescription="@string/keyboardview_keycode_enter" />
+</merge>
+
diff --git a/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
new file mode 100644
index 0000000..d055efa
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values-h1000dp/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+<resources>
+    <dimen name="pin_pattern_pad_margin_vertical">178dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values-land/dimens.xml b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
new file mode 100644
index 0000000..805a134
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values-land/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+<resources>
+    <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_5</dimen>
+    <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_4</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/colors.xml b/packages/CarSystemUI/res-keyguard/values/colors.xml
new file mode 100644
index 0000000..e6edbea3
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2018, 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.
+-->
+
+<resources>
+    <color name="button_background">@color/car_dark_blue_grey_600</color>
+    <color name="button_text">@color/car_action1_light</color>
+</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res-keyguard/values/dimens.xml b/packages/CarSystemUI/res-keyguard/values/dimens.xml
new file mode 100644
index 0000000..9424dc3
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, 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.
+-->
+<resources>
+    <dimen name="num_pad_margin_left">112dp</dimen>
+    <dimen name="num_pad_margin_right">144dp</dimen>
+    <dimen name="num_pad_key_width">80dp</dimen>
+    <dimen name="num_pad_key_height">80dp</dimen>
+    <dimen name="num_pad_key_margin_horizontal">@dimen/car_padding_6</dimen>
+    <dimen name="num_pad_key_margin_bottom">@dimen/car_padding_5</dimen>
+    <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
+    <dimen name="divider_height">1dp</dimen>
+    <dimen name="key_enter_margin_top">128dp</dimen>
+    <dimen name="keyguard_pattern_dimension">400dp</dimen>
+    <dimen name="password_field_width">350dp</dimen>
+    <dimen name="pin_pattern_pad_margin_vertical">0dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/integers.xml b/packages/CarSystemUI/res-keyguard/values/integers.xml
new file mode 100644
index 0000000..bad1346
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, 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.
+-->
+<resources>
+    <integer name="password_text_view_scale">40</integer>
+    <integer name="password_max_length">500</integer>
+</resources>
diff --git a/packages/CarSystemUI/res-keyguard/values/styles.xml b/packages/CarSystemUI/res-keyguard/values/styles.xml
new file mode 100644
index 0000000..b39e6e6
--- /dev/null
+++ b/packages/CarSystemUI/res-keyguard/values/styles.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2017, 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- The style for the volume icons in the volume dialog. This style makes the icon scale to
+         fit its container since auto wants the icon to be larger. The padding is added to make it
+         so the icon does not press along the edges of the dialog. -->
+    <style name="NumPadKeyButton">
+        <item name="android:layout_width">@dimen/num_pad_key_width</item>
+        <item name="android:layout_height">@dimen/num_pad_key_height</item>
+        <item name="android:layout_marginBottom">@dimen/num_pad_key_margin_bottom</item>
+        <item name="textView">@id/pinEntry</item>
+    </style>
+
+    <style name="NumPadKeyButton.MiddleColumn">
+        <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+        <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+    </style>
+
+    <style name="NumPadKeyButton.LastRow">
+        <item name="android:layout_marginBottom">0dp</item>
+    </style>
+
+    <style name="NumPadKeyButton.LastRow.MiddleColumn">
+        <item name="android:layout_marginStart">@dimen/num_pad_key_margin_horizontal</item>
+        <item name="android:layout_marginEnd">@dimen/num_pad_key_margin_horizontal</item>
+    </style>
+
+    <style name="KeyguardButton" parent="Widget.Car.Button">
+        <item name="android:background">@drawable/keyguard_button_background</item>
+        <item name="android:textColor">@color/button_text</item>
+        <item name="android:textAllCaps">false</item>
+    </style>
+
+    <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
+        <!-- Only replaces the text size. -->
+        <item name="android:textSize">@dimen/car_body1_size</item>
+    </style>
+</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index f57f26d..7039a2c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -17,25 +17,42 @@
 package com.android.systemui;
 
 import android.content.Context;
-import android.util.ArrayMap;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.car.CarNotificationEntryManager;
 import com.android.systemui.statusbar.car.CarFacetButtonController;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.volume.CarVolumeDialogComponent;
 import com.android.systemui.volume.VolumeDialogComponent;
 
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
 /**
  * Class factory to provide car specific SystemUI components.
  */
 public class CarSystemUIFactory extends SystemUIFactory {
 
+    private CarDependencyComponent mCarDependencyComponent;
+
+    @Override
+    protected void init(Context context) {
+        super.init(context);
+        mCarDependencyComponent = DaggerCarSystemUIFactory_CarDependencyComponent.builder()
+                .contextHolder(new ContextHolder(context))
+                .build();
+    }
+
+    public CarDependencyComponent getCarDependencyComponent() {
+        return mCarDependencyComponent;
+    }
+
     public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
         ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
         return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -46,12 +63,27 @@
     }
 
     @Override
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
-        Context context) {
-        super.injectDependencies(providers, context);
-        providers.put(NotificationEntryManager.class,
-            () -> new CarNotificationEntryManager(context));
-        providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
-        providers.put(HvacController.class, () -> new HvacController(context));
+    public NotificationEntryManager provideNotificationEntryManager(Context context) {
+        return new CarNotificationEntryManager(context);
+    }
+
+    @Module
+    protected static class ContextHolder {
+        private Context mContext;
+
+        public ContextHolder(Context context) {
+            mContext = context;
+        }
+
+        @Provides
+        public Context provideContext() {
+            return mContext;
+        }
+    }
+
+    @Singleton
+    @Component(modules = ContextHolder.class)
+    public interface CarDependencyComponent {
+        CarFacetButtonController getCarFacetButtonController();
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index cea4ab0..0a20eaa 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -26,8 +26,9 @@
 import android.widget.LinearLayout;
 
 import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.Dependency;
+import com.android.systemui.CarSystemUIFactory;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 
 /**
  * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
@@ -76,8 +77,9 @@
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
         setupIntents(typedArray);
         setupIcons(typedArray);
-        CarFacetButtonController carFacetButtonController = Dependency.get(
-                CarFacetButtonController.class);
+        CarSystemUIFactory factory = SystemUIFactory.getInstance();
+        CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
+                .getCarFacetButtonController();
         carFacetButtonController.addFacetButton(this);
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 56db242..7811a1c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -29,11 +29,15 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
  * application on screen is associated with it. This is basically a similar concept to a radio
  * button group.
  */
+@Singleton
 public class CarFacetButtonController {
 
     protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
@@ -42,6 +46,7 @@
     protected CarFacetButton mSelectedFacetButton;
     protected Context mContext;
 
+    @Inject
     public CarFacetButtonController(Context context) {
         mContext = context;
     }
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 5da236c..7028999c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -29,9 +29,11 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BatteryMeterView;
+import com.android.systemui.CarSystemUIFactory;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
@@ -102,7 +104,9 @@
 
         mHvacController.connectToCarService();
 
-        mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
+        CarSystemUIFactory factory = SystemUIFactory.getInstance();
+        mCarFacetButtonController = factory.getCarDependencyComponent()
+                .getCarFacetButtonController();
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
         if (!mDeviceIsProvisioned) {
@@ -239,7 +243,7 @@
     @Override
     protected void makeStatusBarView() {
         super.makeStatusBarView();
-        mHvacController = Dependency.get(HvacController.class);
+        mHvacController = new HvacController(mContext);
 
         mNotificationPanelBackground = getDefaultWallpaper();
         mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ad44b9a..8be67d9 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -115,6 +115,8 @@
         "mockito-target-inline-minus-junit4",
         "testables",
         "truth-prebuilt",
+        "dagger2-2.19",
+        "jsr330"
     ],
     libs: [
         "android.test.runner",
@@ -125,6 +127,7 @@
         "--extra-packages",
         "com.android.keyguard:com.android.systemui",
     ],
+    annotation_processors: ["dagger2-compiler-2.19"],
 }
 
 android_app {
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
new file mode 100644
index 0000000..565d765f
--- /dev/null
+++ b/packages/SystemUI/docs/dagger.md
@@ -0,0 +1,136 @@
+# Dagger 2 in SystemUI
+*Dagger 2 is a dependency injection framework that compiles annotations to code
+to create dependencies without reflection*
+
+## Recommended reading
+
+Go read about Dagger 2.
+
+TODO: Add some links.
+
+## State of the world
+
+Dagger 2 has been turned on for SystemUI and a early first pass has been taken
+for converting everything in Dependency.java to use Dagger. Since a lot of
+SystemUI depends on Dependency, stubs have been added to Dependency to proxy
+any gets through to the instances provided by dagger, this will allow migration
+of SystemUI through a number of CLs.
+
+### How it works in SystemUI
+
+For the classes that we're using in Dependency and are switching to dagger, the
+equivalent dagger version is using @Singleton and only having one instance.
+To have the single instance span all of SystemUI and be easily accessible for
+other components, there is a single root Component that exists that generates
+these. The component lives in SystemUIFactory and is called SystemUIRootComponent.
+
+```java
+@Singleton
+@Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+public interface SystemUIRootComponent {
+    @Singleton
+    Dependency.DependencyInjector createDependency();
+}
+```
+
+The root modules are what provides the global singleton dependencies across
+SystemUI. ContextHolder is just a wrapper that provides a context.
+SystemUIFactory @Provide dependencies that need to be overridden by SystemUI
+variants (like other form factors). DependencyProvider provides or binds any
+remaining depedencies required.
+
+### Adding injection to a new SystemUI object
+
+Anything that depends on any @Singleton provider from SystemUIRootComponent
+should be declared as a Subcomponent of the root component, this requires
+declaring your own interface for generating your own modules or just the
+object you need injected. The subcomponent also needs to be added to
+SystemUIRootComponent in SystemUIFactory so it can be acquired.
+
+```java
+public interface SystemUIRootComponent {
++    @Singleton
++    Dependency.DependencyInjector createDependency();
+}
+
+public class Dependency extends SystemUI {
+  ...
++  @Subcomponent
++  public interface DependencyInjector {
++      Dependency createSystemUI();
++  }
+}
+```
+
+For objects that extend SystemUI and require injection, you can define an
+injector that creates the injected object for you. This other class should
+be referenced in @string/config_systemUIServiceComponents.
+
+```java
+public static class DependencyCreator implements Injector {
+    @Override
+    public SystemUI apply(Context context) {
+        return SystemUIFactory.getInstance().getRootComponent()
+                .createDependency()
+                .createSystemUI();
+    }
+}
+```
+
+### Adding a new injectable object
+
+First tag the constructor with @Inject. Also tag it with @Singleton if only one
+instance should be created.
+
+```java
+@Singleton
+public class SomethingController {
+  @Inject
+  public SomethingController(Context context,
+    @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+      // context and mainHandler will be automatically populated.
+  }
+}
+```
+
+If you have an interface class and an implementation class, dagger needs to know
+how to map it. The simplest way to do this is to add a provides method to
+DependencyProvider.
+
+```java
+public class DependencyProvider {
+  ...
+  @Singleton
+  @Provide
+  public SomethingController provideSomethingController(Context context,
+      @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+    return new SomethingControllerImpl(context, mainHandler);
+  }
+}
+```
+
+If you need to access this from Dependency#get, then add an adapter to Dependency
+that maps to the instance provided by Dagger. The changes should be similar
+to the following diff.
+
+```java
+public class Dependency {
+  ...
+  @Inject Lazy<SomethingController> mSomethingController;
+  ...
+  public void start() {
+    ...
+    mProviders.put(SomethingController.class, mSomethingController::get);
+  }
+}
+```
+
+## TODO List
+
+ - Eliminate usages of Depndency#get
+ - Add support for Fragments to handle injection automatically
+   - (this could be through dagger2-android or something custom)
+ - Reduce number of things with @Provide in DependencyProvider (many can be
+   @Inject instead)
+ - Migrate as many remaining DependencyProvider instances to @Bind
+ - Add links in above TODO
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index ee94aed..cc6848f 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -17,6 +17,7 @@
 -keep class com.android.systemui.car.CarSystemUIFactory
 -keep class com.android.systemui.SystemUIFactory
 -keep class * extends com.android.systemui.SystemUI
+-keep class * implements com.android.systemui.SystemUI$Injector
 
 -keepclasseswithmembers class * {
     public <init>(android.content.Context, android.util.AttributeSet);
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index ffc7b3c..b9966cf 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -33,7 +33,7 @@
         <item name="android:gravity">center_horizontal|center_vertical</item>
         <item name="android:background">@null</item>
         <item name="android:textSize">32sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:textColor">?attr/wallpaperTextColor</item>
         <item name="android:paddingBottom">-16dp</item>
     </style>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 61efbd5..889db66 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -290,7 +290,7 @@
 
     <!-- SystemUI Services: The classes of the stuff to start. -->
     <string-array name="config_systemUIServiceComponents" translatable="false">
-        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.Dependency$DependencyCreator</item>
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
@@ -318,7 +318,7 @@
 
     <!-- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents -->
     <string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
-        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.Dependency$DependencyCreator</item>
         <item>com.android.systemui.util.NotificationChannels</item>
     </string-array>
 
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 327ffcd..4e5af15 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -14,20 +14,15 @@
 
 package com.android.systemui;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.hardware.SensorManager;
 import android.hardware.SensorPrivacyManager;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
-import android.os.Process;
-import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
@@ -36,92 +31,86 @@
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
-import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
 import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
 import com.android.systemui.statusbar.policy.IconLogger;
-import com.android.systemui.statusbar.policy.IconLoggerImpl;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
 import com.android.systemui.util.AsyncSensorManager;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.function.Consumer;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import dagger.Lazy;
+import dagger.Subcomponent;
+
 /**
  * Class to handle ugly dependencies throughout sysui until we determine the
  * long-term dependency injection solution.
@@ -143,235 +132,302 @@
     /**
      * Key for getting a background Looper for background work.
      */
-    public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>("background_looper");
+    public static final String BG_LOOPER_NAME = "background_looper";
     /**
      * Key for getting a background Handler for background work.
      */
-    public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>("background_handler");
+    public static final String BG_HANDLER_NAME = "background_handler";
     /**
      * Key for getting a Handler for receiving time tick broadcasts on.
      */
-    public static final DependencyKey<Handler> TIME_TICK_HANDLER =
-            new DependencyKey<>("time_tick_handler");
+    public static final String TIME_TICK_HANDLER_NAME = "time_tick_handler";
     /**
      * Generic handler on the main thread.
      */
-    public static final DependencyKey<Handler> MAIN_HANDLER = new DependencyKey<>("main_handler");
+    public static final String MAIN_HANDLER_NAME = "main_handler";
 
     /**
      * An email address to send memory leak reports to by default.
      */
-    public static final DependencyKey<String> LEAK_REPORT_EMAIL
-            = new DependencyKey<>("leak_report_email");
+    public static final String LEAK_REPORT_EMAIL_NAME = "leak_report_email";
+
+    /**
+     * Key for getting a background Looper for background work.
+     */
+    public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
+    /**
+     * Key for getting a background Handler for background work.
+     */
+    public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>(BG_HANDLER_NAME);
+    /**
+     * Key for getting a Handler for receiving time tick broadcasts on.
+     */
+    public static final DependencyKey<Handler> TIME_TICK_HANDLER =
+            new DependencyKey<>(TIME_TICK_HANDLER_NAME);
+    /**
+     * Generic handler on the main thread.
+     */
+    public static final DependencyKey<Handler> MAIN_HANDLER =
+            new DependencyKey<>(MAIN_HANDLER_NAME);
+
+    /**
+     * An email address to send memory leak reports to by default.
+     */
+    public static final DependencyKey<String> LEAK_REPORT_EMAIL =
+            new DependencyKey<>(LEAK_REPORT_EMAIL_NAME);
 
     private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
     private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
 
+    @Inject Lazy<ActivityStarter> mActivityStarter;
+    @Inject Lazy<ActivityStarterDelegate> mActivityStarterDelegate;
+    @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
+    @Inject Lazy<BluetoothController> mBluetoothController;
+    @Inject Lazy<LocationController> mLocationController;
+    @Inject Lazy<RotationLockController> mRotationLockController;
+    @Inject Lazy<NetworkController> mNetworkController;
+    @Inject Lazy<ZenModeController> mZenModeController;
+    @Inject Lazy<HotspotController> mHotspotController;
+    @Inject Lazy<CastController> mCastController;
+    @Inject Lazy<FlashlightController> mFlashlightController;
+    @Inject Lazy<UserSwitcherController> mUserSwitcherController;
+    @Inject Lazy<UserInfoController> mUserInfoController;
+    @Inject Lazy<KeyguardMonitor> mKeyguardMonitor;
+    @Inject Lazy<BatteryController> mBatteryController;
+    @Inject Lazy<ColorDisplayController> mColorDisplayController;
+    @Inject Lazy<ManagedProfileController> mManagedProfileController;
+    @Inject Lazy<NextAlarmController> mNextAlarmController;
+    @Inject Lazy<DataSaverController> mDataSaverController;
+    @Inject Lazy<AccessibilityController> mAccessibilityController;
+    @Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
+    @Inject Lazy<PluginManager> mPluginManager;
+    @Inject Lazy<AssistManager> mAssistManager;
+    @Inject Lazy<SecurityController> mSecurityController;
+    @Inject Lazy<LeakDetector> mLeakDetector;
+    @Inject Lazy<LeakReporter> mLeakReporter;
+    @Inject Lazy<GarbageMonitor> mGarbageMonitor;
+    @Inject Lazy<IconLogger> mIconLogger;
+    @Inject Lazy<TunerService> mTunerService;
+    @Inject Lazy<StatusBarWindowController> mStatusBarWindowController;
+    @Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
+    @Inject Lazy<ConfigurationController> mConfigurationController;
+    @Inject Lazy<StatusBarIconController> mStatusBarIconController;
+    @Inject Lazy<ScreenLifecycle> mScreenLifecycle;
+    @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
+    @Inject Lazy<FragmentService> mFragmentService;
+    @Inject Lazy<ExtensionController> mExtensionController;
+    @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider;
+    @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager;
+    @Inject Lazy<VolumeDialogController> mVolumeDialogController;
+    @Inject Lazy<MetricsLogger> mMetricsLogger;
+    @Inject Lazy<AccessibilityManagerWrapper> mAccessibilityManagerWrapper;
+    @Inject Lazy<SysuiColorExtractor> mSysuiColorExtractor;
+    @Inject Lazy<TunablePaddingService> mTunablePaddingService;
+    @Inject Lazy<ForegroundServiceController> mForegroundServiceController;
+    @Inject Lazy<UiOffloadThread> mUiOffloadThread;
+    @Inject Lazy<PowerUI.WarningsUI> mWarningsUI;
+    @Inject Lazy<LightBarController> mLightBarController;
+    @Inject Lazy<IWindowManager> mIWindowManager;
+    @Inject Lazy<OverviewProxyService> mOverviewProxyService;
+    @Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
+    @Inject Lazy<VibratorHelper> mVibratorHelper;
+    @Inject Lazy<IStatusBarService> mIStatusBarService;
+    @Inject Lazy<DisplayMetrics> mDisplayMetrics;
+    @Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
+    @Inject Lazy<KeyguardEnvironment> mKeyguardEnvironment;
+    @Inject Lazy<ShadeController> mShadeController;
+    @Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
+    @Inject Lazy<InitController> mInitController;
+    @Inject Lazy<AppOpsController> mAppOpsController;
+    @Inject Lazy<DisplayNavigationBarController> mDisplayNavigationBarController;
+    @Inject Lazy<StatusBarStateController> mStatusBarStateController;
+    @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
+    @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
+    @Inject Lazy<NotificationGroupManager> mNotificationGroupManager;
+    @Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
+    @Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
+    @Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
+    @Inject Lazy<AmbientPulseManager> mAmbientPulseManager;
+    @Inject Lazy<NotificationBlockingHelperManager> mNotificationBlockingHelperManager;
+    @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
+    @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
+    @Inject Lazy<NotificationListener> mNotificationListener;
+    @Inject Lazy<NotificationLogger> mNotificationLogger;
+    @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
+    @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
+    @Inject Lazy<SmartReplyController> mSmartReplyController;
+    @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
+    @Inject Lazy<BubbleController> mBubbleController;
+    @Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
+    @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
+    @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
+    @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
+    @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
+    @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
+    @Nullable
+    @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
+
+    @Inject
+    public Dependency() {
+    }
+
     @Override
     public void start() {
         // TODO: Think about ways to push these creation rules out of Dependency to cut down
         // on imports.
-        mProviders.put(TIME_TICK_HANDLER, () -> {
-            HandlerThread thread = new HandlerThread("TimeTick");
-            thread.start();
-            return new Handler(thread.getLooper());
-        });
-        mProviders.put(BG_LOOPER, () -> {
-            HandlerThread thread = new HandlerThread("SysUiBg",
-                    Process.THREAD_PRIORITY_BACKGROUND);
-            thread.start();
-            return thread.getLooper();
-        });
-        mProviders.put(BG_HANDLER, () -> new Handler(getDependency(BG_LOOPER)));
-        mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
-        mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
-        mProviders.put(ActivityStarterDelegate.class, () ->
-                getDependency(ActivityStarter.class));
+        mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
+        mProviders.put(BG_LOOPER, mBgLooper::get);
+        mProviders.put(BG_HANDLER, mBgHandler::get);
+        mProviders.put(MAIN_HANDLER, mMainHandler::get);
+        mProviders.put(ActivityStarter.class, mActivityStarter::get);
+        mProviders.put(ActivityStarterDelegate.class, mActivityStarterDelegate::get);
 
-        mProviders.put(AsyncSensorManager.class, () ->
-                new AsyncSensorManager(mContext.getSystemService(SensorManager.class),
-                        getDependency(PluginManager.class)));
+        mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
 
-        mProviders.put(SensorPrivacyManager.class, () ->
-                mContext.getSystemService(SensorPrivacyManager.class));
+        mProviders.put(BluetoothController.class, mBluetoothController::get);
+        mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
 
-        mProviders.put(BluetoothController.class, () ->
-                new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(LocationController.class, mLocationController::get);
 
-        mProviders.put(LocationController.class, () ->
-                new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(RotationLockController.class, mRotationLockController::get);
 
-        mProviders.put(RotationLockController.class, () ->
-                new RotationLockControllerImpl(mContext));
+        mProviders.put(NetworkController.class, mNetworkController::get);
 
-        mProviders.put(NetworkController.class, () ->
-                new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
-                        getDependency(DeviceProvisionedController.class)));
+        mProviders.put(ZenModeController.class, mZenModeController::get);
 
-        mProviders.put(ZenModeController.class, () ->
-                new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));
+        mProviders.put(HotspotController.class, mHotspotController::get);
 
-        mProviders.put(HotspotController.class, () ->
-                new HotspotControllerImpl(mContext));
+        mProviders.put(CastController.class, mCastController::get);
 
-        mProviders.put(CastController.class, () ->
-                new CastControllerImpl(mContext));
+        mProviders.put(FlashlightController.class, mFlashlightController::get);
 
-        mProviders.put(FlashlightController.class, () ->
-                new FlashlightControllerImpl(mContext));
+        mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get);
 
-        mProviders.put(KeyguardMonitor.class, () ->
-                new KeyguardMonitorImpl(mContext));
+        mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
 
-        mProviders.put(UserSwitcherController.class, () ->
-                new UserSwitcherController(mContext, getDependency(KeyguardMonitor.class),
-                        getDependency(MAIN_HANDLER), getDependency(ActivityStarter.class)));
+        mProviders.put(UserInfoController.class, mUserInfoController::get);
 
-        mProviders.put(UserInfoController.class, () ->
-                new UserInfoControllerImpl(mContext));
+        mProviders.put(BatteryController.class, mBatteryController::get);
 
-        mProviders.put(BatteryController.class, () ->
-                new BatteryControllerImpl(mContext));
+        mProviders.put(ColorDisplayController.class, mColorDisplayController::get);
 
-        mProviders.put(ColorDisplayController.class, () ->
-                new ColorDisplayController(mContext));
+        mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
 
-        mProviders.put(ManagedProfileController.class, () ->
-                new ManagedProfileControllerImpl(mContext));
+        mProviders.put(NextAlarmController.class, mNextAlarmController::get);
 
-        mProviders.put(NextAlarmController.class, () ->
-                new NextAlarmControllerImpl(mContext));
+        mProviders.put(DataSaverController.class, mDataSaverController::get);
 
-        mProviders.put(DataSaverController.class, () ->
-                get(NetworkController.class).getDataSaverController());
+        mProviders.put(AccessibilityController.class, mAccessibilityController::get);
 
-        mProviders.put(AccessibilityController.class, () ->
-                new AccessibilityController(mContext));
+        mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
 
-        mProviders.put(DeviceProvisionedController.class, () ->
-                new DeviceProvisionedControllerImpl(mContext));
+        mProviders.put(PluginManager.class, mPluginManager::get);
 
-        mProviders.put(PluginManager.class, () ->
-                new PluginManagerImpl(mContext, new PluginInitializerImpl()));
+        mProviders.put(AssistManager.class, mAssistManager::get);
 
-        mProviders.put(AssistManager.class, () ->
-                new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
+        mProviders.put(SecurityController.class, mSecurityController::get);
 
-        mProviders.put(SecurityController.class, () ->
-                new SecurityControllerImpl(mContext));
+        mProviders.put(LeakDetector.class, mLeakDetector::get);
 
-        mProviders.put(LeakDetector.class, LeakDetector::create);
+        mProviders.put(LEAK_REPORT_EMAIL, mLeakReportEmail::get);
 
-        mProviders.put(LEAK_REPORT_EMAIL, () -> null);
+        mProviders.put(LeakReporter.class, mLeakReporter::get);
 
-        mProviders.put(LeakReporter.class, () -> new LeakReporter(
-                mContext,
-                getDependency(LeakDetector.class),
-                getDependency(LEAK_REPORT_EMAIL)));
+        mProviders.put(GarbageMonitor.class, mGarbageMonitor::get);
 
-        mProviders.put(
-                GarbageMonitor.class,
-                () ->
-                        new GarbageMonitor(
-                                mContext,
-                                getDependency(BG_LOOPER),
-                                getDependency(LeakDetector.class),
-                                getDependency(LeakReporter.class)));
+        mProviders.put(TunerService.class, mTunerService::get);
 
-        mProviders.put(TunerService.class, () ->
-                new TunerServiceImpl(mContext));
+        mProviders.put(StatusBarWindowController.class, mStatusBarWindowController::get);
 
-        mProviders.put(StatusBarWindowController.class, () ->
-                new StatusBarWindowController(mContext));
+        mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
 
-        mProviders.put(DarkIconDispatcher.class, () ->
-                new DarkIconDispatcherImpl(mContext));
+        mProviders.put(ConfigurationController.class, mConfigurationController::get);
 
-        mProviders.put(ConfigurationController.class, () ->
-                new ConfigurationControllerImpl(mContext));
+        mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
 
-        mProviders.put(StatusBarIconController.class, () ->
-                new StatusBarIconControllerImpl(mContext));
+        mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
 
-        mProviders.put(ScreenLifecycle.class, () ->
-                new ScreenLifecycle());
+        mProviders.put(WakefulnessLifecycle.class, mWakefulnessLifecycle::get);
 
-        mProviders.put(WakefulnessLifecycle.class, () ->
-                new WakefulnessLifecycle());
+        mProviders.put(FragmentService.class, mFragmentService::get);
 
-        mProviders.put(FragmentService.class, () ->
-                new FragmentService());
+        mProviders.put(ExtensionController.class, mExtensionController::get);
 
-        mProviders.put(ExtensionController.class, () ->
-                new ExtensionControllerImpl(mContext));
+        mProviders.put(PluginDependencyProvider.class, mPluginDependencyProvider::get);
 
-        mProviders.put(PluginDependencyProvider.class, () ->
-                new PluginDependencyProvider(get(PluginManager.class)));
+        mProviders.put(LocalBluetoothManager.class, mLocalBluetoothManager::get);
 
-        mProviders.put(LocalBluetoothManager.class, () ->
-                LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER),
-                        UserHandle.ALL));
+        mProviders.put(VolumeDialogController.class, mVolumeDialogController::get);
 
-        mProviders.put(VolumeDialogController.class, () ->
-                new VolumeDialogControllerImpl(mContext));
+        mProviders.put(MetricsLogger.class, mMetricsLogger::get);
 
-        mProviders.put(MetricsLogger.class, () -> new MetricsLogger());
+        mProviders.put(AccessibilityManagerWrapper.class, mAccessibilityManagerWrapper::get);
 
-        mProviders.put(AccessibilityManagerWrapper.class,
-                () -> new AccessibilityManagerWrapper(mContext));
+        mProviders.put(SysuiColorExtractor.class, mSysuiColorExtractor::get);
 
-        // Creating a new instance will trigger color extraction.
-        // Thankfully this only happens once - during boot - and WallpaperManagerService
-        // loads colors from cache.
-        mProviders.put(SysuiColorExtractor.class, () -> new SysuiColorExtractor(mContext));
+        mProviders.put(TunablePaddingService.class, mTunablePaddingService::get);
 
-        mProviders.put(TunablePaddingService.class, () -> new TunablePaddingService());
+        mProviders.put(ForegroundServiceController.class, mForegroundServiceController::get);
 
-        mProviders.put(ForegroundServiceController.class,
-                () -> new ForegroundServiceControllerImpl(mContext));
+        mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
 
-        mProviders.put(UiOffloadThread.class, UiOffloadThread::new);
+        mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
 
-        mProviders.put(PowerUI.WarningsUI.class, () -> new PowerNotificationWarnings(mContext));
+        mProviders.put(IconLogger.class, mIconLogger::get);
 
-        mProviders.put(IconLogger.class, () -> new IconLoggerImpl(mContext,
-                getDependency(BG_LOOPER), getDependency(MetricsLogger.class)));
+        mProviders.put(LightBarController.class, mLightBarController::get);
 
-        mProviders.put(LightBarController.class, () -> new LightBarController(mContext));
+        mProviders.put(IWindowManager.class, mIWindowManager::get);
 
-        mProviders.put(IWindowManager.class, () -> WindowManagerGlobal.getWindowManagerService());
+        mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
 
-        mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
+        mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get);
 
-        mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+        mProviders.put(VibratorHelper.class, mVibratorHelper::get);
 
-        mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext));
+        mProviders.put(IStatusBarService.class, mIStatusBarService::get);
 
-        mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE)));
+        mProviders.put(DisplayMetrics.class, mDisplayMetrics::get);
 
-        // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
-        // anywhere it is needed.
-        mProviders.put(DisplayMetrics.class, () -> new DisplayMetrics());
+        mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
 
-        mProviders.put(LockscreenGestureLogger.class, () -> new LockscreenGestureLogger());
-
-        mProviders.put(KeyguardEnvironment.class, () -> new KeyguardEnvironmentImpl());
-        mProviders.put(ShadeController.class, () ->
-                SysUiServiceProvider.getComponent(mContext, StatusBar.class));
+        mProviders.put(KeyguardEnvironment.class, mKeyguardEnvironment::get);
+        mProviders.put(ShadeController.class, mShadeController::get);
         mProviders.put(NotificationRemoteInputManager.Callback.class,
-                () -> new StatusBarRemoteInputCallback(mContext));
+                mNotificationRemoteInputManagerCallback::get);
 
-        mProviders.put(InitController.class, InitController::new);
+        mProviders.put(InitController.class, mInitController::get);
 
-        mProviders.put(AppOpsController.class, () ->
-                new AppOpsControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(AppOpsController.class, mAppOpsController::get);
 
-        mProviders.put(DisplayNavigationBarController.class, () ->
-                new DisplayNavigationBarController(mContext, getDependency(MAIN_HANDLER)));
+        mProviders.put(DisplayNavigationBarController.class,
+                mDisplayNavigationBarController::get);
 
-        // Put all dependencies above here so the factory can override them if it wants.
-        SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
+        mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
+        mProviders.put(NotificationLockscreenUserManager.class,
+                mNotificationLockscreenUserManager::get);
+        mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
+        mProviders.put(NotificationGroupManager.class, mNotificationGroupManager::get);
+        mProviders.put(NotificationGroupAlertTransferHelper.class,
+                mNotificationGroupAlertTransferHelper::get);
+        mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
+        mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
+        mProviders.put(AmbientPulseManager.class, mAmbientPulseManager::get);
+        mProviders.put(NotificationBlockingHelperManager.class,
+                mNotificationBlockingHelperManager::get);
+        mProviders.put(NotificationRemoteInputManager.class,
+                mNotificationRemoteInputManager::get);
+        mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
+        mProviders.put(NotificationListener.class, mNotificationListener::get);
+        mProviders.put(NotificationLogger.class, mNotificationLogger::get);
+        mProviders.put(NotificationViewHierarchyManager.class,
+                mNotificationViewHierarchyManager::get);
+        mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
+        mProviders.put(SmartReplyController.class, mSmartReplyController::get);
+        mProviders.put(RemoteInputQuickSettingsDisabler.class,
+                mRemoteInputQuickSettingsDisabler::get);
+        mProviders.put(BubbleController.class, mBubbleController::get);
+        mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
 
         sDependency = this;
     }
@@ -484,4 +540,20 @@
             return mDisplayName;
         }
     }
+
+    @Subcomponent
+    public interface DependencyInjector {
+        void createSystemUI(Dependency dependency);
+    }
+
+    public static class DependencyCreator implements Injector {
+        @Override
+        public SystemUI apply(Context context) {
+            Dependency dependency = new Dependency();
+            SystemUIFactory.getInstance().getRootComponent()
+                    .createDependency()
+                    .createSystemUI(dependency);
+            return dependency;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
new file mode 100644
index 0000000..e828b23
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2018 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;
+
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.app.ColorDisplayController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginDependencyProvider;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.IconLogger;
+import com.android.systemui.statusbar.policy.IconLoggerImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import com.android.systemui.tuner.TunablePadding;
+import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.leak.LeakReporter;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies for the root component of sysui injection.
+ * See SystemUI/docs/dagger.md
+ */
+@Module
+public class DependencyProvider {
+
+    @Singleton
+    @Provides
+    @Named(TIME_TICK_HANDLER_NAME)
+    public Handler provideHandler() {
+        HandlerThread thread = new HandlerThread("TimeTick");
+        thread.start();
+        return new Handler(thread.getLooper());
+    }
+
+    @Singleton
+    @Provides
+    @Named(BG_LOOPER_NAME)
+    public Looper provideBgLooper() {
+        HandlerThread thread = new HandlerThread("SysUiBg",
+                Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        return thread.getLooper();
+    }
+
+    @Singleton
+    @Provides
+    @Named(BG_HANDLER_NAME)
+    public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) {
+        return new Handler(bgLooper);
+    }
+
+    @Singleton
+    @Provides
+    @Named(MAIN_HANDLER_NAME)
+    public Handler provideMainHandler() {
+        return new Handler(Looper.getMainLooper());
+    }
+
+    @Singleton
+    @Provides
+    public ActivityStarter provideActivityStarter() {
+        return new ActivityStarterDelegate();
+    }
+
+    @Singleton
+    @Provides
+    public InitController provideInitController() {
+        return new InitController();
+    }
+
+    @Singleton
+    @Provides
+    public ActivityStarterDelegate provideActivityStarterDelegate(ActivityStarter starter) {
+        return (ActivityStarterDelegate) starter;
+    }
+
+    @Singleton
+    @Provides
+    public AsyncSensorManager provideAsyncSensorManager(Context context, PluginManager manager) {
+        return new AsyncSensorManager(context.getSystemService(SensorManager.class),
+                manager);
+
+    }
+
+    @Singleton
+    @Provides
+    public BluetoothController provideBluetoothController(Context context,
+            @Named(BG_LOOPER_NAME) Looper looper) {
+        return new BluetoothControllerImpl(context, looper);
+
+    }
+
+    @Singleton
+    @Provides
+    public LocationController provideLocationController(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper) {
+        return new LocationControllerImpl(context, bgLooper);
+
+    }
+
+    @Singleton
+    @Provides
+    public RotationLockController provideRotationLockController(Context context) {
+        return new RotationLockControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public NetworkController provideNetworkController(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper, DeviceProvisionedController controller) {
+        return new NetworkControllerImpl(context, bgLooper,
+                controller);
+
+    }
+
+    @Singleton
+    @Provides
+    public ZenModeController provideZenModeController(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+        return new ZenModeControllerImpl(context, mainHandler);
+
+    }
+
+    @Singleton
+    @Provides
+    public HotspotController provideHotspotController(Context context) {
+        return new HotspotControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public CastController provideCastController(Context context) {
+        return new CastControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public FlashlightController provideFlashlightController(Context context) {
+        return new FlashlightControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public KeyguardMonitor provideKeyguardMonitor(Context context) {
+        return new KeyguardMonitorImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public UserSwitcherController provideUserSwitcherController(Context context,
+            KeyguardMonitor keyguardMonitor, @Named(MAIN_HANDLER_NAME) Handler mainHandler,
+            ActivityStarter activityStarter) {
+        return new UserSwitcherController(context, keyguardMonitor, mainHandler, activityStarter);
+    }
+
+    @Singleton
+    @Provides
+    public UserInfoController provideUserInfoContrller(Context context) {
+        return new UserInfoControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public BatteryController provideBatteryController(Context context) {
+        return new BatteryControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public ColorDisplayController provideColorDisplayController(Context context) {
+        return new ColorDisplayController(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public ManagedProfileController provideManagedProfileController(Context context) {
+        return new ManagedProfileControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public NextAlarmController provideNextAlarmController(Context context) {
+        return new NextAlarmControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public DataSaverController provideDataSaverController(NetworkController networkController) {
+        return networkController.getDataSaverController();
+    }
+
+    @Singleton
+    @Provides
+    public AccessibilityController provideAccessibilityController(Context context) {
+        return new AccessibilityController(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public DeviceProvisionedController provideDeviceProvisionedController(Context context) {
+        return new DeviceProvisionedControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public PluginManager providePluginManager(Context context) {
+        return new PluginManagerImpl(context, new PluginInitializerImpl());
+
+    }
+
+    @Singleton
+    @Provides
+    public SecurityController provideSecurityController(Context context) {
+        return new SecurityControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public LeakDetector provideLeakDetector() {
+        return LeakDetector.create();
+
+    }
+
+    @Singleton
+    @Provides
+    public LeakReporter provideLeakReporter(Context context, LeakDetector detector,
+            @Nullable @Named(LEAK_REPORT_EMAIL_NAME) String email) {
+        return new LeakReporter(context, detector, email);
+    }
+
+    @Singleton
+    @Provides
+    public GarbageMonitor provideGarbageMonitor(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper, LeakDetector detector, LeakReporter reporter) {
+        return new GarbageMonitor(context, bgLooper, detector, reporter);
+    }
+
+    @Singleton
+    @Provides
+    public TunerService provideTunerService(Context context) {
+        return new TunerServiceImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public StatusBarWindowController provideStatusBarWindowController(Context context) {
+        return new StatusBarWindowController(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public DarkIconDispatcher provideDarkIconDispatcher(Context context) {
+        return new DarkIconDispatcherImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public ConfigurationController provideConfigurationController(Context context) {
+        return new ConfigurationControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public StatusBarIconController provideStatusBarIconController(Context context) {
+        return new StatusBarIconControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public ScreenLifecycle provideScreenLifecycle() {
+        return new ScreenLifecycle();
+    }
+
+    @Singleton
+    @Provides
+    public WakefulnessLifecycle provideWakefulnessLifecycle() {
+        return new WakefulnessLifecycle();
+    }
+
+    @Singleton
+    @Provides
+    public FragmentService provideFragmentService() {
+        return new FragmentService();
+    }
+
+    @Singleton
+    @Provides
+    public ExtensionController provideExtensionController(Context context) {
+        return new ExtensionControllerImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public PluginDependencyProvider providePluginDependency(PluginManager pluginManager) {
+        return new PluginDependencyProvider(pluginManager);
+    }
+
+    @Singleton
+    @Provides
+    public LocalBluetoothManager provideLocalBluetoothController(Context context,
+            @Named(BG_HANDLER_NAME) Handler bgHandler) {
+        return LocalBluetoothManager.create(context, bgHandler,
+                UserHandle.ALL);
+    }
+
+    @Singleton
+    @Provides
+    public VolumeDialogController provideVolumeDialogController(Context context) {
+        return new VolumeDialogControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public MetricsLogger provideMetricsLogger() {
+        return new MetricsLogger();
+
+    }
+
+    @Singleton
+    @Provides
+    public AccessibilityManagerWrapper provideAccessibilityManagerWrapper(Context context) {
+        return new AccessibilityManagerWrapper(context);
+
+    }
+
+    @Singleton
+    @Provides
+    // Creating a new instance will trigger color extraction.
+    // Thankfully this only happens once - during boot - and WallpaperManagerService
+    // loads colors from cache.
+    public SysuiColorExtractor provideSysuiColorExtractor(Context context) {
+        return new SysuiColorExtractor(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public TunablePadding.TunablePaddingService provideTunablePaddingService() {
+        return new TunablePaddingService();
+
+    }
+
+    @Singleton
+    @Provides
+    public ForegroundServiceController provideForegroundService(Context context) {
+        return new ForegroundServiceControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public UiOffloadThread provideUiOffloadThread() {
+        Log.d("TestTest", "provideUiOffloadThread");
+        return new UiOffloadThread();
+    }
+
+    @Singleton
+    @Provides
+    public PowerUI.WarningsUI provideWarningsUi(Context context) {
+        return new PowerNotificationWarnings(context);
+    }
+
+    @Singleton
+    @Provides
+    public IconLogger provideIconLogger(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+            MetricsLogger logger) {
+        return new IconLoggerImpl(context, bgLooper, logger);
+    }
+
+    @Singleton
+    @Provides
+    public LightBarController provideLightBarController(Context context) {
+        return new LightBarController(context);
+    }
+
+    @Singleton
+    @Provides
+    public IWindowManager provideIWindowManager() {
+        return WindowManagerGlobal.getWindowManagerService();
+    }
+
+    @Singleton
+    @Provides
+    public OverviewProxyService provideOverviewProxyService(Context context) {
+        return new OverviewProxyService(context);
+    }
+
+    @Singleton
+    @Provides
+    public VibratorHelper provideVibratorHelper(Context context) {
+        return new VibratorHelper(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public IStatusBarService provideIStatusBarService() {
+        return IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    @Singleton
+    @Provides
+    // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
+// anywhere it is needed.
+    public DisplayMetrics provideDisplayMetrics() {
+        return new DisplayMetrics();
+
+    }
+
+    @Singleton
+    @Provides
+    public LockscreenGestureLogger provideLockscreenGestureLogger() {
+        return new LockscreenGestureLogger();
+    }
+
+    @Singleton
+    @Provides
+    public ShadeController provideShadeController(Context context) {
+        return SysUiServiceProvider.getComponent(context, StatusBar.class);
+    }
+
+    @Singleton
+    @Provides
+    public NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
+            Context context) {
+        return new StatusBarRemoteInputCallback(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public AppOpsController provideAppOpsController(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper) {
+        return new AppOpsControllerImpl(context, bgLooper);
+
+    }
+
+    @Singleton
+    @Provides
+    public DisplayNavigationBarController provideDisplayNavigationBarController(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+        return new DisplayNavigationBarController(context, mainHandler);
+    }
+
+    @Singleton
+    @Provides
+    public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
+        return context.getSystemService(SensorPrivacyManager.class);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 30fbef6..78a1246 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -24,6 +24,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Map;
+import java.util.function.Function;
 
 public abstract class SystemUI implements SysUiServiceProvider {
     public Context mContext;
@@ -61,4 +62,7 @@
 
         n.addExtras(extras);
     }
+
+    public interface Injector extends Function<Context, SystemUI> {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 92aa652..1d8a21d 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -170,7 +170,11 @@
             Class cls;
             try {
                 cls = Class.forName(clsName);
-                mServices[i] = (SystemUI) cls.newInstance();
+                Object o = cls.newInstance();
+                if (o instanceof SystemUI.Injector) {
+                    o = ((SystemUI.Injector) o).apply(this);
+                }
+                mServices[i] = (SystemUI) o;
             } catch(ClassNotFoundException ex){
                 throw new RuntimeException(ex);
             } catch (IllegalAccessException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 867c917..9bc91ee 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,9 +16,11 @@
 
 package com.android.systemui;
 
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.content.Context;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.view.ViewGroup;
 
@@ -26,56 +28,54 @@
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ScrimState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.volume.VolumeDialogComponent;
 
 import java.util.function.Consumer;
 
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
 /**
  * Class factory to provide customizable SystemUI components.
  */
+@Module
 public class SystemUIFactory {
     private static final String TAG = "SystemUIFactory";
 
     static SystemUIFactory mFactory;
+    private SystemUIRootComponent mRootComponent;
 
-    public static SystemUIFactory getInstance() {
-        return mFactory;
+    public static <T extends SystemUIFactory> T getInstance() {
+        return (T) mFactory;
     }
 
     public static void createFromConfig(Context context) {
@@ -88,6 +88,7 @@
             Class<?> cls = null;
             cls = context.getClassLoader().loadClass(clsName);
             mFactory = (SystemUIFactory) cls.newInstance();
+            mFactory.init(context);
         } catch (Throwable t) {
             Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
             throw new RuntimeException(t);
@@ -96,6 +97,18 @@
 
     public SystemUIFactory() {}
 
+    protected void init(Context context) {
+        mRootComponent = DaggerSystemUIFactory_SystemUIRootComponent.builder()
+                .systemUIFactory(this)
+                .dependencyProvider(new com.android.systemui.DependencyProvider())
+                .contextHolder(new ContextHolder(context))
+                .build();
+    }
+
+    public SystemUIRootComponent getRootComponent() {
+        return mRootComponent;
+    }
+
     public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
             ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
         return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -137,33 +150,70 @@
         return new VolumeDialogComponent(systemUi, context);
     }
 
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
+    @Singleton
+    @Provides
+    public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
+        return new KeyguardEnvironmentImpl();
+    }
+
+    @Singleton
+    @Provides
+    public NotificationLockscreenUserManager provideNotificationLockscreenUserManager(
             Context context) {
-        providers.put(StatusBarStateController.class, StatusBarStateController::new);
-        providers.put(NotificationLockscreenUserManager.class,
-                () -> new NotificationLockscreenUserManagerImpl(context));
-        providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
-        providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
-        providers.put(NotificationGroupAlertTransferHelper.class,
-                NotificationGroupAlertTransferHelper::new);
-        providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
-        providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
-        providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
-        providers.put(NotificationBlockingHelperManager.class,
-                () -> new NotificationBlockingHelperManager(context));
-        providers.put(NotificationRemoteInputManager.class,
-                () -> new NotificationRemoteInputManager(context));
-        providers.put(SmartReplyConstants.class,
-                () -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context));
-        providers.put(NotificationListener.class, () -> new NotificationListener(context));
-        providers.put(NotificationLogger.class, NotificationLogger::new);
-        providers.put(NotificationViewHierarchyManager.class,
-                () -> new NotificationViewHierarchyManager(context));
-        providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
-        providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
-        providers.put(SmartReplyController.class, () -> new SmartReplyController());
-        providers.put(RemoteInputQuickSettingsDisabler.class,
-                () -> new RemoteInputQuickSettingsDisabler(context));
-        providers.put(BubbleController.class, () -> new BubbleController(context));
+        return new NotificationLockscreenUserManagerImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public AssistManager provideAssistManager(DeviceProvisionedController controller,
+            Context context) {
+        return new AssistManager(controller, context);
+    }
+
+    @Singleton
+    @Provides
+    public NotificationEntryManager provideNotificationEntryManager(Context context) {
+        return new NotificationEntryManager(context);
+    }
+
+    @Singleton
+    @Provides
+    public EnhancedEstimates provideEnhancedEstimates(Context context) {
+        return new EnhancedEstimatesImpl();
+    }
+
+    @Singleton
+    @Provides
+    @Named(LEAK_REPORT_EMAIL_NAME)
+    @Nullable
+    public String provideLeakReportEmail() {
+        return null;
+    }
+
+    @Singleton
+    @Provides
+    public NotificationListener provideNotificationListener(Context context) {
+        return new NotificationListener(context);
+    }
+
+    @Module
+    protected static class ContextHolder {
+        private Context mContext;
+
+        public ContextHolder(Context context) {
+            mContext = context;
+        }
+
+        @Provides
+        public Context provideContext() {
+            return mContext;
+        }
+    }
+
+    @Singleton
+    @Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+    public interface SystemUIRootComponent {
+        @Singleton
+        Dependency.DependencyInjector createDependency();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1e91ef3..c8595eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,12 +42,16 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Bubbles are a special type of content that can "float" on top of other apps or System UI.
  * Bubbles can be expanded to show more content.
  *
  * The controller manages addition, removal, and visible state of bubbles on screen.
  */
+@Singleton
 public class BubbleController {
     private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
 
@@ -117,6 +121,7 @@
         void onBubbleExpandChanged(boolean isExpanding, float amount);
     }
 
+    @Inject
     public BubbleController(Context context) {
         mContext = context;
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 8906665..466c808 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -183,6 +183,7 @@
     public void updateState(Tile tile) {
         mTile.setIcon(tile.getIcon());
         mTile.setLabel(tile.getLabel());
+        mTile.setSubtitle(tile.getSubtitle());
         mTile.setContentDescription(tile.getContentDescription());
         mTile.setState(tile.getState());
     }
@@ -322,6 +323,14 @@
             return null;
         };
         state.label = mTile.getLabel();
+
+        CharSequence subtitle = mTile.getSubtitle();
+        if (subtitle != null && subtitle.length() > 0) {
+            state.secondaryLabel = subtitle;
+        } else {
+            state.secondaryLabel = null;
+        }
+
         if (mTile.getContentDescription() != null) {
             state.contentDescription = mTile.getContentDescription();
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 8821679..9bfd4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -28,17 +28,22 @@
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Manager which handles high priority notifications that should "pulse" in when the device is
  * dozing and/or in AOD.  The pulse uses the notification's ambient view and pops in briefly
  * before automatically dismissing the alert.
  */
+@Singleton
 public class AmbientPulseManager extends AlertingNotificationManager {
 
     protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
     @VisibleForTesting
     protected long mExtensionTime;
 
+    @Inject
     public AmbientPulseManager(@NonNull final Context context) {
         Resources resources = context.getResources();
         mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f045548..7d80860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -61,10 +61,14 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles tasks and state related to media notifications. For example, there is a 'current' media
  * notification, which this class keeps track of.
  */
+@Singleton
 public class NotificationMediaManager implements Dumpable {
     private static final String TAG = "NotificationMediaManager";
     public static final boolean DEBUG_MEDIA = false;
@@ -157,6 +161,7 @@
         return mEntryManager;
     }
 
+    @Inject
     public NotificationMediaManager(Context context) {
         mContext = context;
         mMediaSessionManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9391737..d1556fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -59,12 +59,16 @@
 import java.util.ArrayList;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Class for handling remote input state over a set of notifications. This class handles things
  * like keeping notifications temporarily that were cancelled as a response to a remote input
  * interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
  * and handling clicks on remote views.
  */
+@Singleton
 public class NotificationRemoteInputManager implements Dumpable {
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
@@ -229,6 +233,7 @@
         return mShadeController;
     }
 
+    @Inject
     public NotificationRemoteInputManager(Context context) {
         mContext = context;
         mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 0702f1b..2524747 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -41,6 +41,9 @@
 import java.util.List;
 import java.util.Stack;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
  * on their group structure. For example, if a notification becomes bundled with another,
@@ -48,6 +51,7 @@
  * tell NotificationListContainer which notifications to display, and inform it of changes to those
  * notifications that might affect their display.
  */
+@Singleton
 public class NotificationViewHierarchyManager {
     private static final String TAG = "NotificationViewHierarchyManager";
 
@@ -123,6 +127,7 @@
         return mShadeController;
     }
 
+    @Inject
     public NotificationViewHierarchyManager(Context context) {
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index e31f90d5..6f1548d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -27,10 +27,14 @@
 
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles when smart replies are added to a notification
  * and clicked upon.
  */
+@Singleton
 public class SmartReplyController {
     private IStatusBarService mBarService;
     private Set<String> mSendingKeys = new ArraySet<>();
@@ -38,7 +42,7 @@
     private final NotificationEntryManager mEntryManager =
             Dependency.get(NotificationEntryManager.class);
 
-
+    @Inject
     public SmartReplyController() {
         mBarService = Dependency.get(IStatusBarService.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 3f84416..087b655 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -35,9 +35,13 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Tracks and reports on {@link StatusBarState}.
  */
+@Singleton
 public class StatusBarStateController implements CallbackController<StateListener> {
     private static final String TAG = "SbStateController";
 
@@ -101,6 +105,10 @@
     public static final int RANK_STACK_SCROLLER = 2;
     public static final int RANK_SHELF = 3;
 
+    @Inject
+    public StatusBarStateController() {
+    }
+
     public int getState() {
         return mState;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fce7980..abb7b416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -25,10 +25,14 @@
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A manager that ensures that notifications are visually stable. It will suppress reorderings
  * and reorder at the right time when they are out of view.
  */
+@Singleton
 public class VisualStabilityManager implements OnHeadsUpChangedListener {
 
     private final ArrayList<Callback> mCallbacks =  new ArrayList<>();
@@ -42,6 +46,10 @@
     private ArraySet<View> mAddedChildren = new ArraySet<>();
     private boolean mPulsing;
 
+    @Inject
+    public VisualStabilityManager() {
+    }
+
     /**
      * Add a callback to invoke when reordering is allowed again.
      * @param callback
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9f02e54..eb1fc30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -40,10 +40,14 @@
 import java.util.Collection;
 import java.util.Collections;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles notification logging, in particular, logging which notifications are visible and which
  * are not.
  */
+@Singleton
 public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
 
@@ -145,6 +149,7 @@
         }
     };
 
+    @Inject
     public NotificationLogger() {
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1d79152..8b0a682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -800,7 +800,7 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, long delay,
+    public long performRemoveAnimation(long duration, long delay,
             float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
         enableAppearDrawing(true);
@@ -812,6 +812,7 @@
         } else if (onFinishedRunnable != null) {
             onFinishedRunnable.run();
         }
+        return 0;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 91d08ff..a58c7cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2612,6 +2612,29 @@
     }
 
     @Override
+    public long performRemoveAnimation(long duration, long delay, float translationDirection,
+            boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
+        if (mMenuRow.isMenuVisible()) {
+            Animator anim = getTranslateViewAnimator(0f, null /* listener */);
+            if (anim != null) {
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        ExpandableNotificationRow.super.performRemoveAnimation(
+                                duration, delay, translationDirection, isHeadsUpAnimation,
+                                endLocation, onFinishedRunnable, animationListener);
+                    }
+                });
+                anim.start();
+                return anim.getDuration();
+            }
+        }
+        return super.performRemoveAnimation(duration, delay, translationDirection,
+                isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+    }
+
+    @Override
     protected void onAppearAnimationFinished(boolean wasAppearing) {
         super.onAppearAnimationFinished(wasAppearing);
         if (wasAppearing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index b1fa6a5..d1a89b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -312,16 +312,19 @@
      * @param duration The duration of the remove animation.
      * @param delay The delay of the animation
      * @param translationDirection The direction value from [-1 ... 1] indicating in which the
- *                             animation should be performed. A value of -1 means that The
- *                             remove animation should be performed upwards,
- *                             such that the  child appears to be going away to the top. 1
- *                             Should mean the opposite.
+     *                             animation should be performed. A value of -1 means that The
+     *                             remove animation should be performed upwards,
+     *                             such that the  child appears to be going away to the top. 1
+     *                             Should mean the opposite.
      * @param isHeadsUpAnimation Is this a headsUp animation.
      * @param endLocation The location where the horizonal heads up disappear animation should end.
      * @param onFinishedRunnable A runnable which should be run when the animation is finished.
      * @param animationListener An animation listener to add to the animation.
+     *
+     * @return The additional delay, in milliseconds, that this view needs to add before the
+     * animation starts.
      */
-    public abstract void performRemoveAnimation(long duration,
+    public abstract long performRemoveAnimation(long duration,
             long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 16796dd..607d96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -34,10 +34,14 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Manager for the notification blocking helper - tracks and helps create the blocking helper
  * affordance.
  */
+@Singleton
 public class NotificationBlockingHelperManager {
     /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
     private static final boolean DEBUG = false;
@@ -54,6 +58,7 @@
      */
     private boolean mIsShadeExpanded;
 
+    @Inject
     public NotificationBlockingHelperManager(Context context) {
         mContext = context;
         mNonBlockablePkgs = new HashSet<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 2e45527..ac4e583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -57,10 +57,14 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
  * closing guts, and keeping track of the currently exposed notification guts.
  */
+@Singleton
 public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
     private static final String TAG = "NotificationGutsManager";
 
@@ -91,6 +95,7 @@
     @VisibleForTesting
     protected String mKeyToRemoveOnGutsClosed;
 
+    @Inject
     public NotificationGutsManager(Context context) {
         mContext = context;
         mAccessibilityManager = (AccessibilityManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 1b40c06..eaa2eaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -190,12 +190,13 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, long delay,
+    public long performRemoveAnimation(long duration, long delay,
             float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener) {
         // TODO: Use duration
         setContentVisible(false);
+        return 0;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dbe6e8e..67a5cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,10 +16,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
-        .ANIMATION_DURATION_SWIPE;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
 
 import android.animation.Animator;
@@ -651,6 +649,8 @@
                 < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
                 || mAmbientState.isDark())) {
             drawBackground(canvas);
+        } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
+            drawHeadsUpBackground(canvas);
         }
 
         if (DEBUG) {
@@ -749,6 +749,32 @@
                 mCornerRadius, mCornerRadius, mBackgroundPaint);
     }
 
+    private void drawHeadsUpBackground(Canvas canvas) {
+        int left = mSidePaddings;
+        int right = getWidth() - mSidePaddings;
+
+        float top = getHeight();
+        float bottom = 0;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE
+                    && child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
+                    top = Math.min(top, row.getTranslationY());
+                    bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
+                }
+            }
+        }
+
+        if (top < bottom) {
+            canvas.drawRoundRect(
+                    left, top, right, bottom,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+        }
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
@@ -2157,19 +2183,7 @@
             }
             return;
         }
-        NotificationSection firstSection = getFirstVisibleSection();
-        int top = 0;
-        if (firstSection != null) {
-            ActivatableNotificationView firstView = firstSection.getFirstVisibleChild();
-            // Round Y up to avoid seeing the background during animation
-            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
-            if (mAnimateNextBackgroundTop || firstSection.isTargetTop(finalTranslationY)) {
-                // we're ending up at the same location as we are now, lets just skip the animation
-                top = finalTranslationY;
-            } else {
-                top = (int) Math.ceil(firstView.getTranslationY());
-            }
-        }
+        int top = getSectionTopOrFinalTop(getFirstVisibleSection(), mAnimateNextBackgroundTop);
         NotificationSection lastSection = getLastVisibleSection();
         ActivatableNotificationView lastView =
                 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
@@ -2177,21 +2191,8 @@
                         : lastSection == null ? null : lastSection.getLastVisibleChild();
         int bottom;
         if (lastView != null) {
-            int finalTranslationY;
-            if (lastView == mShelf) {
-                finalTranslationY = (int) mShelf.getTranslationY();
-            } else {
-                finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
-            }
-            int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
-            int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
-            if (mAnimateNextBackgroundBottom || lastSection.isTargetBottom(finalBottom)) {
-                // we're ending up at the same location as we are now, lets just skip the animation
-                bottom = finalBottom;
-            } else {
-                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
-                        - lastView.getClipBottomAmount());
-            }
+            bottom = getSectionBottomOrFinalBottom(
+                    lastSection, lastView, mAnimateNextBackgroundBottom);
         } else {
             top = mTopPadding;
             bottom = top;
@@ -2207,6 +2208,57 @@
         setSectionBoundsByPriority(left, right, top, bottom, mSections[0], mSections[1]);
     }
 
+    private int getSectionTopOrFinalTop(
+            @Nullable NotificationSection section, boolean alreadyAnimating) {
+        int top = 0;
+        if (section != null) {
+            ActivatableNotificationView firstView = section.getFirstVisibleChild();
+            // Round Y up to avoid seeing the background during animation
+            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+            if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
+                // we're ending up at the same location as we are now, let's just skip the animation
+                top = finalTranslationY;
+            } else {
+                top = (int) Math.ceil(firstView.getTranslationY());
+            }
+        }
+        return top;
+    }
+
+    private int getSectionBottomOrFinalBottom(
+            @Nullable NotificationSection section, boolean alreadyAnimating) {
+        return section == null ? 0
+                : getSectionBottomOrFinalBottom(
+                        section, section.getLastVisibleChild(), alreadyAnimating);
+    }
+
+    private int getSectionBottomOrFinalBottom(
+            NotificationSection section,
+            ActivatableNotificationView lastView,
+            boolean alreadyAnimating) {
+        int bottom = 0;
+        if (lastView != null) {
+            float finalTranslationY;
+            if (lastView == mShelf) {
+                finalTranslationY = mShelf.getTranslationY();
+            } else {
+                finalTranslationY = ViewState.getFinalTranslationY(lastView);
+            }
+            int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
+            // Round Y down to avoid seeing the background during animation
+            int finalBottom = (int) Math.floor(
+                    finalTranslationY + finalHeight - lastView.getClipBottomAmount());
+            if (alreadyAnimating || section.isTargetBottom(finalBottom)) {
+                // we're ending up at the same location as we are now, lets just skip the animation
+                bottom = finalBottom;
+            } else {
+                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+                        - lastView.getClipBottomAmount());
+            }
+        }
+        return bottom;
+    }
+
     private void setSectionBoundsByPriority(int left, int right, int top, int bottom,
             NotificationSection highPrioritySection, NotificationSection lowPrioritySection) {
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
@@ -2214,13 +2266,14 @@
             ActivatableNotificationView lastChildAboveGap = getLastHighPriorityChild();
             ActivatableNotificationView firstChildBelowGap = getFirstLowPriorityChild();
             if (lastChildAboveGap != null && firstChildBelowGap != null) {
-                int gapTop =
-                        (int) Math.max(top,
-                                Math.min(lastChildAboveGap.getTranslationY()
-                                                + lastChildAboveGap.getActualHeight(),
-                                        bottom));
-                int gapBottom = (int) Math.max(top,
-                        Math.min(firstChildBelowGap.getTranslationY(), bottom));
+                int gapTop = getSectionBottomOrFinalBottom(
+                        highPrioritySection, mAnimateNextSectionBoundsChange);
+                gapTop = Math.max(top, Math.min(gapTop, bottom));
+
+                int gapBottom = getSectionTopOrFinalTop(
+                        lowPrioritySection, mAnimateNextSectionBoundsChange);
+                gapBottom = Math.max(top, Math.min(gapBottom, bottom));
+
                 highPrioritySection.getBounds().set(left, top, right, gapTop);
                 lowPrioritySection.getBounds().set(left, gapBottom, right, bottom);
             } else if (lastChildAboveGap != null) {
@@ -5574,15 +5627,21 @@
             if (translatingParentView != null && row == translatingParentView) {
                 mSwipeHelper.clearExposedMenuView();
                 mSwipeHelper.clearTranslatingParentView();
+                if (row instanceof ExpandableNotificationRow) {
+                    mHeadsUpManager.setMenuShown(
+                            ((ExpandableNotificationRow) row).getEntry(), false);
+
+                }
             }
         }
 
         @Override
         public void onMenuShown(View row) {
             if (row instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
-                        ((ExpandableNotificationRow) row).getStatusBarNotification()
-                                .getPackageName());
+                        notificationRow.getStatusBarNotification().getPackageName());
+                mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
             }
             mSwipeHelper.onMenuShown(row);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d690547..19fce48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -406,13 +406,8 @@
 
                 }
                 changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
-                        0 /* delay */, translationDirection,  false /* isHeadsUpAppear */,
-                        0, new Runnable() {
-                    @Override
-                    public void run() {
-                        removeTransientView(changingView);
-                    }
-                }, null);
+                        0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
+                        0, () -> removeTransientView(changingView), null);
             } else if (event.animationType ==
                 NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
                 if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
@@ -507,10 +502,11 @@
                     // We need to add the global animation listener, since once no animations are
                     // running anymore, the panel will instantly hide itself. We need to wait until
                     // the animation is fully finished for this though.
-                    changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
-                                    + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
-                            true /* isHeadsUpAppear */, targetLocation, endRunnable,
-                            getGlobalAnimationFinishedListener());
+                    long removeAnimationDelay = changingView.performRemoveAnimation(
+                            ANIMATION_DURATION_HEADS_UP_DISAPPEAR + ANIMATION_DELAY_HEADS_UP,
+                            extraDelay, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+                            endRunnable, getGlobalAnimationFinishedListener());
+                    mAnimationProperties.delay += removeAnimationDelay;
                 } else if (endRunnable != null) {
                     endRunnable.run();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index aa0b7b6..f4cfd41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -264,6 +264,17 @@
         }
     }
 
+    /**
+     * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
+     * area if it's pinned until it's hidden again.
+     */
+    public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) {
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
+        if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
+            ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  HeadsUpManager public methods overrides:
 
@@ -469,6 +480,14 @@
     //  HeadsUpEntryPhone:
 
     protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+
+        private boolean mMenuShownPinned;
+
+        @Override
+        protected boolean isSticky() {
+            return super.isSticky() || mMenuShownPinned;
+        }
+
         public void setEntry(@NonNull final NotificationData.Entry entry) {
            Runnable removeHeadsUpRunnable = () -> {
                 if (!mVisualStabilityManager.isReorderingAllowed()) {
@@ -510,6 +529,25 @@
                 updateEntry(false /* updatePostTime */);
             }
         }
+
+        public void setMenuShownPinned(boolean menuShownPinned) {
+            if (mMenuShownPinned == menuShownPinned) {
+                return;
+            }
+
+            mMenuShownPinned = menuShownPinned;
+            if (menuShownPinned) {
+                removeAutoRemovalCallbacks();
+            } else {
+                updateEntry(false /* updatePostTime */);
+            }
+        }
+
+        @Override
+        public void reset() {
+            super.reset();
+            mMenuShownPinned = false;
+        }
     }
 
     public interface AnimationStateHandler {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index b3d0bf8..e541e14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -20,15 +20,23 @@
 
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Executes actions that require the screen to be unlocked. Delegates the actual handling to an
  * implementation passed via {@link #setDismissHandler}.
  */
+@Singleton
 public class KeyguardDismissUtil implements KeyguardDismissHandler {
     private static final String TAG = "KeyguardDismissUtil";
 
     private volatile KeyguardDismissHandler mDismissHandler;
 
+    @Inject
+    public KeyguardDismissUtil() {
+    }
+
     /** Sets the actual {@link DismissHandler} implementation. */
     public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
         mDismissHandler = dismissHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index dd81c4e..6732bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -42,11 +42,15 @@
 import java.util.ArrayList;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A helper class dealing with the alert interactions between {@link NotificationGroupManager},
  * {@link HeadsUpManager}, {@link AmbientPulseManager}. In particular, this class deals with keeping
  * the correct notification in a group alerting based off the group suppression.
  */
+@Singleton
 public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
         OnAmbientChangedListener, StateListener {
 
@@ -73,6 +77,7 @@
 
     private boolean mIsDozing;
 
+    @Inject
     public NotificationGroupAlertTransferHelper() {
         Dependency.get(StatusBarStateController.class).addCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8f4369a..3c1c076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -39,9 +39,13 @@
 import java.util.Map;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A class to handle notifications and their corresponding groups.
  */
+@Singleton
 public class NotificationGroupManager implements OnHeadsUpChangedListener,
         OnAmbientChangedListener, StateListener {
 
@@ -54,6 +58,7 @@
     private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
     private boolean mIsUpdatingUnchangedGroup;
 
+    @Inject
     public NotificationGroupManager() {
         Dependency.get(StatusBarStateController.class).addCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index e7280643..a02c9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,7 +31,6 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
index c2933e1..2a10db6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -27,9 +27,13 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Let {@link RemoteInputView} to control the visibility of QuickSetting.
  */
+@Singleton
 public class RemoteInputQuickSettingsDisabler
         implements ConfigurationController.ConfigurationListener {
 
@@ -39,6 +43,7 @@
     private int mLastOrientation;
     @VisibleForTesting CommandQueue mCommandQueue;
 
+    @Inject
     public RemoteInputQuickSettingsDisabler(Context context) {
         mContext = context;
         mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 71d6e54..6193159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -27,6 +29,11 @@
 
 import com.android.systemui.R;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Singleton
 public final class SmartReplyConstants extends ContentObserver {
 
     private static final String TAG = "SmartReplyConstants";
@@ -47,7 +54,8 @@
     private final Context mContext;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
 
-    public SmartReplyConstants(Handler handler, Context context) {
+    @Inject
+    public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) {
         super(handler);
 
         mContext = context;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 0c8d137..18bf75e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -29,9 +29,10 @@
             mComponents = ((SysuiTestableContext) context).getComponents();
         }
         mContext = context;
-        if (SystemUIFactory.getInstance() == null) {
-            SystemUIFactory.createFromConfig(context);
-        }
+        SystemUIFactory.createFromConfig(context);
+        SystemUIFactory.getInstance().getRootComponent()
+                .createDependency()
+                .createSystemUI(this);
         start();
     }
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index a533640..fd20437 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -146,7 +146,7 @@
     protected void startServiceForUser(int userId) {
         UserBackupManagerService userBackupManagerService =
                 UserBackupManagerService.createAndInitializeService(
-                        mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+                        userId, mContext, mTrampoline, mBackupThread, mTransportWhitelist);
         startServiceForUser(userId, userBackupManagerService);
     }
 
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 796ef40..d357404 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -251,6 +251,7 @@
     private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
+    private final @UserIdInt int mUserId;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final TransportManager mTransportManager;
 
@@ -372,10 +373,11 @@
      * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
      * includes setting up the directories where we keep our bookkeeping and transport management.
      *
-     * @see #createAndInitializeService(Context, Trampoline, HandlerThread, File, File,
+     * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
      *     TransportManager)
      */
     static UserBackupManagerService createAndInitializeService(
+            @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
             HandlerThread backupThread,
@@ -399,12 +401,13 @@
         File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
 
         return createAndInitializeService(
-                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
     }
 
     /**
      * Creates an instance of {@link UserBackupManagerService}.
      *
+     * @param userId The user which this service is for.
      * @param context The system server context.
      * @param trampoline A reference to the proxy to {@link BackupManagerService}.
      * @param backupThread The thread running backup/restore operations for the user.
@@ -415,6 +418,7 @@
      */
     @VisibleForTesting
     public static UserBackupManagerService createAndInitializeService(
+            @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
             HandlerThread backupThread,
@@ -422,16 +426,18 @@
             File dataDir,
             TransportManager transportManager) {
         return new UserBackupManagerService(
-                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
     }
 
     private UserBackupManagerService(
+            @UserIdInt int userId,
             Context context,
             Trampoline parent,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
+        mUserId = userId;
         mContext = checkNotNull(context, "context cannot be null");
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d114397..3bfd363 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -15711,7 +15712,7 @@
     }
 
     private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
-            boolean doingAll, long now) {
+            boolean doingAll, long now, boolean cycleReEval) {
         if (mAdjSeq == app.adjSeq) {
             if (app.adjSeq == app.completedAdjSeq) {
                 // This adjustment has already been computed successfully.
@@ -15777,20 +15778,21 @@
                 app.systemNoUi = false;
             }
             if (!app.systemNoUi) {
-              if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
-                  // screen on, promote UI
-                  app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
-                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
-              } else {
-                  // screen off, restrict UI scheduling
-                  app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
-              }
+                if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+                    // screen on, promote UI
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                } else {
+                    // screen off, restrict UI scheduling
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+                }
             }
+            app.setCurRawProcState(app.getCurProcState());
             app.curAdj = app.maxAdj;
             app.completedAdjSeq = app.adjSeq;
             // if curAdj is less than prevAppAdj, then this process was promoted
-            return app.curAdj < prevAppAdj;
+            return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
         }
 
         app.systemNoUi = false;
@@ -16032,8 +16034,13 @@
         // By default, we use the computed adjustment.  It may be changed if
         // there are applications dependent on our services or providers, but
         // this gives us a baseline and makes sure we don't get into an
-        // infinite recursion.
-        app.setCurRawAdj(adj);
+        // infinite recursion. If we're re-evaluating due to cycles, use the previously computed
+        // values.
+        app.setCurRawAdj(!cycleReEval ? adj : Math.min(adj, app.getCurRawAdj()));
+        app.setCurRawProcState(!cycleReEval
+                ? procState
+                : Math.min(procState, app.getCurRawProcState()));
+
         app.hasStartedServices = false;
         app.adjSeq = mAdjSeq;
 
@@ -16135,21 +16142,15 @@
                     boolean trackedProcState = false;
                     if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                         ProcessRecord client = cr.binding.client;
-                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
-                        if (client.containsCycle) {
-                            // We've detected a cycle. We should retry computeOomAdjLocked later in
-                            // case a later-checked connection from a client  would raise its
-                            // priority legitimately.
-                            app.containsCycle = true;
-                            // If the client has not been completely evaluated, skip using its
-                            // priority. Else use the conservative value for now and look for a
-                            // better state in the next iteration.
-                            if (client.completedAdjSeq < mAdjSeq) {
-                                continue;
-                            }
+                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                            continue;
                         }
+
                         int clientAdj = client.getCurRawAdj();
-                        int clientProcState = client.getCurProcState();
+                        int clientProcState = client.getCurRawProcState();
+
                         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                             // If the other app is cached for any reason, for purposes here
                             // we are going to consider it empty.  The specific cached state
@@ -16234,6 +16235,7 @@
                                 }
                                 if (adj >  newAdj) {
                                     adj = newAdj;
+                                    app.setCurRawAdj(adj);
                                     adjType = "service";
                                 }
                             }
@@ -16305,6 +16307,7 @@
                         }
                         if (procState > clientProcState) {
                             procState = clientProcState;
+                            app.setCurRawProcState(procState);
                             if (adjType == null) {
                                 adjType = "service";
                             }
@@ -16336,6 +16339,7 @@
                         if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
                                 && a.isActivityVisible()) {
                             adj = ProcessList.FOREGROUND_APP_ADJ;
+                            app.setCurRawAdj(adj);
                             if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                                     schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -16377,21 +16381,15 @@
                     // Being our own client is not interesting.
                     continue;
                 }
-                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
-                if (client.containsCycle) {
-                    // We've detected a cycle. We should retry computeOomAdjLocked later in
-                    // case a later-checked connection from a client  would raise its
-                    // priority legitimately.
-                    app.containsCycle = true;
-                    // If the client has not been completely evaluated, skip using its
-                    // priority. Else use the conservative value for now and look for a
-                    // better state in the next iteration.
-                    if (client.completedAdjSeq < mAdjSeq) {
-                        continue;
-                    }
+                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                    continue;
                 }
+
                 int clientAdj = client.getCurRawAdj();
-                int clientProcState = client.getCurProcState();
+                int clientProcState = client.getCurRawProcState();
+
                 if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                     // If the other app is cached for any reason, for purposes here
                     // we are going to consider it empty.
@@ -16405,6 +16403,7 @@
                     } else {
                         adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
                                 ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+                        app.setCurRawAdj(adj);
                         adjType = "provider";
                     }
                     app.cached &= client.cached;
@@ -16440,6 +16439,7 @@
                 conn.trackProcState(clientProcState, mAdjSeq, now);
                 if (procState > clientProcState) {
                     procState = clientProcState;
+                    app.setCurRawProcState(procState);
                 }
                 if (client.getCurrentSchedulingGroup() > schedGroup) {
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -16465,6 +16465,7 @@
             if (cpr.hasExternalProcessHandles()) {
                 if (adj > ProcessList.FOREGROUND_APP_ADJ) {
                     adj = ProcessList.FOREGROUND_APP_ADJ;
+                    app.setCurRawAdj(adj);
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                     app.cached = false;
                     app.adjType = "ext-provider";
@@ -16476,6 +16477,7 @@
                 }
                 if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
                     procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                    app.setCurRawProcState(procState);
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                 "Raise procstate to external provider: " + app);
@@ -16620,6 +16622,7 @@
         app.curAdj = app.modifyRawOomAdj(adj);
         app.setCurrentSchedulingGroup(schedGroup);
         app.setCurProcState(procState);
+        app.setCurRawProcState(procState);
         app.setHasForegroundActivities(foregroundActivities);
         app.completedAdjSeq = mAdjSeq;
 
@@ -16627,6 +16630,44 @@
         return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
     }
 
+    /**
+     * Checks if for the given app and client, there's a cycle that should skip over the client
+     * for now or use partial values to evaluate the effect of the client binding.
+     * @param app
+     * @param client
+     * @param procState procstate evaluated so far for this app
+     * @param adj oom_adj evaluated so far for this app
+     * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
+     *                    evaluation.
+     * @return whether to skip using the client connection at this time
+     */
+    private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+            int procState, int adj, boolean cycleReEval) {
+        if (client.containsCycle) {
+            // We've detected a cycle. We should retry computeOomAdjLocked later in
+            // case a later-checked connection from a client  would raise its
+            // priority legitimately.
+            app.containsCycle = true;
+            // If the client has not been completely evaluated, check if it's worth
+            // using the partial values.
+            if (client.completedAdjSeq < mAdjSeq) {
+                if (cycleReEval) {
+                    // If the partial values are no better, skip until the next
+                    // attempt
+                    if (client.getCurRawProcState() >= procState
+                            && client.getCurRawAdj() >= adj) {
+                        return true;
+                    }
+                    // Else use the client's partial procstate and adj to adjust the
+                    // effect of the binding
+                } else {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private static final class RecordPssRunnable implements Runnable {
         private final ActivityManagerService mService;
         private final ProcessRecord mProc;
@@ -17493,7 +17534,7 @@
             return false;
         }
 
-        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
+        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
 
         return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
     }
@@ -17868,12 +17909,14 @@
         for (int i=N-1; i>=0; i--) {
             ProcessRecord app = mProcessList.mLruProcesses.get(i);
             app.containsCycle = false;
+            app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+            app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
         }
         for (int i=N-1; i>=0; i--) {
             ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
                 app.procStateChanged = false;
-                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now, false);
 
                 // if any app encountered a cycle, we need to perform an additional loop later
                 retryCycles |= app.containsCycle;
@@ -17976,8 +18019,8 @@
             for (int i=0; i<N; i++) {
                 ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-
-                    if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
+                    if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now,
+                            true)) {
                         retryCycles = true;
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c4b7150..c15b7c7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -153,6 +153,7 @@
     int trimMemoryLevel;        // Last selected memory trimming level
     private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
     private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+    private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
     int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
     int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
     int pssStatType;            // The type of stat collection that we are currently requesting
@@ -902,6 +903,7 @@
         if (mRepProcState > newState) {
             mRepProcState = newState;
             setCurProcState(newState);
+            setCurRawProcState(newState);
             for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
                 StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
                         uid, processName, pkgList.keyAt(ipkg),
@@ -984,6 +986,14 @@
         return mCurProcState;
     }
 
+    void setCurRawProcState(int curRawProcState) {
+        mCurRawProcState = curRawProcState;
+    }
+
+    int getCurRawProcState() {
+        return mCurRawProcState;
+    }
+
     void setReportedProcState(int repProcState) {
         mRepProcState = repProcState;
         for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 78b3c15..52eccca 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,6 +17,13 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -27,6 +34,8 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.EventLog;
@@ -34,14 +43,15 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 
 class AutomaticBrightnessController {
     private static final String TAG = "AutomaticBrightnessController";
 
-    private static final boolean DEBUG = false;
     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
 
     // If true, enables the use of the screen auto-brightness adjustment setting.
@@ -66,6 +76,8 @@
     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
     private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
+    private static final int MSG_UPDATE_FOREGROUND_APP = 4;
+    private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
 
     // Length of the ambient light horizon used to calculate the long term estimate of ambient
     // light.
@@ -126,6 +138,8 @@
     private final HysteresisLevels mAmbientBrightnessThresholds;
     private final HysteresisLevels mScreenBrightnessThresholds;
 
+    private boolean mLoggingEnabled;
+
     // Amount of time to delay auto-brightness after screen on while waiting for
     // the light sensor to warm-up in milliseconds.
     // May be 0 if no warm-up is required.
@@ -192,6 +206,19 @@
     private float mShortTermModelAnchor;
     private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
 
+    // Context-sensitive brightness configurations require keeping track of the foreground app's
+    // package name and category, which is done by registering a TaskStackListener to call back to
+    // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
+    // package namd and PackageManager to get its category (so might as well cache them).
+    private int mUserId;
+    private String mForegroundAppPackageName;
+    private String mPendingForegroundAppPackageName;
+    private @ApplicationInfo.Category int mForegroundAppCategory;
+    private @ApplicationInfo.Category int mPendingForegroundAppCategory;
+    private TaskStackListenerImpl mTaskStackListener;
+    private IActivityTaskManager mActivityTaskManager;
+    private PackageManagerInternal mPackageManagerInternal;
+
     public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, BrightnessMappingStrategy mapper,
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -226,6 +253,42 @@
         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
         }
+
+        mUserId = ActivityManager.getCurrentUser();
+        mActivityTaskManager = ActivityTaskManager.getService();
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mTaskStackListener = new TaskStackListenerImpl();
+        mForegroundAppPackageName = null;
+        mPendingForegroundAppPackageName = null;
+        mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+        mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+    }
+
+    /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
+     * Update the current user's ID.
+     *
+     * @param userId
+     *      The current user's ID.
+     */
+    public void onSwitchUser(int userId) {
+        mUserId = userId;
     }
 
     public int getAutomaticScreenBrightness() {
@@ -290,7 +353,7 @@
         }
         final int oldPolicy = mDisplayPolicy;
         mDisplayPolicy = policy;
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
         }
         if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
@@ -317,7 +380,7 @@
         mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
         mShortTermModelValid = true;
         mShortTermModelAnchor = mAmbientLux;
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
         }
         return true;
@@ -330,7 +393,7 @@
     }
 
     private void invalidateShortTermModel() {
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "ShortTermModel: invalidate user data");
         }
         mShortTermModelValid = false;
@@ -383,7 +446,11 @@
         pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
         pw.println("  mBrightnessAdjustmentSampleOldBrightness="
                 + mBrightnessAdjustmentSampleOldBrightness);
-        pw.println("  mShortTermModelValid=" + mShortTermModelValid);
+        pw.println("  mUserId=" + mUserId);
+        pw.println("  mForegroundAppPackageName=" + mForegroundAppPackageName);
+        pw.println("  mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+        pw.println("  mForegroundAppCategory=" + mForegroundAppCategory);
+        pw.println("  mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
 
         pw.println();
         mBrightnessMapper.dump(pw);
@@ -399,6 +466,7 @@
                 mLightSensorEnabled = true;
                 mLightSensorEnableTime = SystemClock.uptimeMillis();
                 mCurrentLightSensorRate = mInitialLightSensorRate;
+                registerForegroundAppUpdater();
                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
                         mCurrentLightSensorRate * 1000, mHandler);
                 return true;
@@ -411,6 +479,7 @@
             mAmbientLightRingBuffer.clear();
             mCurrentLightSensorRate = -1;
             mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+            unregisterForegroundAppUpdater();
             mSensorManager.unregisterListener(mLightSensorListener);
         }
         return false;
@@ -441,7 +510,7 @@
     private void adjustLightSensorRate(int lightSensorRate) {
         // if the light sensor rate changed, update the sensor listener
         if (lightSensorRate != mCurrentLightSensorRate) {
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "adjustLightSensorRate: " +
                         "previousRate=" + mCurrentLightSensorRate + ", " +
                         "currentRate=" + lightSensorRate);
@@ -458,7 +527,7 @@
     }
 
     private void setAmbientLux(float lux) {
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "setAmbientLux(" + lux + ")");
         }
         if (lux < 0) {
@@ -476,7 +545,7 @@
             final float maxAmbientLux =
                 mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
             if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
                             minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
                 }
@@ -490,7 +559,7 @@
     }
 
     private float calculateAmbientLux(long now, long horizon) {
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
         }
         final int N = mAmbientLightRingBuffer.size();
@@ -509,7 +578,7 @@
                 break;
             }
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
                     mAmbientLightRingBuffer.getTime(endIndex) + ", " +
                     mAmbientLightRingBuffer.getLux(endIndex) + ")");
@@ -527,7 +596,7 @@
             final long startTime = eventTime - now;
             float weight = calculateWeight(startTime, endTime);
             float lux = mAmbientLightRingBuffer.getLux(i);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
                         "lux=" + lux + ", " +
                         "weight=" + weight);
@@ -536,7 +605,7 @@
             sum += lux * weight;
             endTime = startTime;
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "calculateAmbientLux: " +
                     "totalWeight=" + totalWeight + ", " +
                     "newAmbientLux=" + (sum / totalWeight));
@@ -591,7 +660,7 @@
             final long timeWhenSensorWarmedUp =
                 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
             if (time < timeWhenSensorWarmedUp) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "updateAmbientLux: Sensor not  ready yet: " +
                             "time=" + time + ", " +
                             "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
@@ -602,7 +671,7 @@
             }
             setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
             mAmbientLuxValid = true;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAmbientLux: Initializing: " +
                         "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
                         "mAmbientLux=" + mAmbientLux);
@@ -630,10 +699,10 @@
                         && fastAmbientLux <= mAmbientDarkeningThreshold
                         && nextDarkenTransition <= time)) {
             setAmbientLux(fastAmbientLux);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAmbientLux: "
                         + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
-                        + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+                        + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
                         + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
                         + "mAmbientLux=" + mAmbientLux);
             }
@@ -650,7 +719,7 @@
         // weighted ambient lux or not.
         nextTransitionTime =
                 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
                     nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
         }
@@ -662,7 +731,8 @@
             return;
         }
 
-        float value = mBrightnessMapper.getBrightness(mAmbientLux);
+        float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+                mForegroundAppCategory);
 
         int newScreenAutoBrightness =
                 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
@@ -673,7 +743,7 @@
         if (mScreenAutoBrightness != -1
                 && newScreenAutoBrightness > mScreenDarkeningThreshold
                 && newScreenAutoBrightness < mScreenBrighteningThreshold) {
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
                         + " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
             }
@@ -681,8 +751,7 @@
         }
 
         if (mScreenAutoBrightness != newScreenAutoBrightness) {
-
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAutoBrightness: " +
                         "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
                         "newScreenAutoBrightness=" + newScreenAutoBrightness);
@@ -718,18 +787,11 @@
                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
     }
 
-    private void cancelBrightnessAdjustmentSample() {
-        if (mBrightnessAdjustmentSamplePending) {
-            mBrightnessAdjustmentSamplePending = false;
-            mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
-        }
-    }
-
     private void collectBrightnessAdjustmentSample() {
         if (mBrightnessAdjustmentSamplePending) {
             mBrightnessAdjustmentSamplePending = false;
             if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
                             "lux=" + mAmbientLux + ", " +
                             "brightness=" + mScreenAutoBrightness + ", " +
@@ -745,6 +807,68 @@
         }
     }
 
+    // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
+    // foreground app's package name and category and correct the brightness accordingly.
+    private void registerForegroundAppUpdater() {
+        try {
+            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+            // This will not get called until the foreground app changes for the first time, so
+            // call it explicitly to get the current foreground app's info.
+            updateForegroundApp();
+        } catch (RemoteException e) {
+            // Nothing to do.
+        }
+    }
+
+    private void unregisterForegroundAppUpdater() {
+        try {
+            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+        } catch (RemoteException e) {
+            // Nothing to do.
+        }
+        mForegroundAppPackageName = null;
+        mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+    }
+
+    // Set the foreground app's package name and category, so brightness can be corrected per app.
+    private void updateForegroundApp() {
+        // The ActivityTaskManager's lock tends to get contended, so this is done in a background
+        // thread and applied via this thread's handler synchronously.
+        BackgroundThread.getHandler().post(new Runnable() {
+            public void run() {
+                try {
+                    // The foreground app is the top activity of the focused tasks stack.
+                    final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
+                    if (info == null || info.topActivity == null) {
+                        return;
+                    }
+                    final String packageName = info.topActivity.getPackageName();
+                    // If the app didn't change, there's nothing to do. Otherwise, we have to
+                    // update the category and re-apply the brightness correction.
+                    if (mForegroundAppPackageName != null
+                            && mForegroundAppPackageName.equals(packageName)) {
+                        return;
+                    }
+                    mPendingForegroundAppPackageName = packageName;
+                    ApplicationInfo app = mPackageManagerInternal.getApplicationInfo(packageName,
+                            0 /* flags */, Process.SYSTEM_UID /* filterCallingUid */, mUserId);
+                    mPendingForegroundAppCategory = app.category;
+                    mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
+                } catch (RemoteException e) {
+                    // Nothing to do.
+                }
+            }
+        });
+    }
+
+    private void updateForegroundAppSync() {
+        mForegroundAppPackageName = mPendingForegroundAppPackageName;
+        mPendingForegroundAppPackageName = null;
+        mForegroundAppCategory = mPendingForegroundAppCategory;
+        mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+        updateAutoBrightness(true /* sendUpdate */);
+    }
+
     private final class AutomaticBrightnessHandler extends Handler {
         public AutomaticBrightnessHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -764,6 +888,14 @@
                 case MSG_INVALIDATE_SHORT_TERM_MODEL:
                     invalidateShortTermModel();
                     break;
+
+                case MSG_UPDATE_FOREGROUND_APP:
+                    updateForegroundApp();
+                    break;
+
+                case MSG_UPDATE_FOREGROUND_APP_SYNC:
+                    updateForegroundAppSync();
+                    break;
             }
         }
     }
@@ -784,6 +916,15 @@
         }
     };
 
+    // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
+    // moving to top.
+    class TaskStackListenerImpl extends TaskStackListener {
+        @Override
+        public void onTaskStackChanged() {
+            mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
+        }
+    }
+
     /** Callbacks to request updates to the display's power state. */
     interface Callbacks {
         void updateBrightness();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 76c191d..9fce644 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,9 +17,11 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessCorrection;
 import android.os.PowerManager;
 import android.util.MathUtils;
 import android.util.Pair;
@@ -42,11 +44,12 @@
  */
 public abstract class BrightnessMappingStrategy {
     private static final String TAG = "BrightnessMappingStrategy";
-    private static final boolean DEBUG = false;
 
     private static final float LUX_GRAD_SMOOTHING = 0.25f;
     private static final float MAX_GRAD = 1.0f;
 
+    protected boolean mLoggingEnabled;
+
     private static final Plog PLOG = Plog.createSystemPlog(TAG);
 
     @Nullable
@@ -161,6 +164,22 @@
     }
 
     /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
      * Sets the {@link BrightnessConfiguration}.
      *
      * @param config The new configuration. If {@code null} is passed, the default configuration is
@@ -170,15 +189,33 @@
     public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
 
     /**
-     * Returns the desired brightness of the display based on the current ambient lux.
+     * Returns the desired brightness of the display based on the current ambient lux, including
+     * any context-related corrections.
      *
      * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
      * brightness and 0 is the display at minimum brightness.
      *
      * @param lux The current ambient brightness in lux.
+     * @param packageName the foreground app package name.
+     * @param category the foreground app package category.
      * @return The desired brightness of the display normalized to the range [0, 1.0].
      */
-    public abstract float getBrightness(float lux);
+    public abstract float getBrightness(float lux, String packageName,
+            @ApplicationInfo.Category int category);
+
+    /**
+     * Returns the desired brightness of the display based on the current ambient lux.
+     *
+     * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max
+     * brightness and 0 is the display at minimum brightness.
+     *
+     * @param lux The current ambient brightness in lux.
+     *
+     * @return The desired brightness of the display normalized to the range [0, 1.0].
+     */
+    public float getBrightness(float lux) {
+        return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
+    }
 
     /**
      * Returns the current auto-brightness adjustment.
@@ -239,13 +276,13 @@
 
     public abstract void dump(PrintWriter pw);
 
-    private static float normalizeAbsoluteBrightness(int brightness) {
+    protected float normalizeAbsoluteBrightness(int brightness) {
         brightness = MathUtils.constrain(brightness,
                 PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
         return (float) brightness / PowerManager.BRIGHTNESS_ON;
     }
 
-    private static Pair<float[], float[]> insertControlPoint(
+    private Pair<float[], float[]> insertControlPoint(
             float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
         final int idx = findInsertionPoint(luxLevels, lux);
         final float[] newLuxLevels;
@@ -278,7 +315,7 @@
      * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
      * than val, then it will return the length of arr as the insertion point.
      */
-    private static int findInsertionPoint(float[] arr, float val) {
+    private int findInsertionPoint(float[] arr, float val) {
         for (int i = 0; i < arr.length; i++) {
             if (val <= arr[i]) {
                 return i;
@@ -287,8 +324,8 @@
         return arr.length;
     }
 
-    private static void smoothCurve(float[] lux, float[] brightness, int idx) {
-        if (DEBUG) {
+    private void smoothCurve(float[] lux, float[] brightness, int idx) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("unsmoothed curve", lux, brightness);
         }
         float prevLux = lux[idx];
@@ -323,19 +360,19 @@
             prevBrightness = newBrightness;
             brightness[i] = newBrightness;
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("smoothed curve", lux, brightness);
         }
     }
 
-    private static float permissibleRatio(float currLux, float prevLux) {
+    private float permissibleRatio(float currLux, float prevLux) {
         return MathUtils.exp(MAX_GRAD
                 * (MathUtils.log(currLux + LUX_GRAD_SMOOTHING)
                     - MathUtils.log(prevLux + LUX_GRAD_SMOOTHING)));
     }
 
-    private static float inferAutoBrightnessAdjustment(float maxGamma,
-            float desiredBrightness, float currentBrightness) {
+    protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
+            float currentBrightness) {
         float adjustment = 0;
         float gamma = Float.NaN;
         // Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
@@ -355,7 +392,7 @@
             adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
         }
         adjustment = MathUtils.constrain(adjustment, -1, +1);
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
@@ -364,16 +401,16 @@
         return adjustment;
     }
 
-    private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
+    protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
             float userLux, float userBrightness, float adjustment, float maxGamma) {
         float[] newLux = lux;
         float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("unadjusted curve", newLux, newBrightness);
         }
         adjustment = MathUtils.constrain(adjustment, -1, 1);
         float gamma = MathUtils.pow(maxGamma, -adjustment);
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
         }
@@ -382,7 +419,7 @@
                 newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
             }
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
         }
         if (userLux != -1) {
@@ -390,7 +427,7 @@
                     userBrightness);
             newLux = curve.first;
             newBrightness = curve.second;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
                 // This is done for comparison.
                 curve = insertControlPoint(lux, brightness, userLux, userBrightness);
@@ -440,7 +477,7 @@
             mAutoBrightnessAdjustment = 0;
             mUserLux = -1;
             mUserBrightness = -1;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.start("simple mapping strategy");
             }
             computeSpline();
@@ -452,7 +489,8 @@
         }
 
         @Override
-        public float getBrightness(float lux) {
+        public float getBrightness(float lux, String packageName,
+                @ApplicationInfo.Category int category) {
             return mSpline.interpolate(lux);
         }
 
@@ -467,7 +505,7 @@
             if (adjustment == mAutoBrightnessAdjustment) {
                 return false;
             }
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
                 PLOG.start("auto-brightness adjustment");
@@ -485,7 +523,7 @@
         @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
-            if (DEBUG){
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
                 PLOG.start("add user data point")
                         .logPoint("user data point", lux, brightness)
@@ -494,7 +532,7 @@
             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
                     brightness /* desiredBrightness */,
                     unadjustedBrightness /* currentBrightness */);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
             }
@@ -507,7 +545,7 @@
         @Override
         public void clearUserDataPoints() {
             if (mUserLux != -1) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
                     PLOG.start("clear user data points")
                             .logPoint("user data point", mUserLux, mUserBrightness);
@@ -614,7 +652,7 @@
             mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
 
             mDefaultConfig = config;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.start("physical mapping strategy");
             }
             mConfig = config;
@@ -629,7 +667,7 @@
             if (config.equals(mConfig)) {
                 return false;
             }
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.start("brightness configuration");
             }
             mConfig = config;
@@ -638,9 +676,17 @@
         }
 
         @Override
-        public float getBrightness(float lux) {
+        public float getBrightness(float lux, String packageName,
+                @ApplicationInfo.Category int category) {
             float nits = mBrightnessSpline.interpolate(lux);
             float backlight = mNitsToBacklightSpline.interpolate(nits);
+            // Correct the brightness according to the current application and its category, but
+            // only if no user data point is set (as this will oevrride the user setting).
+            if (mUserLux == -1) {
+                backlight = correctBrightness(backlight, packageName, category);
+            } else if (mLoggingEnabled) {
+                Slog.d(TAG, "user point set, correction not applied");
+            }
             return backlight;
         }
 
@@ -655,7 +701,7 @@
             if (adjustment == mAutoBrightnessAdjustment) {
                 return false;
             }
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
                 PLOG.start("auto-brightness adjustment");
@@ -673,7 +719,7 @@
         @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
-            if (DEBUG){
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
                 PLOG.start("add user data point")
                         .logPoint("user data point", lux, brightness)
@@ -682,7 +728,7 @@
             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
                     brightness /* desiredBrightness */,
                     unadjustedBrightness /* currentBrightness */);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
             }
@@ -695,7 +741,7 @@
         @Override
         public void clearUserDataPoints() {
             if (mUserLux != -1) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
                     PLOG.start("clear user data points")
                             .logPoint("user data point", mUserLux, mUserBrightness);
@@ -758,5 +804,21 @@
             Spline spline = Spline.createSpline(curve.first, curve.second);
             return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
         }
+
+        private float correctBrightness(float brightness, String packageName, int category) {
+            if (packageName != null) {
+                BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
+                if (correction != null) {
+                    return correction.apply(brightness);
+                }
+            }
+            if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
+                BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
+                if (correction != null) {
+                    return correction.apply(brightness);
+                }
+            }
+            return brightness;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0a1a9a2..b1ba05c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2144,6 +2144,14 @@
                     mContext.getPackageName());
         }
 
+        void setAutoBrightnessLoggingEnabled(boolean enabled) {
+            if (mDisplayPowerController != null) {
+                synchronized (mSyncRoot) {
+                    mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+                }
+            }
+        }
+
         private boolean validatePackageName(int uid, String packageName) {
             if (packageName != null) {
                 String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 27cad1e..abbfc7b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -17,14 +17,9 @@
 package com.android.server.display;
 
 import android.content.Intent;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
 import android.os.ShellCommand;
-import android.util.Slog;
 
 import java.io.PrintWriter;
-import java.lang.NumberFormatException;
 
 class DisplayManagerShellCommand extends ShellCommand {
     private static final String TAG = "DisplayManagerShellCommand";
@@ -46,6 +41,10 @@
                 return setBrightness();
             case "reset-brightness-configuration":
                 return resetBrightnessConfiguration();
+            case "ab-logging-enable":
+                return setAutoBrightnessLoggingEnabled(true);
+            case "ab-logging-disable":
+                return setAutoBrightnessLoggingEnabled(false);
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -62,6 +61,10 @@
         pw.println("    Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
         pw.println("  reset-brightness-configuration");
         pw.println("    Reset the brightness to its default configuration.");
+        pw.println("  ab-logging-enable");
+        pw.println("    Enable auto-brightness logging.");
+        pw.println("  ab-logging-disable");
+        pw.println("    Disable auto-brightness logging.");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
@@ -89,4 +92,9 @@
         mService.resetBrightnessConfiguration();
         return 0;
     }
+
+    private int setAutoBrightnessLoggingEnabled(boolean enabled) {
+        mService.setAutoBrightnessLoggingEnabled(enabled);
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 249270b..c9ed9f7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -523,6 +523,9 @@
     public void onSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         mBrightnessTracker.onSwitchUser(newUserId);
+        if (mAutomaticBrightnessController != null) {
+            mAutomaticBrightnessController.onSwitchUser(newUserId);
+        }
     }
 
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -1836,4 +1839,10 @@
             mHandler.sendMessage(msg);
         }
     }
+
+    void setAutoBrightnessLoggingEnabled(boolean enabled) {
+        if (mAutomaticBrightnessController != null) {
+            mAutomaticBrightnessController.setLoggingEnabled(enabled);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 89cef62..9aec43b 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -16,13 +16,6 @@
 
 package com.android.server.display;
 
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.Nullable;
 import android.graphics.Point;
 import android.hardware.display.BrightnessConfiguration;
@@ -30,13 +23,20 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.Pair;
 import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -50,12 +50,9 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
-import libcore.io.IoUtils;
-
 /**
  * Manages persistent state recorded by the display manager service as an XML file.
  * Caller must acquire lock on the data store before accessing it.
@@ -110,14 +107,9 @@
 
     private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
     private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
-    private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
-    private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
     private static final String ATTR_USER_SERIAL = "user-serial";
     private static final String ATTR_PACKAGE_NAME = "package-name";
     private static final String ATTR_TIME_STAMP = "timestamp";
-    private static final String ATTR_LUX = "lux";
-    private static final String ATTR_NITS = "nits";
-    private static final String ATTR_DESCRIPTION = "description";
 
     // Remembered Wifi display devices.
     private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -646,7 +638,8 @@
                     }
 
                     try {
-                        BrightnessConfiguration config = loadConfigurationFromXml(parser);
+                        BrightnessConfiguration config =
+                                BrightnessConfiguration.loadFromXml(parser);
                         if (userSerial >= 0 && config != null) {
                             mConfigurations.put(userSerial, config);
                             if (timeStamp != -1) {
@@ -663,56 +656,6 @@
             }
         }
 
-        private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            String description = null;
-            Pair<float[], float[]> curve = null;
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
-                    description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
-                    curve = loadCurveFromXml(parser);
-                }
-            }
-            if (curve == null) {
-                return null;
-            }
-            final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
-                    curve.first, curve.second);
-            builder.setDescription(description);
-            return builder.build();
-        }
-
-        private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            List<Float> luxLevels = new ArrayList<>();
-            List<Float> nitLevels = new ArrayList<>();
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
-                    luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
-                    nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
-                }
-            }
-            final int N = luxLevels.size();
-            float[] lux = new float[N];
-            float[] nits = new float[N];
-            for (int i = 0; i < N; i++) {
-                lux[i] = luxLevels.get(i);
-                nits[i] = nitLevels.get(i);
-            }
-            return Pair.create(lux, nits);
-        }
-
-        private static float loadFloat(String val) {
-            try {
-                return Float.parseFloat(val);
-            } catch (NullPointerException | NumberFormatException e) {
-                Slog.e(TAG, "Failed to parse float loading brightness config", e);
-                return Float.NEGATIVE_INFINITY;
-            }
-        }
-
         public void saveToXml(XmlSerializer serializer) throws IOException {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
@@ -728,27 +671,11 @@
                 if (timestamp != -1) {
                     serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
                 }
-                saveConfigurationToXml(serializer, config);
+                config.saveToXml(serializer);
                 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
             }
         }
 
-        private static void saveConfigurationToXml(XmlSerializer serializer,
-                BrightnessConfiguration config) throws IOException {
-            serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
-            if (config.getDescription() != null) {
-                serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
-            }
-            final Pair<float[], float[]> curve = config.getCurve();
-            for (int i = 0; i < curve.first.length; i++) {
-                serializer.startTag(null, TAG_BRIGHTNESS_POINT);
-                serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
-                serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
-                serializer.endTag(null, TAG_BRIGHTNESS_POINT);
-            }
-            serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
-        }
-
         public void dump(final PrintWriter pw, final String prefix) {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c0d3fdf..f468c0bf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -81,8 +81,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import libcore.util.EmptyArray;
 
 /**
@@ -94,6 +96,31 @@
     private final Locale HONG_KONG = new Locale("zh", "HK");
     private final Locale MACAU = new Locale("zh", "MO");
 
+    private static final Map<String, String> mTerminologyToBibliographicMap;
+    static {
+        mTerminologyToBibliographicMap = new HashMap<>();
+        // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
+        mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian
+        mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian
+        mTerminologyToBibliographicMap.put("eus", "baq"); // Basque
+        mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese
+        mTerminologyToBibliographicMap.put("ces", "cze"); // Czech
+        mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch
+        mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian
+        mTerminologyToBibliographicMap.put("deu", "ger"); // German
+        mTerminologyToBibliographicMap.put("ell", "gre"); // Greek
+        mTerminologyToBibliographicMap.put("fra", "fre"); // French
+        mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic
+        mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian
+        mTerminologyToBibliographicMap.put("mri", "mao"); // Maori
+        mTerminologyToBibliographicMap.put("msa", "may"); // Malay
+        mTerminologyToBibliographicMap.put("fas", "per"); // Persian
+        mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian
+        mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak
+        mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan
+        mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh
+    }
+
     static final String PERMISSION = "android.permission.HDMI_CEC";
 
     // The reason code to initiate initializeCec().
@@ -177,7 +204,18 @@
                 // Chinese used in Taiwan/Hong Kong/Macau.
                 return "chi";
             } else {
-                return locale.getISO3Language();
+                String language = locale.getISO3Language();
+
+                // locale.getISO3Language() returns terminology code and need to
+                // send it as bibliographic code instead since the Bibliographic
+                // codes of ISO/FDIS 639-2 shall be used.
+                // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
+                // But, as it depends on the locale, is not handled here.
+                if (mTerminologyToBibliographicMap.containsKey(language)) {
+                    language = mTerminologyToBibliographicMap.get(language);
+                }
+
+                return language;
             }
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8e21863..096b28a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2667,6 +2667,10 @@
          * Note that since notification posting is done asynchronously, this will not return
          * notifications that are in the process of being posted.
          *
+         * From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as
+         * an app's notification delegate via
+         * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+         *
          * @returns A list of all the package's notifications, in natural order.
          */
         @Override
@@ -2709,16 +2713,18 @@
 
         private StatusBarNotification sanitizeSbn(String pkg, int userId,
                 StatusBarNotification sbn) {
-            if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
-                // We could pass back a cloneLight() but clients might get confused and
-                // try to send this thing back to notify() again, which would not work
-                // very well.
-                return new StatusBarNotification(
-                        sbn.getPackageName(),
-                        sbn.getOpPkg(),
-                        sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
-                        sbn.getNotification().clone(),
-                        sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+            if (sbn.getUserId() == userId) {
+                if (sbn.getPackageName().equals(pkg) || sbn.getOpPkg().equals(pkg)) {
+                    // We could pass back a cloneLight() but clients might get confused and
+                    // try to send this thing back to notify() again, which would not work
+                    // very well.
+                    return new StatusBarNotification(
+                            sbn.getPackageName(),
+                            sbn.getOpPkg(),
+                            sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+                            sbn.getNotification().clone(),
+                            sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+                }
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 8d49bf3..d8b2b52 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1837,23 +1837,23 @@
                     return false;
                 }
             }
-
-            if (transferStartingWindow(transferFrom)) {
-                return true;
-            }
-
-            // There is no existing starting window, and we don't want to create a splash screen, so
-            // that's it!
-            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
-                return false;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
-            startingData = new SplashScreenStartingData(mWmService, pkg,
-                    theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                    getMergedOverrideConfiguration());
-            scheduleAddStartingWindow();
         }
+
+        if (transferStartingWindow(transferFrom)) {
+            return true;
+        }
+
+        // There is no existing starting window, and we don't want to create a splash screen, so
+        // that's it!
+        if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+            return false;
+        }
+
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+        startingData = new SplashScreenStartingData(mWmService, pkg,
+                theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+                getMergedOverrideConfiguration());
+        scheduleAddStartingWindow();
         return true;
     }
 
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index efbcb96..1e468d4 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -87,6 +87,7 @@
     private static final String TAG = "BMSTest";
     private static final String PACKAGE_1 = "some.package.1";
     private static final String PACKAGE_2 = "some.package.2";
+    private static final int USER_ID = 10;
 
     @Mock private TransportManager mTransportManager;
     private HandlerThread mBackupThread;
@@ -979,6 +980,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
 
         UserBackupManagerService.createAndInitializeService(
+                USER_ID,
                 mContext,
                 new Trampoline(mContext),
                 mBackupThread,
@@ -1000,6 +1002,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
 
         UserBackupManagerService.createAndInitializeService(
+                USER_ID,
                 mContext,
                 new Trampoline(mContext),
                 mBackupThread,
@@ -1022,6 +1025,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 /* context */ null,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1041,6 +1045,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 /* trampoline */ null,
                                 mBackupThread,
@@ -1060,6 +1065,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 /* backupThread */ null,
@@ -1079,6 +1085,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1089,8 +1096,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+     * File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1098,6 +1105,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1108,8 +1116,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+     * File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1117,6 +1125,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1127,7 +1136,7 @@
 
     private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
         return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
-                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+                USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
     }
 
     private void setUpPowerManager(UserBackupManagerService backupManagerService) {
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 099127c..cf51e19 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -169,6 +169,7 @@
     private static final PackageData PACKAGE_2 = keyValuePackage(2);
     private static final String BACKUP_AGENT_SHARED_PREFS_SYNCHRONIZER_CLASS =
             "android.app.backup.BackupAgent$SharedPrefsSynchronizer";
+    private static final int USER_ID = 10;
 
     @Mock private TransportManager mTransportManager;
     @Mock private DataChangedJournal mOldJournal;
@@ -224,7 +225,7 @@
         setUpBinderCallerAndApplicationAsSystem(mApplication);
         mBackupManagerService =
                 spy(createUserBackupManagerServiceAndRunTasks(
-                        mContext, mBaseStateDir, mDataDir, mTransportManager));
+                        USER_ID, mContext, mBaseStateDir, mDataDir, mTransportManager));
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
                 mApplication,
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 06f6d21..b978570 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -58,13 +58,17 @@
      * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
      * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
      *
-     * @see #createUserBackupManagerServiceAndRunTasks(Context, HandlerThread, File, File,
+     * @see #createUserBackupManagerServiceAndRunTasks(int, Context, HandlerThread, File, File,
      *     TransportManager)
      */
     public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
-            Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
+            int userId,
+            Context context,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
         return createUserBackupManagerServiceAndRunTasks(
-                context, startBackupThread(null), baseStateDir, dataDir, transportManager);
+                userId, context, startBackupThread(null), baseStateDir, dataDir, transportManager);
     }
 
     /**
@@ -75,6 +79,7 @@
      * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
      */
     public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
+            int userId,
             Context context,
             HandlerThread backupThread,
             File baseStateDir,
@@ -82,6 +87,7 @@
             TransportManager transportManager) {
         UserBackupManagerService backupManagerService =
                 UserBackupManagerService.createAndInitializeService(
+                        userId,
                         context,
                         new Trampoline(context),
                         backupThread,
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 60cb08f..294b750 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,12 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import static com.android.internal.usb.DumpUtils.writeAccessory;
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
@@ -36,6 +42,7 @@
 import android.content.res.Resources;
 import android.debug.AdbManagerInternal;
 import android.debug.IAdbTransport;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbConfiguration;
 import android.hardware.usb.UsbConstants;
@@ -294,9 +301,10 @@
         BroadcastReceiver portReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                UsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+                ParcelableUsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
                 UsbPortStatus status = intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS);
-                mHandler.updateHostState(port, status);
+                mHandler.updateHostState(
+                        port.getUsbPort(context.getSystemService(UsbManager.class)), status);
             }
         };
 
@@ -821,23 +829,20 @@
                     boolean prevHostConnected = mHostConnected;
                     UsbPort port = (UsbPort) args.arg1;
                     UsbPortStatus status = (UsbPortStatus) args.arg2;
-                    mHostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
-                    mSourcePower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE;
-                    mSinkPower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SINK;
-                    mAudioAccessoryConnected =
-                            (status.getCurrentMode() == UsbPort.MODE_AUDIO_ACCESSORY);
-                    mAudioAccessorySupported = port.isModeSupported(UsbPort.MODE_AUDIO_ACCESSORY);
+                    mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+                    mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+                    mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+                    mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+                    mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
                     // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
                     // But, this should be suffice, since, all four combinations are only supported
                     // when PR_SWAP and DR_SWAP are supported.
                     mSupportsAllCombinations = status.isRoleCombinationSupported(
-                            UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
-                            UsbPort.DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE,
-                            UsbPort.DATA_ROLE_DEVICE)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
-                            UsbPort.DATA_ROLE_HOST);
+                            POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+                            DATA_ROLE_DEVICE)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST);
 
                     args.recycle();
                     updateUsbNotification(false);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 96618f5..6f210e3 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,12 +16,22 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import static com.android.internal.usb.DumpUtils.writePort;
 import static com.android.internal.usb.DumpUtils.writePortStatus;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
@@ -78,13 +88,13 @@
 
     // All non-trivial role combinations.
     private static final int COMBO_SOURCE_HOST =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
-    private static final int COMBO_SOURCE_DEVICE =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
+            UsbPort.combineRolesAsBit(POWER_ROLE_SOURCE, DATA_ROLE_HOST);
+    private static final int COMBO_SOURCE_DEVICE = UsbPort.combineRolesAsBit(
+            POWER_ROLE_SOURCE, DATA_ROLE_DEVICE);
     private static final int COMBO_SINK_HOST =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
-    private static final int COMBO_SINK_DEVICE =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
+            UsbPort.combineRolesAsBit(POWER_ROLE_SINK, DATA_ROLE_HOST);
+    private static final int COMBO_SINK_DEVICE = UsbPort.combineRolesAsBit(
+            POWER_ROLE_SINK, DATA_ROLE_DEVICE);
 
     // The system context.
     private final Context mContext;
@@ -217,12 +227,12 @@
             final int newMode;
             if ((!canChangePowerRole && currentPowerRole != newPowerRole)
                     || (!canChangeDataRole && currentDataRole != newDataRole)) {
-                if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
-                        && newDataRole == UsbPort.DATA_ROLE_HOST) {
-                    newMode = UsbPort.MODE_DFP;
-                } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
-                        && newDataRole == UsbPort.DATA_ROLE_DEVICE) {
-                    newMode = UsbPort.MODE_UFP;
+                if (canChangeMode && newPowerRole == POWER_ROLE_SOURCE
+                        && newDataRole == DATA_ROLE_HOST) {
+                    newMode = MODE_DFP;
+                } else if (canChangeMode && newPowerRole == POWER_ROLE_SINK
+                        && newDataRole == DATA_ROLE_DEVICE) {
+                    newMode = MODE_UFP;
                 } else {
                     logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
                             + "while attempting to change role: " + portInfo
@@ -607,7 +617,7 @@
             IndentingPrintWriter pw) {
         // Only allow mode switch capability for dual role ports.
         // Validate that the current mode matches the supported modes we expect.
-        if ((supportedModes & UsbPort.MODE_DUAL) != UsbPort.MODE_DUAL) {
+        if ((supportedModes & MODE_DUAL) != MODE_DUAL) {
             canChangeMode = false;
             if (currentMode != 0 && currentMode != supportedModes) {
                 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
@@ -633,16 +643,16 @@
                 // Can only change power role.
                 // Assume data role must remain at its current value.
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        UsbPort.POWER_ROLE_SOURCE, currentDataRole);
+                        POWER_ROLE_SOURCE, currentDataRole);
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        UsbPort.POWER_ROLE_SINK, currentDataRole);
+                        POWER_ROLE_SINK, currentDataRole);
             } else if (canChangeDataRole) {
                 // Can only change data role.
                 // Assume power role must remain at its current value.
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        currentPowerRole, UsbPort.DATA_ROLE_HOST);
+                        currentPowerRole, DATA_ROLE_HOST);
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
+                        currentPowerRole, DATA_ROLE_DEVICE);
             } else if (canChangeMode) {
                 // Can only change the mode.
                 // Assume both standard UFP and DFP configurations will become available
@@ -654,7 +664,8 @@
         // Update the port data structures.
         PortInfo portInfo = mPorts.get(portId);
         if (portInfo == null) {
-            portInfo = new PortInfo(portId, supportedModes);
+            portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId,
+                    supportedModes);
             portInfo.setStatus(currentMode, canChangeMode,
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
@@ -701,12 +712,13 @@
         intent.addFlags(
                 Intent.FLAG_RECEIVER_FOREGROUND |
                         Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
+        intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort));
         intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
 
         // Guard against possible reentrance by posting the broadcast from the handler
         // instead of from within the critical section.
-        mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL));
+        mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                Manifest.permission.MANAGE_USB));
 
         // Log to statsd
         if (!mConnected.containsKey(portInfo.mUsbPort.getId())
@@ -772,8 +784,8 @@
         // 0 when port is connected. Else reports the last connected duration
         public long mLastConnectDurationMillis;
 
-        public PortInfo(String portId, int supportedModes) {
-            mUsbPort = new UsbPort(portId, supportedModes);
+        PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes) {
+            mUsbPort = new UsbPort(usbManager, portId, supportedModes);
         }
 
         public boolean setStatus(int currentMode, boolean canChangeMode,
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index f9abedf..9115477 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,14 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
@@ -27,6 +35,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.hardware.usb.IUsbManager;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
@@ -52,7 +61,9 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * UsbService manages all USB related state, including both host and device support.
@@ -489,12 +500,25 @@
     }
 
     @Override
-    public UsbPort[] getPorts() {
+    public List<ParcelableUsbPort> getPorts() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            return mPortManager != null ? mPortManager.getPorts() : null;
+            if (mPortManager == null) {
+                return null;
+            } else {
+                final UsbPort[] ports = mPortManager.getPorts();
+
+                final int numPorts = ports.length;
+                ArrayList<ParcelableUsbPort> parcelablePorts = new ArrayList<>();
+                for (int i = 0; i < numPorts; i++) {
+                    parcelablePorts.add(ParcelableUsbPort.of(ports[i]));
+                }
+
+                return parcelablePorts;
+            }
+
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -588,10 +612,10 @@
                 final int powerRole;
                 switch (args[2]) {
                     case "source":
-                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        powerRole = POWER_ROLE_SOURCE;
                         break;
                     case "sink":
-                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        powerRole = POWER_ROLE_SINK;
                         break;
                     case "no-power":
                         powerRole = 0;
@@ -603,10 +627,10 @@
                 final int dataRole;
                 switch (args[3]) {
                     case "host":
-                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        dataRole = DATA_ROLE_HOST;
                         break;
                     case "device":
-                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        dataRole = DATA_ROLE_DEVICE;
                         break;
                     case "no-data":
                         dataRole = 0;
@@ -631,13 +655,13 @@
                 final int supportedModes;
                 switch (args[2]) {
                     case "ufp":
-                        supportedModes = UsbPort.MODE_UFP;
+                        supportedModes = MODE_UFP;
                         break;
                     case "dfp":
-                        supportedModes = UsbPort.MODE_DFP;
+                        supportedModes = MODE_DFP;
                         break;
                     case "dual":
-                        supportedModes = UsbPort.MODE_DUAL;
+                        supportedModes = MODE_DUAL;
                         break;
                     case "none":
                         supportedModes = 0;
@@ -658,10 +682,10 @@
                 final boolean canChangeMode = args[2].endsWith("?");
                 switch (canChangeMode ? removeLastChar(args[2]) : args[2]) {
                     case "ufp":
-                        mode = UsbPort.MODE_UFP;
+                        mode = MODE_UFP;
                         break;
                     case "dfp":
-                        mode = UsbPort.MODE_DFP;
+                        mode = MODE_DFP;
                         break;
                     default:
                         pw.println("Invalid mode: " + args[2]);
@@ -671,10 +695,10 @@
                 final boolean canChangePowerRole = args[3].endsWith("?");
                 switch (canChangePowerRole ? removeLastChar(args[3]) : args[3]) {
                     case "source":
-                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        powerRole = POWER_ROLE_SOURCE;
                         break;
                     case "sink":
-                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        powerRole = POWER_ROLE_SINK;
                         break;
                     default:
                         pw.println("Invalid power role: " + args[3]);
@@ -684,10 +708,10 @@
                 final boolean canChangeDataRole = args[4].endsWith("?");
                 switch (canChangeDataRole ? removeLastChar(args[4]) : args[4]) {
                     case "host":
-                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        dataRole = DATA_ROLE_HOST;
                         break;
                     case "device":
-                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        dataRole = DATA_ROLE_DEVICE;
                         break;
                     default:
                         pw.println("Invalid data role: " + args[4]);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 04c28e5..c8a899b 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -169,6 +170,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 04b6a6c..8e1877d 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -197,6 +198,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 8b1c1b9..f77c468 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 
@@ -116,6 +117,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 3416ffe..31f9e6d 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -173,6 +174,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c7df36b..29d32e9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4908,7 +4908,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void requestCellInfoUpdate(
-            @NonNull Executor executor, @NonNull CellInfoCallback callback) {
+            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null) return;
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c91134c..58702dc 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@
   ResourceId id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool allow_new = false;
-  Maybe<Overlayable> overlayable;
+  Maybe<OverlayableItem> overlayable_item;
 
   std::string comment;
   std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@
     }
   }
 
-  if (res->overlayable) {
-    if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
+  if (res->overlayable_item) {
+    if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) {
       return false;
     }
   }
@@ -1059,92 +1059,119 @@
 bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
-                    << "ignoring configuration '" << out_resource->config
-                    << "' for <overlayable> tag");
+                << "ignoring configuration '" << out_resource->config
+                << "' for <overlayable> tag");
   }
 
+  Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+  if (!overlayable_name) {
+    diag_->Error(DiagMessage(out_resource->source)
+                  << "<overlayable> tag must have a 'name' attribute");
+    return false;
+  }
+
+  const std::string kActorUriScheme =
+      android::base::StringPrintf("%s://", Overlayable::kActorScheme);
+  Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+  if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "specified <overlayable> tag 'actor' attribute must use the scheme '"
+                 << Overlayable::kActorScheme << "'");
+    return false;
+  }
+
+  // Create a overlayable entry grouping that represents this <overlayable>
+  auto overlayable = std::make_shared<Overlayable>(
+      overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
+      out_resource->source);
+
   bool error = false;
   std::string comment;
-  Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
+  OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
   const size_t start_depth = parser->depth();
   while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
     xml::XmlPullParser::Event event = parser->event();
     if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
-      // Break the loop when exiting the overlayable element
+      // Break the loop when exiting the <overlayable>
       break;
     } else if (event == xml::XmlPullParser::Event::kEndElement
                && parser->depth() == start_depth + 1) {
-      // Clear the current policies when exiting the policy element
-      current_policies = Overlayable::Policy::kNone;
+      // Clear the current policies when exiting the <policy> tags
+      current_policies = OverlayableItem::Policy::kNone;
       continue;
     } else if (event == xml::XmlPullParser::Event::kComment) {
-      // Get the comment of individual item elements
+      // Retrieve the comment of individual <item> tags
       comment = parser->comment();
       continue;
     } else if (event != xml::XmlPullParser::Event::kStartElement) {
-      // Skip to the next element
+      // Skip to the start of the next element
       continue;
     }
 
-    const Source item_source = source_.WithLine(parser->line_number());
+    const Source element_source = source_.WithLine(parser->line_number());
     const std::string& element_name = parser->element_name();
     const std::string& element_namespace = parser->element_namespace();
     if (element_namespace.empty() && element_name == "item") {
       // Items specify the name and type of resource that should be overlayable
-      Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
-      if (!maybe_name) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'name' attribute");
+      Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+      if (!item_name) {
+        diag_->Error(DiagMessage(element_source)
+                     << "<item> within an <overlayable> must have a 'name' attribute");
         error = true;
         continue;
       }
 
-      Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
-      if (!maybe_type) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'type' attribute");
+      Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+      if (!item_type) {
+        diag_->Error(DiagMessage(element_source)
+                     << "<item> within an <overlayable> must have a 'type' attribute");
         error = true;
         continue;
       }
 
-      const ResourceType* type = ParseResourceType(maybe_type.value());
+      const ResourceType* type = ParseResourceType(item_type.value());
       if (type == nullptr) {
-        diag_->Error(DiagMessage(item_source)
-                     << "invalid resource type '" << maybe_type.value()
+        diag_->Error(DiagMessage(element_source)
+                     << "invalid resource type '" << item_type.value()
                      << "' in <item> within an <overlayable>");
         error = true;
         continue;
       }
 
-      ParsedResource child_resource;
+      OverlayableItem overlayable_item(overlayable);
+      overlayable_item.policies = current_policies;
+      overlayable_item.comment = comment;
+      overlayable_item.source = element_source;
+
+      ParsedResource child_resource{};
       child_resource.name.type = *type;
-      child_resource.name.entry = maybe_name.value().to_string();
-      child_resource.overlayable = Overlayable{current_policies, item_source, comment};
+      child_resource.name.entry = item_name.value().to_string();
+      child_resource.overlayable_item = overlayable_item;
       out_resource->child_resources.push_back(std::move(child_resource));
 
     } else if (element_namespace.empty() && element_name == "policy") {
-      if (current_policies != Overlayable::Policy::kNone) {
+      if (current_policies != OverlayableItem::Policy::kNone) {
         // If the policy list is not empty, then we are currently inside a policy element
-        diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
+        diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
         error = true;
         break;
       } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
         // Parse the polices separated by vertical bar characters to allow for specifying multiple
-        // policies
+        // policies. Items within the policy tag will have the specified policy.
         for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
           StringPiece trimmed_part = util::TrimWhitespace(part);
           if (trimmed_part == "public") {
-            current_policies |= Overlayable::Policy::kPublic;
+            current_policies |= OverlayableItem::Policy::kPublic;
           } else if (trimmed_part == "product") {
-            current_policies |= Overlayable::Policy::kProduct;
+            current_policies |= OverlayableItem::Policy::kProduct;
           } else if (trimmed_part == "product_services") {
-            current_policies |= Overlayable::Policy::kProductServices;
+            current_policies |= OverlayableItem::Policy::kProductServices;
           } else if (trimmed_part == "system") {
-            current_policies |= Overlayable::Policy::kSystem;
+            current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
-            current_policies |= Overlayable::Policy::kVendor;
+            current_policies |= OverlayableItem::Policy::kVendor;
           } else {
-            diag_->Error(DiagMessage(item_source)
+            diag_->Error(DiagMessage(element_source)
                          << "<policy> has unsupported type '" << trimmed_part << "'");
             error = true;
             continue;
@@ -1152,11 +1179,13 @@
         }
       }
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
-      diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
+      diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
                                             << " in <overlayable>");
       error = true;
       break;
     }
+
+    comment.clear();
   }
 
   return !error;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 03e6197..debca9c 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -892,11 +892,8 @@
 }
 
 TEST_F(ResourceParserTest, ParseOverlayable) {
-  std::string input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable>
+  std::string input = R"(
+      <overlayable name="Name" actor="overlay://theme">
         <item type="string" name="foo" />
         <item type="drawable" name="bar" />
       </overlayable>)";
@@ -905,24 +902,35 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
 
   search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
+  EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)"));
+  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)"));
+  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)"));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) {
+  EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)"));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
-  std::string input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable>
+  std::string input = R"(
+      <overlayable name="Name">
         <item type="string" name="foo" />
         <policy type="product">
           <item type="string" name="bar" />
@@ -945,49 +953,55 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="illegal_policy">
           <item type="string" name="foo" />
         </policy>
@@ -995,7 +1009,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item name="foo" />
         </policy>
@@ -1003,7 +1017,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor">
           <item type="string" />
         </policy>
@@ -1013,7 +1027,7 @@
 
 TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor|product_services">
           <item type="string" name="foo" />
         </policy>
@@ -1026,39 +1040,59 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
-                                       | Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
+                                                   | OverlayableItem::Policy::kProductServices));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                       | Overlayable::Policy::kSystem));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kSystem));
 }
 
 TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
       </overlayable>
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable name="Other">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name" actor="overlay://my.actor.one">
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable name="Other" actor="overlay://my.actor.two">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
           <item type="string" name="foo" />
@@ -1067,7 +1101,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1076,7 +1110,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1087,13 +1121,13 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
       </overlayable>
 
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1103,7 +1137,7 @@
 
 TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor|product">
           <policy type="product_services">
             <item type="string" name="foo" />
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 54633ad..dbd0a0c 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -40,6 +40,8 @@
 
 namespace aapt {
 
+const char* Overlayable::kActorScheme = "overlay";
+
 static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
                                   const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
   return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
@@ -625,17 +627,18 @@
   return true;
 }
 
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
                                    IDiagnostics* diag) {
   return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
 }
 
 bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
-                                          const Overlayable& overlayable, IDiagnostics* diag) {
+                                          const OverlayableItem& overlayable, IDiagnostics* diag) {
   return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
 }
 
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
+                                       const OverlayableItem& overlayable,
                                        NameValidator name_validator, IDiagnostics *diag) {
   CHECK(diag != nullptr);
 
@@ -647,14 +650,15 @@
   ResourceTableType* type = package->FindOrCreateType(name.type);
   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
 
-  if (entry->overlayable) {
+  if (entry->overlayable_item) {
     diag->Error(DiagMessage(overlayable.source)
-                    << "duplicate overlayable declaration for resource '" << name << "'");
-    diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+                << "duplicate overlayable declaration for resource '" << name << "'");
+    diag->Error(DiagMessage(entry->overlayable_item.value().source)
+                << "previous declaration here");
     return false;
   }
 
-  entry->overlayable = overlayable;
+  entry->overlayable_item = overlayable;
   return true;
 }
 
@@ -690,7 +694,7 @@
         new_entry->id = entry->id;
         new_entry->visibility = entry->visibility;
         new_entry->allow_new = entry->allow_new;
-        new_entry->overlayable = entry->overlayable;
+        new_entry->overlayable_item = entry->overlayable_item;
 
         for (const auto& config_value : entry->values) {
           ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index e646f5b..eaf6a47 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,10 +57,27 @@
   std::string comment;
 };
 
-// Represents a declaration that a resource is overlayable at runtime.
 struct Overlayable {
+  Overlayable() = default;
+   Overlayable(const android::StringPiece& name, const android::StringPiece& actor)
+       : name(name.to_string()), actor(actor.to_string()) {}
+   Overlayable(const android::StringPiece& name, const android::StringPiece& actor,
+                    const Source& source)
+       : name(name.to_string()), actor(actor.to_string()), source(source ){}
+
+  static const char* kActorScheme;
+  std::string name;
+  std::string actor;
+  Source source;
+};
+
+// Represents a declaration that a resource is overlayable at runtime.
+struct OverlayableItem {
+  explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
+      : overlayable(overlayable) {}
 
   // Represents the types overlays that are allowed to overlay the resource.
+  typedef uint32_t PolicyFlags;
   enum Policy : uint32_t {
     kNone = 0x00,
 
@@ -80,11 +97,10 @@
     kProductServices = 0x10
   };
 
-  typedef uint32_t PolicyFlags;
+  std::shared_ptr<Overlayable> overlayable;
   PolicyFlags policies = Policy::kNone;
-
-  Source source;
   std::string comment;
+  Source source;
 };
 
 class ResourceConfigValue {
@@ -121,7 +137,7 @@
   Maybe<AllowNew> allow_new;
 
   // The declarations of this resource as overlayable for RROs
-  Maybe<Overlayable> overlayable;
+  Maybe<OverlayableItem> overlayable_item;
 
   // The resource's values for each configuration.
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -251,9 +267,9 @@
   bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
                                   const ResourceId& res_id, IDiagnostics* diag);
 
-  bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
                       IDiagnostics *diag);
-  bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool SetOverlayableMangled(const ResourceNameRef& name, const OverlayableItem& overlayable,
                              IDiagnostics* diag);
 
   bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -328,7 +344,7 @@
   bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
                        NameValidator name_validator, IDiagnostics* diag);
 
-  bool SetOverlayableImpl(const ResourceNameRef &name, const Overlayable &overlayable,
+  bool SetOverlayableImpl(const ResourceNameRef &name, const OverlayableItem& overlayable,
                           NameValidator name_validator, IDiagnostics *diag);
 
   bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 31095c4..a733134 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -244,48 +244,90 @@
 
 TEST(ResourceTableTest, SetOverlayable) {
   ResourceTable table;
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kProductServices;
-  overlayable.comment = "comment";
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
+                                                   Source("res/values/overlayable.xml", 40));
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item.comment = "comment";
+  overlayable_item.source = Source("res/values/overlayable.xml", 42);
 
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
   Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
 
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
 
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  ASSERT_THAT(result_overlayable.comment, StrEq("comment"));
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                              | Overlayable::Policy::kProductServices));
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kProductServices));
+  ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
+  EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.source.line, 42);
 }
 
-TEST(ResourceTableTest, AddDuplicateOverlayableSamePolicyFail) {
+TEST(ResourceTableTest, SetMultipleOverlayableResources) {
+  ResourceTable table;
+
+  const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+  auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable(group);
+  overlayable.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
+
+  const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+  OverlayableItem overlayable2(group);
+  overlayable2.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
+
+  const ResourceName baz = test::ParseNameOrDie("android:string/baz");
+  OverlayableItem overlayable3(group);
+  overlayable3.policies = OverlayableItem::Policy::kVendor;
+  ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
+  ResourceTable table;
+
+  const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
+  overlayable_item.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
+
+  const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+  OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2",  "overlay://theme"));
+  overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableSameResourcesFail) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
-  Overlayable overlayable{};
-  overlayable.policies = Overlayable::Policy::kProduct;
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable_item(overlayable);
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
 
-  Overlayable overlayable2{};
-  overlayable2.policies = Overlayable::Policy::kProduct;
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+  OverlayableItem overlayable_item2(overlayable);
+  ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
 }
 
-TEST(ResourceTableTest, AddDuplicateOverlayableDifferentPolicyFail) {
+TEST(ResourceTableTest,  SetOverlayableSameResourcesDifferentNameFail) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
-  Overlayable overlayable{};
-  overlayable.policies = Overlayable::Policy::kProduct;
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable_item(overlayable);
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
 
-  Overlayable overlayable2{};
-  overlayable2.policies = Overlayable::Policy::kVendor;
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+  auto overlayable2 = std::make_shared<Overlayable>("Other", "overlay://theme");
+  OverlayableItem overlayable_item2(overlayable2);
+  ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
 }
 
 TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 81a2c2e..da541be 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -49,6 +49,9 @@
 
   // Resource definitions corresponding to an Android package.
   repeated Package package = 2;
+
+  // The <overlayable> declarations within the resource table.
+  repeated Overlayable overlayable = 3;
 }
 
 // A package ID in the range [0x00, 0xff].
@@ -133,8 +136,20 @@
   string comment = 2;
 }
 
-// Represents a declaration that a resource is overayable at runtime.
+// Represents a set of overlayable resources.
 message Overlayable {
+  // The name of the <overlyabale>.
+  string name = 1;
+
+  // The location of the <overlyabale> declaration in the source.
+  Source source = 2;
+
+  // The component responsible for enabling and disabling overlays targeting this <overlayable>.
+  string actor = 3;
+}
+
+// Represents an overlayable <item> declaration within an <overlayable> tag.
+message OverlayableItem {
   enum Policy {
     PUBLIC = 0;
     SYSTEM = 1;
@@ -143,14 +158,18 @@
     PRODUCT_SERVICES = 4;
   }
 
-  // Where this declaration was defined in source.
+  // The location of the <item> declaration in source.
   Source source = 1;
 
   // Any comment associated with the declaration.
   string comment = 2;
 
-  // The policy defined in the overlayable declaration.
+  // The policy defined by the enclosing <policy> tag of this <item>.
   repeated Policy policy = 3;
+
+  // The index into overlayable list that points to the <overlayable> tag that contains
+  // this <item>.
+  uint32 overlayable_idx = 4;
 }
 
 // An entry ID in the range [0x0000, 0xffff].
@@ -180,7 +199,7 @@
   AllowNew allow_new = 4;
 
   // Whether this resource can be overlaid by a runtime resource overlay (RRO).
-  Overlayable overlayable = 5;
+  OverlayableItem overlayable_item = 5;
 
   // The set of values defined for this entry, each corresponding to a different
   // configuration/variant.
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 61ebd4e..c496ff0 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -434,6 +434,8 @@
     return false;
   }
 
+  auto overlayable = std::make_shared<Overlayable>();
+
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
   while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
@@ -441,25 +443,25 @@
       const ResTable_overlayable_policy_header* policy_header =
           ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
 
-      Overlayable::PolicyFlags policies = Overlayable::Policy::kNone;
+      OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
       if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
-        policies |= Overlayable::Policy::kPublic;
+        policies |= OverlayableItem::Policy::kPublic;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
-        policies |= Overlayable::Policy::kSystem;
+        policies |= OverlayableItem::Policy::kSystem;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
-        policies |= Overlayable::Policy::kVendor;
+        policies |= OverlayableItem::Policy::kVendor;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
-        policies |= Overlayable::Policy::kProduct;
+        policies |= OverlayableItem::Policy::kProduct;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
-        policies |= Overlayable::Policy::kProductServices;
+        policies |= OverlayableItem::Policy::kProductServices;
       }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
@@ -478,10 +480,10 @@
           return false;
         }
 
-        Overlayable overlayable{};
-        overlayable.source = source_.WithLine(0);
-        overlayable.policies = policies;
-        if (!table_->SetOverlayable(iter->second, overlayable, diag_)) {
+        OverlayableItem overlayable_item(overlayable);
+        overlayable_item.source = source_.WithLine(0);
+        overlayable_item.policies = policies;
+        if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
           return false;
         }
       }
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 200e2d4..931d57b 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -429,29 +429,29 @@
       CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
       for (auto& entry : type->entries) {
         CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
-        if (!entry->overlayable) {
+        if (!entry->overlayable_item) {
           continue;
         }
 
-        Overlayable overlayable = entry->overlayable.value();
-        uint32_t policy_flags = Overlayable::Policy::kNone;
-        if (overlayable.policies & Overlayable::Policy::kPublic) {
+        OverlayableItem& overlayable = entry->overlayable_item.value();
+        uint32_t policy_flags = OverlayableItem::Policy::kNone;
+        if (overlayable.policies & OverlayableItem::Policy::kPublic) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
         }
-        if (overlayable.policies & Overlayable::Policy::kSystem) {
+        if (overlayable.policies & OverlayableItem::Policy::kSystem) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kVendor) {
+        if (overlayable.policies & OverlayableItem::Policy::kVendor) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kProduct) {
+        if (overlayable.policies & OverlayableItem::Policy::kProduct) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kProductServices) {
+        if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
         }
 
-        if (overlayable.policies == Overlayable::Policy::kNone) {
+        if (overlayable.policies == OverlayableItem::Policy::kNone) {
           // Encode overlayable entries defined without a policy as publicly overlayable
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
         }
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e99ab1f..a5fb6fd 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,17 +628,17 @@
 }
 
 TEST_F(TableFlattenerTest, FlattenOverlayable) {
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kSystem;
-  overlayable.policies |= Overlayable::Policy::kVendor;
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item.policies |= OverlayableItem::Policy::kVendor;
 
   std::string name = "com.app.test:integer/overlayable";
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name, ResourceId(0x7f020000))
-          .SetOverlayable(name, overlayable)
+          .SetOverlayable(name, overlayable_item)
           .Build();
 
   ResourceTable output_table;
@@ -647,45 +647,46 @@
   auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kVendor
-                                         | Overlayable::Policy::kProduct);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                         | OverlayableItem::Policy::kVendor
+                                         | OverlayableItem::Policy::kProduct);
 }
 
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
-  std::string name_zero = "com.app.test:integer/overlayable_zero";
-  Overlayable overlayable_zero{};
-  overlayable_zero.policies |= Overlayable::Policy::kProduct;
-  overlayable_zero.policies |= Overlayable::Policy::kSystem;
-  overlayable_zero.policies |= Overlayable::Policy::kProductServices;
+  auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero_item";
+  OverlayableItem overlayable_zero_item(overlayable);
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices;
 
-  std::string name_one = "com.app.test:integer/overlayable_one";
-  Overlayable overlayable_one{};
-  overlayable_one.policies |= Overlayable::Policy::kPublic;
-  overlayable_one.policies |= Overlayable::Policy::kProductServices;
+  std::string name_one = "com.app.test:integer/overlayable_one_item";
+  OverlayableItem overlayable_one_item(overlayable);
+  overlayable_one_item.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices;
 
-  std::string name_two = "com.app.test:integer/overlayable_two";
-  Overlayable overlayable_two{};
-  overlayable_two.policies |= Overlayable::Policy::kProduct;
-  overlayable_two.policies |= Overlayable::Policy::kSystem;
-  overlayable_two.policies |= Overlayable::Policy::kVendor;
+  std::string name_two = "com.app.test:integer/overlayable_two_item";
+  OverlayableItem overlayable_two_item(overlayable);
+  overlayable_two_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_two_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_two_item.policies |= OverlayableItem::Policy::kVendor;
 
-  std::string name_three = "com.app.test:integer/overlayable_three";
-  Overlayable overlayable_three{};
+  std::string name_three = "com.app.test:integer/overlayable_three_item";
+  OverlayableItem overlayable_three_item(overlayable);
 
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name_zero, ResourceId(0x7f020000))
-          .SetOverlayable(name_zero, overlayable_zero)
+          .SetOverlayable(name_zero, overlayable_zero_item)
           .AddSimple(name_one, ResourceId(0x7f020001))
-          .SetOverlayable(name_one, overlayable_one)
+          .SetOverlayable(name_one, overlayable_one_item)
           .AddSimple(name_two, ResourceId(0x7f020002))
-          .SetOverlayable(name_two, overlayable_two)
+          .SetOverlayable(name_two, overlayable_two_item)
           .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_three)
+          .SetOverlayable(name_three, overlayable_three_item)
           .Build();
 
   ResourceTable output_table;
@@ -694,35 +695,35 @@
   auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kProduct
-                                         | Overlayable::Policy::kProductServices);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                       | OverlayableItem::Policy::kProduct
+                                       | OverlayableItem::Policy::kProductServices);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic
-                                         | Overlayable::Policy::kProductServices);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic
+                                       | OverlayableItem::Policy::kProductServices);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kProduct
-                                         | Overlayable::Policy::kVendor);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                       | OverlayableItem::Policy::kProduct
+                                       | OverlayableItem::Policy::kVendor);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index cf2ab0f..6b5746d 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -373,9 +373,44 @@
   return Visibility::Level::kUndefined;
 }
 
+bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable,
+                                      const android::ResStringPool& src_pool,
+                                      OverlayableItem* out_overlayable, std::string* out_error) {
+  for (const int policy : pb_overlayable.policy()) {
+    switch (policy) {
+      case pb::OverlayableItem::PUBLIC:
+        out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+        break;
+      case pb::OverlayableItem::SYSTEM:
+        out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+        break;
+      case pb::OverlayableItem::VENDOR:
+        out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+        break;
+      case pb::OverlayableItem::PRODUCT:
+        out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+        break;
+      case pb::OverlayableItem::PRODUCT_SERVICES:
+        out_overlayable->policies |= OverlayableItem::Policy::kProductServices;
+        break;
+      default:
+        *out_error = "unknown overlayable policy";
+        return false;
+    }
+  }
+
+  if (pb_overlayable.has_source()) {
+    DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &out_overlayable->source);
+  }
+
+  out_overlayable->comment = pb_overlayable.comment();
+  return true;
+}
+
 static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
-                                     io::IFileCollection* files, ResourceTable* out_table,
-                                     std::string* out_error) {
+                                     io::IFileCollection* files,
+                                     const std::vector<std::shared_ptr<Overlayable>>& overlayables,
+                                     ResourceTable* out_table, std::string* out_error) {
   Maybe<uint8_t> id;
   if (pb_package.has_package_id()) {
     id = static_cast<uint8_t>(pb_package.package_id().id());
@@ -437,39 +472,22 @@
         entry->allow_new = std::move(allow_new);
       }
 
-      if (pb_entry.has_overlayable()) {
-        Overlayable overlayable{};
-
-        const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
-        for (const int policy : pb_overlayable.policy()) {
-          switch (policy) {
-            case pb::Overlayable::PUBLIC:
-              overlayable.policies |= Overlayable::Policy::kPublic;
-              break;
-            case pb::Overlayable::SYSTEM:
-              overlayable.policies |= Overlayable::Policy::kSystem;
-              break;
-            case pb::Overlayable::VENDOR:
-              overlayable.policies |= Overlayable::Policy::kVendor;
-              break;
-            case pb::Overlayable::PRODUCT:
-              overlayable.policies |= Overlayable::Policy::kProduct;
-              break;
-            case pb::Overlayable::PRODUCT_SERVICES:
-              overlayable.policies |= Overlayable::Policy::kProductServices;
-              break;
-            default:
-              *out_error = "unknown overlayable policy";
-              return false;
-          }
+      if (pb_entry.has_overlayable_item()) {
+        // Find the overlayable to which this item belongs
+        pb::OverlayableItem pb_overlayable_item = pb_entry.overlayable_item();
+        if (pb_overlayable_item.overlayable_idx() >= overlayables.size()) {
+          *out_error = android::base::StringPrintf("invalid overlayable_idx value %d",
+                                                   pb_overlayable_item.overlayable_idx());
+          return false;
         }
 
-        if (pb_overlayable.has_source()) {
-          DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
+        OverlayableItem overlayable_item(overlayables[pb_overlayable_item.overlayable_idx()]);
+        if (!DeserializeOverlayableItemFromPb(pb_overlayable_item, src_pool, &overlayable_item,
+                                              out_error)) {
+          return false;
         }
 
-        overlayable.comment = pb_overlayable.comment();
-        entry->overlayable = overlayable;
+        entry->overlayable_item = std::move(overlayable_item);
       }
 
       ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
@@ -522,8 +540,19 @@
     }
   }
 
+  // Deserialize the overlayable groups of the table
+  std::vector<std::shared_ptr<Overlayable>> overlayables;
+  for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) {
+    auto group = std::make_shared<Overlayable>(pb_overlayable.name(), pb_overlayable.actor());
+    if (pb_overlayable.has_source()) {
+      DeserializeSourceFromPb(pb_overlayable.source(), source_pool, &group->source);
+    }
+    overlayables.push_back(group);
+  }
+
   for (const pb::Package& pb_package : pb_table.package()) {
-    if (!DeserializePackageFromPb(pb_package, source_pool, files, out_table, out_error)) {
+    if (!DeserializePackageFromPb(pb_package, source_pool, files, overlayables, out_table,
+                                  out_error)) {
       return false;
     }
   }
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 70bf868..76fbb46 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -272,9 +272,57 @@
   out_pb_config->set_sdk_version(config.sdkVersion);
 }
 
+static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item,
+                                         std::vector<Overlayable*>& serialized_overlayables,
+                                         StringPool* source_pool, pb::Entry* pb_entry,
+                                         pb::ResourceTable* pb_table) {
+  // Retrieve the index of the overlayable in the list of groups that have already been serialized.
+  size_t i;
+  for (i = 0 ; i < serialized_overlayables.size(); i++) {
+    if (overlayable_item.overlayable.get() == serialized_overlayables[i]) {
+      break;
+    }
+  }
+
+  // Serialize the overlayable if it has not been serialized already.
+  if (i == serialized_overlayables.size()) {
+    serialized_overlayables.push_back(overlayable_item.overlayable.get());
+    pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
+    pb_overlayable->set_name(overlayable_item.overlayable->name);
+    pb_overlayable->set_actor(overlayable_item.overlayable->actor);
+    SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
+                        pb_overlayable->mutable_source());
+  }
+
+  pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
+  pb_overlayable_item->set_overlayable_idx(i);
+
+  if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kProductServices) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT_SERVICES);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
+  }
+
+  SerializeSourceToPb(overlayable_item.source, source_pool,
+                      pb_overlayable_item->mutable_source());
+  pb_overlayable_item->set_comment(overlayable_item.comment);
+}
+
 void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
                         IDiagnostics* diag) {
   StringPool source_pool;
+
+  std::vector<Overlayable*> overlayables;
   for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
     pb::Package* pb_package = out_table->add_package();
     if (package->id) {
@@ -310,29 +358,9 @@
           pb_allow_new->set_comment(entry->allow_new.value().comment);
         }
 
-        if (entry->overlayable) {
-          pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
-
-          Overlayable overlayable = entry->overlayable.value();
-          if (overlayable.policies & Overlayable::Policy::kPublic) {
-            pb_overlayable->add_policy(pb::Overlayable::PUBLIC);
-          }
-          if (overlayable.policies & Overlayable::Policy::kProduct) {
-            pb_overlayable->add_policy(pb::Overlayable::PRODUCT);
-          }
-          if (overlayable.policies & Overlayable::Policy::kProductServices) {
-            pb_overlayable->add_policy(pb::Overlayable::PRODUCT_SERVICES);
-          }
-          if (overlayable.policies & Overlayable::Policy::kSystem) {
-            pb_overlayable->add_policy(pb::Overlayable::SYSTEM);
-          }
-          if (overlayable.policies & Overlayable::Policy::kVendor) {
-            pb_overlayable->add_policy(pb::Overlayable::VENDOR);
-          }
-
-          SerializeSourceToPb(overlayable.source, &source_pool,
-                              pb_overlayable->mutable_source());
-          pb_overlayable->set_comment(overlayable.comment);
+        if (entry->overlayable_item) {
+          SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables, &source_pool,
+                                       pb_entry, out_table);
         }
 
         for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index fb913f40..4a3c1b8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,8 +93,11 @@
       util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
 
   // Make an overlayable resource.
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>(
+      "OverlayableName", "overlay://theme", Source("res/values/overlayable.xml", 40)));
+  overlayable_item.source = Source("res/values/overlayable.xml", 42);
   ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
-                                    Overlayable{}, test::GetDiagnostics()));
+                                    overlayable_item, test::GetDiagnostics()));
 
   pb::ResourceTable pb_table;
   SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
@@ -160,9 +163,15 @@
       new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("OverlayableName"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -503,26 +512,31 @@
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
-  Overlayable overlayable_foo{};
-  overlayable_foo.policies |= Overlayable::Policy::kSystem;
-  overlayable_foo.policies |= Overlayable::Policy::kProduct;
+  OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
+      "CustomizableResources", "overlay://customization"));
+  overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
 
-  Overlayable overlayable_bar{};
-  overlayable_bar.policies |= Overlayable::Policy::kProductServices;
-  overlayable_bar.policies |= Overlayable::Policy::kVendor;
+  OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
+      "TaskBar", "overlay://theme"));
+  overlayable_item_bar.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
 
-  Overlayable overlayable_baz{};
-  overlayable_baz.policies |= Overlayable::Policy::kPublic;
+  OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
+      "FontPack", "overlay://theme"));
+  overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
 
-  Overlayable overlayable_biz{};
+  OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
+      "Other", "overlay://customization"));
+  overlayable_item_biz.comment ="comment";
 
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
-          .SetOverlayable("com.app.a:bool/foo", overlayable_foo)
-          .SetOverlayable("com.app.a:bool/bar", overlayable_bar)
-          .SetOverlayable("com.app.a:bool/baz", overlayable_baz)
-          .SetOverlayable("com.app.a:bool/biz", overlayable_biz)
+          .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
+          .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
+          .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+          .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
 
@@ -538,33 +552,41 @@
   Maybe<ResourceTable::SearchResult> search_result =
       new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kSystem
-                                              | Overlayable::Policy::kProduct));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
+                                              | OverlayableItem::Policy::kProduct));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProductServices
-                                              | Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices
+                                              | OverlayableItem::Policy::kVendor));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kPublic);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kNone);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(overlayable_item.comment, Eq("comment"));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
   ASSERT_TRUE(search_result);
-  ASSERT_FALSE(search_result.value().entry->overlayable);
+  ASSERT_FALSE(search_result.value().entry->overlayable_item);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8cbc037..c2340ba 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -374,8 +374,8 @@
         }
 
         // Ensure that definitions for values declared as overlayable exist
-        if (entry->overlayable && entry->values.empty()) {
-          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable.value().source)
+        if (entry->overlayable_item && entry->values.empty()) {
+          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
                                            << "no definition for overlayable symbol '"
                                            << name << "'");
           error = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22e1723..cc9fed5 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -134,18 +134,18 @@
     dst_entry->allow_new = std::move(src_entry->allow_new);
   }
 
-  if (src_entry->overlayable) {
-    if (dst_entry->overlayable) {
+  if (src_entry->overlayable_item) {
+    if (dst_entry->overlayable_item) {
       // Do not allow a resource with an overlayable declaration to have that overlayable
       // declaration redefined
-      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable_item.value().source)
                                        << "duplicate overlayable declaration for resource '"
                                        << src_entry->name << "'");
-      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable_item.value().source)
                                        << "previous declaration here");
       return false;
     } else {
-      dst_entry->overlayable = std::move(src_entry->overlayable);
+      dst_entry->overlayable_item = std::move(src_entry->overlayable_item);
     }
   }
 
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 17b2a83..921d634 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -437,14 +437,16 @@
 }
 
 TEST_F(TableMergerTest, SetOverlayable) {
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kVendor;
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kVendor;
 
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable)
+          .SetOverlayable("bool/foo", overlayable_item)
           .Build();
 
   std::unique_ptr<ResourceTable> table_b =
@@ -463,26 +465,30 @@
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                              | Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kVendor));
 }
 
 TEST_F(TableMergerTest, SetOverlayableLater) {
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
           .AddSimple("bool/foo")
           .Build();
 
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kPublic;
-  overlayable.policies |= Overlayable::Policy::kProductServices;
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable)
+          .SetOverlayable("bool/foo", overlayable_item)
           .Build();
 
   ResourceTable final_table;
@@ -495,27 +501,33 @@
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kPublic
-                                              | Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
+                                                   | OverlayableItem::Policy::kProductServices));
 }
 
-TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
-  Overlayable overlayable_first{};
-  overlayable_first.policies |= Overlayable::Policy::kProduct;
+TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
+  auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
+                                                         "overlay://customization");
+  OverlayableItem overlayable_item_first(overlayable_first);
+  overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_first)
+          .SetOverlayable("bool/foo", overlayable_item_first)
           .Build();
 
-  Overlayable overlayable_second{};
-  overlayable_second.policies |= Overlayable::Policy::kProduct;
+  auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
+                                                          "overlay://theme");
+  OverlayableItem overlayable_item_second(overlayable_second);
+  overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_second)
+          .SetOverlayable("bool/foo", overlayable_item_second)
           .Build();
 
   ResourceTable final_table;
@@ -526,21 +538,24 @@
   ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
 }
 
-TEST_F(TableMergerTest, SetOverlayableDifferentPolicesFail) {
-  Overlayable overlayable_first{};
-  overlayable_first.policies |= Overlayable::Policy::kVendor;
+TEST_F(TableMergerTest, SameResourceSameNameFail) {
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
+
+  OverlayableItem overlayable_item_first(overlayable);
+  overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo",overlayable_first)
+          .SetOverlayable("bool/foo", overlayable_item_first)
           .Build();
 
-  Overlayable overlayable_second{};
-  overlayable_second.policies |= Overlayable::Policy::kProduct;
+  OverlayableItem overlayable_item_second(overlayable);
+  overlayable_item_second.policies |= OverlayableItem::Policy::kSystem;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_second)
+          .SetOverlayable("bool/foo", overlayable_item_second)
           .Build();
 
   ResourceTable final_table;
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 9c5b5d3..24cd5ba 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,7 +248,7 @@
             if (!split_entry->id) {
               split_entry->id = entry->id;
               split_entry->visibility = entry->visibility;
-              split_entry->overlayable = entry->overlayable;
+              split_entry->overlayable_item = entry->overlayable_item;
             }
 
             // Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 884ec38..9a93f2a 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -136,7 +136,7 @@
 }
 
 ResourceTableBuilder& ResourceTableBuilder::SetOverlayable(const StringPiece& name,
-                                                           const Overlayable& overlayable) {
+                                                           const OverlayableItem& overlayable) {
 
   ResourceName res_name = ParseNameOrDie(name);
   CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index a120484..c971a1b 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -74,7 +74,7 @@
   ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
                                        Visibility::Level level, bool allow_new = false);
   ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
-                                       const Overlayable& overlayable);
+                                       const OverlayableItem& overlayable);
 
   StringPool* string_pool();
   std::unique_ptr<ResourceTable> Build();