Merge "Fix NPE onNotificationRemoved"
diff --git a/api/current.txt b/api/current.txt
index e3a20f5..96485a8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23462,10 +23462,14 @@
   public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
+    method public int getAudioSource();
     method public int getClientAudioSessionId();
     method public int getClientAudioSource();
+    method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getClientEffects();
     method public android.media.AudioFormat getClientFormat();
+    method public java.util.List<android.media.audiofx.AudioEffect.Descriptor> getEffects();
     method public android.media.AudioFormat getFormat();
+    method public boolean isClientSilenced();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
@@ -35147,6 +35151,7 @@
 
   public final class StorageVolume implements android.os.Parcelable {
     method public deprecated android.content.Intent createAccessIntent(java.lang.String);
+    method public android.content.Intent createOpenDocumentTreeIntent();
     method public int describeContents();
     method public java.lang.String getDescription(android.content.Context);
     method public java.lang.String getState();
diff --git a/api/test-current.txt b/api/test-current.txt
index 1e15792..71a06f1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -638,6 +638,11 @@
     method public static boolean isEncodingLinearPcm(int);
   }
 
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
+    ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]);
+    ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, java.lang.String);
+  }
+
   public final class BufferingParams implements android.os.Parcelable {
     method public int describeContents();
     method public int getInitialMarkMs();
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index e3fe928..0775afe 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -16,207 +16,193 @@
 package com.android.statsd.shelltools.testdrive;
 
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import com.android.os.AtomsProto.Atom;
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.StatsLogReport;
 import com.android.statsd.shelltools.Utils;
 
 import com.google.common.io.Files;
-import com.google.protobuf.TextFormat;
-import com.google.protobuf.TextFormat.ParseException;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 public class TestDrive {
 
-    public static final int PULL_ATOM_START = 10000;
-    public static final long ATOM_MATCHER_ID = 1234567;
+    private static final int METRIC_ID_BASE = 1111;
+    private static final long ATOM_MATCHER_ID_BASE = 1234567;
+    private static final int PULL_ATOM_START = 10000;
+    private static final long CONFIG_ID = 54321;
+    private static final String[] ALLOWED_LOG_SOURCES = {
+        "AID_GRAPHICS",
+        "AID_INCIDENTD",
+        "AID_STATSD",
+        "AID_RADIO",
+        "com.android.systemui",
+        "com.android.vending",
+        "AID_SYSTEM",
+        "AID_ROOT",
+        "AID_BLUETOOTH",
+        "AID_LMKD"
+    };
+    private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
 
-    public static final long CONFIG_ID = 54321;
-
-    private static boolean mIsPushedAtom = false;
-
-    private static final Logger logger = Logger.getLogger(TestDrive.class.getName());
+    private final Set<Long> mTrackedMetrics = new HashSet<>();
 
     public static void main(String[] args) {
         TestDrive testDrive = new TestDrive();
-        Utils.setUpLogger(logger, false);
+        Set<Integer> trackedAtoms = new HashSet<>();
+        Utils.setUpLogger(LOGGER, false);
+        String remoteConfigPath = null;
 
-        if (args.length != 1) {
-            logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>");
+        if (args.length < 1) {
+            LOGGER.log(Level.SEVERE, "Usage: ./test_drive <atomId1> <atomId2> ... <atomIdN>");
             return;
         }
-        int atomId;
-        try {
-            atomId = Integer.valueOf(args[0]);
-        } catch (NumberFormatException e) {
-            logger.log(Level.SEVERE, "Bad atom id provided: " + args[0]);
-            return;
-        }
-        if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
-            logger.log(Level.SEVERE, "No such atom found: " + args[0]);
-            return;
-        }
-        mIsPushedAtom = atomId < PULL_ATOM_START;
 
+        for (int i = 0; i < args.length; i++) {
+            try {
+                int atomId = Integer.valueOf(args[i]);
+                if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
+                    LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]);
+                    continue;
+                }
+                trackedAtoms.add(atomId);
+            } catch (NumberFormatException e) {
+                LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]);
+                continue;
+            }
+        }
 
         try {
-            StatsdConfig config = testDrive.createConfig(atomId);
+            StatsdConfig config = testDrive.createConfig(trackedAtoms);
             if (config == null) {
-                logger.log(Level.SEVERE, "Failed to create valid config.");
+                LOGGER.log(Level.SEVERE, "Failed to create valid config.");
                 return;
             }
-            testDrive.pushConfig(config);
-            logger.info("Pushed the following config to statsd:");
-            logger.info(config.toString());
-            if (mIsPushedAtom) {
-                logger.info(
+            remoteConfigPath = testDrive.pushConfig(config);
+            LOGGER.info("Pushed the following config to statsd:");
+            LOGGER.info(config.toString());
+            if (!hasPulledAtom(trackedAtoms)) {
+                LOGGER.info(
                         "Now please play with the device to trigger the event. All events should "
                                 + "be dumped after 1 min ...");
                 Thread.sleep(60_000);
             } else {
                 // wait for 2 min
-                logger.info("Now wait for 2 minutes ...");
+                LOGGER.info("Now wait for 2 minutes ...");
                 Thread.sleep(120_000);
             }
             testDrive.dumpMetrics();
         } catch (Exception e) {
-            logger.log(Level.SEVERE, "Failed to test drive: " + e.getMessage());
+            LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
         } finally {
             testDrive.removeConfig();
-        }
-    }
-
-    private void pushConfig(StatsdConfig config) throws IOException, InterruptedException {
-        File configFile = File.createTempFile("statsdconfig", ".config");
-        configFile.deleteOnExit();
-        Files.write(config.toByteArray(), configFile);
-        String remotePath = "/data/local/tmp/" + configFile.getName();
-        Utils.runCommand(null, logger, "adb", "push", configFile.getAbsolutePath(), remotePath);
-        Utils.runCommand(null, logger,
-                "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
-                String.valueOf(CONFIG_ID));
-    }
-
-    private void removeConfig() {
-        try {
-            Utils.runCommand(null, logger, 
-                    "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
-        } catch (Exception e) {
-            logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
-        }
-    }
-
-    private StatsdConfig createConfig(int atomId) {
-        try {
-            if (mIsPushedAtom) {
-                return createSimpleEventMetricConfig(atomId);
-            } else {
-                return createSimpleGaugeMetricConfig(atomId);
+            if (remoteConfigPath != null) {
+                try {
+                    Utils.runCommand(null, LOGGER, "adb", "shell", "rm", remoteConfigPath);
+                } catch (Exception e) {
+                    LOGGER.log(Level.WARNING,
+                            "Unable to remove remote config file: " + remoteConfigPath, e);
+                }
             }
-        } catch (ParseException e) {
-            logger.log(
-                    Level.SEVERE,
-                    "Failed to parse the config! line: "
-                            + e.getLine()
-                            + " col: "
-                            + e.getColumn()
-                            + " "
-                            + e.getMessage());
         }
-        return null;
     }
 
-    private StatsdConfig createSimpleEventMetricConfig(int atomId) throws ParseException {
-        StatsdConfig.Builder baseBuilder = getSimpleEventMetricBaseConfig();
-        baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
-        return baseBuilder.build();
+    private void dumpMetrics() throws Exception {
+        ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER);
+        // We may get multiple reports. Take the last one.
+        ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
+        for (StatsLogReport statsLog : report.getMetricsList()) {
+            if (mTrackedMetrics.contains(statsLog.getMetricId())) {
+                LOGGER.info(statsLog.toString());
+            }
+        }
     }
 
-    private StatsdConfig createSimpleGaugeMetricConfig(int atomId) throws ParseException {
-        StatsdConfig.Builder baseBuilder = getSimpleGaugeMetricBaseConfig();
-        baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
-        return baseBuilder.build();
+    private StatsdConfig createConfig(Set<Integer> atomIds) {
+        long metricId = METRIC_ID_BASE;
+        long atomMatcherId = ATOM_MATCHER_ID_BASE;
+
+        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+        builder
+            .addAllAllowedLogSource(Arrays.asList(ALLOWED_LOG_SOURCES))
+            .setHashStringsInMetricReport(false);
+
+        for (int atomId : atomIds) {
+            if (isPulledAtom(atomId)) {
+                builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+                GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
+                gaugeMetricBuilder
+                    .setId(metricId)
+                    .setWhat(atomMatcherId)
+                    .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
+                    .setBucket(TimeUnit.ONE_MINUTE);
+                builder.addGaugeMetric(gaugeMetricBuilder.build());
+            } else {
+                EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+                eventMetricBuilder
+                    .setId(metricId)
+                    .setWhat(atomMatcherId);
+                builder.addEventMetric(eventMetricBuilder.build());
+                builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+            }
+            atomMatcherId++;
+            mTrackedMetrics.add(metricId++);
+        }
+        return builder.build();
     }
 
-    private AtomMatcher createAtomMatcher(int atomId) {
+    private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
         AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
         atomMatcherBuilder
-                .setId(ATOM_MATCHER_ID)
+                .setId(matcherId)
                 .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
         return atomMatcherBuilder.build();
     }
 
-    private StatsdConfig.Builder getSimpleEventMetricBaseConfig() throws ParseException {
-        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
-        TextFormat.merge(EVENT_BASE_CONFIG_SRTR, builder);
-        return builder;
+    private static String pushConfig(StatsdConfig config) throws IOException, InterruptedException {
+        File configFile = File.createTempFile("statsdconfig", ".config");
+        configFile.deleteOnExit();
+        Files.write(config.toByteArray(), configFile);
+        String remotePath = "/data/local/tmp/" + configFile.getName();
+        Utils.runCommand(null, LOGGER, "adb", "push", configFile.getAbsolutePath(), remotePath);
+        Utils.runCommand(null, LOGGER,
+                "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
+                String.valueOf(CONFIG_ID));
+        return remotePath;
     }
 
-    private StatsdConfig.Builder getSimpleGaugeMetricBaseConfig() throws ParseException {
-        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
-        TextFormat.merge(GAUGE_BASE_CONFIG_STR, builder);
-        return builder;
-    }
-
-    private void dumpMetrics() throws Exception {
-        ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, logger);
-        // We may get multiple reports. Take the last one.
-        ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
-        // Really should be only one metric.
-        if (report.getMetricsCount() != 1) {
-            logger.log(Level.SEVERE,
-                    "Only one report metric expected, got " + report.getMetricsCount());
-            return;
+    private static void removeConfig() {
+        try {
+            Utils.runCommand(null, LOGGER,
+                    "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
+        } catch (Exception e) {
+            LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
         }
-
-        logger.info("Got following metric data dump:");
-        logger.info(report.getMetrics(0).toString());
     }
 
-    private static final String EVENT_BASE_CONFIG_SRTR =
-            "id: 12345\n"
-                    + "event_metric {\n"
-                    + "  id: 1111\n"
-                    + "  what: 1234567\n"
-                    + "}\n"
-                    + "allowed_log_source: \"AID_GRAPHICS\"\n"
-                    + "allowed_log_source: \"AID_INCIDENTD\"\n"
-                    + "allowed_log_source: \"AID_STATSD\"\n"
-                    + "allowed_log_source: \"AID_RADIO\"\n"
-                    + "allowed_log_source: \"com.android.systemui\"\n"
-                    + "allowed_log_source: \"com.android.vending\"\n"
-                    + "allowed_log_source: \"AID_SYSTEM\"\n"
-                    + "allowed_log_source: \"AID_ROOT\"\n"
-                    + "allowed_log_source: \"AID_BLUETOOTH\"\n"
-                    + "\n"
-                    + "hash_strings_in_metric_report: false";
+    private static boolean isPulledAtom(int atomId) {
+        return atomId >= PULL_ATOM_START;
+    }
 
-    private static final String GAUGE_BASE_CONFIG_STR =
-            "id: 56789\n"
-                    + "gauge_metric {\n"
-                    + "  id: 2222\n"
-                    + "  what: 1234567\n"
-                    + "  gauge_fields_filter {\n"
-                    + "    include_all: true\n"
-                    + "  }\n"
-                    + "  bucket: ONE_MINUTE\n"
-                    + "}\n"
-                    + "allowed_log_source: \"AID_GRAPHICS\"\n"
-                    + "allowed_log_source: \"AID_INCIDENTD\"\n"
-                    + "allowed_log_source: \"AID_STATSD\"\n"
-                    + "allowed_log_source: \"AID_RADIO\"\n"
-                    + "allowed_log_source: \"com.android.systemui\"\n"
-                    + "allowed_log_source: \"com.android.vending\"\n"
-                    + "allowed_log_source: \"AID_SYSTEM\"\n"
-                    + "allowed_log_source: \"AID_ROOT\"\n"
-                    + "allowed_log_source: \"AID_BLUETOOTH\"\n"
-                    + "\n"
-                    + "hash_strings_in_metric_report: false";
-
+    private static boolean hasPulledAtom(Set<Integer> atoms) {
+        for (Integer i : atoms) {
+            if (isPulledAtom(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a278423..78fe002 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2496,7 +2496,7 @@
          * @param packageName The package performing the operation.
          * @param result The result of the note.
          */
-        void onOpNoted(String code, int uid, String packageName, int result);
+        void onOpNoted(int code, int uid, String packageName, int result);
     }
 
     /**
@@ -2953,7 +2953,7 @@
      * @hide
      */
     @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
-    public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) {
+    public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener callback) {
         IAppOpsNotedCallback cb;
         synchronized (mNotedWatchers) {
             cb = mNotedWatchers.get(callback);
@@ -2963,17 +2963,13 @@
             cb = new IAppOpsNotedCallback.Stub() {
                 @Override
                 public void opNoted(int op, int uid, String packageName, int mode) {
-                    callback.onOpNoted(sOpToString[op], uid, packageName, mode);
+                    callback.onOpNoted(op, uid, packageName, mode);
                 }
             };
             mNotedWatchers.put(callback, cb);
         }
         try {
-            final int[] opCodes = new int[ops.length];
-            for (int i = 0; i < opCodes.length; i++) {
-                opCodes[i] = strOpToOp(ops[i]);
-            }
-            mService.startWatchingNoted(opCodes, cb);
+            mService.startWatchingNoted(ops, cb);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2983,7 +2979,7 @@
      * Stop watching for noted app ops. An app op may be immediate or long running.
      * Unregistering a non-registered callback has no effect.
      *
-     * @see #startWatchingNoted(String[], OnOpNotedListener)
+     * @see #startWatchingNoted(int[], OnOpNotedListener)
      * @see #noteOp(String, int, String)
      *
      * @hide
@@ -3078,7 +3074,7 @@
      */
     public int unsafeCheckOpRaw(String op, int uid, String packageName) {
         try {
-            return mService.checkOperation(strOpToOp(op), uid, packageName);
+            return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 7fe21b2..139a39f 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -37,10 +37,11 @@
          * @param uid The UID for which to check.
          * @param packageName The package for which to check.
          * @param superImpl The super implementation.
+         * @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
          * @return The app op check result.
          */
-        int checkOperation(int code, int uid, String packageName,
-                TriFunction<Integer, Integer, String, Integer> superImpl);
+        int checkOperation(int code, int uid, String packageName, boolean raw,
+                QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl);
 
         /**
          * Allows overriding check audio operation behavior.
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8a0d916..f3810bd 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -257,8 +257,7 @@
             return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
         }
         // Make sure we have good settings to use
-        if (globalSettingsDriverPkgs.isEmpty() || globalSettingsDriverValues.isEmpty()
-                || (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size())) {
+        if (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size()) {
             Log.w(TAG,
                     "Global.Settings values are invalid: "
                         + "globalSettingsDriverPkgs.size = "
@@ -299,9 +298,120 @@
     }
 
     /**
+     * Attempt to setup ANGLE with a temporary rules file.
+     * True: Temporary rules file was loaded.
+     * False: Temporary rules file was *not* loaded.
+     */
+    private boolean setupAngleWithTempRulesFile(Context context,
+                                                String packageName,
+                                                String paths,
+                                                String devOptIn) {
+        // Check for temporary rules if debuggable or root
+        if (!isDebuggable(context) && !(getCanLoadSystemLibraries() == 1)) {
+            Log.v(TAG, "Skipping loading temporary rules file");
+            return false;
+        }
+
+        String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
+
+        if ((angleTempRules == null) || angleTempRules.isEmpty()) {
+            Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
+            return false;
+        }
+
+        Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
+
+        File tempRulesFile = new File(angleTempRules);
+        if (tempRulesFile.exists()) {
+            Log.i(TAG, angleTempRules + " exists, loading file.");
+            try {
+                FileInputStream stream = new FileInputStream(angleTempRules);
+
+                try {
+                    FileDescriptor rulesFd = stream.getFD();
+                    long rulesOffset = 0;
+                    long rulesLength = stream.getChannel().size();
+                    Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
+
+                    setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+
+                    stream.close();
+
+                    // We successfully setup ANGLE, so return with good status
+                    return true;
+                } catch (IOException e) {
+                    Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
+                }
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Temp ANGLE rules file not found: " + e);
+            } catch (SecurityException e) {
+                Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
+     * True: Rules file was loaded.
+     * False: Rules file was *not* loaded.
+     */
+    private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
+        // b/121153494
+        // Skip APK rules file checking.
+        if (!DEBUG) {
+            Log.v(TAG, "Skipping loading the rules file.");
+            // Fill in some default values for now, so the loader can get an answer when it asks.
+            // Most importantly, we need to indicate which app we are init'ing and what the
+            // developer options for it are so we can turn on ANGLE if needed.
+            setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
+     * True: APK rules file was loaded.
+     * False: APK rules file was *not* loaded.
+     */
+    private boolean setupAngleRulesApk(String anglePkgName,
+            ApplicationInfo angleInfo,
+            Context context,
+            String packageName,
+            String paths,
+            String devOptIn) {
+        // Pass the rules file to loader for ANGLE decisions
+        try {
+            AssetManager angleAssets =
+                    context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+
+            try {
+                AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+
+                setAngleInfo(paths, packageName, devOptIn, assetsFd.getFileDescriptor(),
+                        assetsFd.getStartOffset(), assetsFd.getLength());
+
+                assetsFd.close();
+
+                return true;
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
+                        + " from '" + anglePkgName + "': " + e);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
+        }
+
+        return false;
+    }
+
+    /**
      * Pass ANGLE details down to trigger enable logic
      */
-    private void setupAngle(Context context, Bundle bundle, String packageName) {
+    public void setupAngle(Context context, Bundle bundle, String packageName) {
         String devOptIn = getDriverForPkg(bundle, packageName);
 
         if (DEBUG) {
@@ -327,86 +437,29 @@
         String abi = chooseAbi(angleInfo);
 
         // Build a path that includes installed native libs and APK
-        StringBuilder sb = new StringBuilder();
-        sb.append(angleInfo.nativeLibraryDir)
-            .append(File.pathSeparator)
-            .append(angleInfo.sourceDir)
-            .append("!/lib/")
-            .append(abi);
-        String paths = sb.toString();
+        String paths = angleInfo.nativeLibraryDir
+                + File.pathSeparator
+                + angleInfo.sourceDir
+                + "!/lib/"
+                + abi;
 
         if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
 
-        // Look up rules file to pass to ANGLE
-        FileDescriptor rulesFd = null;
-        long rulesOffset = 0;
-        long rulesLength = 0;
-
-        // Check for temporary rules if debuggable or root
-        if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
-            String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
-            if (angleTempRules != null && !angleTempRules.isEmpty()) {
-                Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
-                File tempRulesFile = new File(angleTempRules);
-                if (tempRulesFile.exists()) {
-                    Log.i(TAG, angleTempRules + " exists, loading file.");
-                    FileInputStream stream = null;
-                    try {
-                        stream = new FileInputStream(angleTempRules);
-                    } catch (FileNotFoundException e) {
-                        Log.w(TAG, "Unable to create stream for temp ANGLE rules");
-                    }
-
-                    if (stream != null) {
-                        try {
-                            rulesFd = stream.getFD();
-                            rulesOffset = 0;
-                            rulesLength = stream.getChannel().size();
-                            Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
-                        } catch (IOException e) {
-                            Log.w(TAG, "Failed to get input stream for " + angleTempRules);
-                        }
-                    }
-                }
-            }
+        if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
+            // We setup ANGLE with a temp rules file, so we're done here.
+            return;
         }
 
-        // If no temp rules, load the real ones from the APK
-        if (DEBUG && (rulesFd == null)) {
-
-            // Pass the rules file to loader for ANGLE decisions
-            AssetManager angleAssets = null;
-            try {
-                angleAssets =
-                    context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "'");
-                return;
-            }
-
-            AssetFileDescriptor assetsFd = null;
-            try {
-                assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
-                           + "'" + anglePkgName + "'");
-                return;
-            }
-
-            if (assetsFd != null) {
-                rulesFd = assetsFd.getFileDescriptor();
-                rulesOffset = assetsFd.getStartOffset();
-                rulesLength = assetsFd.getLength();
-            } else {
-                Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
-                return;
-            }
+        // b/121153494
+        if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
+            // We setup ANGLE with defaults, so we're done here.
+            return;
         }
 
-        // Further opt-in logic is handled in native, so pass relevant info down
-        // TODO: Move the ANGLE selection logic earlier so we don't need to keep these
-        //       file descriptors open.
-        setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
+        if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
+            // We setup ANGLE with rules from the APK, so we're done here.
+            return;
+        }
     }
 
     /**
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 8a03e9e..df1a713 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -348,6 +349,32 @@
         return intent;
     }
 
+    /**
+     * Builds an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to allow the user to grant access to any
+     * directory subtree (or entire volume) from the {@link android.provider.DocumentsProvider}s
+     * available on the device. The initial location of the document navigation will be the root of
+     * this {@link StorageVolume}.
+     *
+     * Note that the returned {@link Intent} simply suggests that the user picks this {@link
+     * StorageVolume} by default, but the user may select a different location. Callers must respect
+     * the user's chosen location, even if it is different from the originally requested location.
+     *
+     * @return intent to {@link Intent#ACTION_OPEN_DOCUMENT_TREE} initially showing the contents
+     *         of this {@link StorageVolume}
+     * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+     */
+    @NonNull public Intent createOpenDocumentTreeIntent() {
+        final String rootId = isEmulated()
+                ? DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID
+                : mFsUuid;
+        final Uri rootUri = DocumentsContract.buildRootUri(
+                DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId);
+        final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
+                .putExtra(DocumentsContract.EXTRA_INITIAL_URI, rootUri)
+                .putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+        return intent;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof StorageVolume && mPath != null) {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index cd991cc..a323ed1 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -238,6 +238,9 @@
             "com.android.externalstorage.documents";
 
     /** {@hide} */
+    public static final String EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID = "primary";
+
+    /** {@hide} */
     public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
 
     /**
@@ -857,16 +860,6 @@
     }
 
     /**
-     * Builds URI for user home directory on external (local) storage.
-     * {@hide}
-     */
-    public static Uri buildHomeUri() {
-        // TODO: Avoid this type of interpackage copying. Added here to avoid
-        // direct coupling, but not ideal.
-        return DocumentsContract.buildRootUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY, "home");
-    }
-
-    /**
      * Build URI representing the recently modified documents of a specific root
      * in a document provider. When queried, a provider will return zero or more
      * rows with columns defined by {@link Document}.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9f019f7..d93985c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12820,6 +12820,17 @@
                 "privileged_device_identifier_3p_check_relaxed";
 
         /**
+         * If set to 1, the device identifier check will be relaxed to the previous READ_PHONE_STATE
+         * permission check for preloaded non-privileged apps.
+         *
+         * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+         *
+         * @hide
+         */
+        public static final String PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED =
+                "privileged_device_identifier_non_priv_check_relaxed";
+
+        /**
          * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
          * and restoring to lower version of platform API will be skipped.
          *
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index 3fa1b01..e44d6eb 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -26,6 +26,7 @@
 abstract class WebSyncManager implements Runnable {
     protected static final java.lang.String LOGTAG = "websync";
     protected android.webkit.WebViewDatabase mDataBase;
+    @UnsupportedAppUsage
     protected android.os.Handler mHandler;
 
     protected WebSyncManager(Context context, String name) {
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index 213bb75..c093fe5 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -37,12 +37,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.time.DateTimeException;
-import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeParseException;
 
 /**
  * Controller for managing night display and color mode settings.
@@ -152,28 +148,6 @@
     }
 
     /**
-     * Returns the time when Night display's activation state last changed, or {@code null} if it
-     * has never been changed.
-     */
-    public LocalDateTime getLastActivatedTime() {
-        final ContentResolver cr = mContext.getContentResolver();
-        final String lastActivatedTime = Secure.getStringForUser(
-                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
-        if (lastActivatedTime != null) {
-            try {
-                return LocalDateTime.parse(lastActivatedTime);
-            } catch (DateTimeParseException ignored) {}
-            // Uses the old epoch time.
-            try {
-                return LocalDateTime.ofInstant(
-                    Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
-                    ZoneId.systemDefault());
-            } catch (DateTimeException|NumberFormatException ignored) {}
-        }
-        return null;
-    }
-
-    /**
      * Returns the current auto mode value controlling when Night display will be automatically
      * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
      * {@link #AUTO_MODE_TWILIGHT}.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index e571656..e59bee4 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -65,4 +65,6 @@
 
     void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
     void stopWatchingNoted(IAppOpsNotedCallback callback);
+
+    int checkOperationRaw(int code, int uid, String packageName);
 }
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
index 27c5834..d215670 100644
--- a/core/java/com/android/internal/widget/NumericTextView.java
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -53,6 +54,7 @@
 
     private OnValueChangedListener mListener;
 
+    @UnsupportedAppUsage
     public NumericTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 84f53468..fc9ea76 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -102,6 +102,10 @@
     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
 }
 
+static jint saveUnclippedLayer(jlong canvasHandle, jint l, jint t, jint r, jint b) {
+    return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
+}
+
 static bool restore(jlong canvasHandle) {
     Canvas* canvas = get_canvas(canvasHandle);
     if (canvas->getSaveCount() <= 1) {
@@ -651,6 +655,7 @@
     {"nSave","(JI)I", (void*) CanvasJNI::save},
     {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
     {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+    {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
     {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
     {"nRestore","(J)Z", (void*) CanvasJNI::restore},
     {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 283eb03..29d8f30 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -32,6 +32,7 @@
 #include <nativehelper/ScopedLocalRef.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
+#include "android_media_AudioEffectDescriptor.h"
 #include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
 #include "android_media_MicrophoneInfo.h"
@@ -427,9 +428,14 @@
 }
 
 static void
-android_media_AudioSystem_recording_callback(int event, const record_client_info_t *clientInfo,
-        const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
-        audio_patch_handle_t patchHandle)
+android_media_AudioSystem_recording_callback(int event,
+                                             const record_client_info_t *clientInfo,
+                                             const audio_config_base_t *clientConfig,
+                                             std::vector<effect_descriptor_t> clientEffects,
+                                             const audio_config_base_t *deviceConfig,
+                                             std::vector<effect_descriptor_t> effects __unused,
+                                             audio_patch_handle_t patchHandle,
+                                             audio_source_t source)
 {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     if (env == NULL) {
@@ -460,14 +466,24 @@
     recParamData[6] = (jint) patchHandle;
     env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData);
 
+    jobjectArray jClientEffects;
+    convertAudioEffectDescriptorVectorFromNative(env, &jClientEffects, clientEffects);
+
+    jobjectArray jEffects;
+    convertAudioEffectDescriptorVectorFromNative(env, &jEffects, effects);
+
     // callback into java
     jclass clazz = env->FindClass(kClassPathName);
-    env->CallStaticVoidMethod(clazz,
-            gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
-            event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
-    env->DeleteLocalRef(clazz);
 
+    env->CallStaticVoidMethod(clazz,
+                              gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+                              event, (jint) clientInfo->uid, clientInfo->session,
+                              clientInfo->source, clientInfo->port_id, clientInfo->silenced,
+                              recParamArray, jClientEffects, jEffects, source);
+    env->DeleteLocalRef(clazz);
     env->DeleteLocalRef(recParamArray);
+    env->DeleteLocalRef(jClientEffects);
+    env->DeleteLocalRef(jEffects);
 }
 
 static jint
@@ -2260,7 +2276,7 @@
                     "dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
     gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
             GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
-                    "recordingCallbackFromNative", "(IIII[I)V");
+                    "recordingCallbackFromNative", "(IIIIIZ[I[Landroid/media/audiofx/AudioEffect$Descriptor;[Landroid/media/audiofx/AudioEffect$Descriptor;I)V");
 
     jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
     gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0ed8212..56265cc 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1442,7 +1442,7 @@
     <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
         <!-- action bar -->
         <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
-        <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
+        <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
         <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
 
         <!-- Color palette -->
@@ -1678,11 +1678,8 @@
 
     <style name="ThemeOverlay.DeviceDefault" />
 
-    <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for
-             primary text -->
-    <style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar">
-        <item name="textColorPrimary">@color/btn_colored_borderless_text_material</item>
-    </style>
+    <!-- @hide Theme overlay that inherits from material actionbar -->
+    <style name="ThemeOverlay.DeviceDefault.ActionBar" parent="ThemeOverlay.Material.ActionBar" />
 
     <!-- @hide Theme overlay for a light popup in action bar -->
     <style name="ThemeOverlay.DeviceDefault.Popup.Light" parent="@style/ThemeOverlay.Material.Light" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 6d1aae1..f8bd4e3 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -384,6 +384,7 @@
                     Settings.Global.PRIV_APP_OOB_LIST,
                     Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
+                    Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED,
                     Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 6798ab2..63a806e 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -556,7 +556,7 @@
      * @hide
      */
     public int saveUnclippedLayer(int left, int top, int right, int bottom) {
-        return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, 0, 0);
+        return nSaveUnclippedLayer(mNativeCanvasWrapper, left, top, right, bottom);
     }
 
     /**
@@ -1395,6 +1395,8 @@
     private static native int nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b,
             int alpha, int layerFlags);
     @CriticalNative
+    private static native int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b);
+    @CriticalNative
     private static native boolean nRestore(long canvasHandle);
     @CriticalNative
     private static native void nRestoreToCount(long canvasHandle, int saveCount);
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 04cf611..bd1e6c5 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -18,6 +18,7 @@
 X(Save) 
 X(Restore) 
 X(SaveLayer)
+X(SaveBehind)
 X(Concat) 
 X(SetMatrix) 
 X(Translate)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 0b847af..fc813d2 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -18,6 +18,7 @@
 
 #include "VectorDrawable.h"
 
+#include "SkAndroidFrameworkUtils.h"
 #include "SkCanvas.h"
 #include "SkData.h"
 #include "SkDrawShadowInfo.h"
@@ -116,6 +117,16 @@
                       clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags});
     }
 };
+struct SaveBehind final : Op {
+    static const auto kType = Type::SaveBehind;
+    SaveBehind(const SkRect* subset) {
+        if (subset) { this->subset = *subset; }
+    }
+    SkRect  subset = kUnset;
+    void draw(SkCanvas* c, const SkMatrix&) const {
+        SkAndroidFrameworkUtils::SaveBehind(c, &subset);
+    }
+};
 
 struct Concat final : Op {
     static const auto kType = Type::Concat;
@@ -579,6 +590,10 @@
     this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags);
 }
 
+void DisplayListData::saveBehind(const SkRect* subset) {
+    this->push<SaveBehind>(0, subset);
+}
+
 void DisplayListData::concat(const SkMatrix& matrix) {
     this->push<Concat>(0, matrix);
 }
@@ -848,6 +863,11 @@
     fDL->restore();
 }
 
+bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) {
+    fDL->saveBehind(subset);
+    return false;
+}
+
 void RecordingCanvas::didConcat(const SkMatrix& matrix) {
     fDL->concat(matrix);
 }
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index de8777b..22b3a63 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -75,6 +75,7 @@
     void save();
     void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*,
                    const SkMatrix*, SkCanvas::SaveLayerFlags);
+    void saveBehind(const SkRect*);
     void restore();
 
     void concat(const SkMatrix&);
@@ -146,6 +147,7 @@
     void willSave() override;
     SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
     void willRestore() override;
+    bool onDoSaveBehind(const SkRect*) override;
 
     void onFlush() override;
 
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6be7ef7..83b9e7f 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -24,6 +24,7 @@
 #include "hwui/PaintFilter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 
+#include <SkAndroidFrameworkUtils.h>
 #include <SkAnimatedImage.h>
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
@@ -185,6 +186,11 @@
     return this->saveLayer(left, top, right, bottom, nullptr, flags);
 }
 
+int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
+}
+
 class SkiaCanvas::Clip {
 public:
     Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24d9c08..4ab0a59 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -74,6 +74,7 @@
                           SaveFlags::Flags flags) override;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
                                SaveFlags::Flags flags) override;
+    virtual int saveUnclippedLayer(int left, int top, int right, int bottom) override;
 
     virtual void getMatrix(SkMatrix* outMatrix) const override;
     virtual void setMatrix(const SkMatrix& matrix) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 71814c3..4c5365d 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -196,6 +196,7 @@
                           SaveFlags::Flags flags) = 0;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
                                SaveFlags::Flags flags) = 0;
+    virtual int saveUnclippedLayer(int, int, int, int) = 0;
 
     // Matrix
     virtual void getMatrix(SkMatrix* outMatrix) const = 0;
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 9bd5994..b531325 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -17,6 +17,7 @@
 package com.android.internal.location;
 
 import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -27,19 +28,17 @@
 import android.content.IntentFilter;
 import android.location.LocationManager;
 import android.location.INetInitiatedListener;
+import android.os.SystemClock;
 import android.telephony.TelephonyManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
-import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.R;
 import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.TelephonyProperties;
 
 /**
  * A GPS Network-initiated Handler class used by LocationManager.
@@ -50,8 +49,7 @@
 
     private static final String TAG = "GpsNetInitiatedHandler";
 
-    private static final boolean DEBUG = true;
-    private static final boolean VERBOSE = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // NI verify activity for bringing up UI (not used yet)
     public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
@@ -94,6 +92,9 @@
     public static final int GPS_ENC_SUPL_UCS2 = 3;
     public static final int GPS_ENC_UNKNOWN = -1;
 
+    // Limit on SUPL NI emergency mode time extension after emergency sessions ends
+    private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300;  // 5 minute maximum
+
     private final Context mContext;
     private final TelephonyManager mTelephonyManager;
     private final PhoneStateListener mPhoneStateListener;
@@ -109,7 +110,7 @@
     private volatile boolean mIsSuplEsEnabled;
 
     // Set to true if the phone is having emergency call.
-    private volatile boolean mIsInEmergency;
+    private volatile boolean mIsInEmergencyCall;
 
     // If Location function is enabled.
     private volatile boolean mIsLocationEnabled = false;
@@ -119,6 +120,10 @@
     // Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
     static private boolean mIsHexInput = true;
 
+    // End time of emergency call, and extension, if set
+    private long mCallEndElapsedRealtimeMillis = 0;
+    private long mEmergencyExtensionMillis = 0;
+
     public static class GpsNiNotification
     {
         public int notificationId;
@@ -146,16 +151,12 @@
             if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
                 String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
                 /*
-                   Emergency Mode is when during emergency call or in emergency call back mode.
-                   For checking if it is during emergency call:
-                       mIsInEmergency records if the phone is in emergency call or not. It will
+                   Tracks the emergency call:
+                       mIsInEmergencyCall records if the phone is in emergency call or not. It will
                        be set to true when the phone is having emergency call, and then will
                        be set to false by mPhoneStateListener when the emergency call ends.
-                   For checking if it is in emergency call back mode:
-                       Emergency call back mode will be checked by reading system properties
-                       when necessary: SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)
                 */
-                setInEmergency(PhoneNumberUtils.isEmergencyNumber(phoneNumber));
+                mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
                 if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
             } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
                 updateLocationMode();
@@ -195,7 +196,10 @@
                 if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state);
                 // listening for emergency call ends
                 if (state == TelephonyManager.CALL_STATE_IDLE) {
-                    setInEmergency(false);
+                    if (mIsInEmergencyCall) {
+                        mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+                        mIsInEmergencyCall = false;
+                    }
                 }
             }
         };
@@ -229,22 +233,35 @@
         return mIsLocationEnabled;
     }
 
-    // Note: Currently, there are two mechanisms involved to determine if a
-    // phone is in emergency mode:
-    // 1. If the user is making an emergency call, this is provided by activly
-    //    monitoring the outgoing phone number;
-    // 2. If the device is in a emergency callback state, this is provided by
-    //    system properties.
-    // If either one of above exists, the phone is considered in an emergency
-    // mode. Because of this complexity, we need to be careful about how to set
-    // and clear the emergency state.
-    public void setInEmergency(boolean isInEmergency) {
-        mIsInEmergency = isInEmergency;
+    /**
+     * Determines whether device is in user-initiated emergency session based on the following
+     * 1. If the user is making an emergency call, this is provided by actively
+     *    monitoring the outgoing phone number;
+     * 2. If the user has recently ended an emergency call, and the device is in a configured time
+     *    window after the end of that call.
+     * 3. If the device is in a emergency callback state, this is provided by querying
+     *    TelephonyManager.
+     * @return true if is considered in user initiated emergency mode for NI purposes
+     */
+    public boolean getInEmergency() {
+        boolean isInEmergencyExtension =
+                (SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis) <
+                        mEmergencyExtensionMillis;
+        boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+        return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension;
     }
 
-    public boolean getInEmergency() {
-        boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
-        return mIsInEmergency || isInEmergencyCallback;
+    public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
+        if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
+            Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+                    + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
+            emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
+        } else if (emergencyExtensionSeconds < 0) {
+            Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
+                    + " is negative, reset to zero.");
+            emergencyExtensionSeconds = 0;
+        }
+        mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
     }
 
 
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 9ada216..de76aef 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,7 +18,9 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.media.audiofx.AudioEffect;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -27,6 +29,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -48,7 +52,7 @@
 public final class AudioRecordingConfiguration implements Parcelable {
     private final static String TAG = new String("AudioRecordingConfiguration");
 
-    private final int mSessionId;
+    private final int mClientSessionId;
 
     private final int mClientSource;
 
@@ -60,18 +64,50 @@
 
     private final int mPatchHandle;
 
+    private final int mClientPortId;
+
+    private boolean mClientSilenced;
+
+    private final int mDeviceSource;
+
+    private final AudioEffect.Descriptor[] mClientEffects;
+
+    private final AudioEffect.Descriptor[] mDeviceEffects;
+
     /**
      * @hide
      */
+    @TestApi
     public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
-            AudioFormat devFormat, int patchHandle, String packageName) {
+            AudioFormat devFormat, int patchHandle, String packageName, int clientPortId,
+            boolean clientSilenced, int deviceSource,
+            AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] deviceEffects) {
         mClientUid = uid;
-        mSessionId = session;
+        mClientSessionId = session;
         mClientSource = source;
         mClientFormat = clientFormat;
         mDeviceFormat = devFormat;
         mPatchHandle = patchHandle;
         mClientPackageName = packageName;
+        mClientPortId = clientPortId;
+        mClientSilenced = clientSilenced;
+        mDeviceSource = deviceSource;
+        mClientEffects = clientEffects;
+        mDeviceEffects = deviceEffects;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public AudioRecordingConfiguration(int uid, int session, int source,
+                                       AudioFormat clientFormat, AudioFormat devFormat,
+                                       int patchHandle, String packageName) {
+        this(uid, session, source, clientFormat,
+                   devFormat, patchHandle, packageName, 0 /*clientPortId*/,
+                   false /*clientSilenced*/, MediaRecorder.AudioSource.DEFAULT /*deviceSource*/,
+                   new AudioEffect.Descriptor[0] /*clientEffects*/,
+                   new AudioEffect.Descriptor[0] /*deviceEffects*/);
     }
 
     /**
@@ -87,13 +123,26 @@
      * @hide
      */
     public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
-        return new String("session:" + arc.mSessionId
-                + " -- source:" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+        String clientEffects = new String();
+        for (AudioEffect.Descriptor desc : arc.mClientEffects) {
+            clientEffects += "'" + desc.name + "' ";
+        }
+        String deviceEffects = new String();
+        for (AudioEffect.Descriptor desc : arc.mDeviceEffects) {
+            deviceEffects += "'" + desc.name + "' ";
+        }
+
+        return new String("session:" + arc.mClientSessionId
+                + " -- source client=" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+                + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
                 + " -- uid:" + arc.mClientUid
                 + " -- patch:" + arc.mPatchHandle
                 + " -- pack:" + arc.mClientPackageName
                 + " -- format client=" + arc.mClientFormat.toLogFriendlyString()
-                    + ", dev=" + arc.mDeviceFormat.toLogFriendlyString());
+                + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
+                + " -- silenced:" + arc.mClientSilenced
+                + " -- effects client=" + clientEffects
+                + ", dev=" + deviceEffects);
     }
 
     // Note that this method is called server side, so no "privileged" information is ever sent
@@ -106,8 +155,10 @@
      */
     public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) {
         return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
-                in.mSessionId, in.mClientSource, in.mClientFormat,
-                in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/);
+                in.mClientSessionId, in.mClientSource, in.mClientFormat,
+                in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/,
+                in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
+                in.mDeviceEffects);
     }
 
     // matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source)
@@ -129,16 +180,8 @@
     // documented return values match the sources that return false
     //   in MediaRecorder.isSystemOnlyAudioSource(source)
     /**
-     * Returns the audio source being used for the recording.
-     * @return one of {@link MediaRecorder.AudioSource#DEFAULT},
-     *       {@link MediaRecorder.AudioSource#MIC},
-     *       {@link MediaRecorder.AudioSource#VOICE_UPLINK},
-     *       {@link MediaRecorder.AudioSource#VOICE_DOWNLINK},
-     *       {@link MediaRecorder.AudioSource#VOICE_CALL},
-     *       {@link MediaRecorder.AudioSource#CAMCORDER},
-     *       {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
-     *       {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
-     *       {@link MediaRecorder.AudioSource#UNPROCESSED}.
+     * Returns the audio source selected by the client.
+     * @return the audio source selected by the client.
      */
     public @AudioSource int getClientAudioSource() { return mClientSource; }
 
@@ -146,7 +189,9 @@
      * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
      * @return the session number.
      */
-    public int getClientAudioSessionId() { return mSessionId; }
+    public int getClientAudioSessionId() {
+        return mClientSessionId;
+    }
 
     /**
      * Returns the audio format at which audio is recorded on this Android device.
@@ -223,6 +268,54 @@
         return null;
     }
 
+    /**
+     * Returns the system unique ID assigned for the AudioRecord object corresponding to this
+     * AudioRecordingConfiguration client.
+     * @return the port ID.
+     */
+    int getClientPortId() {
+        return mClientPortId;
+    }
+
+    /**
+     * Returns true if the audio returned to the client is currently being silenced by the
+     * audio framework due to concurrent capture policy (e.g the capturing application does not have
+     * an active foreground process or service anymore).
+     * @return true if captured audio is silenced, false otherwise .
+     */
+    public boolean isClientSilenced() {
+        return mClientSilenced;
+    }
+
+    /**
+     * Returns the audio source currently used to configure the capture path. It can be different
+     * from the source returned by {@link #getClientAudioSource()} if another capture is active.
+     * @return the audio source active on the capture path.
+     */
+    public @AudioSource int getAudioSource() {
+        return mDeviceSource;
+    }
+
+    /**
+     * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+     * the audio capture client (e.g. {@link AudioRecord} or {@link MediaRecorder}).
+     * @return List of {@link AudioEffect.Descriptor} containing all effects enabled for the client.
+     */
+    public @NonNull List<AudioEffect.Descriptor> getClientEffects() {
+        return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mClientEffects));
+    }
+
+    /**
+     * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
+     * the capture stream.
+     * @return List of {@link AudioEffect.Descriptor} containing all effects enabled on the
+     * capture stream. This can be different from the list returned by {@link #getClientEffects()}
+     * if another capture is active.
+     */
+    public @NonNull List<AudioEffect.Descriptor> getEffects() {
+        return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mDeviceEffects));
+    }
+
     public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
             = new Parcelable.Creator<AudioRecordingConfiguration>() {
         /**
@@ -240,7 +333,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSessionId, mClientSource);
+        return Objects.hash(mClientSessionId, mClientSource);
     }
 
     @Override
@@ -250,23 +343,45 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSessionId);
+        dest.writeInt(mClientSessionId);
         dest.writeInt(mClientSource);
         mClientFormat.writeToParcel(dest, 0);
         mDeviceFormat.writeToParcel(dest, 0);
         dest.writeInt(mPatchHandle);
         dest.writeString(mClientPackageName);
         dest.writeInt(mClientUid);
+        dest.writeInt(mClientPortId);
+        dest.writeBoolean(mClientSilenced);
+        dest.writeInt(mDeviceSource);
+        dest.writeInt(mClientEffects.length);
+        for (int i = 0; i < mClientEffects.length; i++) {
+            mClientEffects[i].writeToParcel(dest, 0);
+        }
+        dest.writeInt(mDeviceEffects.length);
+        for (int i = 0; i < mDeviceEffects.length; i++) {
+            mDeviceEffects[i].writeToParcel(dest, 0);
+        }
     }
 
     private AudioRecordingConfiguration(Parcel in) {
-        mSessionId = in.readInt();
+        mClientSessionId = in.readInt();
         mClientSource = in.readInt();
         mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
         mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
         mPatchHandle = in.readInt();
         mClientPackageName = in.readString();
         mClientUid = in.readInt();
+        mClientPortId = in.readInt();
+        mClientSilenced = in.readBoolean();
+        mDeviceSource = in.readInt();
+        mClientEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+        for (int i = 0; i < mClientEffects.length; i++) {
+            mClientEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+        }
+        mDeviceEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+        for (int i = 0; i < mClientEffects.length; i++) {
+            mDeviceEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+        }
     }
 
     @Override
@@ -277,11 +392,16 @@
         AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
 
         return ((mClientUid == that.mClientUid)
-                && (mSessionId == that.mSessionId)
+                && (mClientSessionId == that.mClientSessionId)
                 && (mClientSource == that.mClientSource)
                 && (mPatchHandle == that.mPatchHandle)
                 && (mClientFormat.equals(that.mClientFormat))
                 && (mDeviceFormat.equals(that.mDeviceFormat))
-                && (mClientPackageName.equals(that.mClientPackageName)));
+                && (mClientPackageName.equals(that.mClientPackageName))
+                && (mClientPortId == that.mClientPortId)
+                && (mClientSilenced == that.mClientSilenced)
+                && (mDeviceSource == that.mDeviceSource)
+                && (mClientEffects.equals(that.mClientEffects))
+                && (mDeviceEffects.equals(that.mDeviceEffects)));
     }
 }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 36f635a..58fc1ab 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -20,6 +20,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.audiofx.AudioEffect;
 import android.media.audiopolicy.AudioMix;
 import android.os.Build;
 import android.util.Log;
@@ -334,7 +335,9 @@
          * @param packName package name of the client app performing the recording. NOT SUPPORTED
          */
         void onRecordingConfigurationChanged(int event, int uid, int session, int source,
-                int[] recordingFormat, String packName);
+                        int portId, boolean silenced, int[] recordingFormat,
+                        AudioEffect.Descriptor[] clienteffects, AudioEffect.Descriptor[] effects,
+                        int activeSource, String packName);
     }
 
     private static AudioRecordingCallback sRecordingCallback;
