Merge "AudioEffect: Fix memory leak in JNI"
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index f5e2a09..ac126ae 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -275,7 +275,9 @@
     }
 
     /**
-     * Stops an ongoing Bluetooth LE scan started using a PendingIntent.
+     * Stops an ongoing Bluetooth LE scan started using a PendingIntent. When creating the
+     * PendingIntent parameter, please do not use the FLAG_CANCEL_CURRENT flag. Otherwise, the stop
+     * scan may have no effect.
      *
      * @param callbackIntent The PendingIntent that was used to start the scan.
      * @see #startScan(List, ScanSettings, PendingIntent)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a6b81b5..8c2b76f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2250,6 +2250,7 @@
      * <p>Includes the following extras:
      * <ul>
      * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been suspended
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been suspended
      * </ul>
      *
      * <p class="note">This is a protected intent that can only be sent
@@ -2262,6 +2263,7 @@
      * <p>Includes the following extras:
      * <ul>
      * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been unsuspended
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been unsuspended
      * </ul>
      *
      * <p class="note">This is a protected intent that can only be sent
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 76d5328..9e23de1 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -52,7 +52,7 @@
     /**
      * Notification has not been dismissed yet.
      */
-    public static final int DISMISSAL_NOT_DISMISSED = -1;
+    public static final int DISMISSAL_NOT_DISMISSED = -1000;
     /**
      * Notification has been dismissed from a {@link NotificationListenerService} or the app
      * itself.
@@ -71,6 +71,37 @@
      */
     public static final int DISMISSAL_SHADE = 3;
 
+    /** @hide */
+    @IntDef(prefix = { "DISMISS_SENTIMENT_" }, value = {
+            DISMISS_SENTIMENT_UNKNOWN, DISMISS_SENTIMENT_NEGATIVE, DISMISS_SENTIMENT_NEUTRAL,
+            DISMISS_SENTIMENT_POSITIVE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DismissalSentiment {}
+
+    /**
+     * No information is available about why this notification was dismissed, or the notification
+     * isn't dismissed yet.
+     */
+    public static final int DISMISS_SENTIMENT_UNKNOWN = -1000;
+    /**
+     * The user indicated while dismissing that they did not like the notification.
+     */
+    public static final int DISMISS_SENTIMENT_NEGATIVE = 0;
+    /**
+     * The user didn't indicate one way or another how they felt about the notification while
+     * dismissing it.
+     */
+    public static final int DISMISS_SENTIMENT_NEUTRAL = 1;
+    /**
+     * The user indicated while dismissing that they did like the notification.
+     */
+    public static final int DISMISS_SENTIMENT_POSITIVE = 2;
+
+
+    private @DismissalSentiment
+    int mDismissalSentiment = DISMISS_SENTIMENT_UNKNOWN;
+
     public NotificationStats() {
     }
 
@@ -82,6 +113,7 @@
         mViewedSettings = in.readByte() != 0;
         mInteracted = in.readByte() != 0;
         mDismissalSurface = in.readInt();
+        mDismissalSentiment = in.readInt();
     }
 
     @Override
@@ -93,6 +125,7 @@
         dest.writeByte((byte) (mViewedSettings ? 1 : 0));
         dest.writeByte((byte) (mInteracted ? 1 : 0));
         dest.writeInt(mDismissalSurface);
+        dest.writeInt(mDismissalSentiment);
     }
 
     @Override
@@ -212,6 +245,21 @@
         mDismissalSurface = dismissalSurface;
     }
 
+    /**
+     * Records whether the user indicated how they felt about a notification before or
+     * during dismissal.
+     */
+    public void setDismissalSentiment(@DismissalSentiment int dismissalSentiment) {
+        mDismissalSentiment = dismissalSentiment;
+    }
+
+    /**
+     * Returns how the user indicated they felt about a notification before or during dismissal.
+     */
+    public @DismissalSentiment int getDismissalSentiment() {
+        return mDismissalSentiment;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 0f8ca44..c037cd0 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -40,7 +40,6 @@
     static {
         DEFAULT_FLAGS = new HashMap<>();
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
-        DEFAULT_FLAGS.put("settings_condition_manager_v2", "true");
         DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 496bc9f..5f80d31 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -325,6 +325,7 @@
      *
      * @hide
      */
+    @VisibleForTesting
     public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
         Region r = Region.obtain();
         r.set(left, top, right, bottom);
@@ -422,8 +423,11 @@
         m.postTranslate(offsetX, 0);
         p.transform(m);
 
-        addToRegion(p, r);
+        final Rect tmpRect = new Rect();
+        toRectAndAddToRegion(p, r, tmpRect);
+        final int topInset = tmpRect.bottom;
 
+        final int bottomInset;
         if (bottomSpec != null) {
             final Path bottomPath;
             try {
@@ -436,10 +440,17 @@
             m.postTranslate(0, displayHeight);
             bottomPath.transform(m);
             p.addPath(bottomPath);
-            addToRegion(bottomPath, r);
+            toRectAndAddToRegion(bottomPath, r, tmpRect);
+            bottomInset = displayHeight - tmpRect.top;
+        } else {
+            bottomInset = 0;
         }
 
-        final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(r));
+        // Reuse tmpRect as the inset rect we store into the DisplayCutout instance.
+        tmpRect.set(0, topInset, 0, bottomInset);
+        final DisplayCutout cutout = new DisplayCutout(tmpRect, r, false /* copyArguments */);
+
+        final Pair<Path, DisplayCutout> result = new Pair<>(p, cutout);
         synchronized (CACHE_LOCK) {
             sCachedSpec = spec;
             sCachedDisplayWidth = displayWidth;
@@ -450,12 +461,11 @@
         return result;
     }
 
-    private static void addToRegion(Path p, Region r) {
+    private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
         final RectF rectF = new RectF();
-        final Rect rect = new Rect();
         p.computeBounds(rectF, false /* unused */);
-        rectF.round(rect);
-        r.op(rect, Op.UNION);
+        rectF.round(inoutRect);
+        inoutRegion.op(inoutRect, Op.UNION);
     }
 
     private static Region boundingRectsToRegion(List<Rect> rects) {
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index a7755a2..4b946d7 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -40,7 +40,7 @@
     // view hierarchy because display lists are generated recursively.
     private static final int POOL_LIMIT = 25;
 
-    private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
+    public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
 
     private static final SynchronizedPool<DisplayListCanvas> sPool =
             new SynchronizedPool<>(POOL_LIMIT);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index a79e15a..b3af147 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -60,7 +60,7 @@
             int uid, int initialPid, String message, int userId);
     void onClearAllNotifications(int userId);
     void onNotificationClear(String pkg, String tag, int id, int userId, String key,
-            int dismissalSurface, in NotificationVisibility nv);
+            int dismissalSurface, int dismissalSentiment, in NotificationVisibility nv);
     void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys,
             in NotificationVisibility[] noLongerVisibleKeys);
     void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b15c9c5..c6931aa 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2988,6 +2988,10 @@
          -->
     <bool name="config_fillMainBuiltInDisplayCutout">false</bool>
 
+    <!-- If true, and there is a cutout on the main built in display, the cutout will be masked
+         by shrinking the display such that it does not overlap the cutout area. -->
+    <bool name="config_maskMainBuiltInDisplayCutout">false</bool>
+
     <!-- Ultrasound support for Mic/speaker path -->
     <!-- Whether the default microphone audio source supports near-ultrasound frequencies
          (range of 18 - 21 kHz). -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 471170b..73cb59e 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -61,6 +61,15 @@
     <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
     <dimen name="status_bar_edge_ignore">5dp</dimen>
 