@@ -352,19 +355,27 @@
      * @param session
      * @param source
      * @param recordingFormat see
-     *     {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int[])}
+     *     {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int,\
+     boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
      *     for the description of the record format.
      */
     @UnsupportedAppUsage
     private static void recordingCallbackFromNative(int event, int uid, int session, int source,
-            int[] recordingFormat) {
+                          int portId, boolean silenced, int[] recordingFormat,
+                          AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects,
+                          int activeSource) {
         AudioRecordingCallback cb = null;
         synchronized (AudioSystem.class) {
             cb = sRecordingCallback;
         }
+
+        String clientEffectName =  clientEffects.length == 0 ? "None" : clientEffects[0].name;
+        String effectName =  effects.length == 0 ? "None" : effects[0].name;
+
         if (cb != null) {
             // TODO receive package name from native
-            cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
+            cb.onRecordingConfigurationChanged(event, uid, session, source, portId, silenced,
+                                        recordingFormat, clientEffects, effects, activeSource, "");
         }
     }
 
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index aca8dbe..e29bd00 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -44,6 +44,8 @@
     private ParcelFileDescriptor mPFD;
     private long mOffset = 0;
     private long mLength = FD_LENGTH_UNKNOWN;
+    private int mCount = 0;
+    private boolean mClosed = false;
 
     private FileDataSourceDesc() {
         super();
@@ -55,23 +57,48 @@
     @Override
     void close() {
         super.close();
-        closeFD();
+        decCount();
     }
 
     /**
-     * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+     * Decrements usage count by {@link MediaPlayer2}.
+     * If this is the last usage, also releases the file descriptor held by this
+     * {@code FileDataSourceDesc} object.
      */
-    void closeFD() {
+    void decCount() {
         synchronized (this) {
-            if (mPFD != null) {
-                try {
-                    mPFD.close();
-                } catch (IOException e) {
-                    Log.e(TAG, "failed to close pfd: " + e);
-                }
-
-                mPFD = null;
+            --mCount;
+            if (mCount > 0) {
+                return;
             }
+
+            try {
+                mPFD.close();
+                mClosed = true;
+            } catch (IOException e) {
+                Log.e(TAG, "failed to close pfd: " + e);
+            }
+        }
+    }
+
+    /**
+     * Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
+     */
+    void incCount() {
+        synchronized (this) {
+            if (!mClosed) {
+                ++mCount;
+            }
+        }
+    }
+
+    /**
+     * Return the status of underline ParcelFileDescriptor
+     * @return true if underline ParcelFileDescriptor is closed, false otherwise.
+     */
+    boolean isPFDClosed() {
+        synchronized (this) {
+            return mClosed;
         }
     }
 
@@ -150,6 +177,16 @@
          * @return a new {@link FileDataSourceDesc} object
          */
         public @NonNull FileDataSourceDesc build() {
+            if (mPFD == null) {
+                throw new IllegalStateException(
+                        "underline ParcelFileDescriptor should not be null");
+            }
+            try {
+                mPFD.getFd();
+            } catch (IllegalStateException e) {
+                throw new IllegalStateException("ParcelFileDescriptor has been closed");
+            }
+
             FileDataSourceDesc dsd = new FileDataSourceDesc();
             super.build(dsd);
             dsd.mPFD = mPFD;
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b137ce2c..a3702d6 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -696,7 +696,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
             void process() throws IOException {
-                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                checkDataSourceDesc(dsd);
                 int state = getState();
                 try {
                     if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
@@ -729,7 +729,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
-                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                checkDataSourceDesc(dsd);
                 synchronized (mSrcLock) {
                     clearNextSourceInfos_l();
                     mNextSourceInfos.add(new SourceInfo(dsd));
@@ -755,15 +755,35 @@
                 if (dsds == null || dsds.size() == 0) {
                     throw new IllegalArgumentException("data source list cannot be null or empty.");
                 }
+                boolean hasError = false;
+                for (DataSourceDesc dsd : dsds) {
+                    if (dsd != null) {
+                        hasError = true;
+                        continue;
+                    }
+                    if (dsd instanceof FileDataSourceDesc) {
+                        FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+                        if (fdsd.isPFDClosed()) {
+                            hasError = true;
+                            continue;
+                        }
+
+                        fdsd.incCount();
+                    }
+                }
+                if (hasError) {
+                    for (DataSourceDesc dsd : dsds) {
+                        if (dsd != null) {
+                            dsd.close();
+                        }
+                    }
+                    throw new IllegalArgumentException("invalid data source list");
+                }
 
                 synchronized (mSrcLock) {
                     clearNextSourceInfos_l();
                     for (DataSourceDesc dsd : dsds) {
-                        if (dsd != null) {
-                            mNextSourceInfos.add(new SourceInfo(dsd));
-                        } else {
-                            Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
-                        }
+                        mNextSourceInfos.add(new SourceInfo(dsd));
                     }
                 }
                 prepareNextDataSource();
@@ -771,6 +791,20 @@
         });
     }
 
+    // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed.
+    private void checkDataSourceDesc(DataSourceDesc dsd) {
+        if (dsd != null) {
+            throw new IllegalArgumentException("dsd is expected to be non null");
+        }
+        if (dsd instanceof FileDataSourceDesc) {
+            FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
+            if (fdsd.isPFDClosed()) {
+                throw new IllegalArgumentException("the underline FileDescriptor has been closed");
+            }
+            fdsd.incCount();
+        }
+    }
+
     /**
      * Removes all data sources pending to be played.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index 7039a2c..3c0a297 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -21,9 +21,11 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.car.CarNotificationEntryManager;
+import com.android.systemui.car.CarNotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.car.CarFacetButtonController;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.volume.CarVolumeDialogComponent;
 import com.android.systemui.volume.VolumeDialogComponent;
@@ -67,6 +69,12 @@
         return new CarNotificationEntryManager(context);
     }
 
+    @Override
+    public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+            Context context) {
+        return new CarNotificationInterruptionStateProvider(context);
+    }
+
     @Module
     protected static class ContextHolder {
         private Context mContext;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 0563418..323cae0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -39,16 +38,4 @@
         // long click listener.
         return null;
     }
-
-    @Override
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
-        // Because space is usually constrained in the auto use-case, there should not be a
-        // pinned notification when the shade has been expanded. Ensure this by not pinning any
-        // notification if the shade is already opened.
-        if (!getPresenter().isPresenterFullyCollapsed()) {
-            return false;
-        }
-
-        return super.shouldHeadsUp(entry);
-    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..62502ef
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -0,0 +1,42 @@
+/*
+ * 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.car;
+
+import android.content.Context;
+
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+
+/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
+public class CarNotificationInterruptionStateProvider extends
+        NotificationInterruptionStateProvider {
+    public CarNotificationInterruptionStateProvider(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean shouldHeadsUp(NotificationData.Entry entry) {
+        // Because space is usually constrained in the auto use-case, there should not be a
+        // pinned notification when the shade has been expanded. Ensure this by not pinning any
+        // notification if the shade is already opened.
+        if (!getPresenter().isPresenterFullyCollapsed()) {
+            return false;
+        }
+
+        return super.shouldHeadsUp(entry);
+    }
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1eb4b74..8d04702 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -67,7 +67,7 @@
 
     private static final boolean DEBUG = false;
 
-    public static final String AUTHORITY = "com.android.externalstorage.documents";
+    public static final String AUTHORITY = DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY;
 
     private static final Uri BASE_URI =
             new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
@@ -96,7 +96,8 @@
         public boolean reportAvailableBytes = true;
     }
 
-    private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+    private static final String ROOT_ID_PRIMARY_EMULATED =
+            DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
     private static final String ROOT_ID_HOME = "home";
 
     private StorageManager mStorageManager;
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index 89ebf4d..d400159 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -31,7 +31,7 @@
 import java.util.Arrays;
 
 /**
- * This BarChartPreference shows four bar views in this preference at most.
+ * This BarChartPreference shows up to four bar views in this preference at most.
  *
  * <p>The following code sample shows a typical use, with an XML layout and code to initialize the
  * contents of the BarChartPreference:
@@ -74,71 +74,28 @@
     };
 
     private int mMaxBarHeight;
-    private @StringRes int mTitleId;
-    private @StringRes int mDetailsId;
+    @StringRes
+    private int mTitleId;
+    @StringRes
+    private int mDetailsId;
     private BarViewInfo[] mBarViewsInfo;
     private View.OnClickListener mDetailsOnClickListener;
 
-    /**
-     * Constructs a new BarChartPreference with the given context's theme.
-     * It sets a layout with settings bar chart style
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     */
     public BarChartPreference(Context context) {
         super(context);
         init();
     }
 
-    /**
-     * Constructs a new BarChartPreference with the given context's theme and the supplied
-     * attribute set.
-     * It sets a layout with settings bar chart style
-     *
-     * @param context the Context the view is running in
-     * @param attrs the attributes of the XML tag that is inflating the view.
-     */
     public BarChartPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         init();
     }
 
-    /**
-     * Constructs a new BarChartPreference with the given context's theme, the supplied
-     * attribute set, and default style attribute.
-     * It sets a layout with settings bar chart style
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     * @param defStyleAttr An attribute in the current theme that contains a
-     *                     reference to a style resource that supplies default
-     *                     values for the view. Can be 0 to not look for
-     *                     defaults.
-     */
     public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         init();
     }
 
-    /**
-     * Constructs a new BarChartPreference with the given context's theme, the supplied
-     * attribute set, and default styles.
-     * It sets a layout with settings bar chart style
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     * @param defStyleAttr An attribute in the current theme that contains a
-     *                     reference to a style resource that supplies default
-     *                     values for the view. Can be 0 to not look for
-     *                     defaults.
-     * @param defStyleRes  A resource identifier of a style resource that
-     *                     supplies default values for the view, used only if
-     *                     defStyleAttr is 0 or can not be found in the theme.
-     *                     Can be 0 to not look for defaults.
-     */
     public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -172,8 +129,6 @@
     /**
      * Set all bar view information which you'd like to show in preference.
      *
-     * <p>This method helps you do a sort by {@linkBarViewInfo#mBarNumber} in descending order.
-     *
      * @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
      */
     public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
@@ -181,7 +136,7 @@
         // Do a sort in descending order, the first element would have max {@link
         // BarViewInfo#mBarNumber}
         Arrays.sort(mBarViewsInfo);
-        caculateAllBarViewsHeight();
+        calculateAllBarViewHeights();
         notifyChanged();
     }
 
@@ -224,19 +179,19 @@
                 continue;
             }
             barView.setVisibility(View.VISIBLE);
-            barView.updateBarViewUI(mBarViewsInfo[index]);
+            barView.updateView(mBarViewsInfo[index]);
         }
     }
 