+    <!-- Default radius of the software rounded corners. -->
+    <dimen name="rounded_corner_radius">0dp</dimen>
+    <!-- Radius of the software rounded corners at the top of the display in its natural
+        orientation. If zero, the value of rounded_corner_radius is used. -->
+    <dimen name="rounded_corner_radius_top">0dp</dimen>
+    <!-- Radius of the software rounded corners at the bottom of the display in its natural
+        orientation. If zero, the value of rounded_corner_radius is used. -->
+    <dimen name="rounded_corner_radius_bottom">0dp</dimen>
+
     <!-- Width of the window of the divider bar used to resize docked stacks. -->
     <dimen name="docked_stack_divider_thickness">48dp</dimen>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2271baa..7fbad16 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3442,6 +3442,8 @@
   <java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
   <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
 
+  <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
+
   <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
   <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
 </resources>
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 6ee74cb..fe45fe7 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -19,6 +19,7 @@
 import static android.view.DisplayCutout.NO_CUTOUT;
 import static android.view.DisplayCutout.fromSpec;
 
+import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertEquals;
@@ -220,6 +221,19 @@
     }
 
     @Test
+    public void fromSpec_setsSafeInsets_top() {
+        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f);
+        assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 0)));
+    }
+
+    @Test
+    public void fromSpec_setsSafeInsets_top_and_bottom() {
+        DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
+                + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f);
+        assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
+    }
+
+    @Test
     public void parcel_unparcel_nocutout() {
         Parcel p = Parcel.obtain();
 
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index f0f31fb..3333e15 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -316,7 +316,7 @@
                 saveFile();
             }
         } catch (Throwable e) {
-            Slog.e(TAG, "Error occurred processing removal", e);
+            Slog.e(TAG, "Error occurred processing removal of " + sbn, e);
         }
     }
 
@@ -327,17 +327,21 @@
 
     @Override
     public void onNotificationsSeen(List<String> keys) {
-        if (keys == null) {
-            return;
-        }
-
-        for (String key : keys) {
-            NotificationEntry entry = mLiveNotifications.get(key);
-
-            if (entry != null) {
-                entry.setSeen();
-                mAgingHelper.onNotificationSeen(entry);
+        try {
+            if (keys == null) {
+                return;
             }
+
+            for (String key : keys) {
+                NotificationEntry entry = mLiveNotifications.get(key);
+
+                if (entry != null) {
+                    entry.setSeen();
+                    mAgingHelper.onNotificationSeen(entry);
+                }
+            }
+        } catch (Throwable e) {
+            Slog.e(TAG, "Error occurred processing seen", e);
         }
     }
 
diff --git a/packages/ExtServices/tests/AndroidTest.xml b/packages/ExtServices/tests/AndroidTest.xml
new file mode 100644
index 0000000..c3d32de
--- /dev/null
+++ b/packages/ExtServices/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Tests for ExtServices">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="ExtServicesUnitTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="ExtServicesUnitTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.ext.services.tests.unit" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index ffd6791..af73158 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -205,12 +205,9 @@
 
     /**
      * Build a list of DashboardCategory.
-     *
-     * @param extraAction additional intent filter action to be usetileutild to build the dashboard
-     *                    categories
      */
     public static List<DashboardCategory> getCategories(Context context,
-            Map<Pair<String, String>, Tile> cache, String extraAction) {
+            Map<Pair<String, String>, Tile> cache) {
         final long startTime = System.currentTimeMillis();
         boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
                 != 0;
@@ -229,9 +226,6 @@
             if (setup) {
                 getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
                 getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false);
-                if (extraAction != null) {
-                    getTilesForAction(context, user, extraAction, cache, null, tiles, false);
-                }
             }
         }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 3eb273d..91db95c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -152,27 +152,6 @@
     }
 
     @Test
-    public void getCategories_shouldHandleExtraIntentAction() {
-        final String testCategory = "category1";
-        final String testAction = "action1";
-        Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
-        List<ResolveInfo> info = new ArrayList<>();
-        info.add(newInfo(true, testCategory));
-        Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
-        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
-        List<UserHandle> userHandleList = new ArrayList<>();
-        userHandleList.add(UserHandle.CURRENT);
-        when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
-
-        when(mPackageManager.queryIntentActivitiesAsUser(argThat(
-                event -> testAction.equals(event.getAction())), anyInt(), anyInt()))
-                .thenReturn(info);
-
-        List<DashboardCategory> categoryList = TileUtils.getCategories(mContext, cache, testAction);
-        assertThat(categoryList.get(0).getTile(0).getCategory()).isEqualTo(testCategory);
-    }
-
-    @Test
     public void getCategories_withPackageName() {
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
@@ -183,7 +162,7 @@
         userHandleList.add(new UserHandle(ActivityManager.getCurrentUser()));
         when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
 
-        TileUtils.getCategories(mContext, cache, null /* action */);
+        TileUtils.getCategories(mContext, cache);
         verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser(
                 intentCaptor.capture(), anyInt(), anyInt());
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ca6b2d9..412e89a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -945,9 +945,9 @@
     <dimen name="bottom_padding">48dp</dimen>
     <dimen name="edge_margin">8dp</dimen>
 
-    <dimen name="rounded_corner_radius">0dp</dimen>
-    <dimen name="rounded_corner_radius_top">0dp</dimen>
-    <dimen name="rounded_corner_radius_bottom">0dp</dimen>
+    <dimen name="rounded_corner_radius">@*android:dimen/rounded_corner_radius</dimen>
+    <dimen name="rounded_corner_radius_top">@*android:dimen/rounded_corner_radius_top</dimen>
+    <dimen name="rounded_corner_radius_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
     <dimen name="rounded_corner_content_padding">0dp</dimen>
     <dimen name="nav_content_padding">0dp</dimen>
     <dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 86e0e1f..e7fc3c9 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -55,7 +55,7 @@
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -258,8 +258,8 @@
         mProviders.put(TunerService.class, () ->
                 new TunerServiceImpl(mContext));
 
-        mProviders.put(StatusBarWindowManager.class, () ->
-                new StatusBarWindowManager(mContext));
+        mProviders.put(StatusBarWindowController.class, () ->
+                new StatusBarWindowController(mContext));
 
         mProviders.put(DarkIconDispatcher.class, () ->
                 new DarkIconDispatcherImpl(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 50d862d..39e3155 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -30,6 +30,7 @@
 import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.DisplayListCanvas;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.WindowManager;
@@ -366,7 +367,12 @@
                 protected Bitmap doInBackground(Void... params) {
                     Throwable exception;
                     try {
-                        return mWallpaperManager.getBitmap(true /* hardware */);
+                        Bitmap wallpaper = mWallpaperManager.getBitmap(true /* hardware */);
+                        if (wallpaper != null
+                                && wallpaper.getByteCount() > DisplayListCanvas.MAX_BITMAP_SIZE) {
+                            throw new RuntimeException("Wallpaper is too large to draw!");
+                        }
+                        return wallpaper;
                     } catch (RuntimeException | OutOfMemoryError e) {
                         exception = e;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 4e7c3ab..b96a604 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -36,7 +36,7 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.util.NotificationChannels;
 
 import java.util.HashMap;
@@ -210,9 +210,9 @@
                         if (mOverlays == null) mOverlays = new ArraySet<>();
                         if (plugin.holdStatusBarOpen()) {
                             mOverlays.add(plugin);
-                            Dependency.get(StatusBarWindowManager.class).setStateListener(b ->
+                            Dependency.get(StatusBarWindowController.class).setStateListener(b ->
                                     mOverlays.forEach(o -> o.setCollapseDesired(b)));
-                            Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
+                            Dependency.get(StatusBarWindowController.class).setForcePluginOpen(
                                     mOverlays.size() != 0);
 
                         }
@@ -221,7 +221,7 @@
                     @Override
                     public void onPluginDisconnected(OverlayPlugin plugin) {
                         mOverlays.remove(plugin);
-                        Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
+                        Dependency.get(StatusBarWindowController.class).setForcePluginOpen(
                                 mOverlays.size() != 0);
                     }
                 }, OverlayPlugin.class, true /* Allow multiple plugins */);
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 1393f8f..b655a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -414,7 +414,9 @@
             } else if (mListContainer.hasPulsingNotifications()) {
                 dismissalSurface = NotificationStats.DISMISSAL_AOD;
             }
-            mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface, nv);
+            int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+            mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface,
+                    dismissalSentiment, nv);
             removeNotification(n.getKey(), null);
 
         } catch (RemoteException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 094912b..67db68d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1307,6 +1307,7 @@
         }
         setDismissed(fromAccessibility);
         if (isClearable()) {
+            // TODO: track dismiss sentiment
             if (mOnDismissRunnable != null) {
                 mOnDismissRunnable.run();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 2087a16..b57a366 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -101,7 +101,7 @@
     private KeyguardUpdateMonitor mUpdateMonitor;
     private int mMode;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private StatusBarWindowManager mStatusBarWindowManager;
+    private StatusBarWindowController mStatusBarWindowController;
     private DozeScrimController mDozeScrimController;
     private KeyguardViewMediator mKeyguardViewMediator;
     private ScrimController mScrimController;
@@ -125,7 +125,7 @@
         mUpdateMonitor.registerCallback(this);
         Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
         Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
-        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
+        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
         mDozeScrimController = dozeScrimController;
         mKeyguardViewMediator = keyguardViewMediator;
         mScrimController = scrimController;
@@ -214,7 +214,7 @@
             // notifications would light up first, creating an unpleasant animation.
             // Defer changing the screen brightness by forcing doze brightness on our window
             // until the clock and the notifications are faded out.
-            mStatusBarWindowManager.setForceDozeBrightness(true);
+            mStatusBarWindowController.setForceDozeBrightness(true);
         }
         // During wake and unlock, we need to draw black before waking up to avoid abrupt
         // brightness changes due to display state transitions.
@@ -269,7 +269,7 @@
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
                     mUpdateMonitor.awakenFromDream();
                 }