-    private void caculateAllBarViewsHeight() {
+    private void calculateAllBarViewHeights() {
         // Since we sorted this array in advance, the first element must have the max {@link
-        // BarViewInfo#mBarNumber}.
-        final int maxBarViewNumber = mBarViewsInfo[0].getBarNumber();
-        // If the max number of bar view is zero, then we don't caculate the unit for bar height.
-        final int unit = maxBarViewNumber == 0 ? 0 : mMaxBarHeight / maxBarViewNumber;
+        // BarViewInfo#mHeight}.
+        final int maxBarHeight = mBarViewsInfo[0].getHeight();
+        // If the max number of bar view is zero, then we don't calculate the unit for bar height.
+        final int unit = maxBarHeight == 0 ? 0 : mMaxBarHeight / maxBarHeight;
 
         for (BarViewInfo barView : mBarViewsInfo) {
-            barView.setBarHeight(barView.getBarNumber() * unit);
+            barView.setNormalizedHeight(barView.getHeight() * unit);
         }
     }
 }
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
index 6243a2d..6bf61ae 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -30,7 +30,7 @@
 import androidx.annotation.VisibleForTesting;
 
 /**
- * A extension view for bar chart.
+ * {@link View} for a single vertical bar with icon and summary.
  */
 public class BarView extends LinearLayout {
 
@@ -41,24 +41,11 @@
     private TextView mBarTitle;
     private TextView mBarSummary;
 
-    /**
-     * Constructs a new BarView with the given context's theme.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     */
     public BarView(Context context) {
         super(context);
         init();
     }
 
-    /**
-     * Constructs a new BarView with the given context's theme and the supplied
-     * attribute set.
-     *
-     * @param context the Context the view is running in
-     * @param attrs the attributes of the XML tag that is inflating the view.
-     */
     public BarView(Context context, AttributeSet attrs) {
         super(context, attrs);
         init();
@@ -77,17 +64,16 @@
     }
 
     /**
-     * This helps update the bar view UI with a {@link BarViewInfo}.
-     *
-     * @param barViewInfo A {@link BarViewInfo} saves bar view status.
+     * Updates the view with a {@link BarViewInfo}.
      */
-    public void updateBarViewUI(BarViewInfo barViewInfo) {
+    void updateView(BarViewInfo barViewInfo) {
+        setOnClickListener(barViewInfo.getClickListener());
         //Set height of bar view
-        mBarView.getLayoutParams().height = barViewInfo.getBarHeight();
+        mBarView.getLayoutParams().height = barViewInfo.getNormalizedHeight();
         mIcon.setImageDrawable(barViewInfo.getIcon());
         // For now, we use the bar number as title.
-        mBarTitle.setText(Integer.toString(barViewInfo.getBarNumber()));
-        mBarSummary.setText(barViewInfo.getSummaryRes());
+        mBarTitle.setText(Integer.toString(barViewInfo.getHeight()));
+        mBarSummary.setText(barViewInfo.getSummary());
     }
 
     @VisibleForTesting
@@ -106,9 +92,9 @@
         setGravity(Gravity.CENTER);
 
         mBarView = findViewById(R.id.bar_view);
-        mIcon = (ImageView) findViewById(R.id.icon_view);
-        mBarTitle = (TextView) findViewById(R.id.bar_title);
-        mBarSummary = (TextView) findViewById(R.id.bar_summary);
+        mIcon = findViewById(R.id.icon_view);
+        mBarTitle = findViewById(R.id.bar_title);
+        mBarSummary = findViewById(R.id.bar_summary);
     }
 
     private void setOnClickListner(View.OnClickListener listener) {
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
index aa83ce9..409f9ea 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -31,116 +31,71 @@
 public class BarViewInfo implements Comparable<BarViewInfo> {
 
     private final Drawable mIcon;
-    private View.OnClickListener mListener;
-    private @StringRes int mSummaryRes;
+    private View.OnClickListener mClickListener;
+    @StringRes
+    private int mSummary;
     // A number indicates this bar's height. The larger number shows a higher bar view.
-    private int mBarNumber;
+    private int mHeight;
     // A real height of bar view.
-    private int mBarHeight;
+    private int mNormalizedHeight;
 
     /**
      * Construct a BarViewInfo instance.
      *
-     * @param icon the icon of bar view.
-     * @param barNumber the number of bar view. The larger number show a more height of bar view.
-     * @param summaryRes the resource identifier of the string resource to be displayed
-     * @return BarViewInfo object.
+     * @param icon      The icon of bar view.
+     * @param barHeight The height of bar view. Larger number shows a higher bar view.
+     * @param summary   The string resource id for summary.
      */
-    public BarViewInfo(Drawable icon, @IntRange(from = 0) int barNumber,
-            @StringRes int summaryRes) {
+    public BarViewInfo(Drawable icon, @IntRange(from = 0) int barHeight, @StringRes int summary) {
         mIcon = icon;
-        mBarNumber = barNumber;
-        mSummaryRes = summaryRes;
+        mHeight = barHeight;
+        mSummary = summary;
     }
 
     /**
-     * Set number for bar view.
-     *
-     * @param barNumber the number of bar view. The larger number shows a higher bar view.
-     */
-    public void setBarNumber(@IntRange(from = 0) int barNumber) {
-        mBarNumber = barNumber;
-    }
-
-    /**
-     * Set summary resource for bar view
-     *
-     * @param resId the resource identifier of the string resource to be displayed
-     */
-    public void setSummary(@StringRes int resId) {
-        mSummaryRes = resId;
-    }
-
-    /**
-     * Set a click listner for bar view.
-     *
-     * @param listener the click listner is attached on bar view.
+     * Set a click listener for bar view.
      */
     public void setClickListener(@Nullable View.OnClickListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Get the icon of bar view.
-     *
-     * @return Drawable the icon of bar view.
-     */
-    public Drawable getIcon() {
-        return mIcon;
-    }
-
-    /**
-     * Get the OnClickListener of bar view.
-     *
-     * @return View.OnClickListener the click listner of bar view.
-     */
-    public View.OnClickListener getListener() {
-        return mListener;
-    }
-
-    /**
-     * Get the real height of bar view.
-     *
-     * @return the real height of bar view.
-     */
-    public int getBarHeight() {
-        return mBarHeight;
-    }
-
-    /**
-     * Get summary resource of bar view.
-     *
-     * @return summary resource of bar view.
-     */
-    public int getSummaryRes() {
-        return mSummaryRes;
-    }
-
-    /**
-     * Get the number of app uses this permisssion.
-     *
-     * @return the number of app uses this permission.
-     */
-    public int getBarNumber() {
-        return mBarNumber;
+        mClickListener = listener;
     }
 
     @Override
     public int compareTo(BarViewInfo other) {
         // Descending order
-        return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mBarNumber)
+        return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mHeight)
                 .compare(other, this);
     }
 
-    /**
-     * Set a real height for bar view.
-     *
-     * <p>This method should not be called by outside. It usually should be called by
-     * {@link BarChartPreference#caculateAllBarViewsHeight}
-     *
-     * @param barHeight the real bar height for bar view.
-     */
-    void setBarHeight(@IntRange(from = 0) int barHeight) {
-        mBarHeight = barHeight;
+    void setHeight(@IntRange(from = 0) int height) {
+        mHeight = height;
+    }
+
+    void setSummary(@StringRes int resId) {
+        mSummary = resId;
+    }
+
+    Drawable getIcon() {
+        return mIcon;
+    }
+
+    int getHeight() {
+        return mHeight;
+    }
+
+    View.OnClickListener getClickListener() {
+        return mClickListener;
+    }
+
+    @StringRes
+    int getSummary() {
+        return mSummary;
+    }
+
+    void setNormalizedHeight(@IntRange(from = 0) int barHeight) {
+        mNormalizedHeight = barHeight;
+    }
+
+    int getNormalizedHeight() {
+        return mNormalizedHeight;
     }
 }
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
index a046332..7e65848 100644
--- a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -20,7 +20,7 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?android:attr/colorAccent">
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FF000000"
         android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 7914a0b..177ba00 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -468,6 +468,16 @@
     private class AclStateChangedHandler implements Handler {
         @Override
         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            if (device == null) {
+                Log.w(TAG, "AclStateChangedHandler: device is null");
+                return;
+            }
+
+            // Avoid to notify Settings UI for Hearing Aid sub device.
+            if (mDeviceManager.isSubDevice(device)) {
+                return;
+            }
+
             final String action = intent.getAction();
             if (action == null) {
                 Log.w(TAG, "AclStateChangedHandler: action is null");
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 3a62838..5b4a8b4f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -127,6 +127,25 @@
     }
 
     /**
+     * Search for existing sub device {@link CachedBluetoothDevice}.
+     *
+     * @param device the address of the Bluetooth device
+     * @return true for found sub device or false.
+     */
+    public synchronized boolean isSubDevice(BluetoothDevice device) {
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            if (!cachedDevice.getDevice().equals(device)) {
+                // Check sub devices if it exists
+                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+                if (subDevice != null && subDevice.getDevice().equals(device)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Updates the Hearing Aid devices; specifically the HiSyncId's. This routine is called when the
      * Hearing Aid Service is connected and the HiSyncId's are now available.
      * @param LocalBluetoothProfileManager profileManager
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 6c536f0..b1c4f14 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -53,7 +53,7 @@
      *
      * @return true if the MediaDevice is be connected to transfer, false otherwise.
      */
-    protected boolean isConnected() {
+    public boolean isConnected() {
         return mIsConnected;
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 27b8dfc..0dcdaed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -18,6 +18,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -154,4 +155,30 @@
         verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
                 BluetoothAdapter.STATE_CONNECTED);
     }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
+        when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
+        when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_CONNECTED);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 47b1210..43b2894 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -334,6 +335,27 @@
     }
 
     /**
+     * Test to verify isSubDevice_validSubDevice().
+     */
+    @Test
+    public void isSubDevice_validSubDevice() {
+        doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
+        mCachedDeviceManager.addDevice(mDevice1);
+
+        // Both device are not sub device in default value.
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
+
+        // Add Device-2 as sub device of Device-1 with same HiSyncId.
+        doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
+        mCachedDeviceManager.addDevice(mDevice2);
+
+        // Verify Device-2 is sub device, but Device-1 is not.
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
+        assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+    }
+
+    /**
      * Test to verify updateHearingAidsDevices().
      */
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
index 371c3d4..d4e7481 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -208,4 +208,18 @@
         assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app));
     }
+
+    @Test
+    public void setAllBarViewsInfo_setClickListenerForBarView_barViewAttachClickListener() {
+        final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app);
+        viewInfo.setClickListener(v -> {
+        });
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo};
+
+        mPreference.setAllBarViewsInfo(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.hasOnClickListeners()).isTrue();
+    }
 }
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index 8bfa1c2..f81e8cc 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -147,6 +147,52 @@
 FragmentHostManager.get(view).create(NavigationBarFragment.class);
 ```
 
+### Using injection with Views
+
+Generally, you shouldn't need to inject for a view, as the view should
+be relatively self contained and logic that requires injection should be
+moved to a higher level construct such as a Fragment or a top-level SystemUI
+component, see above for how to do injection for both of which.
+
+Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
+lot of code that could benefit from injection and will need to be migrated
+off from Dependency#get uses. Similar to how fragments are injected, the view
+needs to be added to the interface
+com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+
+```java
+public interface ViewInstanceCreator {
++   QuickStatusBarHeader createQsHeader();
+}
+```
+
+Presumably you need to inflate that view from XML (otherwise why do you
+need anything special? see earlier sections about generic injection). To obtain
+an inflater that supports injected objects, call InjectionInflationController#injectable,
+which will wrap the inflater it is passed in one that can create injected
+objects when needed.
+
+```java
+@Override
+public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+        Bundle savedInstanceState) {
+    return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
+}
+```
+
+There is one other important thing to note about injecting with views. SysUI
+already has a Context in its global dagger component, so if you simply inject
+a Context, you will not get the one that the view should have with proper
+theming. Because of this, always ensure to tag views that have @Inject with
+the @Named view context.
+
+```java
+public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
+        OtherCustomDependency something) {
+    ...
+}
+```
+
 ## TODO List
 
  - Eliminate usages of Depndency#get
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 6135aeb..ac69043 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -36,6 +36,13 @@
     View getView();
 
     /**
+     * Get clock view for a large clock that appears behind NSSL.
+     */
+    default View getBigClockView() {
+        return null;
+    }
+
+    /**
      * Set clock paint style.
      * @param style The new style to set in the paint.
      */
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index d57fe8a..22b0ab7 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -31,4 +31,7 @@
 -keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
     *;
 }
+-keep class com.android.systemui.util.InjectionInflationController$ViewInstanceCreator {
+    *;
+}
 -keep class androidx.core.app.CoreComponentFactory
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2674f07c..75c0ec3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -25,6 +25,12 @@
     android:layout_height="match_parent"
     android:background="@android:color/transparent" >
 
+    <FrameLayout
+        android:id="@+id/big_clock_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone" />
+
     <include
         layout="@layout/keyguard_status_view"
         android:visibility="gone" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 22a23a8..570d351 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -35,7 +35,11 @@
     /**
      * Frame for default and custom clock.
      */
-    private FrameLayout mClockFrame;
+    private FrameLayout mSmallClockFrame;
+    /**
+     * Container for big custom clock.
+     */
+    private ViewGroup mBigClockContainer;
     /**
      * Status area (date and other stuff) shown below the clock. Plugin can decide whether
      * or not to show it below the alternate clock.
@@ -46,22 +50,27 @@
             new PluginListener<ClockPlugin>() {
                 @Override
                 public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
-                    View view = plugin.getView();
-                    if (view != null) {
-                        disconnectPlugin();
+                    disconnectPlugin();
+                    View smallClockView = plugin.getView();
+                    if (smallClockView != null) {
                         // For now, assume that the most recently connected plugin is the
                         // selected clock face. In the future, the user should be able to
                         // pick a clock face from the available plugins.
-                        mClockPlugin = plugin;
-                        mClockFrame.addView(view, -1,
+                        mSmallClockFrame.addView(smallClockView, -1,
                                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                         ViewGroup.LayoutParams.WRAP_CONTENT));
                         initPluginParams();
                         mClockView.setVisibility(View.GONE);
-                        if (!plugin.shouldShowStatusArea()) {
-                            mKeyguardStatusArea.setVisibility(View.GONE);
-                        }
                     }
+                    View bigClockView = plugin.getBigClockView();
+                    if (bigClockView != null && mBigClockContainer != null) {
+                        mBigClockContainer.addView(bigClockView);
+                        mBigClockContainer.setVisibility(View.VISIBLE);
+                    }
+                    if (!plugin.shouldShowStatusArea()) {
+                        mKeyguardStatusArea.setVisibility(View.GONE);
+                    }
+                    mClockPlugin = plugin;
                 }
 
                 @Override
@@ -86,7 +95,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mClockView = findViewById(R.id.default_clock_view);
-        mClockFrame = findViewById(R.id.clock_view);
+        mSmallClockFrame = findViewById(R.id.clock_view);
         mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
     }
 
@@ -104,6 +113,20 @@
     }
 
     /**
+     * Set container for big clock face appearing behind NSSL and KeyguardStatusView.
+     */
+    public void setBigClockContainer(ViewGroup container) {
+        if (mClockPlugin != null && container != null) {
+            View bigClockView = mClockPlugin.getBigClockView();
+            if (bigClockView != null) {
+                container.addView(bigClockView);
+                container.setVisibility(View.VISIBLE);
+            }
+        }
+        mBigClockContainer = container;
+    }
+
+    /**
      * It will also update plugin setStyle if plugin is connected.
      */
     public void setStyle(Style style) {
@@ -199,9 +222,13 @@
 
     private void disconnectPlugin() {
         if (mClockPlugin != null) {
-            View view = mClockPlugin.getView();
-            if (view != null) {
-                mClockFrame.removeView(view);
+            View smallClockView = mClockPlugin.getView();
+            if (smallClockView != null) {
+                mSmallClockFrame.removeView(smallClockView);
+            }
+            if (mBigClockContainer != null) {
+                mBigClockContainer.removeAllViews();
+                mBigClockContainer.setVisibility(View.GONE);
             }
             mClockPlugin = null;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 445d156..6b4d07a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -56,6 +56,8 @@
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -256,6 +258,8 @@
     @Inject Lazy<NotificationLogger> mNotificationLogger;
     @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
     @Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
+    @Inject Lazy<NotificationFilter> mNotificationFilter;
+    @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
     @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
     @Inject Lazy<SmartReplyController> mSmartReplyController;
     @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@@ -425,6 +429,9 @@
         mProviders.put(NotificationViewHierarchyManager.class,
                 mNotificationViewHierarchyManager::get);
         mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
+        mProviders.put(NotificationFilter.class, mNotificationFilter::get);
+        mProviders.put(NotificationInterruptionStateProvider.class,
+                mNotificationInterruptionStateProvider::get);
         mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
         mProviders.put(SmartReplyController.class, mSmartReplyController::get);
         mProviders.put(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index 76336bb..b11e189 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -33,7 +33,6 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -470,7 +469,6 @@
     @Singleton
     @Provides
     public UiOffloadThread provideUiOffloadThread() {
-        Log.d("TestTest", "provideUiOffloadThread");
         return new UiOffloadThread();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 1a2473e..384a14c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -42,7 +42,7 @@
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -55,6 +55,7 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.volume.VolumeDialogComponent;
 
 import java.util.function.Consumer;
@@ -198,6 +199,13 @@
         return new NotificationListener(context);
     }
 
+    @Singleton
+    @Provides
+    public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
+            Context context) {
+        return new NotificationInterruptionStateProvider(context);
+    }
+
     @Module
     protected static class ContextHolder {
         private Context mContext;
@@ -223,5 +231,10 @@
          */
         @Singleton
         FragmentService.FragmentCreator createFragmentCreator();
+
+        /**
+         * ViewCreator generates all Views that need injection.
+         */
+        InjectionInflationController.ViewCreator createViewCreator();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 52d1260..af6ee1f 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -57,21 +57,13 @@
     @GuardedBy("mNotedItems")
     private final List<AppOpItem> mNotedItems = new ArrayList<>();
 
-    protected static final int[] OPS;
-    protected static final String[] OPS_STRING = new String[] {
-            AppOpsManager.OPSTR_CAMERA,
-            AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW,
-            AppOpsManager.OPSTR_RECORD_AUDIO,
-            AppOpsManager.OPSTR_COARSE_LOCATION,
-            AppOpsManager.OPSTR_FINE_LOCATION};
-
-    static {
-        int numOps = OPS_STRING.length;
-        OPS = new int[numOps];
-        for (int i = 0; i < numOps; i++) {
-            OPS[i] = AppOpsManager.strOpToOp(OPS_STRING[i]);
-        }
-    }
+    protected static final int[] OPS = new int[] {
+            AppOpsManager.OP_CAMERA,
+            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+            AppOpsManager.OP_RECORD_AUDIO,
+            AppOpsManager.OP_COARSE_LOCATION,
+            AppOpsManager.OP_FINE_LOCATION
+    };
 
     public AppOpsControllerImpl(Context context, Looper bgLooper) {
         mContext = context;
@@ -92,7 +84,7 @@
     protected void setListening(boolean listening) {
         if (listening) {
             mAppOps.startWatchingActive(OPS, this);
-            mAppOps.startWatchingNoted(OPS_STRING, this);
+            mAppOps.startWatchingNoted(OPS, this);
         } else {
             mAppOps.stopWatchingActive(this);
             mAppOps.stopWatchingNoted(this);
@@ -254,14 +246,13 @@
     }
 
     @Override
-    public void onOpNoted(String code, int uid, String packageName, int result) {
+    public void onOpNoted(int code, int uid, String packageName, int result) {
         if (DEBUG) {
             Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
         }
         if (result != AppOpsManager.MODE_ALLOWED) return;
-        int op_code = AppOpsManager.strOpToOp(code);
-        addNoted(op_code, uid, packageName);
-        notifySuscribers(op_code, uid, packageName, true);
+        addNoted(code, uid, packageName);
+        notifySuscribers(code, uid, packageName, true);
     }
 
     private void notifySuscribers(int code, int uid, String packageName, boolean active) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index fa775c0..93130d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.util.InjectionInflationController;
 
 import javax.inject.Inject;
 
@@ -76,16 +77,20 @@
     private boolean mQsDisabled;
 
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+    private final InjectionInflationController mInjectionInflater;
 
     @Inject
-    public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler) {
+    public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+            InjectionInflationController injectionInflater) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
+        mInjectionInflater = injectionInflater;
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-        inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme));
+        inflater = mInjectionInflater.injectable(
+                inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)));
         return inflater.inflate(R.layout.qs_panel, container, false);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 3cecff0..f2f83c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,8 @@
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
 
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.ColorInt;
@@ -58,7 +60,6 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
@@ -84,6 +85,9 @@
 import java.util.Locale;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /**
  * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
  * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
@@ -102,6 +106,11 @@
     public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
 
     private final Handler mHandler = new Handler();
+    private final BatteryController mBatteryController;
+    private final NextAlarmController mAlarmController;
+    private final ZenModeController mZenController;
+    private final StatusBarIconController mStatusBarIconController;
+    private final ActivityStarter mActivityStarter;
 
     private QSPanel mQsPanel;
 
@@ -141,8 +150,6 @@
     private TextView mBatteryRemainingText;
     private boolean mShowBatteryPercentAndEstimate;
 
-    private NextAlarmController mAlarmController;
-    private ZenModeController mZenController;
     private PrivacyItemController mPrivacyItemController;
     /** Counts how many times the long press tooltip has been shown to the user. */
     private int mShownCount;
@@ -172,10 +179,17 @@
         }
     };
 
-    public QuickStatusBarHeader(Context context, AttributeSet attrs) {
+    @Inject
+    public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+            NextAlarmController nextAlarmController, ZenModeController zenModeController,
+            BatteryController batteryController, StatusBarIconController statusBarIconController,
+            ActivityStarter activityStarter) {
         super(context, attrs);
-        mAlarmController = Dependency.get(NextAlarmController.class);
-        mZenController = Dependency.get(ZenModeController.class);
+        mAlarmController = nextAlarmController;
+        mZenController = zenModeController;
+        mBatteryController = batteryController;
+        mStatusBarIconController = statusBarIconController;
+        mActivityStarter = activityStarter;
         mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
         mShownCount = getStoredShownCount();
     }
@@ -405,8 +419,7 @@
         if (!mShowBatteryPercentAndEstimate) {
             return;
         }
-        mBatteryRemainingText.setText(
-                Dependency.get(BatteryController.class).getEstimatedTimeRemainingString());
+        mBatteryRemainingText.setText(mBatteryController.getEstimatedTimeRemainingString());
     }
 
     public void setExpanded(boolean expanded) {
@@ -472,7 +485,7 @@
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
+        mStatusBarIconController.addIconGroup(mIconManager);
         requestApplyInsets();
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mPercentSettingObserver,
@@ -515,7 +528,7 @@
     @VisibleForTesting
     public void onDetachedFromWindow() {
         setListening(false);
-        Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
+        mStatusBarIconController.removeIconGroup(mIconManager);
         mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
         super.onDetachedFromWindow();
     }
@@ -544,10 +557,10 @@
     @Override
     public void onClick(View v) {
         if (v == mClockView) {
-            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+            mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     AlarmClock.ACTION_SHOW_ALARMS),0);
         } else if (v == mBatteryMeterView) {
-            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+            mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     Intent.ACTION_POWER_USAGE_SUMMARY),0);
         } else if (v == mPrivacyChip) {
             Handler mUiHandler = new Handler(Looper.getMainLooper());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index ee0d1a2..c945afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.statusbar;
 
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -29,7 +28,6 @@
  */
 public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
         ActivatableNotificationView.OnActivatedListener,
-        NotificationEntryManager.Callback,
         NotificationRowBinder.BindRowCallback {
     /**
      * Returns true if the presenter is not visible. For example, it may not be necessary to do
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 2524747..017cda7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -133,7 +133,6 @@
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
         mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
-        mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
         Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
deleted file mode 100644
index 13e991b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AlertTransferListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.statusbar.notification;
-
-/**
- * Listener interface for when NotificationEntryManager needs to tell
- * NotificationGroupAlertTransferHelper things. Will eventually grow to be a general-purpose
- * listening interface for the NotificationEntryManager.
- */
-public interface AlertTransferListener {
-    /**
-     * Called when a new notification is posted. At this point, the notification is "pending": its
-     * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
-     */
-    void onPendingEntryAdded(NotificationData.Entry entry);
-
-    /**
-     * Called when an existing notification's views are reinflated (usually due to an update being
-     * posted to that notification).
-     */
-    void onEntryReinflated(NotificationData.Entry entry);
-
-    /**
-     * Called when a notification has been removed (either because the user swiped it away or
-     * because the developer retracted it).
-     */
-    void onEntryRemoved(NotificationData.Entry entry);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index ef7e0fe..433a994 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -27,20 +27,15 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 
-import android.Manifest;
 import android.annotation.NonNull;
-import android.app.AppGlobals;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.Person;
 import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Parcelable;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -58,17 +53,13 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 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.policy.HeadsUpManager;
 
 import java.io.PrintWriter;
@@ -83,14 +74,13 @@
  */
 public class NotificationData {
 
+    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
     /**
      * These dependencies are late init-ed
      */
     private KeyguardEnvironment mEnvironment;
-    private ShadeController mShadeController;
     private NotificationMediaManager mMediaManager;
-    private ForegroundServiceController mFsc;
-    private NotificationLockscreenUserManager mUserManager;
 
     private HeadsUpManager mHeadsUpManager;
 
@@ -120,6 +110,9 @@
         @NonNull
         public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
         public CharSequence[] smartReplies = new CharSequence[0];
+        @VisibleForTesting
+        public int suppressedVisualEffects;
+        public boolean suspended;
 
         private Entry parent; // our parent (if we're in a group)
         private ArrayList<Entry> children = new ArrayList<Entry>();
@@ -183,6 +176,8 @@
             smartReplies = ranking.getSmartReplies() == null
                     ? new CharSequence[0]
                     : ranking.getSmartReplies().toArray(new CharSequence[0]);
+            suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+            suspended = ranking.isSuspended();
         }
 
         public void setInterruption() {
@@ -625,6 +620,71 @@
             if (row == null) return true;
             return row.canViewBeDismissed();
         }
+
+        boolean isExemptFromDndVisualSuppression() {
+            if (isNotificationBlockedByPolicy(notification.getNotification())) {
+                return false;
+            }
+
+            if ((notification.getNotification().flags
+                    & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+                return true;
+            }
+            if (notification.getNotification().isMediaNotification()) {
+                return true;
+            }
+            if (mIsSystemNotification != null && mIsSystemNotification) {
+                return true;
+            }
+            return false;
+        }
+
+        private boolean shouldSuppressVisualEffect(int effect) {
+            if (isExemptFromDndVisualSuppression()) {
+                return false;
+            }
+            return (suppressedVisualEffects & effect) != 0;
+        }
+
+        /**
+         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+         * is set for this entry.
+         */
+        public boolean shouldSuppressFullScreenIntent() {
+            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+        }
+
+        /**
+         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
+         * is set for this entry.
+         */
+        public boolean shouldSuppressPeek() {
+            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+        }
+
+        /**
+         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+         * is set for this entry.
+         */
+        public boolean shouldSuppressStatusBar() {
+            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+        }
+
+        /**
+         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
+         * is set for this entry.
+         */
+        public boolean shouldSuppressAmbient() {
+            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+        }
+
+        /**
+         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+         * is set for this entry.
+         */
+        public boolean shouldSuppressNotificationList() {
+            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+        }
     }
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -706,13 +766,6 @@
         return mEnvironment;
     }
 
-    private ShadeController getShadeController() {
-        if (mShadeController == null) {
-            mShadeController = Dependency.get(ShadeController.class);
-        }
-        return mShadeController;
-    }
-
     private NotificationMediaManager getMediaManager() {
         if (mMediaManager == null) {
             mMediaManager = Dependency.get(NotificationMediaManager.class);
@@ -720,20 +773,6 @@
         return mMediaManager;
     }
 
-    private ForegroundServiceController getFsc() {
-        if (mFsc == null) {
-            mFsc = Dependency.get(ForegroundServiceController.class);
-        }
-        return mFsc;
-    }
-
-    private NotificationLockscreenUserManager getUserManager() {
-        if (mUserManager == null) {
-            mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
-        }
-        return mUserManager;
-    }
-
     /**
      * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
      *
@@ -778,7 +817,7 @@
     }
 
     public Entry remove(String key, RankingMap ranking) {
-        Entry removed = null;
+        Entry removed;
         synchronized (mEntries) {
             removed = mEntries.remove(key);
         }
@@ -849,62 +888,12 @@
         return Ranking.VISIBILITY_NO_OVERRIDE;
     }
 
-    public boolean shouldSuppressFullScreenIntent(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
-    }
-
-    public boolean shouldSuppressPeek(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_PEEK);
-    }
-
-    public boolean shouldSuppressStatusBar(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_STATUS_BAR);
-    }
-
-    public boolean shouldSuppressAmbient(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_AMBIENT);
-    }
-
-    public boolean shouldSuppressNotificationList(Entry entry) {
-        return shouldSuppressVisualEffect(entry, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
-    }
-
-    private boolean shouldSuppressVisualEffect(Entry entry, int effect) {
-        if (isExemptFromDndVisualSuppression(entry)) {
-            return false;
-        }
-        String key = entry.key;
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
-        }
-        return false;
-    }
-
-    protected boolean isExemptFromDndVisualSuppression(Entry entry) {
-        if (isNotificationBlockedByPolicy(entry.notification.getNotification())) {
-            return false;
-        }
-
-        if ((entry.notification.getNotification().flags
-                & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-            return true;
-        }
-        if (entry.notification.getNotification().isMediaNotification()) {
-            return true;
-        }
-        if (entry.mIsSystemNotification != null && entry.mIsSystemNotification) {
-            return true;
-        }
-        return false;
-    }
-
     /**
      * Categories that are explicitly called out on DND settings screens are always blocked, if
      * DND has flagged them, even if they are foreground or system notifications that might
      * otherwise visually bypass DND.
      */
-    protected boolean isNotificationBlockedByPolicy(Notification n) {
+    private static boolean isNotificationBlockedByPolicy(Notification n) {
         if (isCategory(CATEGORY_CALL, n)
                 || isCategory(CATEGORY_MESSAGE, n)
                 || isCategory(CATEGORY_ALARM, n)
@@ -915,7 +904,7 @@
         return false;
     }
 
-    private boolean isCategory(String category, Notification n) {
+    private static boolean isCategory(String category, Notification n) {
         return Objects.equals(n.category, category);
     }
 
@@ -1013,7 +1002,7 @@
             for (int i = 0; i < N; i++) {
                 Entry entry = mEntries.valueAt(i);
 
-                if (shouldFilterOut(entry)) {
+                if (mNotificationFilter.shouldFilterOut(entry)) {
                     continue;
                 }
 
@@ -1024,87 +1013,6 @@
         Collections.sort(mSortedAndFiltered, mRankingComparator);
     }
 
-    /**
-     * @return true if this notification should NOT be shown right now
-     */
-    public boolean shouldFilterOut(Entry entry) {
-        final StatusBarNotification sbn = entry.notification;
-        if (!(getEnvironment().isDeviceProvisioned() ||
-                showNotificationEvenIfUnprovisioned(sbn))) {
-            return true;
-        }
-
-        if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
-            return true;
-        }
-
-        if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) &&
-                (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
-                        || getUserManager().shouldHideNotifications(sbn.getUserId())
-                        || getUserManager().shouldHideNotifications(sbn.getKey()))) {
-            return true;
-        }
-
-        if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) {
-            return true;
-        }
-
-        if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) {
-            return true;
-        }
-
-        if (shouldHide(sbn.getKey())) {
-            return true;
-        }
-
-        if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
-                && mGroupManager.isChildInGroupWithSummary(sbn)) {
-            return true;
-        }
-
-        if (getFsc().isDungeonNotification(sbn)
-                && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
-            // this is a foreground-service disclosure for a user that does not need to show one
-            return true;
-        }
-        if (getFsc().isSystemAlertNotification(sbn)) {
-            final String[] apps = sbn.getNotification().extras.getStringArray(
-                    Notification.EXTRA_FOREGROUND_APPS);
-            if (apps != null && apps.length >= 1) {
-                if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    // Q: What kinds of notifications should show during setup?
-    // A: Almost none! Only things coming from packages with permission
-    // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
-    // as relevant for setup (see below).
-    public static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
-        return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
-    }
-
-    @VisibleForTesting
-    static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
-            StatusBarNotification sbn) {
-        return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
-                sbn.getUid()) == PackageManager.PERMISSION_GRANTED
-                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
-    }
-
-    private static int checkUidPermission(IPackageManager packageManager, String permission,
-            int uid) {
-        try {
-            return packageManager.checkUidPermission(permission, uid);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     public void dump(PrintWriter pw, String indent) {
         int N = mSortedAndFiltered.size();
         pw.print(indent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
new file mode 100644
index 0000000..361ae8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -0,0 +1,75 @@
+/*
+ * 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.statusbar.notification;
+
+import android.service.notification.StatusBarNotification;
+
+/**
+ * Listener interface for changes sent by NotificationEntryManager.
+ */
+public interface NotificationEntryListener {
+    /**
+     * Called when a new notification is posted. At this point, the notification is "pending": its
+     * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
+     */
+    default void onPendingEntryAdded(NotificationData.Entry entry) {
+    }
+
+    /**
+     * Called when a new entry is created.
+     */
+    default void onNotificationAdded(NotificationData.Entry entry) {
+    }
+
+    /**
+     * Called when a notification was updated.
+     */
+    default void onNotificationUpdated(StatusBarNotification notification) {
+    }
+
+    /**
+     * Called when an existing notification's views are reinflated (usually due to an update being
+     * posted to that notification).
+     */
+    default void onEntryReinflated(NotificationData.Entry entry) {
+    }
+
+    /**
+     * Called when a notification has been removed (either because the user swiped it away or
+     * because the developer retracted it).
+     *
+     * TODO: combine this with onNotificationRemoved().
+     */
+    default void onEntryRemoved(NotificationData.Entry entry) {
+    }
+
+    /**
+     * Called when a notification was removed.
+     *
+     * @param key key of notification that was removed
+     * @param old StatusBarNotification of the notification before it was removed
+     */
+    default void onNotificationRemoved(String key, StatusBarNotification old) {
+    }
+
+    /**
+     * Removes a notification immediately.
+     *
+     * TODO: combine this with onNotificationRemoved().
+     */
+    default void onPerformRemoveNotification(StatusBarNotification statusBarNotification) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 535ea62..98ddd6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -17,43 +17,30 @@
 
 import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
 import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.EventLog;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.EventLogTags;
 import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.UiOffloadThread;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AmbientPulseManager;
@@ -64,7 +51,6 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -97,8 +83,6 @@
         BubbleController.BubbleDismissListener {
     private static final String TAG = "NotificationEntryMgr";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final boolean ENABLE_HEADS_UP = true;
-    private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
     public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
 
@@ -109,7 +93,6 @@
             Dependency.get(NotificationGroupManager.class);
     private final NotificationGutsManager mGutsManager =
             Dependency.get(NotificationGutsManager.class);
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     private final DeviceProvisionedController mDeviceProvisionedController =
             Dependency.get(DeviceProvisionedController.class);
     private final VisualStabilityManager mVisualStabilityManager =
@@ -119,6 +102,8 @@
     private final AmbientPulseManager mAmbientPulseManager =
             Dependency.get(AmbientPulseManager.class);
     private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
 
     // Lazily retrieved dependencies
     private NotificationRemoteInputManager mRemoteInputManager;
@@ -130,23 +115,18 @@
     private final Handler mDeferredNotificationViewUpdateHandler;
     private Runnable mUpdateNotificationViewsCallback;
 
-    protected IDreamManager mDreamManager;
     protected IStatusBarService mBarService;
     private NotificationPresenter mPresenter;
-    private Callback mCallback;
+    private NotificationEntryListener mCallback;
     protected PowerManager mPowerManager;
     private NotificationListenerService.RankingMap mLatestRankingMap;
     protected HeadsUpManager mHeadsUpManager;
     protected NotificationData mNotificationData;
-    private ContentObserver mHeadsUpObserver;
-    protected boolean mUseHeadsUp = false;
-    private boolean mDisableNotificationAlerts;
     protected NotificationListContainer mListContainer;
     @VisibleForTesting
     final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
             = new ArrayList<>();
-    private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
-    @Nullable private AlertTransferListener mAlertTransferListener;
+    private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
 
     private final DeviceProvisionedController.DeviceProvisionedListener
             mDeviceProvisionedListener =
@@ -157,11 +137,6 @@
                 }
             };
 
-    public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
-        mDisableNotificationAlerts = disableNotificationAlerts;
-        mHeadsUpObserver.onChange(true);
-    }
-
     public void destroy() {
         mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
     }
@@ -177,8 +152,6 @@
                 pw.println(entry.notification);
             }
         }
-        pw.print("  mUseHeadsUp=");
-        pw.println(mUseHeadsUp);
     }
 
     public NotificationEntryManager(Context context) {
@@ -186,15 +159,14 @@
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        mDreamManager = IDreamManager.Stub.asInterface(
-                ServiceManager.checkService(DreamService.DREAM_SERVICE));
         mBubbleController.setDismissListener(this /* bubbleEventListener */);
         mNotificationData = new NotificationData();
         mDeferredNotificationViewUpdateHandler = new Handler();
     }
 
-    public void setAlertTransferListener(AlertTransferListener listener) {
-        mAlertTransferListener = listener;
+    /** Adds a {@link NotificationEntryListener}. */
+    public void addNotificationEntryListener(NotificationEntryListener listener) {
+        mNotificationEntryListeners.add(listener);
     }
 
     /**
@@ -236,7 +208,7 @@
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationListContainer listContainer, Callback callback,
+            NotificationListContainer listContainer, NotificationEntryListener callback,
             HeadsUpManager headsUpManager) {
         mPresenter = presenter;
         mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
@@ -245,36 +217,6 @@
         mNotificationData.setHeadsUpManager(mHeadsUpManager);
         mListContainer = listContainer;
 
-        mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
-            @Override
-            public void onChange(boolean selfChange) {
-                boolean wasUsing = mUseHeadsUp;
-                mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
-                        && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
-                        mContext.getContentResolver(),
-                        Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
-                        Settings.Global.HEADS_UP_OFF);
-                Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
-                if (wasUsing != mUseHeadsUp) {
-                    if (!mUseHeadsUp) {
-                        Log.d(TAG,
-                                "dismissing any existing heads up notification on disable event");
-                        mHeadsUpManager.releaseAllImmediately();
-                    }
-                }
-            }
-        };
-
-        if (ENABLE_HEADS_UP) {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
-                    true,
-                    mHeadsUpObserver);
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
-                    mHeadsUpObserver);
-        }
-
         mNotificationLifetimeExtenders.add(mHeadsUpManager);
         mNotificationLifetimeExtenders.add(mAmbientPulseManager);
         mNotificationLifetimeExtenders.add(mGutsManager);
@@ -285,20 +227,6 @@
         }
 
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-
-        mHeadsUpObserver.onChange(true); // set up
-
-        getRowBinder().setInterruptionStateProvider(new InterruptionStateProvider() {
-            @Override
-            public boolean shouldHeadsUp(NotificationData.Entry entry) {
-                return NotificationEntryManager.this.shouldHeadsUp(entry);
-            }
-
-            @Override
-            public boolean shouldPulse(NotificationData.Entry entry) {
-                return NotificationEntryManager.this.shouldPulse(entry);
-            }
-        });
     }
 
     public NotificationData getNotificationData() {
@@ -322,14 +250,6 @@
         updateNotifications();
     }
 
-    private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
-        if (mPresenter.isDeviceInVrMode()) {
-            return true;
-        }
-
-        return mNotificationData.shouldSuppressFullScreenIntent(entry);
-    }
-
     public void performRemoveNotification(StatusBarNotification n) {
         final int rank = mNotificationData.getRank(n.getKey());
         final int count = mNotificationData.getActiveNotifications().size();
@@ -441,7 +361,7 @@
         if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
             // If it does and we no longer need to heads up, we should free the view.
-            if (shouldHeadsUp(entry)) {
+            if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
                 mHeadsUpManager.showNotification(entry);
                 // Mark as seen immediately
                 setNotificationShown(entry.notification);
@@ -450,7 +370,7 @@
             }
         }
         if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
-            if (shouldPulse(entry)) {
+            if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
                 mAmbientPulseManager.showNotification(entry);
             } else {
                 entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
@@ -474,8 +394,8 @@
                     mVisualStabilityManager.onLowPriorityUpdated(entry);
                     mPresenter.updateNotificationViews();
                 }
-                if (mAlertTransferListener != null) {
-                    mAlertTransferListener.onEntryReinflated(entry);
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onEntryReinflated(entry);
                 }
             }
         }
@@ -492,8 +412,10 @@
         final NotificationData.Entry entry = mNotificationData.get(key);
 
         abortExistingInflation(key);
-        if (mAlertTransferListener != null && entry != null) {
-            mAlertTransferListener.onEntryRemoved(entry);
+        if (entry != null) {
+            for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                listener.onEntryRemoved(entry);
+            }
         }
 
         // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
@@ -650,51 +572,15 @@
         mNotificationData.updateRanking(rankingMap);
         NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
         rankingMap.getRanking(key, ranking);
-        NotificationData.Entry shadeEntry = createNotificationEntry(notification, ranking);
-        boolean isHeadsUped = shouldHeadsUp(shadeEntry);
-        if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
-            if (shouldSuppressFullScreenIntent(shadeEntry)) {
-                if (DEBUG) {
-                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
-                }
-            } else if (mNotificationData.getImportance(key)
-                    < NotificationManager.IMPORTANCE_HIGH) {
-                if (DEBUG) {
-                    Log.d(TAG, "No Fullscreen intent: not important enough: "
-                            + key);
-                }
-            } else {
-                // Stop screensaver if the notification has a fullscreen intent.
-                // (like an incoming phone call)
-                Dependency.get(UiOffloadThread.class).submit(() -> {
-                    try {
-                        mDreamManager.awaken();
-                    } catch (RemoteException e) {
-                        e.printStackTrace();
-                    }
-                });
-
-                // not immersive & a fullscreen alert should be shown
-                if (DEBUG)
-                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
-                try {
-                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
-                            key);
-                    notification.getNotification().fullScreenIntent.send();
-                    shadeEntry.notifyFullScreenIntentLaunched();
-                    mMetricsLogger.count("note_fullscreen", 1);
-                } catch (PendingIntent.CanceledException e) {
-                }
-            }
-        }
+        NotificationData.Entry entry = createNotificationEntry(notification, ranking);
         abortExistingInflation(key);
 
         mForegroundServiceController.addNotification(notification,
                 mNotificationData.getImportance(key));
 
-        mPendingNotifications.put(key, shadeEntry);
-        if (mAlertTransferListener != null) {
-            mAlertTransferListener.onPendingEntryAdded(shadeEntry);
+        mPendingNotifications.put(key, entry);
+        for (NotificationEntryListener listener : mNotificationEntryListeners) {
+            listener.onPendingEntryAdded(entry);
         }
     }
 
@@ -767,9 +653,11 @@
 
         boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
         if (getShadeController().isDozing()) {
-            updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+            updateAlertState(entry, mNotificationInterruptionStateProvider.shouldPulse(entry),
+                    alertAgain, mAmbientPulseManager);
         } else {
-            updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+            updateAlertState(entry, mNotificationInterruptionStateProvider.shouldHeadsUp(entry),
+                    alertAgain, mHeadsUpManager);
         }
         updateNotifications();
 
@@ -828,7 +716,7 @@
 
         // By comparing the old and new UI adjustments, reinflate the view accordingly.
         for (NotificationData.Entry entry : entries) {
-            mNotificationRowBinder.onNotificationRankingUpdated(
+            getRowBinder().onNotificationRankingUpdated(
                     entry,
                     oldImportances.get(entry.key),
                     oldAdjustments.get(entry.key),
@@ -851,182 +739,6 @@
         }
     }
 
-    public void setStatusBarStateListener(
-            NotificationViewHierarchyManager.StatusBarStateListener listener) {
-        mStatusBarStateListener  = listener;
-    }
-
-    /**
-     * Whether the notification should peek in from the top and alert the user.
-     *
-     * @param entry the entry to check
-     * @return true if the entry should heads up, false otherwise
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
-        StatusBarNotification sbn = entry.notification;
-
-        if (getShadeController().isDozing()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
-        // message bubble from the bubble to go through heads up path
-        boolean inShade = mStatusBarStateListener != null
-                && mStatusBarStateListener.getCurrentState() == SHADE;
-        if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
-            return false;
-        }
-
-        if (!canAlertCommon(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: no huns or vr mode");
-            }
-            return false;
-        }
-
-        boolean isDreaming = false;
-        try {
-            isDreaming = mDreamManager.isDreaming();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to query dream manager.", e);
-        }
-        boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
-
-        if (!inUse) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.shouldSuppressPeek(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (entry.hasJustLaunchedFullScreenIntent()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (!mCallback.canHeadsUp(entry, sbn)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Whether or not the notification should "pulse" on the user's display when the phone is
-     * dozing.  This displays the ambient view of the notification.
-     *
-     * @param entry the entry to check
-     * @return true if the entry should ambient pulse, false otherwise
-     */
-    private boolean shouldPulse(NotificationData.Entry entry) {
-        StatusBarNotification sbn = entry.notification;
-
-        if (!getShadeController().isDozing()) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (!canAlertCommon(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.shouldSuppressAmbient(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (mNotificationData.getImportance(sbn.getKey())
-                < NotificationManager.IMPORTANCE_DEFAULT) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        Bundle extras = sbn.getNotification().extras;
-        CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
-        CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
-        if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
-            if (DEBUG) {
-                Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Common checks between heads up alerting and ambient pulse alerting.  See
-     * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
-     * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}.  Notifications that
-     * fail any of these checks should not alert at all.
-     *
-     * @param entry the entry to check
-     * @return true if these checks pass, false if the notification should not alert
-     */
-    protected boolean canAlertCommon(NotificationData.Entry entry) {
-        StatusBarNotification sbn = entry.notification;
-
-        if (mNotificationData.shouldFilterOut(entry)) {
-            if (DEBUG) {
-                Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        // Don't alert notifications that are suppressed due to group alert behavior
-        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
-            if (DEBUG) {
-                Log.d(TAG, "No alerting: suppressed due to group alert behavior");
-            }
-            return false;
-        }
-
-        return true;
-    }
-
     private void setNotificationShown(StatusBarNotification n) {
         setNotificationsShown(new String[]{n.getKey()});
     }
@@ -1039,10 +751,6 @@
         }
     }
 
-    private boolean isSnoozedPackage(StatusBarNotification sbn) {
-        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
-    }
-
     /**
      * Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
      * method.
@@ -1076,64 +784,4 @@
     public Iterable<NotificationData.Entry> getPendingNotificationsIterator() {
         return mPendingNotifications.values();
     }
-
-    /**
-     * Interface for retrieving heads-up and pulsing state for an entry.
-     */
-    public interface InterruptionStateProvider {
-        /**
-         * Whether the provided entry should be marked as heads-up when inflated.
-         */
-        boolean shouldHeadsUp(NotificationData.Entry entry);
-
-        /**
-         * Whether the provided entry should be marked as pulsing (displayed in ambient) when
-         * inflated.
-         */
-        boolean shouldPulse(NotificationData.Entry entry);
-    }
-
-    /**
-     * Callback for NotificationEntryManager.
-     */
-    public interface Callback {
-
-        /**
-         * Called when a new entry is created.
-         *
-         * @param shadeEntry entry that was created
-         */
-        void onNotificationAdded(NotificationData.Entry shadeEntry);
-
-        /**
-         * Called when a notification was updated.
-         *
-         * @param notification notification that was updated
-         */
-        void onNotificationUpdated(StatusBarNotification notification);
-
-        /**
-         * Called when a notification was removed.
-         *
-         * @param key key of notification that was removed
-         * @param old StatusBarNotification of the notification before it was removed
-         */
-        void onNotificationRemoved(String key, StatusBarNotification old);
-
-        /**
-         * Removes a notification immediately.
-         *
-         * @param statusBarNotification notification that is being removed
-         */
-        void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
-
-        /**
-         * Returns true if NotificationEntryManager can heads up this notification.
-         *
-         * @param entry entry of the notification that might be heads upped
-         * @param sbn notification that might be heads upped
-         * @return true if the notification can be heads upped
-         */
-        boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
new file mode 100644
index 0000000..5e99c38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -0,0 +1,162 @@
+/*
+ * 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.statusbar.notification;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Component which manages the various reasons a notification might be filtered out. */
+@Singleton
+public class NotificationFilter {
+
+    private final NotificationGroupManager mGroupManager = Dependency.get(
+            NotificationGroupManager.class);
+
+    private NotificationData.KeyguardEnvironment mEnvironment;
+    private ShadeController mShadeController;
+    private ForegroundServiceController mFsc;
+    private NotificationLockscreenUserManager mUserManager;
+
+    @Inject
+    public NotificationFilter() {}
+
+    private NotificationData.KeyguardEnvironment getEnvironment() {
+        if (mEnvironment == null) {
+            mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+        }
+        return mEnvironment;
+    }
+
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
+    private ForegroundServiceController getFsc() {
+        if (mFsc == null) {
+            mFsc = Dependency.get(ForegroundServiceController.class);
+        }
+        return mFsc;
+    }
+
+    private NotificationLockscreenUserManager getUserManager() {
+        if (mUserManager == null) {
+            mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+        }
+        return mUserManager;
+    }
+
+
+    /**
+     * @return true if the provided notification should NOT be shown right now.
+     */
+    public boolean shouldFilterOut(NotificationData.Entry entry) {
+        final StatusBarNotification sbn = entry.notification;
+        if (!(getEnvironment().isDeviceProvisioned()
+                || showNotificationEvenIfUnprovisioned(sbn))) {
+            return true;
+        }
+
+        if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+            return true;
+        }
+
+        if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
+                && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
+                        || getUserManager().shouldHideNotifications(sbn.getUserId())
+                        || getUserManager().shouldHideNotifications(sbn.getKey()))) {
+            return true;
+        }
+
+        if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) {
+            return true;
+        }
+
+        if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) {
+            return true;
+        }
+
+        if (entry.suspended) {
+            return true;
+        }
+
+        if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
+                && mGroupManager.isChildInGroupWithSummary(sbn)) {
+            return true;
+        }
+
+        if (getFsc().isDungeonNotification(sbn)
+                && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
+            // this is a foreground-service disclosure for a user that does not need to show one
+            return true;
+        }
+        if (getFsc().isSystemAlertNotification(sbn)) {
+            final String[] apps = sbn.getNotification().extras.getStringArray(
+                    Notification.EXTRA_FOREGROUND_APPS);
+            if (apps != null && apps.length >= 1) {
+                if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    // Q: What kinds of notifications should show during setup?
+    // A: Almost none! Only things coming from packages with permission
+    // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
+    // as relevant for setup (see below).
+    private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+        return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
+    }
+
+    @VisibleForTesting
+    static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
+            StatusBarNotification sbn) {
+        return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
+                sbn.getUid()) == PackageManager.PERMISSION_GRANTED
+                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+    }
+
+    private static int checkUidPermission(IPackageManager packageManager, String permission,
+            int uid) {
+        try {
+            return packageManager.checkUidPermission(permission, uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..8bd0e9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -0,0 +1,332 @@
+/*
+ * 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.statusbar.notification;
+
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Provides heads-up and pulsing state for notification entries.
+ */
+public class NotificationInterruptionStateProvider {
+
+    private static final String TAG = "InterruptionStateProvider";
+    private static final boolean DEBUG = false;
+    private static final boolean ENABLE_HEADS_UP = true;
+    private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+
+    private final StatusBarStateController mStatusBarStateController =
+            Dependency.get(StatusBarStateController.class);
+    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+    private final Context mContext;
+    private final PowerManager mPowerManager;
+    private final IDreamManager mDreamManager;
+
+    private NotificationPresenter mPresenter;
+    private ShadeController mShadeController;
+    private HeadsUpManager mHeadsUpManager;
+    private HeadsUpSuppressor mHeadsUpSuppressor;
+
+    private ContentObserver mHeadsUpObserver;
+    @VisibleForTesting
+    protected boolean mUseHeadsUp = false;
+    private boolean mDisableNotificationAlerts;
+
+    public NotificationInterruptionStateProvider(Context context) {
+        this(context,
+                (PowerManager) context.getSystemService(Context.POWER_SERVICE),
+                IDreamManager.Stub.asInterface(
+                        ServiceManager.checkService(DreamService.DREAM_SERVICE)));
+    }
+
+    @VisibleForTesting
+    protected NotificationInterruptionStateProvider(
+            Context context,
+            PowerManager powerManager,
+            IDreamManager dreamManager) {
+        mContext = context;
+        mPowerManager = powerManager;
+        mDreamManager = dreamManager;
+    }
+
+    /** Sets up late-binding dependencies for this component. */
+    public void setUpWithPresenter(
+            NotificationPresenter notificationPresenter,
+            HeadsUpManager headsUpManager,
+            HeadsUpSuppressor headsUpSuppressor) {
+        mPresenter = notificationPresenter;
+        mHeadsUpManager = headsUpManager;
+        mHeadsUpSuppressor = headsUpSuppressor;
+
+        mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+            @Override
+            public void onChange(boolean selfChange) {
+                boolean wasUsing = mUseHeadsUp;
+                mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
+                        && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+                        mContext.getContentResolver(),
+                        Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+                        Settings.Global.HEADS_UP_OFF);
+                Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+                if (wasUsing != mUseHeadsUp) {
+                    if (!mUseHeadsUp) {
+                        Log.d(TAG,
+                                "dismissing any existing heads up notification on disable event");
+                        mHeadsUpManager.releaseAllImmediately();
+                    }
+                }
+            }
+        };
+
+        if (ENABLE_HEADS_UP) {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+                    true,
+                    mHeadsUpObserver);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+                    mHeadsUpObserver);
+        }
+        mHeadsUpObserver.onChange(true); // set up
+    }
+
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
+    /**
+     * Whether the notification should peek in from the top and alert the user.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should heads up, false otherwise
+     */
+    public boolean shouldHeadsUp(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (getShadeController().isDozing()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
+        // message bubble from the bubble to go through heads up path
+        boolean inShade = mStatusBarStateController.getState() == SHADE;
+        if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+            return false;
+        }
+
+        if (!canAlertCommon(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: no huns or vr mode");
+            }
+            return false;
+        }
+
+        boolean isDreaming = false;
+        try {
+            isDreaming = mDreamManager.isDreaming();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query dream manager.", e);
+        }
+        boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+
+        if (!inUse) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.shouldSuppressPeek()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.hasJustLaunchedFullScreenIntent()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Whether or not the notification should "pulse" on the user's display when the phone is
+     * dozing.  This displays the ambient view of the notification.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should ambient pulse, false otherwise
+     */
+    public boolean shouldPulse(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (!getShadeController().isDozing()) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!canAlertCommon(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.shouldSuppressAmbient()) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.importance < NotificationManager.IMPORTANCE_DEFAULT) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        Bundle extras = sbn.getNotification().extras;
+        CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
+        CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
+        if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Common checks between heads up alerting and ambient pulse alerting.  See
+     * {@link #shouldHeadsUp(NotificationData.Entry)} and
+     * {@link #shouldPulse(NotificationData.Entry)}.  Notifications that fail any of these checks
+     * should not alert at all.
+     *
+     * @param entry the entry to check
+     * @return true if these checks pass, false if the notification should not alert
+     */
+    protected boolean canAlertCommon(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (mNotificationFilter.shouldFilterOut(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        // Don't alert notifications that are suppressed due to group alert behavior
+        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+            if (DEBUG) {
+                Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean isSnoozedPackage(StatusBarNotification sbn) {
+        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+    }
+
+    /** Sets whether to disable all alerts. */
+    public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
+        mDisableNotificationAlerts = disableNotificationAlerts;
+        mHeadsUpObserver.onChange(true);
+    }
+
+    protected NotificationPresenter getPresenter() {
+        return mPresenter;
+    }
+
+    /** A component which can suppress heads-up notifications due to the overall state of the UI. */
+    public interface HeadsUpSuppressor {
+        /**
+         * Returns false if the provided notification is ineligible for heads-up according to this
+         * component.
+         *
+         * @param entry entry of the notification that might be heads upped
+         * @param sbn   notification that might be heads upped
+         * @return false if the notification can not be heads upped
+         */
+        boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index 824bd81..b241b8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -64,6 +64,8 @@
     private final NotificationGutsManager mGutsManager =
             Dependency.get(NotificationGutsManager.class);
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
 
     private final Context mContext;
     private final IStatusBarService mBarService;
@@ -79,7 +81,6 @@
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
-    private NotificationEntryManager.InterruptionStateProvider mInterruptionStateProvider;
 
     @Inject
     public NotificationRowBinder(Context context) {
@@ -116,11 +117,6 @@
         mNotificationClicker = clicker;
     }
 
-    public void setInterruptionStateProvider(
-            NotificationEntryManager.InterruptionStateProvider interruptionStateProvider) {
-        mInterruptionStateProvider = interruptionStateProvider;
-    }
-
     /**
      * Inflates the views for the given entry (possibly asynchronously).
      */
@@ -253,10 +249,10 @@
         row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
         row.setEntry(entry);
 
-        if (mInterruptionStateProvider.shouldHeadsUp(entry)) {
+        if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
             row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
         }
-        if (mInterruptionStateProvider.shouldPulse(entry)) {
+        if (mNotificationInterruptionStateProvider.shouldPulse(entry)) {
             row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
         }
         row.setNeedsRedaction(
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 6732bbe..b1f74c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -29,8 +29,8 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
@@ -95,7 +95,7 @@
         // not being up to date.
         mEntryManager = entryManager;
 
-        mEntryManager.setAlertTransferListener(mAlertTransferListener);
+        mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
         groupManager.addOnGroupChangeListener(mOnGroupChangeListener);
     }
 
@@ -186,7 +186,8 @@
         }
     }
 
-    private final AlertTransferListener mAlertTransferListener = new AlertTransferListener() {
+    private final NotificationEntryListener mNotificationEntryListener =
+            new NotificationEntryListener() {
         // Called when a new notification has been posted but is not inflated yet. We use this to
         // see as early as we can if we need to abort a transfer.
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 2d5d562..e40835f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -207,7 +207,7 @@
         }
 
         // showAmbient == show in shade but not shelf
-        if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(entry)) {
+        if (!showAmbient && entry.shouldSuppressStatusBar()) {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c7e4d34..c0909e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -53,6 +53,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
@@ -133,7 +134,7 @@
     public static final int FLING_COLLAPSE = 1;
 
     /**
-     * Fing until QS is completely hidden.
+     * Fling until QS is completely hidden.
      */
     public static final int FLING_HIDE = 2;
 
@@ -359,6 +360,10 @@
         mKeyguardStatusBar = findViewById(R.id.keyguard_header);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
 
+        KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
+        ViewGroup bigClockContainer = findViewById(R.id.big_clock_container);
+        keyguardClockSwitch.setBigClockContainer(bigClockContainer);
+
         mNotificationContainerParent = findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1b43f8f..008c21d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -191,6 +191,7 @@
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -377,6 +378,7 @@
     private NotificationGutsManager mGutsManager;
     protected NotificationLogger mNotificationLogger;
     protected NotificationEntryManager mEntryManager;
+    private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private NotificationRowBinder mNotificationRowBinder;
     protected NotificationViewHierarchyManager mViewHierarchyManager;
     protected ForegroundServiceController mForegroundServiceController;
@@ -622,6 +624,8 @@
         mGutsManager = Dependency.get(NotificationGutsManager.class);
         mMediaManager = Dependency.get(NotificationMediaManager.class);
         mEntryManager = Dependency.get(NotificationEntryManager.class);
+        mNotificationInterruptionStateProvider =
+                Dependency.get(NotificationInterruptionStateProvider.class);
         mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
         mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
         mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
@@ -1413,7 +1417,7 @@
         }
 
         if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
-            mEntryManager.setDisableNotificationAlerts(
+            mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
                     (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index c93d151..8d1b911 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -24,6 +24,7 @@
 import android.app.ActivityTaskManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
 import android.content.Context;
@@ -33,15 +34,21 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
+import android.util.EventLog;
 import android.util.Log;
 import android.view.RemoteAnimationAdapter;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.UiOffloadThread;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.CommandQueue;
@@ -54,7 +61,9 @@
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -66,6 +75,7 @@
 public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
 
     private static final String TAG = "NotificationClickHandler";
+    protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
     private final NotificationGroupManager mGroupManager =
@@ -84,6 +94,9 @@
             Dependency.get(NotificationEntryManager.class);
     private final StatusBarStateController mStatusBarStateController =
             Dependency.get(StatusBarStateController.class);
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     private final Context mContext;
     private final NotificationPanelView mNotificationPanel;
@@ -94,6 +107,7 @@
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
     private final IStatusBarService mBarService;
     private final CommandQueue mCommandQueue;
+    private final IDreamManager mDreamManager;
 
     private boolean mIsCollapsingToShowActivityOverLockscreen;
 
@@ -112,6 +126,15 @@
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mCommandQueue = getComponent(context, CommandQueue.class);
+        mDreamManager = IDreamManager.Stub.asInterface(
+                ServiceManager.checkService(DreamService.DREAM_SERVICE));
+
+        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onPendingEntryAdded(NotificationData.Entry entry) {
+                handleFullScreenIntent(entry);
+            }
+        });
     }
 
     /**
@@ -322,6 +345,45 @@
         }, null, false /* afterKeyguardGone */);
     }
 
+    private void handleFullScreenIntent(NotificationData.Entry entry) {
+        boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+        if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
+            if (shouldSuppressFullScreenIntent(entry)) {
+                if (DEBUG) {
+                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
+                }
+            } else if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
+                if (DEBUG) {
+                    Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
+                }
+            } else {
+                // Stop screensaver if the notification has a fullscreen intent.
+                // (like an incoming phone call)
+                Dependency.get(UiOffloadThread.class).submit(() -> {
+                    try {
+                        mDreamManager.awaken();
+                    } catch (RemoteException e) {
+                        e.printStackTrace();
+                    }
+                });
+
+                // not immersive & a fullscreen alert should be shown
+                if (DEBUG) {
+                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+                }
+                try {
+                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+                            entry.key);
+                    entry.notification.getNotification().fullScreenIntent.send();
+                    entry.notifyFullScreenIntentLaunched();
+                    mMetricsLogger.count("note_fullscreen", 1);
+                } catch (PendingIntent.CanceledException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
     @Override
     public boolean isCollapsingToShowActivityOverLockscreen() {
         return mIsCollapsingToShowActivityOverLockscreen;
@@ -351,6 +413,14 @@
                 || !mActivityLaunchAnimator.isAnimationPending();
     }
 
+    private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
+        if (mPresenter.isDeviceInVrMode()) {
+            return true;
+        }
+
+        return entry.shouldSuppressFullScreenIntent();
+    }
+
     private void removeNotification(StatusBarNotification notification) {
         // We have to post it to the UI thread for synchronization
         Dependency.get(MAIN_HANDLER).post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index c8c9ebe5..d643f07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -57,7 +57,9 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -91,6 +93,8 @@
             Dependency.get(NotificationEntryManager.class);
     private final NotificationRowBinder mNotificationRowBinder =
             Dependency.get(NotificationRowBinder.class);
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
     private final NotificationMediaManager mMediaManager =
             Dependency.get(NotificationMediaManager.class);
     protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
@@ -169,10 +173,37 @@
 
         NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
         Dependency.get(InitController.class).addPostInitTask(() -> {
+            NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
+                @Override
+                public void onNotificationAdded(Entry entry) {
+                    // Recalculate the position of the sliding windows and the titles.
+                    mShadeController.updateAreThereNotifications();
+                }
+
+                @Override
+                public void onNotificationUpdated(StatusBarNotification notification) {
+                    mShadeController.updateAreThereNotifications();
+                }
+
+                @Override
+                public void onNotificationRemoved(String key, StatusBarNotification old) {
+                    StatusBarNotificationPresenter.this.onNotificationRemoved(key, old);
+                }
+
+                @Override
+                public void onPerformRemoveNotification(
+                        StatusBarNotification statusBarNotification) {
+                    StatusBarNotificationPresenter.this.onPerformRemoveNotification();
+                }
+            };
+
             mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
-            mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
+            mEntryManager.setUpWithPresenter(
+                    this, notifListContainer, notificationEntryListener, mHeadsUpManager);
             mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
                     mEntryManager, this);
+            mNotificationInterruptionStateProvider.setUpWithPresenter(
+                    this, mHeadsUpManager, this::canHeadsUp);
             mLockscreenUserManager.setUpWithPresenter(this);
             mMediaManager.setUpWithPresenter(this);
             Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this,
@@ -222,10 +253,9 @@
                 || mActivityLaunchAnimator.isAnimationRunning();
     }
 
-    @Override
-    public void onPerformRemoveNotification(StatusBarNotification n) {
+    private void onPerformRemoveNotification() {
         if (mNotificationPanel.hasPulsingNotifications() &&
-                    !mAmbientPulseManager.hasNotifications()) {
+                !mAmbientPulseManager.hasNotifications()) {
             // We were showing a pulse for a notification, but no notifications are pulsing anymore.
             // Finish the pulse.
             mDozeScrimController.pulseOutNow();
@@ -249,18 +279,6 @@
         mNotificationPanel.updateNotificationViews();
     }
 
-    @Override
-    public void onNotificationAdded(Entry shadeEntry) {
-        // Recalculate the position of the sliding windows and the titles.
-        mShadeController.updateAreThereNotifications();
-    }
-
-    @Override
-    public void onNotificationUpdated(StatusBarNotification notification) {
-        mShadeController.updateAreThereNotifications();
-    }
-
-    @Override
     public void onNotificationRemoved(String key, StatusBarNotification old) {
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
@@ -282,7 +300,6 @@
         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
     }
 
-    @Override
     public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
         if (mShadeController.isDozing()) {
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
new file mode 100644
index 0000000..e458e63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -0,0 +1,167 @@
+/*
+ * 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.util;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.qs.QuickStatusBarHeader;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Manages inflation that requires dagger injection.
+ * See docs/dagger.md for details.
+ */
+@Singleton
+public class InjectionInflationController {
+
+    public static final String VIEW_CONTEXT = "view_context";
+    private final ViewCreator mViewCreator;
+    private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
+    private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
+
+    @Inject
+    public InjectionInflationController(SystemUIFactory.SystemUIRootComponent rootComponent) {
+        mViewCreator = rootComponent.createViewCreator();
+        initInjectionMap();
+    }
+
+    ArrayMap<String, Method> getInjectionMap() {
+        return mInjectionMap;
+    }
+
+    ViewCreator getFragmentCreator() {
+        return mViewCreator;
+    }
+
+    /**
+     * Wraps a {@link LayoutInflater} to support creating dagger injected views.
+     * See docs/dagger.md for details.
+     */
+    public LayoutInflater injectable(LayoutInflater inflater) {
+        LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
+        ret.setPrivateFactory(mFactory);
+        return ret;
+    }
+
+    private void initInjectionMap() {
+        for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
+            if (View.class.isAssignableFrom(method.getReturnType())
+                    && (method.getModifiers() & Modifier.PUBLIC) != 0) {
+                mInjectionMap.put(method.getReturnType().getName(), method);
+            }
+        }
+    }
+
+    /**
+     * The subcomponent of dagger that holds all views that need injection.
+     */
+    @Subcomponent
+    public interface ViewCreator {
+        /**
+         * Creates another subcomponent to actually generate the view.
+         */
+        ViewInstanceCreator createInstanceCreator(ViewAttributeProvider attributeProvider);
+    }
+
+    /**
+     * Secondary sub-component that actually creates the views.
+     *
+     * Having two subcomponents lets us hide the complexity of providing the named context
+     * and AttributeSet from the SystemUIRootComponent, instead we have one subcomponent that
+     * creates a new ViewInstanceCreator any time we need to inflate a view.
+     */
+    @Subcomponent(modules = ViewAttributeProvider.class)
+    public interface ViewInstanceCreator {
+        /**
+         * Creates the QuickStatusBarHeader.
+         */
+        QuickStatusBarHeader createQsHeader();
+    }
+
+    /**
+     * Module for providing view-specific constructor objects.
+     */
+    @Module
+    public class ViewAttributeProvider {
+        private final Context mContext;
+        private final AttributeSet mAttrs;
+
+        private ViewAttributeProvider(Context context, AttributeSet attrs) {
+            mContext = context;
+            mAttrs = attrs;
+        }
+
+        /**
+         * Provides the view-themed context (as opposed to the global sysui application context).
+         */
+        @Provides
+        @Named(VIEW_CONTEXT)
+        public Context provideContext() {
+            return mContext;
+        }
+
+        /**
+         * Provides the AttributeSet for the current view being inflated.
+         */
+        @Provides
+        public AttributeSet provideAttributeSet() {
+            return mAttrs;
+        }
+    }
+
+    private class InjectionFactory implements LayoutInflater.Factory2 {
+
+        @Override
+        public View onCreateView(String name, Context context, AttributeSet attrs) {
+            Method creationMethod = mInjectionMap.get(name);
+            if (creationMethod != null) {
+                ViewAttributeProvider provider = new ViewAttributeProvider(context, attrs);
+                try {
+                    return (View) creationMethod.invoke(
+                            mViewCreator.createInstanceCreator(provider));
+                } catch (IllegalAccessException e) {
+                    throw new InflateException("Could not inflate " + name, e);
+                } catch (InvocationTargetException e) {
+                    throw new InflateException("Could not inflate " + name, e);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+            return onCreateView(name, context, attrs);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index fb2ceac..4150602 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -107,6 +107,25 @@
     }
 
     @Test
+    public void onPluginConnected_showPluginBigClock() {
+        // GIVEN that the container for the big clock has visibility GONE
+        FrameLayout bigClockContainer = new FrameLayout(getContext());
+        bigClockContainer.setVisibility(GONE);
+        mKeyguardClockSwitch.setBigClockContainer(bigClockContainer);
+        // AND the plugin returns a view for the big clock
+        ClockPlugin plugin = mock(ClockPlugin.class);
+        TextClock pluginView = new TextClock(getContext());
+        when(plugin.getBigClockView()).thenReturn(pluginView);
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        // WHEN the plugin is connected
+        listener.onPluginConnected(plugin, null);
+        // THEN the big clock container is visible and it is the parent of the
+        // big clock view.
+        assertThat(bigClockContainer.getVisibility()).isEqualTo(VISIBLE);
+        assertThat(pluginView.getParent()).isEqualTo(bigClockContainer);
+    }
+
+    @Test
     public void onPluginConnected_nullView() {
         ClockPlugin plugin = mock(ClockPlugin.class);
         PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
@@ -146,6 +165,26 @@
     }
 
     @Test
+    public void onPluginDisconnected_hidePluginBigClock() {
+        // GIVEN that the big clock container is visible
+        FrameLayout bigClockContainer = new FrameLayout(getContext());
+        bigClockContainer.setVisibility(VISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(bigClockContainer);
+        // AND the plugin returns a view for the big clock
+        ClockPlugin plugin = mock(ClockPlugin.class);
+        TextClock pluginView = new TextClock(getContext());
+        when(plugin.getBigClockView()).thenReturn(pluginView);
+        PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
+        listener.onPluginConnected(plugin, null);
+        // WHEN the plugin is disconnected
+        listener.onPluginDisconnected(plugin);
+        // THEN the big lock container is GONE and the big clock view doesn't have
+        // a parent.
+        assertThat(bigClockContainer.getVisibility()).isEqualTo(GONE);
+        assertThat(pluginView.getParent()).isNull();
+    }
+
+    @Test
     public void onPluginDisconnected_nullView() {
         ClockPlugin plugin = mock(ClockPlugin.class);
         PluginListener listener = mKeyguardClockSwitch.getClockPluginListener();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index bb44548..2582946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -86,7 +86,7 @@
                 mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
-        mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 AppOpsManager.MODE_ALLOWED);
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
@@ -136,7 +136,7 @@
                 TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
                 TEST_UID, TEST_PACKAGE_NAME, true);
-        mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION,
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
                 TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
         assertEquals(3, mController.getActiveAppOps().size());
     }
@@ -147,7 +147,7 @@
                 TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
                 TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
-        mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION,
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
                 TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
         assertEquals(2,
                 mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
@@ -158,7 +158,7 @@
     @Test
     public void opNotedScheduledForRemoval() {
         mController.setBGHandler(mMockHandler);
-        mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 AppOpsManager.MODE_ALLOWED);
         verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 39afbac..ab508a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -34,11 +34,13 @@
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.util.InjectionInflationController;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -122,6 +124,7 @@
 
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
-        return new QSFragment(new RemoteInputQuickSettingsDisabler(context));
+        return new QSFragment(new RemoteInputQuickSettingsDisabler(context),
+                new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index f8ff583..894ef3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -26,6 +26,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -55,7 +56,8 @@
 public class NonPhoneDependencyTest extends SysuiTestCase {
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationListContainer mListContainer;
-    @Mock private NotificationEntryManager.Callback mEntryManagerCallback;
+    @Mock
+    private NotificationEntryListener mEntryManagerCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private RemoteInputController.Delegate mDelegate;
     @Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index def7513..871ff89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -29,8 +29,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -50,7 +48,6 @@
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
-import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -105,6 +102,7 @@
         com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+        when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification);
 
         when(mMockPackageManager.checkUidPermission(
                 eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
@@ -129,41 +127,6 @@
     }
 
     @Test
-    @UiThreadTest
-    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
-        initStatusBarNotification(false);
-        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
-        assertFalse(
-                NotificationData.showNotificationEvenIfUnprovisioned(
-                        mMockPackageManager,
-                        mMockStatusBarNotification));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
-        initStatusBarNotification(true);
-
-        assertFalse(
-                NotificationData.showNotificationEvenIfUnprovisioned(
-                        mMockPackageManager,
-                        mMockStatusBarNotification));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
-        initStatusBarNotification(true);
-        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
-        assertTrue(
-                NotificationData.showNotificationEvenIfUnprovisioned(
-                        mMockPackageManager,
-                        mMockStatusBarNotification));
-    }
-
-    @Test
     public void testChannelSetWhenAdded() {
         mNotificationData.add(mRow.getEntry());
         assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel);
@@ -230,76 +193,6 @@
     }
 
     @Test
-    public void testSuppressSystemAlertNotification() {
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-        StatusBarNotification sbn = mRow.getEntry().notification;
-        Bundle bundle = new Bundle();
-        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
-        sbn.getNotification().extras = bundle;
-
-        assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry()));
-    }
-
-    @Test
-    public void testDoNotSuppressSystemAlertNotification() {
-        StatusBarNotification sbn = mRow.getEntry().notification;
-        Bundle bundle = new Bundle();
-        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
-        sbn.getNotification().extras = bundle;
-
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
-
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-    }
-
-    @Test
-    public void testDoNotSuppressMalformedSystemAlertNotification() {
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
-
-        // missing extra
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-
-        StatusBarNotification sbn = mRow.getEntry().notification;
-        Bundle bundle = new Bundle();
-        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
-        sbn.getNotification().extras = bundle;
-
-        // extra missing values
-        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
-    }
-
-    @Test
-    public void testShouldFilterHiddenNotifications() {
-        initStatusBarNotification(false);
-        // setup
-        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
-        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
-
-        // test should filter out hidden notifications:
-        // hidden
-        when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
-        assertTrue(mNotificationData.shouldFilterOut(entry));
-
-        // not hidden
-        when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
-        entry = new NotificationData.Entry(mMockStatusBarNotification);
-        assertFalse(mNotificationData.shouldFilterOut(entry));
-    }
-
-    @Test
     public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
             throws Exception {
         mNotificationData.add(mRow.getEntry());
@@ -325,9 +218,10 @@
         Notification n = mMockStatusBarNotification.getNotification();
         n.flags = Notification.FLAG_FOREGROUND_SERVICE;
         NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        mNotificationData.add(entry);
 
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+        assertTrue(entry.isExemptFromDndVisualSuppression());
+        assertFalse(entry.shouldSuppressAmbient());
     }
 
     @Test
@@ -341,9 +235,10 @@
         n = nb.build();
         when(mMockStatusBarNotification.getNotification()).thenReturn(n);
         NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        mNotificationData.add(entry);
 
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+        assertTrue(entry.isExemptFromDndVisualSuppression());
+        assertFalse(entry.shouldSuppressAmbient());
     }
 
     @Test
@@ -353,9 +248,10 @@
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
         NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
+        mNotificationData.add(entry);
 
-        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
+        assertTrue(entry.isExemptFromDndVisualSuppression());
+        assertFalse(entry.shouldSuppressAmbient());
     }
 
     @Test
@@ -365,31 +261,33 @@
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
         NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
+        mNotificationData.add(entry);
+
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
-        assertTrue(mNotificationData.shouldSuppressAmbient(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
+        assertTrue(entry.shouldSuppressAmbient());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
 
         when(mMockStatusBarNotification.getNotification()).thenReturn(
                 new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
 
-        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
+        assertFalse(entry.isExemptFromDndVisualSuppression());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 8fe91cd..701ea7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -103,7 +103,8 @@
     @Mock private KeyguardEnvironment mEnvironment;
     @Mock private ExpandableNotificationRow mRow;
     @Mock private NotificationListContainer mListContainer;
-    @Mock private NotificationEntryManager.Callback mCallback;
+    @Mock
+    private NotificationEntryListener mCallback;
     @Mock
     private NotificationRowBinder.BindRowCallback mBindCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
@@ -137,7 +138,6 @@
             super(context);
             mBarService = barService;
             mCountDownLatch = new CountDownLatch(1);
-            mUseHeadsUp = true;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
new file mode 100644
index 0000000..da8bc01d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.statusbar.notification;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class NotificationFilterTest extends SysuiTestCase {
+
+    private static final int UID_NORMAL = 123;
+    private static final int UID_ALLOW_DURING_SETUP = 456;
+    private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+
+    private final StatusBarNotification mMockStatusBarNotification =
+            mock(StatusBarNotification.class);
+
+    @Mock
+    ForegroundServiceController mFsc;
+    @Mock
+    NotificationData.KeyguardEnvironment mEnvironment;
+    private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
+
+    private NotificationFilter mNotificationFilter;
+    private ExpandableNotificationRow mRow;
+
+    @Before
+    public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
+        MockitoAnnotations.initMocks(this);
+        when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+
+        when(mMockPackageManager.checkUidPermission(
+                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+                eq(UID_NORMAL)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mMockPackageManager.checkUidPermission(
+                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
+                eq(UID_ALLOW_DURING_SETUP)))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
+        mDependency.injectTestDependency(NotificationGroupManager.class,
+                new NotificationGroupManager());
+        mDependency.injectMockDependency(ShadeController.class);
+        mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
+        when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
+        mRow = new NotificationTestHelper(getContext()).createRow();
+        mNotificationFilter = new NotificationFilter();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+        assertFalse(
+                NotificationFilter.showNotificationEvenIfUnprovisioned(
+                        mMockPackageManager,
+                        mMockStatusBarNotification));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
+        initStatusBarNotification(true);
+
+        assertFalse(
+                NotificationFilter.showNotificationEvenIfUnprovisioned(
+                        mMockPackageManager,
+                        mMockStatusBarNotification));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
+        initStatusBarNotification(true);
+        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
+
+        assertTrue(
+                NotificationFilter.showNotificationEvenIfUnprovisioned(
+                        mMockPackageManager,
+                        mMockStatusBarNotification));
+    }
+
+    @Test
+    public void testSuppressSystemAlertNotification() {
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+        sbn.getNotification().extras = bundle;
+
+        assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+    }
+
+    @Test
+    public void testDoNotSuppressSystemAlertNotification() {
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
+        sbn.getNotification().extras = bundle;
+
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+    }
+
+    @Test
+    public void testDoNotSuppressMalformedSystemAlertNotification() {
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+
+        // missing extra
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{});
+        sbn.getNotification().extras = bundle;
+
+        // extra missing values
+        assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
+    }
+
+    @Test
+    public void testShouldFilterHiddenNotifications() {
+        initStatusBarNotification(false);
+        // setup
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
+        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
+
+        // test should filter out hidden notifications:
+        // hidden
+        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        entry.suspended = true;
+        assertTrue(mNotificationFilter.shouldFilterOut(entry));
+
+        // not hidden
+        entry = new NotificationData.Entry(mMockStatusBarNotification);
+        entry.suspended = false;
+        assertFalse(mNotificationFilter.shouldFilterOut(entry));
+    }
+
+    private void initStatusBarNotification(boolean allowDuringSetup) {
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
+        Notification notification = new Notification.Builder(mContext, "test")
+                .addExtras(bundle)
+                .build();
+        when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index e65e806..b4f99c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -126,7 +126,7 @@
         mPowerManager = new PowerManager(mContext, powerManagerService,
                 Handler.createAsync(Looper.myLooper()));
 
-        mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
+        mEntryManager = new TestableNotificationEntryManager(mPowerManager,
                 mContext);
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         Dependency.get(InitController.class).executePostInitTasks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index ee39e10..490288e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -32,9 +32,9 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.AlertTransferListener;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -61,8 +61,9 @@
     private AmbientPulseManager mAmbientPulseManager;
     private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationEntryManager mNotificationEntryManager;
-    @Captor private ArgumentCaptor<AlertTransferListener> mListenerCaptor;
-    private AlertTransferListener mAlertTransferListener;
+    @Captor
+    private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+    private NotificationEntryListener mNotificationEntryListener;
     private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
     private final NotificationGroupTestHelper mGroupTestHelper =
             new NotificationGroupTestHelper(mContext);
@@ -85,8 +86,8 @@
         mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
 
         mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
-        verify(mNotificationEntryManager).setAlertTransferListener(mListenerCaptor.capture());
-        mAlertTransferListener = mListenerCaptor.getValue();
+        verify(mNotificationEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
+        mNotificationEntryListener = mListenerCaptor.getValue();
         mHeadsUpManager.addListener(mGroupAlertTransferHelper);
         mAmbientPulseManager.addListener(mGroupAlertTransferHelper);
     }
@@ -121,7 +122,7 @@
 
         // Add second child notification so that summary is no longer suppressed.
         mPendingEntries.put(childEntry2.key, childEntry2);
-        mAlertTransferListener.onPendingEntryAdded(childEntry2);
+        mNotificationEntryListener.onPendingEntryAdded(childEntry2);
         mGroupManager.onEntryAdded(childEntry2);
 
         // The alert state should transfer back to the summary as there is now more than one
@@ -148,7 +149,7 @@
 
         // Add second child notification so that summary is no longer suppressed.
         mPendingEntries.put(childEntry2.key, childEntry2);
-        mAlertTransferListener.onPendingEntryAdded(childEntry2);
+        mNotificationEntryListener.onPendingEntryAdded(childEntry2);
         mGroupManager.onEntryAdded(childEntry2);
 
         // Dozing changed so no reason to re-alert summary.
@@ -186,7 +187,7 @@
 
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(true);
-        mAlertTransferListener.onEntryReinflated(childEntry);
+        mNotificationEntryListener.onEntryReinflated(childEntry);
 
         // Alert is immediately removed from summary, and we show child as its content is inflated.
         assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
@@ -210,13 +211,13 @@
 
         // Add second child notification so that summary is no longer suppressed.
         mPendingEntries.put(childEntry2.key, childEntry2);
-        mAlertTransferListener.onPendingEntryAdded(childEntry2);
+        mNotificationEntryListener.onPendingEntryAdded(childEntry2);
         mGroupManager.onEntryAdded(childEntry2);
 
         // Child entry finishes its inflation.
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(true);
-        mAlertTransferListener.onEntryReinflated(childEntry);
+        mNotificationEntryListener.onEntryReinflated(childEntry);
 
         verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
             .getContentFlag());
@@ -236,7 +237,7 @@
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        mAlertTransferListener.onEntryRemoved(childEntry);
+        mNotificationEntryListener.onEntryRemoved(childEntry);
 
         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e5620a5..c584d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -91,7 +92,10 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -129,6 +133,8 @@
     @Mock private ArrayList<Entry> mNotificationList;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private NotificationData mNotificationData;
+    @Mock
+    private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
 
     // Mock dependencies:
     @Mock private NotificationViewHierarchyManager mViewHierarchyManager;
@@ -141,13 +147,17 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private NotificationPresenter mNotificationPresenter;
-    @Mock private NotificationEntryManager.Callback mCallback;
+    @Mock
+    private NotificationEntryListener mCallback;
     @Mock private BubbleController mBubbleController;
+    @Mock
+    private NotificationFilter mNotificationFilter;
 
     private TestableStatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
     private PowerManager mPowerManager;
     private TestableNotificationEntryManager mEntryManager;
+    private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private NotificationLogger mNotificationLogger;
     private CommandQueue mCommandQueue;
 
@@ -168,6 +178,17 @@
         mDependency.injectTestDependency(DeviceProvisionedController.class,
                 mDeviceProvisionedController);
         mDependency.injectMockDependency(BubbleController.class);
+        mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
+
+        IPowerManager powerManagerService = mock(IPowerManager.class);
+        mPowerManager = new PowerManager(mContext, powerManagerService,
+                Handler.createAsync(Looper.myLooper()));
+
+        mNotificationInterruptionStateProvider =
+                new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+                        mDreamManager);
+        mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
+                mNotificationInterruptionStateProvider);
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -178,10 +199,6 @@
         mNotificationLogger = new NotificationLogger();
         DozeLog.traceDozing(mContext, false /* dozing */);
 
-        IPowerManager powerManagerService = mock(IPowerManager.class);
-        mPowerManager = new PowerManager(mContext, powerManagerService,
-                Handler.createAsync(Looper.myLooper()));
-
         mCommandQueue = mock(CommandQueue.class);
         when(mCommandQueue.asBinder()).thenReturn(new Binder());
         mContext.putComponent(CommandQueue.class, mCommandQueue);
@@ -205,7 +222,10 @@
             return null;
         }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
 
-        mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext);
+        mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
+                mHeadsUpManager, mHeadsUpSuppressor);
+
+        mEntryManager = new TestableNotificationEntryManager(mPowerManager, mContext);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
@@ -362,11 +382,9 @@
     public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -376,19 +394,18 @@
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertTrue(mEntryManager.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
     public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -398,46 +415,44 @@
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertFalse(mEntryManager.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
     public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
-        when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertFalse(mEntryManager.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
     public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
-        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
-        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
-
-        when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        entry.importance = IMPORTANCE_HIGH;
 
-        assertTrue(mEntryManager.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -725,20 +740,29 @@
 
     public static class TestableNotificationEntryManager extends NotificationEntryManager {
 
-        public TestableNotificationEntryManager(IDreamManager dreamManager,
-                PowerManager powerManager, Context context) {
+        public TestableNotificationEntryManager(PowerManager powerManager, Context context) {
             super(context);
-            mDreamManager = dreamManager;
             mPowerManager = powerManager;
         }
 
         public void setUpForTest(NotificationPresenter presenter,
                 NotificationListContainer listContainer,
-                Callback callback,
+                NotificationEntryListener callback,
                 HeadsUpManagerPhone headsUpManager,
                 NotificationData notificationData) {
             super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
             mNotificationData = notificationData;
+        }
+    }
+
+    public static class TestableNotificationInterruptionStateProvider extends
+            NotificationInterruptionStateProvider {
+
+        public TestableNotificationInterruptionStateProvider(
+                Context context,
+                PowerManager powerManager,
+                IDreamManager dreamManager) {
+            super(context, powerManager, dreamManager);
             mUseHeadsUp = true;
         }
     }
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index f0ec69f..f027253 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -65,7 +65,6 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -1646,29 +1645,40 @@
     }
 
     @Override
+    public int checkOperationRaw(int code, int uid, String packageName) {
+        return checkOperationInternal(code, uid, packageName, true /*raw*/);
+    }
+
+    @Override
     public int checkOperation(int code, int uid, String packageName) {
+        return checkOperationInternal(code, uid, packageName, false /*raw*/);
+    }
+
+    private int checkOperationInternal(int code, int uid, String packageName, boolean raw) {
         final CheckOpsDelegate delegate;
         synchronized (this) {
             delegate = mCheckOpsDelegate;
         }
         if (delegate == null) {
-            return checkOperationImpl(code, uid, packageName);
+            return checkOperationImpl(code, uid, packageName, raw);
         }
-        return delegate.checkOperation(code, uid, packageName,
+        return delegate.checkOperation(code, uid, packageName, raw,
                     AppOpsService.this::checkOperationImpl);
     }
 
-    private int checkOperationImpl(int code, int uid, String packageName) {
+    private int checkOperationImpl(int code, int uid, String packageName,
+                boolean raw) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
             return AppOpsManager.MODE_IGNORED;
         }
-        return checkOperationUnchecked(code, uid, resolvedPackageName);
+        return checkOperationUnchecked(code, uid, resolvedPackageName, raw);
     }
 
-    private int checkOperationUnchecked(int code, int uid, String packageName) {
+    private int checkOperationUnchecked(int code, int uid, String packageName,
+                boolean raw) {
         synchronized (this) {
             if (isOpRestrictedLocked(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
@@ -1677,7 +1687,8 @@
             UidState uidState = getUidStateLocked(uid, false);
             if (uidState != null && uidState.opModes != null
                     && uidState.opModes.indexOfKey(code) >= 0) {
-                return uidState.evalMode(uidState.opModes.get(code));
+                final int rawMode = uidState.opModes.get(code);
+                return raw ? rawMode : uidState.evalMode(rawMode);
             }
             Op op = getOpLocked(code, uid, packageName, false, true, false);
             if (op == null) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7adcaba..2a80644 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -129,6 +129,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.BackgroundThread;
@@ -1709,6 +1710,10 @@
                 ServiceManager.getService("package"));
         mIAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
+        try {
+            mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
+        } catch (RemoteException e) {
+        }
         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
     }
 
@@ -3240,6 +3245,15 @@
         }
     }
 
+    private IAppOpsCallback.Stub mAppOpsCallback = new IAppOpsCallback.Stub() {
+        @Override
+        public void opChanged(int op, int uid, String packageName) throws RemoteException {
+            if (!ENABLE_ISOLATED_STORAGE) return;
+
+            remountUidExternalStorage(uid, getMountMode(uid, packageName));
+        }
+    };
+
     private static final Pattern PATTERN_TRANSLATE = Pattern.compile(
             "(?i)^(/storage/[^/]+/(?:[0-9]+/)?)(.*)");
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fe632e5..a94fa12 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2023,7 +2023,7 @@
                 if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
                         name.getPackageName(), sInfo.applicationInfo.uid)) {
                     String msg = "association not allowed between packages "
-                            + callingPackage + " and " + r.packageName;
+                            + callingPackage + " and " + name.getPackageName();
                     Slog.w(TAG, "Service lookup failed: " + msg);
                     return new ServiceLookupResult(null, msg);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3bfd363..983ec4b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20117,18 +20117,18 @@
         }
 
         @Override
-        public int checkOperation(int code, int uid, String packageName,
-                TriFunction<Integer, Integer, String, Integer> superImpl) {
+        public int checkOperation(int code, int uid, String packageName, boolean raw,
+                QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
             if (uid == mTargetUid && isTargetOp(code)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     return superImpl.apply(code, Process.SHELL_UID,
-                            "com.android.shell");
+                            "com.android.shell", raw);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(code, uid, packageName);
+            return superImpl.apply(code, uid, packageName, raw);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 5208ca5..e483b26 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -15,7 +15,7 @@
 jjaggi@google.com
 racarr@google.com
 chaviw@google.com
-brycelee@google.com
+vishnun@google.com
 akulian@google.com
 roosa@google.com
 
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 2feea41..905f826 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -20,11 +20,11 @@
 import android.content.pm.PackageManager;
 import android.media.AudioFormat;
 import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
 import android.media.AudioRecordingConfiguration;
 import android.media.AudioSystem;
 import android.media.IRecordingConfigDispatcher;
 import android.media.MediaRecorder;
+import android.media.audiofx.AudioEffect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -64,12 +64,19 @@
      * Implementation of android.media.AudioSystem.AudioRecordingCallback
      */
     public void onRecordingConfigurationChanged(int event, int uid, int session, int source,
-            int[] recordingInfo, String packName) {
+                                                int portId, boolean silenced, int[] recordingInfo,
+                                                AudioEffect.Descriptor[] clientEffects,
+                                                AudioEffect.Descriptor[] effects,
+                                                int activeSource, String packName) {
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             return;
         }
+        String clientEffectName =  clientEffects.length == 0 ? "None" : clientEffects[0].name;
+        String effectName =  effects.length == 0 ? "None" : effects[0].name;
+
         final List<AudioRecordingConfiguration> configsSystem =
-                updateSnapshot(event, uid, session, source, recordingInfo);
+                updateSnapshot(event, uid, session, source, recordingInfo,
+                portId, silenced, activeSource, clientEffects, effects);
         if (configsSystem != null){
             synchronized (mClients) {
                 // list of recording configurations for "public consumption". It is only computed if
@@ -179,13 +186,20 @@
      * @param session
      * @param source
      * @param recordingFormat see
-     *     {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
+     *     {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int,\
+     int, int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)}
      *     for the definition of the contents of the array
+     * @param portId
+     * @param silenced
+     * @param activeSource
+     * @param clientEffects
+     * @param effects
      * @return null if the list of active recording sessions has not been modified, a list
      *     with the current active configurations otherwise.
      */
     private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,
-            int source, int[] recordingInfo) {
+            int source, int[] recordingInfo, int portId, boolean silenced, int activeSource,
+            AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects) {
         final boolean configChanged;
         final ArrayList<AudioRecordingConfiguration> configs;
         synchronized(mRecordConfigs) {
@@ -211,7 +225,7 @@
                         .setSampleRate(recordingInfo[5])
                         .build();
                 final int patchHandle = recordingInfo[6];
-                final Integer sessionKey = new Integer(session);
+                final Integer portIdKey = new Integer(portId);
 
                 final String[] packages = mPackMan.getPackagesForUid(uid);
                 final String packageName;
@@ -222,19 +236,20 @@
                 }
                 final AudioRecordingConfiguration updatedConfig =
                         new AudioRecordingConfiguration(uid, session, source,
-                                clientFormat, deviceFormat, patchHandle, packageName);
+                                clientFormat, deviceFormat, patchHandle, packageName,
+                                portId, silenced, activeSource, clientEffects, effects);
 
-                if (mRecordConfigs.containsKey(sessionKey)) {
-                    if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
+                if (mRecordConfigs.containsKey(portIdKey)) {
+                    if (updatedConfig.equals(mRecordConfigs.get(portIdKey))) {
                         configChanged = false;
                     } else {
                         // config exists but has been modified
-                        mRecordConfigs.remove(sessionKey);
-                        mRecordConfigs.put(sessionKey, updatedConfig);
+                        mRecordConfigs.remove(portIdKey);
+                        mRecordConfigs.put(portIdKey, updatedConfig);
                         configChanged = true;
                     }
                 } else {
-                    mRecordConfigs.put(sessionKey, updatedConfig);
+                    mRecordConfigs.put(portIdKey, updatedConfig);
                     configChanged = true;
                 }
                 if (configChanged) {
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 521fa23..b6c82d3 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -39,26 +39,31 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
+import android.provider.Settings.System;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
+import com.android.server.DisplayThread;
 import com.android.server.SystemService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
 
+import java.time.DateTimeException;
+import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
 
 /**
  * Controls the display's color transforms.
  */
-public final class ColorDisplayService extends SystemService
-        implements ColorDisplayController.Callback {
+public final class ColorDisplayService extends SystemService {
 
     private static final String TAG = "ColorDisplayService";
 
@@ -71,6 +76,7 @@
      * The identity matrix, used if one of the given matrices is {@code null}.
      */
     private static final float[] MATRIX_IDENTITY = new float[16];
+
     static {
         Matrix.setIdentityM(MATRIX_IDENTITY, 0);
     }
@@ -90,10 +96,12 @@
     private ContentObserver mUserSetupObserver;
     private boolean mBootCompleted;
 
-    private ColorDisplayController mController;
+    private ColorDisplayController mNightDisplayController;
+    private ContentObserver mContentObserver;
     private ValueAnimator mColorMatrixAnimator;
-    private Boolean mIsActivated;
-    private AutoMode mAutoMode;
+
+    private Boolean mIsNightDisplayActivated;
+    private NightDisplayAutoMode mNightDisplayAutoMode;
 
     public ColorDisplayService(Context context) {
         super(context);
@@ -186,42 +194,102 @@
     private void setUp() {
         Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
 
-        // Create a new controller for the current user and start listening for changes.
-        mController = new ColorDisplayController(getContext(), mCurrentUser);
-        mController.setListener(this);
+        mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
+
+        // Listen for external changes to any of the settings.
+        if (mContentObserver == null) {
+            mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
+                @Override
+                public void onChange(boolean selfChange, Uri uri) {
+                    super.onChange(selfChange, uri);
+
+                    final String setting = uri == null ? null : uri.getLastPathSegment();
+                    if (setting != null) {
+                        switch (setting) {
+                            case Secure.NIGHT_DISPLAY_ACTIVATED:
+                                onNightDisplayActivated(mNightDisplayController.isActivated());
+                                break;
+                            case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
+                                onNightDisplayColorTemperatureChanged(
+                                        mNightDisplayController.getColorTemperature());
+                                break;
+                            case Secure.NIGHT_DISPLAY_AUTO_MODE:
+                                onNightDisplayAutoModeChanged(
+                                        mNightDisplayController.getAutoMode());
+                                break;
+                            case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
+                                onNightDisplayCustomStartTimeChanged(
+                                        mNightDisplayController.getCustomStartTime());
+                                break;
+                            case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
+                                onNightDisplayCustomEndTimeChanged(
+                                        mNightDisplayController.getCustomEndTime());
+                                break;
+                            case System.DISPLAY_COLOR_MODE:
+                                onDisplayColorModeChanged(mNightDisplayController.getColorMode());
+                                break;
+                            case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
+                            case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
+                                onAccessibilityTransformChanged();
+                                break;
+                        }
+                    }
+                }
+            };
+        }
+        final ContentResolver cr = getContext().getContentResolver();
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(
+                Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+        cr.registerContentObserver(
+                Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
+                false /* notifyForDescendants */, mContentObserver, mCurrentUser);
 
         // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
         // existing activated state. This ensures consistency of tint across the color mode change.
-        onDisplayColorModeChanged(mController.getColorMode());
+        onDisplayColorModeChanged(mNightDisplayController.getColorMode());
 
         // Reset the activated state.
-        mIsActivated = null;
+        mIsNightDisplayActivated = null;
 
         setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
 
         // Prepare color transformation matrix.
-        setMatrix(mController.getColorTemperature(), mMatrixNight);
+        setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
 
         // Initialize the current auto mode.
-        onAutoModeChanged(mController.getAutoMode());
+        onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
 
         // Force the initialization current activated state.
-        if (mIsActivated == null) {
-            onActivated(mController.isActivated());
+        if (mIsNightDisplayActivated == null) {
+            onNightDisplayActivated(mNightDisplayController.isActivated());
         }
     }
 
     private void tearDown() {
         Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
 
-        if (mController != null) {
-            mController.setListener(null);
-            mController = null;
+        getContext().getContentResolver().unregisterContentObserver(mContentObserver);
+
+        if (mNightDisplayController != null) {
+            mNightDisplayController = null;
         }
 
-        if (mAutoMode != null) {
-            mAutoMode.onStop();
-            mAutoMode = null;
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onStop();
+            mNightDisplayAutoMode = null;
         }
 
         if (mColorMatrixAnimator != null) {
@@ -230,67 +298,61 @@
         }
     }
 
-    @Override
-    public void onActivated(boolean activated) {
-        if (mIsActivated == null || mIsActivated != activated) {
+    private void onNightDisplayActivated(boolean activated) {
+        if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activated) {
             Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
 
-            mIsActivated = activated;
+            mIsNightDisplayActivated = activated;
 
-            if (mAutoMode != null) {
-                mAutoMode.onActivated(activated);
+            if (mNightDisplayAutoMode != null) {
+                mNightDisplayAutoMode.onActivated(activated);
             }
 
             applyTint(false);
         }
     }
 
-    @Override
-    public void onAutoModeChanged(int autoMode) {
-        Slog.d(TAG, "onAutoModeChanged: autoMode=" + autoMode);
+    private void onNightDisplayAutoModeChanged(int autoMode) {
+        Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
 
-        if (mAutoMode != null) {
-            mAutoMode.onStop();
-            mAutoMode = null;
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onStop();
+            mNightDisplayAutoMode = null;
         }
 
         if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
-            mAutoMode = new CustomAutoMode();
+            mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
         } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
-            mAutoMode = new TwilightAutoMode();
+            mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
         }
 
-        if (mAutoMode != null) {
-            mAutoMode.onStart();
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onStart();
         }
     }
 
-    @Override
-    public void onCustomStartTimeChanged(LocalTime startTime) {
-        Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
+    private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
+        Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
 
-        if (mAutoMode != null) {
-            mAutoMode.onCustomStartTimeChanged(startTime);
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
         }
     }
 
-    @Override
-    public void onCustomEndTimeChanged(LocalTime endTime) {
-        Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
+    private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
+        Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
 
-        if (mAutoMode != null) {
-            mAutoMode.onCustomEndTimeChanged(endTime);
+        if (mNightDisplayAutoMode != null) {
+            mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
         }
     }
 
-    @Override
-    public void onColorTemperatureChanged(int colorTemperature) {
+    private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
         setMatrix(colorTemperature, mMatrixNight);
         applyTint(true);
     }
 
-    @Override
-    public void onDisplayColorModeChanged(int mode) {
+    private void onDisplayColorModeChanged(int mode) {
         if (mode == -1) {
             return;
         }
@@ -301,16 +363,15 @@
         }
 
         setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
-        setMatrix(mController.getColorTemperature(), mMatrixNight);
+        setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
 
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
-        dtm.setColorMode(mode, (mIsActivated != null && mIsActivated) ? mMatrixNight
-                : MATRIX_IDENTITY);
+        dtm.setColorMode(mode, (mIsNightDisplayActivated != null && mIsNightDisplayActivated)
+                ? mMatrixNight : MATRIX_IDENTITY);
     }
 
-    @Override
-    public void onAccessibilityTransformChanged(boolean state) {
-        onDisplayColorModeChanged(mController.getColorMode());
+    private void onAccessibilityTransformChanged() {
+        onDisplayColorModeChanged(mNightDisplayController.getColorMode());
     }
 
     /**
@@ -338,7 +399,7 @@
 
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
-        final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
+        final float[] to = mIsNightDisplayActivated ? mMatrixNight : MATRIX_IDENTITY;
 
         if (immediate) {
             dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
@@ -383,7 +444,7 @@
      * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
      *
      * @param colorTemperature color temperature in Kelvin
-     * @param outTemp          the 4x4 display transformation matrix for that color temperature
+     * @param outTemp the 4x4 display transformation matrix for that color temperature
      */
     private void setMatrix(int colorTemperature, float[] outTemp) {
         if (outTemp.length != 16) {
@@ -412,7 +473,8 @@
      * @param compareTime the LocalDateTime to compare against
      * @return the prior LocalDateTime corresponding to this local time
      */
-    public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+    @VisibleForTesting
+    static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
         final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
                 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
 
@@ -427,7 +489,8 @@
      * @param compareTime the LocalDateTime to compare against
      * @return the next LocalDateTime corresponding to this local time
      */
-    public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+    @VisibleForTesting
+    static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
         final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
                 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
 
@@ -440,13 +503,47 @@
         return dtm.isDeviceColorManaged();
     }
 
-    private abstract class AutoMode implements ColorDisplayController.Callback {
+    /**
+     * Returns the last time the night display transform activation state was changed, or {@link
+     * LocalDateTime#MIN} if night display has never been activated.
+     */
+    private @NonNull LocalDateTime getNightDisplayLastActivatedTimeSetting() {
+        final ContentResolver cr = getContext().getContentResolver();
+        final String lastActivatedTime = Secure.getStringForUser(
+                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
+        if (lastActivatedTime != null) {
+            try {
+                return LocalDateTime.parse(lastActivatedTime);
+            } catch (DateTimeParseException ignored) {
+            }
+            // Uses the old epoch time.
+            try {
+                return LocalDateTime.ofInstant(
+                        Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+                        ZoneId.systemDefault());
+            } catch (DateTimeException | NumberFormatException ignored) {
+            }
+        }
+        return LocalDateTime.MIN;
+    }
+
+    private abstract class NightDisplayAutoMode {
+
+        public abstract void onActivated(boolean activated);
+
         public abstract void onStart();
 
         public abstract void onStop();
+
+        public void onCustomStartTimeChanged(LocalTime startTime) {
+        }
+
+        public void onCustomEndTimeChanged(LocalTime endTime) {
+        }
     }
 
-    private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
+    private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
+            AlarmManager.OnAlarmListener {
 
         private final AlarmManager mAlarmManager;
         private final BroadcastReceiver mTimeChangedReceiver;
@@ -456,7 +553,7 @@
 
         private LocalDateTime mLastActivatedTime;
 
-        CustomAutoMode() {
+        CustomNightDisplayAutoMode() {
             mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
             mTimeChangedReceiver = new BroadcastReceiver() {
                 @Override
@@ -476,15 +573,15 @@
                 // Maintain the existing activated state if within the current period.
                 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
                         && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
-                    activate = mController.isActivated();
+                    activate = mNightDisplayController.isActivated();
                 }
             }
 
-            if (mIsActivated == null || mIsActivated != activate) {
-                mController.setActivated(activate);
+            if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+                mNightDisplayController.setActivated(activate);
             }
 
-            updateNextAlarm(mIsActivated, now);
+            updateNextAlarm(mIsNightDisplayActivated, now);
         }
 
         private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
@@ -502,10 +599,10 @@
             intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
             getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
 
-            mStartTime = mController.getCustomStartTime();
-            mEndTime = mController.getCustomEndTime();
+            mStartTime = mNightDisplayController.getCustomStartTime();
+            mEndTime = mNightDisplayController.getCustomEndTime();
 
-            mLastActivatedTime = mController.getLastActivatedTime();
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
 
             // Force an update to initialize state.
             updateActivated();
@@ -521,7 +618,7 @@
 
         @Override
         public void onActivated(boolean activated) {
-            mLastActivatedTime = mController.getLastActivatedTime();
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
             updateNextAlarm(activated, LocalDateTime.now());
         }
 
@@ -546,11 +643,13 @@
         }
     }
 
-    private class TwilightAutoMode extends AutoMode implements TwilightListener {
+    private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
+            TwilightListener {
 
         private final TwilightManager mTwilightManager;
+        private LocalDateTime mLastActivatedTime;
 
-        TwilightAutoMode() {
+        TwilightNightDisplayAutoMode() {
             mTwilightManager = getLocalService(TwilightManager.class);
         }
 
@@ -562,26 +661,31 @@
             }
 
             boolean activate = state.isNight();
-            final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
-            if (lastActivatedTime != null) {
+            if (mLastActivatedTime != null) {
                 final LocalDateTime now = LocalDateTime.now();
                 final LocalDateTime sunrise = state.sunrise();
                 final LocalDateTime sunset = state.sunset();
                 // Maintain the existing activated state if within the current period.
-                if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
-                        ^ lastActivatedTime.isBefore(sunset))) {
-                    activate = mController.isActivated();
+                if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
+                        ^ mLastActivatedTime.isBefore(sunset))) {
+                    activate = mNightDisplayController.isActivated();
                 }
             }
 
-            if (mIsActivated == null || mIsActivated != activate) {
-                mController.setActivated(activate);
+            if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
+                mNightDisplayController.setActivated(activate);
             }
         }
 
         @Override
+        public void onActivated(boolean activated) {
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
+        }
+
+        @Override
         public void onStart() {
             mTwilightManager.registerListener(this, mHandler);
+            mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
 
             // Force an update to initialize state.
             updateActivated(mTwilightManager.getLastTwilightState());
@@ -590,10 +694,7 @@
         @Override
         public void onStop() {
             mTwilightManager.unregisterListener(this);
-        }
-
-        @Override
-        public void onActivated(boolean activated) {
+            mLastActivatedTime = null;
         }
 
         @Override
@@ -624,6 +725,7 @@
     }
 
     private final class BinderService extends IColorDisplayManager.Stub {
+
         @Override
         public boolean isDeviceColorManaged() {
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a51b018..fb6eaa0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -73,6 +73,7 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
@@ -4300,19 +4301,10 @@
                     ? new File(Environment.getDataDirectory(), SYSTEM_PATH)
                     : Environment.getUserSystemDirectory(userId);
             final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
-            if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
-                Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath());
-            }
             final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
             mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile, "input-subtypes");
-            if (!subtypeFile.exists()) {
-                // If "subtypes.xml" doesn't exist, create a blank file.
-                writeAdditionalInputMethodSubtypes(
-                        mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
-            } else {
-                readAdditionalInputMethodSubtypes(
-                        mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
-            }
+            readAdditionalInputMethodSubtypes(mAdditionalSubtypesMap,
+                    mAdditionalInputMethodSubtypeFile);
         }
 
         private void deleteAllInputMethodSubtypes(String imiId) {
@@ -4352,6 +4344,25 @@
         private static void writeAdditionalInputMethodSubtypes(
                 ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
                 ArrayMap<String, InputMethodInfo> methodMap) {
+            if (allSubtypes.isEmpty()) {
+                if (subtypesFile.exists()) {
+                    subtypesFile.delete();
+                }
+                final File parentDir = subtypesFile.getBaseFile().getParentFile();
+                if (parentDir != null && FileUtils.listFilesOrEmpty(parentDir).length == 0) {
+                    if (!parentDir.delete()) {
+                        Slog.e(TAG, "Failed to delete the empty parent directory " + parentDir);
+                    }
+                }
+                return;
+            }
+
+            final File parentDir = subtypesFile.getBaseFile().getParentFile();
+            if (!parentDir.exists() && !parentDir.mkdirs()) {
+                Slog.e(TAG, "Failed to create a parent directory " + parentDir);
+                return;
+            }
+
             // Safety net for the case that this function is called before methodMap is set.
             final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
             FileOutputStream fos = null;
@@ -4408,6 +4419,10 @@
                 ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
             if (allSubtypes == null || subtypesFile == null) return;
             allSubtypes.clear();
+            if (!subtypesFile.exists()) {
+                // Not having the file means there is no additional subtype.
+                return;
+            }
             try (final FileInputStream fis = subtypesFile.openRead()) {
                 final XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(fis, StandardCharsets.UTF_8.name());
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 29e1878..0f060fe 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -642,6 +642,17 @@
                 Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
             }
         }
+
+        String emergencyExtensionSecondsString
+                = properties.getProperty("ES_EXTENSION_SEC", "0");
+        try {
+            int emergencyExtensionSeconds =
+                    Integer.parseInt(emergencyExtensionSecondsString);
+            mNIHandler.setEmergencyExtensionSeconds(emergencyExtensionSeconds);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "unable to parse ES_EXTENSION_SEC: "
+                    + emergencyExtensionSecondsString);
+        }
     }
 
     private void loadPropertiesFromResource(Context context,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 47d1bb9..d961bad 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4058,7 +4058,7 @@
                 final ArraySet<ComponentName> listeners =
                     mListenersDisablingEffects.valueAt(i);
                 for (int j = 0; j < listeners.size(); j++) {
-                    final ComponentName componentName = listeners.valueAt(i);
+                    final ComponentName componentName = listeners.valueAt(j);
                     componentName.writeToProto(proto,
                             ListenersDisablingEffectsProto.LISTENER_COMPONENTS);
                 }
@@ -4203,8 +4203,8 @@
                     final int listenerSize = listeners.size();
 
                     for (int j = 0; j < listenerSize; j++) {
-                        if (i > 0) pw.print(',');
-                        final ComponentName listener = listeners.valueAt(i);
+                        if (j > 0) pw.print(',');
+                        final ComponentName listener = listeners.valueAt(j);
                         if (listener != null) {
                             pw.print(listener);
                         }
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 7ae2271..3a7919a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -120,7 +120,7 @@
                         android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
                         -1, true);
                 if (permissionFlag != PackageManager.PERMISSION_GRANTED
-                        || !mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)) {
+                        || !isSameProfileGroup(callerUserId, userId)) {
                     throw new SecurityException("Attempt to launch activity without required "
                             + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
                             + " or target user is not in the same profile group.");
@@ -209,6 +209,15 @@
         }
     }
 
+    private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+        final long ident = mInjector.clearCallingIdentity();
+        try {
+            return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId);
+        } finally {
+            mInjector.restoreCallingIdentity(ident);
+        }
+    }
+
     /**
      * Verify that the given calling package is belong to the calling UID.
      */
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 28fb01d..6453db5d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -888,7 +888,7 @@
     volatile boolean mSystemReady;
     volatile boolean mSafeMode;
     volatile boolean mHasSystemUidErrors;
-    private volatile boolean mWebInstantAppsDisabled;
+    private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray();
 
     ApplicationInfo mAndroidApplication;
     final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -5904,8 +5904,8 @@
     /**
      * Returns whether or not instant apps have been disabled remotely.
      */
-    private boolean areWebInstantAppsDisabled() {
-        return mWebInstantAppsDisabled;
+    private boolean areWebInstantAppsDisabled(int userId) {
+        return mWebInstantAppsDisabled.get(userId);
     }
 
     private boolean isInstantAppResolutionAllowed(
@@ -5936,7 +5936,7 @@
         } else {
             if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
                 return false;
-            } else if (areWebInstantAppsDisabled()) {
+            } else if (areWebInstantAppsDisabled(userId)) {
                 return false;
             }
         }
@@ -6816,7 +6816,7 @@
     private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
             String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
             boolean resolveForStart, int userId, Intent intent) {
-        final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled();
+        final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
         for (int i = resolveInfos.size() - 1; i >= 0; i--) {
             final ResolveInfo info = resolveInfos.get(i);
             // remove locally resolved instant app web results when disabled
@@ -20124,16 +20124,21 @@
         ContentObserver co = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
-                mWebInstantAppsDisabled =
-                        (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
-                                (Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
+                boolean ephemeralFeatureDisabled =
+                        Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
+                for (int userId : UserManagerService.getInstance().getUserIds()) {
+                    boolean instantAppsDisabledForUser =
+                            ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
+                                    Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
+                    mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
+                }
             }
         };
         mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
                         .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
-                false, co, UserHandle.USER_SYSTEM);
+                false, co, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
-                        .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
+                        .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
         co.onChange(true);
 
         // Disable any carrier apps. We do this very early in boot to prevent the apps from being
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index fff42c5..8dda485 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -2,6 +2,6 @@
 jjaggi@google.com
 racarr@google.com
 chaviw@google.com
-brycelee@google.com
+vishnun@google.com
 akulian@google.com
 roosa@google.com
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
index 52f434d..edd89f9 100644
--- a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
@@ -52,8 +52,8 @@
         // Try to start watching noted ops
         final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
         try {
-            appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
-                    AppOpsManager.OPSTR_RECORD_AUDIO}, listener);
+            appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
+                    AppOpsManager.OP_RECORD_AUDIO}, listener);
             fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS);
         } catch (SecurityException expected) {
             /*ignored*/
@@ -67,23 +67,23 @@
 
         // Start watching noted ops
         final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
-        appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
-                AppOpsManager.OPSTR_CAMERA}, listener);
+        appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
+                AppOpsManager.OP_CAMERA}, listener);
 
         // Note some ops
-        appOpsManager.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, Process.myUid(),
+        appOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION, Process.myUid(),
                 getContext().getPackageName());
-        appOpsManager.noteOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(),
+        appOpsManager.noteOp(AppOpsManager.OP_CAMERA, Process.myUid(),
                 getContext().getPackageName());
 
         // Verify that we got called for the ops being noted
         final InOrder inOrder = inOrder(listener);
         inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION),
+                .times(1)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(AppOpsManager.MODE_ALLOWED));
         inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_CAMERA),
+                .times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(AppOpsManager.MODE_ALLOWED));
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 522ab9f..04e433e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -164,6 +164,7 @@
             }
 
             private void tearDown() {
+                cancelAllPendingAnimations();
                 waitUntilWindowManagerHandlersIdle();
                 destroyAllSurfaceTransactions();
                 destroyAllSurfaceControls();
@@ -178,6 +179,15 @@
         return mService;
     }
 
+    private void cancelAllPendingAnimations() {
+        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
+            final SurfaceControl sc = reference.get();
+            if (sc != null) {
+                mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
+            }
+        }
+    }
+
     void waitUntilWindowManagerHandlersIdle() {
         final WindowManagerService wm = getWindowManagerService();
         if (wm == null) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b61e99b..2c712a1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2617,8 +2617,7 @@
         if (availableList == null) {
             return null;
         } else {
-            return getAvailableSubscriptionInfoList().stream()
-                    .filter(subInfo -> !shouldHideSubscription(subInfo))
+            return availableList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
                     .collect(Collectors.toList());
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 553e3fb..0edc002 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -284,8 +284,6 @@
      */
     private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
             int uid, String callingPackage, String message) {
-        Log.wtf(LOG_TAG,
-                "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
         // If the device identifier check is enabled then enforce the new access requirements for
         // both 1P and 3P apps.
         boolean enableDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
@@ -295,17 +293,40 @@
         boolean relax3PDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
                 Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED, 0) == 1;
         boolean is3PApp = true;
+        // Also check if the application is a preloaded non-privileged app; if so there is a
+        // separate setting to relax the check for these apps to ensure users can relax the check
+        // for 3P or non-priv apps as needed while continuing to test the other.
+        boolean relaxNonPrivDeviceIdentifierCheck = Settings.Global.getInt(
+                context.getContentResolver(),
+                Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED, 0) == 1;
+        boolean isNonPrivApp = false;
         ApplicationInfo callingPackageInfo = null;
         try {
             callingPackageInfo = context.getPackageManager().getApplicationInfo(callingPackage, 0);
-            if (callingPackageInfo.isSystemApp()) {
+            if (callingPackageInfo.isPrivilegedApp()) {
                 is3PApp = false;
+            } else if (callingPackageInfo.isSystemApp()) {
+                is3PApp = false;
+                isNonPrivApp = true;
             }
         } catch (PackageManager.NameNotFoundException e) {
             // If the application info for the calling package could not be found then assume the
             // calling app is a 3P app to detect any issues with the check
+            Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
+                    e);
         }
-        if (enableDeviceIdentifierCheck || (is3PApp && !relax3PDeviceIdentifierCheck)) {
+        Log.wtf(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+                + ":is3PApp=" + is3PApp + ":isNonPrivApp=" + isNonPrivApp);
+        // The new Q restrictions for device identifier access will be enforced if any of the
+        // following are true:
+        // - The PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED setting has been set.
+        // - The app requesting a device identifier is not a preloaded app (3P), and the
+        //   PRIVILEGED_DEVICE_IDENTIFIER_3P_CHECK_RELAXED setting has not been set.
+        // - The app requesting a device identifier is a preloaded app but is not a privileged app,
+        //   and the PRIVILEGED_DEVICE_IDENTIFIER_NON_PRIV_CHECK_RELAXED setting has not been set.
+        if (enableDeviceIdentifierCheck
+                || (is3PApp && !relax3PDeviceIdentifierCheck)
+                || (isNonPrivApp && !relaxNonPrivDeviceIdentifierCheck)) {
             boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
                     Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
             if (callingPackage != null) {
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index b5a990e..6476abd 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -209,24 +209,42 @@
         return self.raw
 
 
-def _parse_stream(f, clazz_cb=None, base_f=None):
+def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None,
+                  in_classes_with_base=[]):
     api = {}
+    in_classes_with_base = _retry_iterator(in_classes_with_base)
 
     if base_f:
-        base_classes = _parse_stream_to_generator(base_f)
+        base_classes = _retry_iterator(_parse_stream_to_generator(base_f))
     else:
         base_classes = []
 
-    for clazz in _parse_stream_to_generator(f):
-        base_class = _parse_to_matching_class(base_classes, clazz)
-        if base_class:
-            clazz.merge_from(base_class)
-
+    def handle_class(clazz):
         if clazz_cb:
             clazz_cb(clazz)
         else: # In callback mode, don't keep track of the full API
             api[clazz.fullname] = clazz
 
+    def handle_missed_classes_with_base(clazz):
+        for c in _yield_until_matching_class(in_classes_with_base, clazz):
+            base_class = _skip_to_matching_class(base_classes, c)
+            if base_class:
+                handle_class(base_class)
+
+    for clazz in _parse_stream_to_generator(f):
+        # Before looking at clazz, let's see if there's some classes that were not present, but
+        # may have an entry in the base stream.
+        handle_missed_classes_with_base(clazz)
+
+        base_class = _skip_to_matching_class(base_classes, clazz)
+        if base_class:
+            clazz.merge_from(base_class)
+            if out_classes_with_base is not None:
+                out_classes_with_base.append(clazz)
+        handle_class(clazz)
+
+    handle_missed_classes_with_base(None)
+
     return api
 
 def _parse_stream_to_generator(f):
@@ -257,18 +275,22 @@
         elif raw.startswith("    field"):
             clazz.fields.append(Field(clazz, line, raw, blame))
         elif raw.startswith("  }") and clazz:
-            while True:
-                retry = yield clazz
-                if not retry:
-                    break
-                # send() was called, asking us to redeliver clazz on next(). Still need to yield
-                # a dummy value to the send() first though.
-                if (yield "Returning clazz on next()"):
-                        raise TypeError("send() must be followed by next(), not send()")
+            yield clazz
 
+def _retry_iterator(it):
+    """Wraps an iterator, such that calling send(True) on it will redeliver the same element"""
+    for e in it:
+        while True:
+            retry = yield e
+            if not retry:
+                break
+            # send() was called, asking us to redeliver clazz on next(). Still need to yield
+            # a dummy value to the send() first though.
+            if (yield "Returning clazz on next()"):
+                raise TypeError("send() must be followed by next(), not send()")
 
-def _parse_to_matching_class(classes, needle):
-    """Takes a classes generator and parses it until it returns the class we're looking for
+def _skip_to_matching_class(classes, needle):
+    """Takes a classes iterator and consumes entries until it returns the class we're looking for
 
     This relies on classes being sorted by package and class name."""
 
@@ -276,8 +298,8 @@
         if clazz.pkg.name < needle.pkg.name:
             # We haven't reached the right package yet
             continue
-        if clazz.name < needle.name:
-            # We haven't reached the right class yet
+        if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+            # We're in the right package, but not the right class yet
             continue
         if clazz.fullname == needle.fullname:
             return clazz
@@ -285,6 +307,28 @@
         classes.send(clazz)
         return None
 
+def _yield_until_matching_class(classes, needle):
+    """Takes a class iterator and yields entries it until it reaches the class we're looking for.
+
+    This relies on classes being sorted by package and class name."""
+
+    for clazz in classes:
+        if needle is None:
+            yield clazz
+        elif clazz.pkg.name < needle.pkg.name:
+            # We haven't reached the right package yet
+            yield clazz
+        elif clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+            # We're in the right package, but not the right class yet
+            yield clazz
+        elif clazz.fullname == needle.fullname:
+            # Class found, abort.
+            return
+        else:
+            # We ran past the right class. Send it back into the iterator, then abort.
+            classes.send(clazz)
+            return
+
 class Failure():
     def __init__(self, sig, clazz, detail, error, rule, msg):
         self.sig = sig
@@ -1543,12 +1587,14 @@
     verify_singleton(clazz)
 
 
-def examine_stream(stream, base_stream=None):
+def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None):
     """Find all style issues in the given API stream."""
     global failures, noticed
     failures = {}
     noticed = {}
-    _parse_stream(stream, examine_clazz, base_f=base_stream)
+    _parse_stream(stream, examine_clazz, base_f=base_stream,
+                  in_classes_with_base=in_classes_with_base,
+                  out_classes_with_base=out_classes_with_base)
     return (failures, noticed)
 
 
@@ -1734,19 +1780,24 @@
         show_stats(cur, prev)
         sys.exit()
 
+    classes_with_base = []
+
     with current_file as f:
         if base_current_file:
             with base_current_file as base_f:
-                cur_fail, cur_noticed = examine_stream(f, base_f)
+                cur_fail, cur_noticed = examine_stream(f, base_f,
+                                                       out_classes_with_base=classes_with_base)
         else:
-            cur_fail, cur_noticed = examine_stream(f)
+            cur_fail, cur_noticed = examine_stream(f, out_classes_with_base=classes_with_base)
+
     if not previous_file is None:
         with previous_file as f:
             if base_previous_file:
                 with base_previous_file as base_f:
-                    prev_fail, prev_noticed = examine_stream(f, base_f)
+                    prev_fail, prev_noticed = examine_stream(f, base_f,
+                                                             in_classes_with_base=classes_with_base)
             else:
-                prev_fail, prev_noticed = examine_stream(f)
+                prev_fail, prev_noticed = examine_stream(f, in_classes_with_base=classes_with_base)
 
         # ignore errors from previous API level
         for p in prev_fail:
diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh
new file mode 100755
index 0000000..8538a3d
--- /dev/null
+++ b/tools/apilint/apilint_sha_system.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# 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.
+
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+  python tools/apilint/apilint.py \
+    --base-current <(git show $1:api/current.txt) \
+    --base-previous <(git show $1^:api/current.txt) \
+    <(git show $1:api/system-current.txt) \
+    <(git show $1^:api/system-current.txt)
+fi
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
new file mode 100644
index 0000000..ece69a9
--- /dev/null
+++ b/tools/apilint/apilint_test.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+# 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.
+
+import unittest
+
+import apilint
+
+def cls(pkg, name):
+    return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999,
+                  "public final class %s {" % name, None)
+
+_ri = apilint._retry_iterator
+
+c1 = cls("android.app", "ActivityManager")
+c2 = cls("android.app", "Notification")
+c3 = cls("android.app", "Notification.Action")
+c4 = cls("android.graphics", "Bitmap")
+
+class UtilTests(unittest.TestCase):
+    def test_retry_iterator(self):
+        it = apilint._retry_iterator([1, 2, 3, 4])
+        self.assertEqual(it.next(), 1)
+        self.assertEqual(it.next(), 2)
+        self.assertEqual(it.next(), 3)
+        it.send("retry")
+        self.assertEqual(it.next(), 3)
+        self.assertEqual(it.next(), 4)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_retry_iterator_one(self):
+        it = apilint._retry_iterator([1])
+        self.assertEqual(it.next(), 1)
+        it.send("retry")
+        self.assertEqual(it.next(), 1)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_retry_iterator_one(self):
+        it = apilint._retry_iterator([1])
+        self.assertEqual(it.next(), 1)
+        it.send("retry")
+        self.assertEqual(it.next(), 1)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_skip_to_matching_class_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(apilint._skip_to_matching_class(it, c3),
+                          c3)
+        self.assertEqual(it.next(), c4)
+
+    def test_skip_to_matching_class_not_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(apilint._skip_to_matching_class(it, cls("android.content", "ContentProvider")),
+                          None)
+        self.assertEqual(it.next(), c4)
+
+    def test_yield_until_matching_class_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(list(apilint._yield_until_matching_class(it, c3)),
+                          [c1, c2])
+        self.assertEqual(it.next(), c4)
+
+    def test_yield_until_matching_class_not_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(list(apilint._yield_until_matching_class(it, cls("android.content", "ContentProvider"))),
+                          [c1, c2, c3])
+        self.assertEqual(it.next(), c4)
+
+    def test_yield_until_matching_class_None(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(list(apilint._yield_until_matching_class(it, None)),
+                          [c1, c2, c3, c4])
+
+
+faulty_current_txt = """
+package android.app {
+  public final class Activity {
+  }
+
+  public final class WallpaperColors implements android.os.Parcelable {
+    ctor public WallpaperColors(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+  }
+}
+""".split('\n')
+
+ok_current_txt = """
+package android.app {
+  public final class Activity {
+  }
+
+  public final class WallpaperColors implements android.os.Parcelable {
+    ctor public WallpaperColors();
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+  }
+}
+""".split('\n')
+
+system_current_txt = """
+package android.app {
+  public final class WallpaperColors implements android.os.Parcelable {
+    method public int getSomething();
+  }
+}
+""".split('\n')
+
+
+
+class BaseFileTests(unittest.TestCase):
+    def test_base_file_avoids_errors(self):
+        failures, _ = apilint.examine_stream(system_current_txt, ok_current_txt)
+        self.assertEquals(failures, {})
+
+    def test_class_with_base_finds_same_errors(self):
+        failures_with_classes_with_base, _ = apilint.examine_stream("", faulty_current_txt,
+                                                                    in_classes_with_base=[cls("android.app", "WallpaperColors")])
+        failures_with_system_txt, _ = apilint.examine_stream(system_current_txt, faulty_current_txt)
+
+        self.assertEquals(failures_with_classes_with_base.keys(), failures_with_system_txt.keys())
+
+    def test_classes_with_base_is_emited(self):
+        classes_with_base = []
+        _, _ = apilint.examine_stream(system_current_txt, faulty_current_txt,
+                                      out_classes_with_base=classes_with_base)
+        self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
+
+if __name__ == "__main__":
+    unittest.main()
\ No newline at end of file
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index faa3547..f6c9c0e 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -453,7 +453,7 @@
     map<string, bool> variableNames;
     set<string> parents;
     vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
-    bool skip[fieldsInOrder.size()];
+    vector<bool> skip(fieldsInOrder.size());
     const Destination incidentDest = getPrivacyFlags(descriptor).dest();
 
     for (size_t i=0; i<fieldsInOrder.size(); i++) {