-                mStatusBarWindowManager.setStatusBarFocusable(false);
+                mStatusBarWindowController.setStatusBarFocusable(false);
                 if (delayWakeUp) {
                     mHandler.postDelayed(wakeUp, 50);
                 } else {
@@ -384,7 +384,7 @@
         mHandler.postDelayed(new Runnable() {
             @Override
             public void run() {
-                mStatusBarWindowManager.setForceDozeBrightness(false);
+                mStatusBarWindowController.setForceDozeBrightness(false);
             }
         }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
     }
@@ -395,7 +395,7 @@
 
     private void resetMode() {
         mMode = MODE_NONE;
-        mStatusBarWindowManager.setForceDozeBrightness(false);
+        mStatusBarWindowController.setForceDozeBrightness(false);
         if (mStatusBar.getNavigationBarView() != null) {
             mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
         }
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 3701eaf..dcd794d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -370,7 +370,7 @@
     protected StatusBarWindowView mStatusBarWindow;
     protected PhoneStatusBarView mStatusBarView;
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
-    protected StatusBarWindowManager mStatusBarWindowManager;
+    protected StatusBarWindowController mStatusBarWindowController;
     protected UnlockMethodCache mUnlockMethodCache;
     private DozeServiceHost mDozeServiceHost = new DozeServiceHost();
     private boolean mWakeUpComingFromTouch;
@@ -515,7 +515,7 @@
             final boolean supportsAmbientMode = info != null &&
                     info.supportsAmbientMode();
 
-            mStatusBarWindowManager.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+            mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
             mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
         }
     };
@@ -957,8 +957,8 @@
                 scrimBehind, scrimInFront, mLockscreenWallpaper,
                 (state, alpha, color) -> mLightBarController.setScrimState(state, alpha, color),
                 scrimsVisible -> {
-                    if (mStatusBarWindowManager != null) {
-                        mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
+                    if (mStatusBarWindowController != null) {
+                        mStatusBarWindowController.setScrimsVisibility(scrimsVisible);
                     }
                 }, DozeParameters.getInstance(mContext),
                 mContext.getSystemService(AlarmManager.class));
@@ -1702,7 +1702,7 @@
                     mBackdrop.animate().cancel();
                     mBackdrop.setAlpha(1f);
                 }
-                mStatusBarWindowManager.setBackdropShowing(true);
+                mStatusBarWindowController.setBackdropShowing(true);
                 metaDataChanged = true;
                 if (DEBUG_MEDIA) {
                     Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
@@ -1762,9 +1762,9 @@
                     // We are unlocking directly - no animation!
                     mBackdrop.setVisibility(View.GONE);
                     mBackdropBack.setImageDrawable(null);
-                    mStatusBarWindowManager.setBackdropShowing(false);
+                    mStatusBarWindowController.setBackdropShowing(false);
                 } else {
-                    mStatusBarWindowManager.setBackdropShowing(false);
+                    mStatusBarWindowController.setBackdropShowing(false);
                     mBackdrop.animate()
                             .alpha(SRC_MIN_ALPHA)
                             .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
@@ -1918,7 +1918,7 @@
     }
 
     public void setQsExpanded(boolean expanded) {
-        mStatusBarWindowManager.setQsExpanded(expanded);
+        mStatusBarWindowController.setQsExpanded(expanded);
         mNotificationPanel.setStatusAccessibilityImportance(expanded
                 ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
@@ -2001,31 +2001,31 @@
     @Override
     public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
         if (inPinnedMode) {
-            mStatusBarWindowManager.setHeadsUpShowing(true);
-            mStatusBarWindowManager.setForceStatusBarVisible(true);
+            mStatusBarWindowController.setHeadsUpShowing(true);
+            mStatusBarWindowController.setForceStatusBarVisible(true);
             if (mNotificationPanel.isFullyCollapsed()) {
                 // We need to ensure that the touchable region is updated before the window will be
                 // resized, in order to not catch any touches. A layout will ensure that
                 // onComputeInternalInsets will be called and after that we can resize the layout. Let's
                 // make sure that the window stays small for one frame until the touchableRegion is set.
                 mNotificationPanel.requestLayout();
-                mStatusBarWindowManager.setForceWindowCollapsed(true);
+                mStatusBarWindowController.setForceWindowCollapsed(true);
                 mNotificationPanel.post(() -> {
-                    mStatusBarWindowManager.setForceWindowCollapsed(false);
+                    mStatusBarWindowController.setForceWindowCollapsed(false);
                 });
             }
         } else {
             if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) {
                 // We are currently tracking or is open and the shade doesn't need to be kept
                 // open artificially.
-                mStatusBarWindowManager.setHeadsUpShowing(false);
+                mStatusBarWindowController.setHeadsUpShowing(false);
             } else {
                 // we need to keep the panel open artificially, let's wait until the animation
                 // is finished.
                 mHeadsUpManager.setHeadsUpGoingAway(true);
                 mStackScroller.runAfterAnimationFinished(() -> {
                     if (!mHeadsUpManager.hasPinnedHeadsUp()) {
-                        mStatusBarWindowManager.setHeadsUpShowing(false);
+                        mStatusBarWindowController.setHeadsUpShowing(false);
                         mHeadsUpManager.setHeadsUpGoingAway(false);
                     }
                     mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
@@ -2065,7 +2065,7 @@
     public void setPanelExpanded(boolean isExpanded) {
         mPanelExpanded = isExpanded;
         updateHideIconsForBouncer(false /* animate */);
-        mStatusBarWindowManager.setPanelExpanded(isExpanded);
+        mStatusBarWindowController.setPanelExpanded(isExpanded);
         mVisualStabilityManager.setPanelExpanded(isExpanded);
         if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
             if (DEBUG) {
@@ -2274,7 +2274,7 @@
 
         // Expand the window to encompass the full screen in anticipation of the drag.
         // This is only possible to do atomically because the status bar is at the top of the screen!
-        mStatusBarWindowManager.setPanelVisible(true);
+        mStatusBarWindowController.setPanelVisible(true);
 
         visibilityChanged(true);
         recomputeDisableFlags(!force /* animate */);
@@ -2348,7 +2348,7 @@
                 + mNotificationPanel.canPanelBeCollapsed());
         if (mStatusBarWindow != null && mNotificationPanel.canPanelBeCollapsed()) {
             // release focus immediately to kick off focus change transition
-            mStatusBarWindowManager.setStatusBarFocusable(false);
+            mStatusBarWindowController.setStatusBarFocusable(false);
 
             mStatusBarWindow.cancelExpandHelper();
             mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
@@ -2420,8 +2420,8 @@
         visibilityChanged(false);
 
         // Shrink the window to the size of the status bar only
-        mStatusBarWindowManager.setPanelVisible(false);
-        mStatusBarWindowManager.setForceStatusBarVisible(false);
+        mStatusBarWindowController.setPanelVisible(false);
+        mStatusBarWindowController.setForceStatusBarVisible(false);
 
         // Close any guts that might be visible
         mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
@@ -2894,7 +2894,7 @@
 
     private void addStatusBarWindow() {
         makeStatusBarView();
-        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
+        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
         mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,
                 new RemoteInputController.Delegate() {
                     public void setRemoteInputActive(NotificationData.Entry entry,
@@ -2911,8 +2911,8 @@
                         mStackScroller.requestDisallowDismiss();
                     }
                 });
-        mRemoteInputManager.getController().addCallback(mStatusBarWindowManager);
-        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
+        mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
+        mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
     }
 
     // called by makeStatusbar and also by PhoneStatusBarView
@@ -3222,8 +3222,8 @@
         int oldBarHeight = mNaturalBarHeight;
         mNaturalBarHeight = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
-        if (mStatusBarWindowManager != null && mNaturalBarHeight != oldBarHeight) {
-            mStatusBarWindowManager.setBarHeight(mNaturalBarHeight);
+        if (mStatusBarWindowController != null && mNaturalBarHeight != oldBarHeight) {
+            mStatusBarWindowController.setBarHeight(mNaturalBarHeight);
         }
         mMaxAllowedKeyguardNotifications = res.getInteger(
                 R.integer.keyguard_max_notification_count);
@@ -3881,7 +3881,7 @@
      * Switches theme from light to dark and vice-versa.
      */
     protected void updateTheme() {
-        final boolean inflated = mStackScroller != null && mStatusBarWindowManager != null;
+        final boolean inflated = mStackScroller != null && mStatusBarWindowController != null;
 
         // Lock wallpaper defines the color of the majority of the views, hence we'll use it
         // to set our default theme.
@@ -3907,7 +3907,7 @@
             mStackScroller.updateDecorViews(useDarkText);
 
             // Make sure we have the correct navbar/statusbar colors.
-            mStatusBarWindowManager.setKeyguardDark(useDarkText);
+            mStatusBarWindowController.setKeyguardDark(useDarkText);
         }
     }
 
@@ -4071,7 +4071,7 @@
         mGroupManager.setStatusBarState(state);
         mHeadsUpManager.setStatusBarState(state);
         mFalsingManager.setStatusBarState(state);
-        mStatusBarWindowManager.setStatusBarState(state);
+        mStatusBarWindowController.setStatusBarState(state);
         mStackScroller.setStatusBarState(state);
         updateReportRejectedTouchVisibility();
         updateDozing();
@@ -4101,7 +4101,7 @@
         if (!isPresenterFullyCollapsed()) {
             // if we set it not to be focusable when collapsing, we have to undo it when we aborted
             // the closing
-            mStatusBarWindowManager.setStatusBarFocusable(true);
+            mStatusBarWindowController.setStatusBarFocusable(true);
         }
     }
 
@@ -4718,7 +4718,7 @@
         if (mDozing != dozing) {
             mDozing = dozing;
             mKeyguardViewMediator.setAodShowing(mDozing);
-            mStatusBarWindowManager.setDozing(mDozing);
+            mStatusBarWindowController.setDozing(mDozing);
             mStatusBarKeyguardViewManager.setDozing(mDozing);
             if (mAmbientIndicationContainer instanceof DozeReceiver) {
                 ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
@@ -4948,7 +4948,7 @@
 
         @Override
         public void setDozeScreenBrightness(int value) {
-            mStatusBarWindowManager.setDozeScreenBrightness(value);
+            mStatusBarWindowController.setDozeScreenBrightness(value);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c4424d8..9bc0252 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -74,7 +74,7 @@
     private static String TAG = "StatusBarKeyguardViewManager";
 
     protected final Context mContext;
-    private final StatusBarWindowManager mStatusBarWindowManager;
+    private final StatusBarWindowController mStatusBarWindowController;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
         public void onFullyShown() {
@@ -135,7 +135,7 @@
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
-        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
+        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
     }
 
@@ -190,7 +190,7 @@
      */
     public void show(Bundle options) {
         mShowing = true;
-        mStatusBarWindowManager.setKeyguardShowing(true);
+        mStatusBarWindowController.setKeyguardShowing(true);
         reset(true /* hideBouncerWhenShowing */);
         StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
             StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
@@ -342,11 +342,11 @@
     }
 
     public void setNeedsInput(boolean needsInput) {
-        mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
+        mStatusBarWindowController.setKeyguardNeedsInput(needsInput);
     }
 
     public boolean isUnlockWithWallpaper() {
-        return mStatusBarWindowManager.isShowingWallpaper();
+        return mStatusBarWindowController.isShowingWallpaper();
     }
 
     public void setOccluded(boolean occluded, boolean animate) {
@@ -360,7 +360,7 @@
                         new Runnable() {
                             @Override
                             public void run() {
-                                mStatusBarWindowManager.setKeyguardOccluded(mOccluded);
+                                mStatusBarWindowController.setKeyguardOccluded(mOccluded);
                                 reset(true /* hideBouncerWhenShowing */);
                             }
                         });
@@ -375,7 +375,7 @@
         if (mShowing) {
             mStatusBar.updateMediaMetaData(false, animate && !occluded);
         }
-        mStatusBarWindowManager.setKeyguardOccluded(occluded);
+        mStatusBarWindowController.setKeyguardOccluded(occluded);
 
         // setDozing(false) will call reset once we stop dozing.
         if (!mDozing) {
@@ -425,8 +425,8 @@
             mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
                 @Override
                 public void run() {
-                    mStatusBarWindowManager.setKeyguardShowing(false);
-                    mStatusBarWindowManager.setKeyguardFadingAway(true);
+                    mStatusBarWindowController.setKeyguardShowing(false);
+                    mStatusBarWindowController.setKeyguardFadingAway(true);
                     hideBouncer(true /* destroyView */);
                     updateStates();
                 }
@@ -434,7 +434,7 @@
                 @Override
                 public void run() {
                     mStatusBar.hideKeyguard();
-                    mStatusBarWindowManager.setKeyguardFadingAway(false);
+                    mStatusBarWindowController.setKeyguardFadingAway(false);
                     mViewMediatorCallback.keyguardGone();
                     executeAfterKeyguardGoneAction();
                 }
@@ -456,7 +456,7 @@
             } else {
                 boolean staying = mStatusBar.hideKeyguard();
                 if (!staying) {
-                    mStatusBarWindowManager.setKeyguardFadingAway(true);
+                    mStatusBarWindowController.setKeyguardFadingAway(true);
                     wakeAndUnlockDejank();
                 } else {
                     mStatusBar.finishKeyguardFadingAway();
@@ -464,7 +464,7 @@
                 }
             }
             updateStates();
-            mStatusBarWindowManager.setKeyguardShowing(false);
+            mStatusBarWindowController.setKeyguardShowing(false);
             mViewMediatorCallback.keyguardGone();
         }
         StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
@@ -481,7 +481,7 @@
     }
 
     public void onKeyguardFadedAway() {
-        mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
+        mContainer.postDelayed(() -> mStatusBarWindowController.setKeyguardFadingAway(false),
                 100);
         mStatusBar.finishKeyguardFadingAway();
         mBiometricUnlockController.finishKeyguardFadingAway();
@@ -599,7 +599,7 @@
         }
 
         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
-            mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
+            mStatusBarWindowController.setBouncerShowing(bouncerShowing);
             mStatusBar.setBouncerShowing(bouncerShowing);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index a5716f2..4d60121 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -36,6 +36,7 @@
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.R;
 import com.android.systemui.Dumpable;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -49,9 +50,9 @@
 /**
  * Encapsulates all logic for the status bar window state management.
  */
-public class StatusBarWindowManager implements RemoteInputController.Callback, Dumpable {
+public class StatusBarWindowController implements RemoteInputController.Callback, Dumpable {
 
-    private static final String TAG = "StatusBarWindowManager";
+    private static final String TAG = "StatusBarWindowController";
 
     private final Context mContext;
     private final WindowManager mWindowManager;
@@ -68,12 +69,19 @@
     private final State mCurrentState = new State();
     private OtherwisedCollapsedListener mListener;
 
-    public StatusBarWindowManager(Context context) {
+    public StatusBarWindowController(Context context) {
+        this(context, context.getSystemService(WindowManager.class), ActivityManager.getService(),
+                DozeParameters.getInstance(context));
+    }
+
+    @VisibleForTesting
+    StatusBarWindowController(Context context, WindowManager windowManager,
+            IActivityManager activityManager, DozeParameters dozeParameters) {
         mContext = context;
-        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mActivityManager = ActivityManager.getService();
+        mWindowManager = windowManager;
+        mActivityManager = activityManager;
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
-        mDozeParameters = DozeParameters.getInstance(mContext);
+        mDozeParameters = dozeParameters;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
     }
 
@@ -149,6 +157,12 @@
         } else {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
         }
+
+        if (state.dozing) {
+            mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+        } else {
+            mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+        }
     }
 
     private void adjustScreenOrientation(State state) {
@@ -433,7 +447,7 @@
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("StatusBarWindowManager state:");
+        pw.println("StatusBarWindowController state:");
         pw.println(mCurrentState);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 2bf62bb..533bd86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -30,6 +30,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.R;
 
 import java.io.FileDescriptor;
@@ -41,12 +42,16 @@
 
 import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
 
+import androidx.annotation.VisibleForTesting;
+
+
 /** Platform implementation of the cast controller. **/
 public class CastControllerImpl implements CastController {
     private static final String TAG = "CastController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
+    @GuardedBy("mCallbacks")
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final MediaRouter mMediaRouter;
     private final ArrayMap<String, RouteInfo> mRoutes = new ArrayMap<>();
@@ -72,7 +77,7 @@
         pw.println("CastController state:");
         pw.print("  mDiscovering="); pw.println(mDiscovering);
         pw.print("  mCallbackRegistered="); pw.println(mCallbackRegistered);
-        pw.print("  mCallbacks.size="); pw.println(mCallbacks.size());
+        pw.print("  mCallbacks.size="); synchronized (mCallbacks) {pw.println(mCallbacks.size());}
         pw.print("  mRoutes.size="); pw.println(mRoutes.size());
         for (int i = 0; i < mRoutes.size(); i++) {
             final RouteInfo route = mRoutes.valueAt(i);
@@ -83,7 +88,9 @@
 
     @Override
     public void addCallback(Callback callback) {
-        mCallbacks.add(callback);
+        synchronized (mCallbacks) {
+            mCallbacks.add(callback);
+        }
         fireOnCastDevicesChanged(callback);
         synchronized (mDiscoveringLock) {
             handleDiscoveryChangeLocked();
@@ -92,7 +99,9 @@
 
     @Override
     public void removeCallback(Callback callback) {
-        mCallbacks.remove(callback);
+        synchronized (mCallbacks) {
+            mCallbacks.remove(callback);
+        }
         synchronized (mDiscoveringLock) {
             handleDiscoveryChangeLocked();
         }
@@ -117,10 +126,16 @@
             mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback,
                     MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
             mCallbackRegistered = true;
-        } else if (mCallbacks.size() != 0) {
-            mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback,
-                    MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
-            mCallbackRegistered = true;
+        } else {
+            boolean hasCallbacks = false;
+            synchronized (mCallbacks) {
+                hasCallbacks = mCallbacks.isEmpty();
+            }
+            if (!hasCallbacks) {
+                mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback,
+                        MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+                mCallbackRegistered = true;
+            }
         }
     }
 
@@ -248,12 +263,17 @@
         }
     }
 
-    private void fireOnCastDevicesChanged() {
-        for (Callback callback : mCallbacks) {
-            fireOnCastDevicesChanged(callback);
+    @VisibleForTesting
+    void fireOnCastDevicesChanged() {
+        synchronized (mCallbacks) {
+            for (Callback callback : mCallbacks) {
+                fireOnCastDevicesChanged(callback);
+            }
+
         }
     }
 
+
     private void fireOnCastDevicesChanged(Callback callback) {
         callback.onCastDevicesChanged();
     }
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 9121473..8129b01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -33,7 +33,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationInfo;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -96,6 +96,6 @@
         viewHierarchyManager.setUpWithPresenter(mPresenter, entryManager, mListContainer);
         notificationListener.setUpWithPresenter(mPresenter, entryManager);
 
-        assertFalse(mDependency.hasInstantiatedDependency(StatusBarWindowManager.class));
+        assertFalse(mDependency.hasInstantiatedDependency(StatusBarWindowController.class));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6933328..be4560b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -68,7 +68,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mDependency.injectMockDependency(StatusBarWindowManager.class);
+        mDependency.injectMockDependency(StatusBarWindowController.class);
         mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager(getContext(),
                 mViewMediatorCallback, mLockPatternUtils);
         mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
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 e39238d..a90346b 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
@@ -204,7 +204,7 @@
                 mEntryManager, mScrimController, mBiometricUnlockController,
                 mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
                 mRemoteInputManager, mock(NotificationGroupManager.class),
-                mock(FalsingManager.class), mock(StatusBarWindowManager.class),
+                mock(FalsingManager.class), mock(StatusBarWindowController.class),
                 mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
                 mock(NotificationShelf.class), mLockscreenUserManager,
                 mock(CommandQueue.class));
@@ -716,7 +716,7 @@
                 NotificationRemoteInputManager notificationRemoteInputManager,
                 NotificationGroupManager notificationGroupManager,
                 FalsingManager falsingManager,
-                StatusBarWindowManager statusBarWindowManager,
+                StatusBarWindowController statusBarWindowController,
                 NotificationIconAreaController notificationIconAreaController,
                 DozeScrimController dozeScrimController,
                 NotificationShelf notificationShelf,
@@ -744,7 +744,7 @@
             mRemoteInputManager = notificationRemoteInputManager;
             mGroupManager = notificationGroupManager;
             mFalsingManager = falsingManager;
-            mStatusBarWindowManager = statusBarWindowManager;
+            mStatusBarWindowController = statusBarWindowController;
             mNotificationIconAreaController = notificationIconAreaController;
             mDozeScrimController = dozeScrimController;
             mNotificationShelf = notificationShelf;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
new file mode 100644
index 0000000..f8223f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.phone;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class StatusBarWindowControllerTest extends SysuiTestCase {
+
+    @Mock
+    private WindowManager mWindowManager;
+    @Mock
+    private DozeParameters mDozeParameters;
+    @Mock
+    private View mStatusBarView;
+    @Mock
+    private IActivityManager mActivityManager;
+
+    private StatusBarWindowController mStatusBarWindowController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+
+        mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
+                mActivityManager, mDozeParameters);
+        mStatusBarWindowController.add(mStatusBarView, 100 /* height */);
+    }
+
+    @Test
+    public void testSetDozing_hidesSystemOverlays() {
+        mStatusBarWindowController.setDozing(true);
+        ArgumentCaptor<WindowManager.LayoutParams> captor =
+                ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
+        verify(mWindowManager).updateViewLayout(any(), captor.capture());
+        int flag = captor.getValue().privateFlags
+                & WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+        assertThat(flag).isNotEqualTo(0);
+
+        reset(mWindowManager);
+        mStatusBarWindowController.setDozing(false);
+        verify(mWindowManager).updateViewLayout(any(), captor.capture());
+        flag = captor.getValue().privateFlags
+                & WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+        assertThat(flag).isEqualTo(0);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index 1b22f09..1ee7094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -6,8 +6,8 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.junit.Assert.fail;
 
-import android.content.Context;
 import android.media.MediaRouter;
 import android.media.projection.MediaProjectionInfo;
 import android.media.projection.MediaProjectionManager;
@@ -24,6 +24,11 @@
 import org.mockito.MockitoAnnotations;
 import org.junit.Test;
 
+import java.util.ConcurrentModificationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -49,15 +54,15 @@
     }
 
     @Test
-    public void testAddCallback(){
+    public void testAddCallback() {
         Callback mockCallback = mock(Callback.class);
 
         mController.addCallback(mockCallback);
-        verify(mockCallback,times(1)).onCastDevicesChanged();
+        verify(mockCallback, times(1)).onCastDevicesChanged();
     }
 
     @Test
-    public void testRemoveCallback(){
+    public void testRemoveCallback() {
         Callback mockCallback = mock(Callback.class);
 
         mController.addCallback(mockCallback);
@@ -68,10 +73,54 @@
     }
 
     @Test
-    public void testRemoveCallbackFromEmptyList(){
+    public void testRemoveCallbackFromEmptyList() {
         Callback mockCallback = mock(Callback.class);
 
         mController.removeCallback(mockCallback);
         verify(mockCallback, never()).onCastDevicesChanged();
     }
+
+    @Test
+    public void testAddCallbackRemoveCallback_concurrently() throws InterruptedException {
+        int callbackCount = 20;
+        int numThreads = 2 * callbackCount;
+        CountDownLatch startThreadsLatch = new CountDownLatch(1);
+        CountDownLatch threadsDone = new CountDownLatch(numThreads);
+        Callback[] callbackList = new Callback[callbackCount];
+        mController.setDiscovering(true);
+        AtomicBoolean error = new AtomicBoolean(false);
+        for (int cbIndex = 0; cbIndex < callbackCount; cbIndex++) {
+            callbackList[cbIndex] = mock(Callback.class);
+        }
+        for (int i = 0; i < numThreads; i++) {
+            final Callback mCallback = callbackList[i / 2];
+            final boolean shouldAdd = (i % 2 == 0);
+            new Thread() {
+                public void run() {
+                    try {
+                        startThreadsLatch.await(10, TimeUnit.SECONDS);
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
+                    try {
+                        if (shouldAdd) {
+                            mController.addCallback(mCallback);
+                        } else {
+                            mController.removeCallback(mCallback);
+                        }
+                        mController.fireOnCastDevicesChanged();
+                    } catch (ConcurrentModificationException exc) {
+                        error.compareAndSet(false, true);
+                    } finally {
+                        threadsDone.countDown();
+                    }
+                }
+            }.start();
+        }
+        startThreadsLatch.countDown();
+        threadsDone.await(10, TimeUnit.SECONDS);
+        if (error.get()) {
+            fail("Concurrent modification exception");
+        }
+    }
 }
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/strings.xml
index 754ba72..b08924b 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/strings.xml
@@ -16,7 +16,7 @@
   -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <string name="display_cutout_emulation_overlay">Corner display cutout</string>
+    <string name="display_cutout_emulation_overlay">Corner cutout</string>
 
 </resources>
 
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/strings.xml
index 68c2dcb..0a106fa 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/strings.xml
@@ -16,7 +16,7 @@
   -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <string name="display_cutout_emulation_overlay">Double display cutout</string>
+    <string name="display_cutout_emulation_overlay">Double cutout</string>
 
 </resources>
 
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
index 4989677..0bf8330 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
@@ -18,7 +18,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <string name="display_cutout_emulation_overlay">Narrow display cutout</string>
+    <string name="display_cutout_emulation_overlay">Narrow cutout</string>
 
 </resources>
 
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml
index 6dcbbd9..bcc7c97 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml
@@ -16,7 +16,7 @@
   -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <string name="display_cutout_emulation_overlay">Tall display cutout</string>
+    <string name="display_cutout_emulation_overlay">Tall cutout</string>
 
 </resources>
 
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml
index f4b9f7e..0fcbdebb 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml
@@ -16,7 +16,7 @@
   -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <string name="display_cutout_emulation_overlay">Wide display cutout</string>
+    <string name="display_cutout_emulation_overlay">Wide cutout</string>
 
 </resources>
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6f03b76..5771748 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -438,8 +438,13 @@
         Slog.i(TAG, "setLogLevel(): " + level);
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.AUTOFILL_LOGGING_LEVEL, level);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.AUTOFILL_LOGGING_LEVEL, level);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void setLogLevelFromSettings() {
@@ -492,8 +497,13 @@
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxPartitions(): " + max);
 
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void setMaxPartitionsFromSettings() {
@@ -521,8 +531,13 @@
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
 
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, max);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, max);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void setMaxVisibleDatasetsFromSettings() {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 784dfb4..02a62ff 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1714,10 +1714,9 @@
     }
 
     @Override
-    public void addClient(IInputMethodClient client,
-            IInputContext inputContext, int uid, int pid) {
-        if (!calledFromValidUser()) {
-            return;
+    public void addClient(IInputMethodClient client, IInputContext inputContext, int uid, int pid) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only system process can call this method.");
         }
         synchronized (mMethodMap) {
             mClients.put(client.asBinder(), new ClientState(client,
@@ -1727,8 +1726,8 @@
 
     @Override
     public void removeClient(IInputMethodClient client) {
-        if (!calledFromValidUser()) {
-            return;
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only system process can call this method.");
         }
         synchronized (mMethodMap) {
             ClientState cs = mClients.remove(client.asBinder());
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 349e1c8..512e851 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -104,6 +104,12 @@
     public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10;
 
     /**
+     * Flag: The display cutout of this display is masked.
+     * @hide
+     */
+    public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
+
+    /**
      * Touch attachment: Display does not receive touch.
      */
     public static final int TOUCH_NONE = 0;
@@ -453,6 +459,9 @@
         if ((flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
             msg.append(", FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD");
         }
+        if ((flags & FLAG_MASK_DISPLAY_CUTOUT) != 0) {
+            msg.append(", FLAG_MASK_DISPLAY_CUTOUT");
+        }
         return msg.toString();
     }
 }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 21ae048..16d82df 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -402,6 +402,10 @@
                             && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
                     }
+                    if (res.getBoolean(
+                            com.android.internal.R.bool.config_maskMainBuiltInDisplayCutout)) {
+                        mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
+                    }
                     mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
                             mInfo.width, mInfo.height);
                     mInfo.type = Display.TYPE_BUILT_IN;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 23ee56b..373de63 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -23,6 +23,8 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.server.wm.utils.InsetUtils;
+
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.List;
@@ -251,14 +253,18 @@
             if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
                 mBaseDisplayInfo.flags |= Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
             }
+            Rect maskingInsets = getMaskingInsets(deviceInfo);
+            int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
+            int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
+
             mBaseDisplayInfo.type = deviceInfo.type;
             mBaseDisplayInfo.address = deviceInfo.address;
             mBaseDisplayInfo.name = deviceInfo.name;
             mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;
-            mBaseDisplayInfo.appWidth = deviceInfo.width;
-            mBaseDisplayInfo.appHeight = deviceInfo.height;
-            mBaseDisplayInfo.logicalWidth = deviceInfo.width;
-            mBaseDisplayInfo.logicalHeight = deviceInfo.height;
+            mBaseDisplayInfo.appWidth = maskedWidth;
+            mBaseDisplayInfo.appHeight = maskedHeight;
+            mBaseDisplayInfo.logicalWidth = maskedWidth;
+            mBaseDisplayInfo.logicalHeight = maskedHeight;
             mBaseDisplayInfo.rotation = Surface.ROTATION_0;
             mBaseDisplayInfo.modeId = deviceInfo.modeId;
             mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
@@ -275,13 +281,15 @@
             mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
             mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
             mBaseDisplayInfo.state = deviceInfo.state;
-            mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
-            mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
-            mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
-            mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
+            mBaseDisplayInfo.smallestNominalAppWidth = maskedWidth;
+            mBaseDisplayInfo.smallestNominalAppHeight = maskedHeight;
+            mBaseDisplayInfo.largestNominalAppWidth = maskedWidth;
+            mBaseDisplayInfo.largestNominalAppHeight = maskedHeight;
             mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid;
             mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName;
-            mBaseDisplayInfo.displayCutout = deviceInfo.displayCutout;
+            boolean maskCutout =
+                    (deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0;
+            mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout;
 
             mPrimaryDisplayDeviceInfo = deviceInfo;
             mInfo = null;
@@ -289,6 +297,18 @@
     }
 
     /**
+     * Returns insets in ROTATION_0 for areas that are masked.
+     */
+    private static Rect getMaskingInsets(DisplayDeviceInfo deviceInfo) {
+        boolean maskCutout = (deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0;
+        if (maskCutout && deviceInfo.displayCutout != null) {
+            return deviceInfo.displayCutout.getSafeInsets();
+        } else {
+            return new Rect();
+        }
+    }
+
+    /**
      * Applies the layer stack and transformation to the given display device
      * so that it shows the contents of this logical display.
      *
@@ -349,6 +369,12 @@
         int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
         int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
 
+        Rect maskingInsets = getMaskingInsets(displayDeviceInfo);
+        InsetUtils.rotateInsets(maskingInsets, orientation);
+        // Don't consider the masked area as available when calculating the scaling below.
+        physWidth -= maskingInsets.left + maskingInsets.right;
+        physHeight -= maskingInsets.top + maskingInsets.bottom;
+
         // Determine whether the width or height is more constrained to be scaled.
         //    physWidth / displayInfo.logicalWidth    => letter box
         // or physHeight / displayInfo.logicalHeight  => pillar box
@@ -375,6 +401,9 @@
         mTempDisplayRect.set(displayRectLeft, displayRectTop,
                 displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
 
+        // Now add back the offset for the masked area.
+        mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top);
+
         mTempDisplayRect.left += mDisplayOffsetX;
         mTempDisplayRect.right += mDisplayOffsetX;
         mTempDisplayRect.top += mDisplayOffsetY;
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 8be8450..decdac6 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -30,6 +30,7 @@
     void onNotificationClear(int callingUid, int callingPid,
             String pkg, String tag, int id, int userId, String key,
             @NotificationStats.DismissalSurface int dismissalSurface,
+            @NotificationStats.DismissalSentiment int dismissalSentiment,
             NotificationVisibility nv);
     void onNotificationError(int callingUid, int callingPid,
             String pkg, String tag, int id,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dd3e2d4..ce71dd2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -700,7 +700,8 @@
         }
 
         @Override
-        public void onNotificationClick(int callingUid, int callingPid, String key, NotificationVisibility nv) {
+        public void onNotificationClick(int callingUid, int callingPid, String key,
+                NotificationVisibility nv) {
             exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
@@ -757,11 +758,13 @@
         public void onNotificationClear(int callingUid, int callingPid,
                 String pkg, String tag, int id, int userId, String key,
                 @NotificationStats.DismissalSurface int dismissalSurface,
+                @NotificationStats.DismissalSentiment int dismissalSentiment,
                 NotificationVisibility nv) {
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
                     r.recordDismissalSurface(dismissalSurface);
+                    r.recordDismissalSentiment(dismissalSentiment);
                 }
             }
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
@@ -797,8 +800,8 @@
         }
 
         @Override
-        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
-                int uid, int initialPid, String message, int userId) {
+        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag,
+                int id, int uid, int initialPid, String message, int userId) {
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
                     REASON_ERROR, null);
         }
@@ -1040,10 +1043,12 @@
                     uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                     cancelNotifications = false;
                     hideNotifications = true;
                 } else if (action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                     cancelNotifications = false;
                     unhideNotifications = true;
                 } else if (queryRestart) {
@@ -5964,6 +5969,7 @@
             }
             notificationList.remove(i);
             mNotificationsByKey.remove(r.getKey());
+            r.recordDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_NEUTRAL);
             canceledNotifications.add(r);
             cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index a6d8615..fbb42ea 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1065,6 +1065,10 @@
         mStats.setDismissalSurface(surface);
     }
 
+    public void recordDismissalSentiment(@NotificationStats.DismissalSentiment int sentiment) {
+        mStats.setDismissalSentiment(sentiment);
+    }
+
     public void recordSnoozed() {
         mStats.setSnoozed();
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f3d333b..3c2b089 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -254,6 +254,7 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.ExceptionUtils;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.LongSparseArray;
@@ -14169,11 +14170,12 @@
         info.sendPackageRemovedBroadcasts(true /*killApp*/);
     }
 
-    private void sendPackagesSuspendedForUser(String[] pkgList, int userId, boolean suspended,
-            PersistableBundle launcherExtras) {
+    private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
+            boolean suspended, PersistableBundle launcherExtras) {
         if (pkgList.length > 0) {
             Bundle extras = new Bundle(1);
             extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+            extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
             if (launcherExtras != null) {
                 extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
                         new Bundle(launcherExtras.deepCopy()));
@@ -14355,6 +14357,7 @@
         }
 
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
+        final IntArray changedUids = new IntArray(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
         final long callingId = Binder.clearCallingIdentity();
         try {
@@ -14382,6 +14385,7 @@
                     pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
                             launcherExtras, userId);
                     changedPackagesList.add(packageName);
+                    changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
                 }
             }
         } finally {
@@ -14390,7 +14394,8 @@
         if (!changedPackagesList.isEmpty()) {
             final String[] changedPackages = changedPackagesList.toArray(
                     new String[changedPackagesList.size()]);
-            sendPackagesSuspendedForUser(changedPackages, userId, suspended, launcherExtras);
+            sendPackagesSuspendedForUser(
+                    changedPackages, changedUids.toArray(), userId, suspended, launcherExtras);
             sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId);
             synchronized (mPackages) {
                 scheduleWritePackageRestrictionsLocked(userId);
@@ -14505,12 +14510,14 @@
 
     private void unsuspendForSuspendingPackages(Predicate<String> packagePredicate, int userId) {
         final List<String> affectedPackages = new ArrayList<>();
+        final IntArray affectedUids = new IntArray();
         synchronized (mPackages) {
             for (PackageSetting ps : mSettings.mPackages.values()) {
                 final PackageUserState pus = ps.readUserState(userId);
                 if (pus.suspended && packagePredicate.test(pus.suspendingPackage)) {
                     ps.setSuspended(false, null, null, null, null, userId);
                     affectedPackages.add(ps.name);
+                    affectedUids.add(UserHandle.getUid(userId, ps.getAppId()));
                 }
             }
         }
@@ -14518,7 +14525,8 @@
             final String[] packageArray = affectedPackages.toArray(
                     new String[affectedPackages.size()]);
             sendMyPackageSuspendedOrUnsuspended(packageArray, false, null, userId);
-            sendPackagesSuspendedForUser(packageArray, userId, false, null);
+            sendPackagesSuspendedForUser(
+                    packageArray, affectedUids.toArray(), userId, false, null);
             // Write package restrictions immediately to avoid an inconsistent state.
             mSettings.writePackageRestrictionsLPr(userId);
         }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f418ad4..b8c9be7 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1053,14 +1053,16 @@
 
     @Override
     public void onNotificationClear(String pkg, String tag, int id, int userId, String key,
-            @NotificationStats.DismissalSurface int dismissalSurface, NotificationVisibility nv) {
+            @NotificationStats.DismissalSurface int dismissalSurface,
+            @NotificationStats.DismissalSentiment int dismissalSentiment,
+            NotificationVisibility nv) {
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId,
-                    key, dismissalSurface, nv);
+                    key, dismissalSurface, dismissalSentiment, nv);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/wm/utils/InsetUtils.java b/services/core/java/com/android/server/wm/utils/InsetUtils.java
index c5b103f..f5cc0d5 100644
--- a/services/core/java/com/android/server/wm/utils/InsetUtils.java
+++ b/services/core/java/com/android/server/wm/utils/InsetUtils.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
+import android.view.Surface;
 
 
 /**
@@ -30,6 +31,32 @@
     }
 
     /**
+     * Transforms insets given in one rotation into insets in a different rotation.
+     *
+     * @param inOutInsets the insets to transform, is set to the transformed insets
+     * @param rotationDelta the delta between the new and old rotation.
+     *                      Must be one of Surface.ROTATION_0/90/180/270.
+     */
+    public static void rotateInsets(Rect inOutInsets, int rotationDelta) {
+        final Rect r = inOutInsets;
+        switch (rotationDelta) {
+            case Surface.ROTATION_0:
+                return;
+            case Surface.ROTATION_90:
+                r.set(r.top, r.right, r.bottom, r.left);
+                break;
+            case Surface.ROTATION_180:
+                r.set(r.right, r.bottom, r.left, r.top);
+                break;
+            case Surface.ROTATION_270:
+                r.set(r.bottom, r.left, r.top, r.right);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown rotation: " + rotationDelta);
+        }
+    }
+
+    /**
      * Adds {@code insetsToAdd} to {@code inOutInsets}.
      */
     public static void addInsets(Rect inOutInsets, Rect insetsToAdd) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
index 75b4d2f..3364aef 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
@@ -16,6 +16,11 @@
 
 package com.android.server.wm.utils;
 
+import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+
 import static junit.framework.Assert.assertEquals;
 
 import android.graphics.Rect;
@@ -39,5 +44,29 @@
         InsetUtils.addInsets(rect1, rect2);
         assertEquals(new Rect(60, 80, 100, 120), rect1);
     }
+
+    @Test
+    public void rotate() {
+        final Rect original = new Rect(1, 2, 3, 4);
+
+        assertEquals("rot0", original, rotateCopy(original, ROTATION_0));
+
+        final Rect rot90 = rotateCopy(original, ROTATION_90);
+        assertEquals("rot90", new Rect(2, 3, 4, 1), rot90);
+
+        final Rect rot180 = rotateCopy(original, ROTATION_180);
+        assertEquals("rot180", new Rect(3, 4, 1, 2), rot180);
+        assertEquals("rot90(rot90)=rot180", rotateCopy(rot90, ROTATION_90), rot180);
+
+        final Rect rot270 = rotateCopy(original, ROTATION_270);
+        assertEquals("rot270", new Rect(4, 1, 2, 3), rot270);
+        assertEquals("rot90(rot180)=rot270", rotateCopy(rot180, ROTATION_90), rot270);
+    }
+
+    private static Rect rotateCopy(Rect insets, int rotationDelta) {
+        final Rect copy = new Rect(insets);
+        InsetUtils.rotateInsets(copy, rotationDelta);
+        return copy;
+    }
 }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4dcb8cf..0ff124e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2483,13 +2483,29 @@
 
         final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
         mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
-                r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD, nv);
+                r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+                NotificationStats.DISMISS_SENTIMENT_POSITIVE, nv);
         waitForIdle();
 
         assertEquals(NotificationStats.DISMISSAL_AOD, r.getStats().getDismissalSurface());
     }
 
     @Test
+    public void testStats_dismissalSentiment() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
+        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
+                r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+                NotificationStats.DISMISS_SENTIMENT_NEGATIVE, nv);
+        waitForIdle();
+
+        assertEquals(NotificationStats.DISMISS_SENTIMENT_NEGATIVE,
+                r.getStats().getDismissalSentiment());
+    }
+
+    @Test
     public void testApplyAdjustmentMultiUser() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
index 0a630f4..bae8564 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
@@ -16,6 +16,8 @@
 package com.android.server.notification;
 
 import static android.service.notification.NotificationStats.DISMISSAL_PEEK;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_POSITIVE;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -46,6 +48,7 @@
         assertFalse(stats.hasViewedSettings());
         assertFalse(stats.hasSnoozed());
         assertEquals(NotificationStats.DISMISSAL_NOT_DISMISSED, stats.getDismissalSurface());
+        assertEquals(NotificationStats.DISMISS_SENTIMENT_UNKNOWN, stats.getDismissalSentiment());
     }
 
     @Test
@@ -97,10 +100,19 @@
     }
 
     @Test
+    public void testDismissalSentiment() {
+        NotificationStats stats = new NotificationStats();
+        stats.setDismissalSentiment(DISMISS_SENTIMENT_NEGATIVE);
+        assertEquals(DISMISS_SENTIMENT_NEGATIVE, stats.getDismissalSentiment());
+        assertFalse(stats.hasInteracted());
+    }
+
+    @Test
     public void testWriteToParcel() {
         NotificationStats stats = new NotificationStats();
         stats.setViewedSettings();
         stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
+        stats.setDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_POSITIVE);
         Parcel parcel = Parcel.obtain();
         stats.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);