Merge "Show a line below the tabs in tabbed view to align with UX mocks."
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 526c0b3..948546b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,7 @@
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
     void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
+    ParceledListSlice getConversations(boolean onlyImportant);
     ParceledListSlice getConversationsForPackage(String pkg, int uid);
     ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
     NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f86c971..0fa261a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8595,6 +8595,15 @@
         public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
 
         /**
+         * When enabled conversations marked as favorites will be set to bubble.
+         *
+         * The value 1 - enable, 0 - disable
+         * @hide
+         */
+        public static final String BUBBLE_IMPORTANT_CONVERSATIONS
+                = "bubble_important_conversations";
+
+        /**
          * Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
          * swipe).
          *
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index 9847695..ab465ab 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -33,6 +33,8 @@
     private CharSequence mGroupLabel;
     private CharSequence mParentChannelLabel;
     private ShortcutInfo mShortcutInfo;
+    private String mPkg;
+    private int mUid;
 
     public ConversationChannelWrapper() {}
 
@@ -41,6 +43,8 @@
         mGroupLabel = in.readCharSequence();
         mParentChannelLabel = in.readCharSequence();
         mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+        mPkg = in.readStringNoHelper();
+        mUid = in.readInt();
     }
 
     @Override
@@ -49,6 +53,8 @@
         dest.writeCharSequence(mGroupLabel);
         dest.writeCharSequence(mParentChannelLabel);
         dest.writeParcelable(mShortcutInfo, flags);
+        dest.writeStringNoHelper(mPkg);
+        dest.writeInt(mUid);
     }
 
     @Override
@@ -103,6 +109,22 @@
         mShortcutInfo = shortcutInfo;
     }
 
+    public String getPkg() {
+        return mPkg;
+    }
+
+    public void setPkg(String pkg) {
+        mPkg = pkg;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public void setUid(int uid) {
+        mUid = uid;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -111,12 +133,14 @@
         return Objects.equals(getNotificationChannel(), that.getNotificationChannel()) &&
                 Objects.equals(getGroupLabel(), that.getGroupLabel()) &&
                 Objects.equals(getParentChannelLabel(), that.getParentChannelLabel()) &&
-                Objects.equals(getShortcutInfo(), that.getShortcutInfo());
+                Objects.equals(getShortcutInfo(), that.getShortcutInfo()) &&
+                Objects.equals(getPkg(), that.getPkg()) &&
+                getUid() == that.getUid();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(getNotificationChannel(), getGroupLabel(), getParentChannelLabel(),
-                getShortcutInfo());
+                getShortcutInfo(), getPkg(), getUid());
     }
 }
diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java
index d21a952..850e9fc 100644
--- a/core/java/android/view/CutoutSpecification.java
+++ b/core/java/android/view/CutoutSpecification.java
@@ -406,9 +406,7 @@
                     }
                     currentIndex += RIGHT_MARKER.length();
                 } else if (specWithoutDp.startsWith(BOTTOM_MARKER, currentIndex)) {
-                    if (!mPositionFromCenterVertical) {
-                        parseSvgPathSpec(region, sb.toString());
-                    }
+                    parseSvgPathSpec(region, sb.toString());
                     currentIndex += BOTTOM_MARKER.length();
 
                     /* prepare to parse the rest path */
@@ -416,9 +414,7 @@
                     mBindBottomCutout = true;
                     mPositionFromBottom = true;
                 } else if (specWithoutDp.startsWith(CENTER_VERTICAL_MARKER, currentIndex)) {
-                    if (!mPositionFromBottom) {
-                        parseSvgPathSpec(region, sb.toString());
-                    }
+                    parseSvgPathSpec(region, sb.toString());
                     currentIndex += CENTER_VERTICAL_MARKER.length();
 
                     /* prepare to parse the rest path */
@@ -431,14 +427,16 @@
                     /* prepare to parse the rest path */
                     resetStatus(sb);
                 } else if (specWithoutDp.startsWith(BIND_LEFT_CUTOUT_MARKER, currentIndex)) {
-                    if (!mBindBottomCutout && !mBindRightCutout) {
-                        mBindLeftCutout = true;
-                    }
+                    mBindBottomCutout = false;
+                    mBindRightCutout = false;
+                    mBindLeftCutout = true;
+
                     currentIndex += BIND_LEFT_CUTOUT_MARKER.length();
                 } else if (specWithoutDp.startsWith(BIND_RIGHT_CUTOUT_MARKER, currentIndex)) {
-                    if (!mBindBottomCutout && !mBindLeftCutout) {
-                        mBindRightCutout = true;
-                    }
+                    mBindBottomCutout = false;
+                    mBindLeftCutout = false;
+                    mBindRightCutout = true;
+
                     currentIndex += BIND_RIGHT_CUTOUT_MARKER.length();
                 } else {
                     currentIndex += 1;
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index e8f84aa..b4a0208 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.app.AppGlobals;
+import android.app.admin.DevicePolicyEventLogger;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +28,7 @@
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.stats.devicepolicy.DevicePolicyEnums;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
@@ -251,6 +253,8 @@
 
     abstract @Nullable ViewGroup getInactiveAdapterView();
 
+    abstract String getMetricsCategory();
+
     /**
      * Rebuilds the tab that is currently visible to the user.
      * <p>Returns {@code true} if rebuild has completed.
@@ -282,6 +286,10 @@
         UserHandle listUserHandle = activeListAdapter.getUserHandle();
         if (listUserHandle == mWorkProfileUserHandle
                 && mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
+                    .setStrings(getMetricsCategory())
+                    .write();
             showEmptyState(activeListAdapter,
                     R.drawable.ic_work_apps_off,
                     R.string.resolver_turn_on_work_apps,
@@ -294,11 +302,19 @@
             if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
                     UserHandle.myUserId(), listUserHandle.getIdentifier())) {
                 if (listUserHandle == mPersonalProfileUserHandle) {
+                    DevicePolicyEventLogger.createEvent(
+                                DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
+                            .setStrings(getMetricsCategory())
+                            .write();
                     showEmptyState(activeListAdapter,
                             R.drawable.ic_sharing_disabled,
                             R.string.resolver_cant_share_with_personal_apps,
                             R.string.resolver_cant_share_cross_profile_explanation);
                 } else {
+                    DevicePolicyEventLogger.createEvent(
+                            DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
+                            .setStrings(getMetricsCategory())
+                            .write();
                     showEmptyState(activeListAdapter,
                             R.drawable.ic_sharing_disabled,
                             R.string.resolver_cant_share_with_work_apps,
@@ -315,6 +331,12 @@
         UserHandle listUserHandle = listAdapter.getUserHandle();
         if (UserHandle.myUserId() == listUserHandle.getIdentifier()
                 || !hasAppsInOtherProfile(listAdapter)) {
+            if (mWorkProfileUserHandle != null) {
+                DevicePolicyEventLogger.createEvent(
+                        DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED)
+                        .setStrings(getMetricsCategory())
+                        .write();
+            }
             showEmptyState(listAdapter,
                     R.drawable.ic_no_apps,
                     R.string.resolver_no_apps_available,
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 16db248..a201a33 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2698,6 +2698,11 @@
     @Override
     protected void resetButtonBar() {}
 
+    @Override
+    protected String getMetricsCategory() {
+        return METRICS_CATEGORY_CHOOSER;
+    }
+
     /**
      * Adapter for all types of items and targets in ShareSheet.
      * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index e350142..d440402 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -165,6 +165,11 @@
         return getListViewForIndex(1 - getCurrentPage());
     }
 
+    @Override
+    String getMetricsCategory() {
+        return ResolverActivity.METRICS_CATEGORY_CHOOSER;
+    }
+
     class ChooserProfileDescriptor extends ProfileDescriptor {
         private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
         private RecyclerView recyclerView;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 36d848c..96bfe73 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -33,6 +33,7 @@
 import android.app.VoiceInteractor.PickOptionRequest;
 import android.app.VoiceInteractor.PickOptionRequest.Option;
 import android.app.VoiceInteractor.Prompt;
+import android.app.admin.DevicePolicyEventLogger;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -60,6 +61,7 @@
 import android.os.UserManager;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.stats.devicepolicy.DevicePolicyEnums;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
@@ -84,6 +86,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
+import com.android.internal.app.chooser.ChooserTargetInfo;
 import com.android.internal.app.chooser.DisplayResolveInfo;
 import com.android.internal.app.chooser.TargetInfo;
 import com.android.internal.content.PackageMonitor;
@@ -99,6 +102,7 @@
 import java.util.Objects;
 import java.util.Set;
 
+
 /**
  * This activity is displayed when the system attempts to start an Intent for
  * which there is more than one matching activity, allowing the user to decide
@@ -152,6 +156,8 @@
     private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
     private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
+    protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
+    protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser";
 
     /**
      * TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized.
@@ -1201,12 +1207,14 @@
         if (!mSafeForwardingMode) {
             if (cti.startAsUser(this, null, currentUserHandle)) {
                 onActivityStarted(cti);
+                maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
             }
             return;
         }
         try {
             if (cti.startAsCaller(this, null, currentUserHandle.getIdentifier())) {
                 onActivityStarted(cti);
+                maybeLogCrossProfileTargetLaunch(cti, currentUserHandle);
             }
         } catch (RuntimeException e) {
             String launchedFromPackage;
@@ -1222,6 +1230,18 @@
         }
     }
 
+    private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
+        if (!hasWorkProfile() || currentUserHandle == getUser()) {
+            return;
+        }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
+                .setBoolean(currentUserHandle == getPersonalProfileUserHandle())
+                .setStrings(getMetricsCategory(),
+                        cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
+                .write();
+    }
+
 
     public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity,
             int userId) {
@@ -1425,7 +1445,8 @@
      * - The target app has declared it supports cross-profile communication via manifest metadata
      */
     private boolean maybeAutolaunchIfCrossProfileSupported() {
-        int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
+        ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
+        int count = activeListAdapter.getUnfilteredCount();
         if (count != 1) {
             return false;
         }
@@ -1434,7 +1455,7 @@
         if (inactiveListAdapter.getUnfilteredCount() != 1) {
             return false;
         }
-        TargetInfo activeProfileTarget = mMultiProfilePagerAdapter.getActiveListAdapter()
+        TargetInfo activeProfileTarget = activeListAdapter
                 .targetInfoForPosition(0, false);
         TargetInfo inactiveProfileTarget = inactiveListAdapter.targetInfoForPosition(0, false);
         if (!Objects.equals(activeProfileTarget.getResolvedComponentName(),
@@ -1449,6 +1470,11 @@
             return false;
         }
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
+                .setBoolean(activeListAdapter.getUserHandle() == getPersonalProfileUserHandle())
+                .setStrings(getMetricsCategory())
+                .write();
         safelyStartActivity(activeProfileTarget);
         finish();
         return true;
@@ -1530,6 +1556,11 @@
                 viewPager.setCurrentItem(1);
             }
             setupViewVisibilities();
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
+                    .setInt(viewPager.getCurrentItem())
+                    .setStrings(getMetricsCategory())
+                    .write();
         });
 
         viewPager.setVisibility(View.VISIBLE);
@@ -1698,6 +1729,10 @@
                 && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
     }
 
+    protected String getMetricsCategory() {
+        return METRICS_CATEGORY_RESOLVER;
+    }
+
     @Override // ResolverListCommunicator
     public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
         if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 96dc83a..21e7fd9 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -150,6 +150,11 @@
         return getListViewForIndex(1 - getCurrentPage());
     }
 
+    @Override
+    String getMetricsCategory() {
+        return ResolverActivity.METRICS_CATEGORY_RESOLVER;
+    }
+
     class ResolverProfileDescriptor extends ProfileDescriptor {
         private ResolverListAdapter resolverListAdapter;
         final ListView listView;
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 782b269..3d8108d 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -179,4 +179,11 @@
   PROVISIONING_DPC_SETUP_STARTED = 152;
   PROVISIONING_DPC_SETUP_COMPLETED = 153;
   PROVISIONING_ORGANIZATION_OWNED_MANAGED_PROFILE = 154;
+  RESOLVER_CROSS_PROFILE_TARGET_OPENED = 155;
+  RESOLVER_SWITCH_TABS = 156;
+  RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED = 157;
+  RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL= 158;
+  RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK= 159;
+  RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED= 160;
+  RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET = 161;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 488698a..6530036 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4953,16 +4953,6 @@
     <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
                 android:protectionLevel="signature|appPredictor" />
 
-    <!-- Feature Id for Country Detector. -->
-    <feature android:featureId="CountryDetector" android:label="@string/country_detector"/>
-    <!-- Feature Id for Location service. -->
-    <feature android:featureId="LocationService" android:label="@string/location_service"/>
-    <!-- Feature Id for Sensor Notification service. -->
-    <feature android:featureId="SensorNotificationService"
-             android:label="@string/sensor_notification_service"/>
-    <!-- Feature Id for Twilight service. -->
-    <feature android:featureId="TwilightService" android:label="@string/twilight_service"/>
-
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e7ad8eb..e6a93e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -421,14 +421,6 @@
          [CHAR LIMIT=NONE] -->
     <string name="location_changed_notification_text">Tap to see your location settings.</string>
 
-    <!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]-->
-    <string name="country_detector">Country Detector</string>
-    <!-- Feature Id for Location service. [CHAR LIMIT=NONE]-->
-    <string name="location_service">Location Service</string>
-    <!-- Feature Id for Sensor Notification service. [CHAR LIMIT=NONE]-->
-    <string name="sensor_notification_service">Sensor Notification Service</string>
-    <!-- Feature Id for Twilight service. [CHAR LIMIT=NONE]-->
-    <string name="twilight_service">Twilight Service</string>
 
     <!-- Factory reset warning dialog strings--> <skip />
     <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
index 1f831bb..b41f90c 100644
--- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -69,6 +69,13 @@
             + "z\n"
             + "@right\n"
             + "@bind_right_cutout\n"
+            + "@bottom\n"
+            + "M 0,0\n"
+            + "h -24\n"
+            + "v -48\n"
+            + "h 48\n"
+            + "v 48\n"
+            + "z\n"
             + "@dp";
     private static final String CORNER_CUTOUT_SPECIFICATION = "M 0,0\n"
             + "h 1\n"
@@ -141,13 +148,66 @@
     }
 
     @Test
+    public void parse_withBindMarker_shouldHaveTopBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+    }
+
+    @Test
     public void parse_withBindMarker_shouldHaveRightBound() {
         CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
         assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(912, 960, 1080, 1128));
     }
 
     @Test
-    public void parse_tallCutout_shouldBeDone() {
+    public void parse_withBindMarker_shouldHaveBottomBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(456, 1752, 624, 1920));
+    }
+
+    @Test
+    public void parse_withBindMarker_shouldMatchExpectedSafeInset() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 168, 168, 168));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveLeftBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getLeftBound()).isEqualTo(new Rect(0, 540, 168, 708));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveTopBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveRightBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(1752, 540, 1920, 708));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveBottomBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(876, 912, 1044, 1080));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldMatchExpectedSafeInset() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 0, 168, 168));
+    }
+
+    @Test
+    public void parse_tallCutout_topBoundShouldMatchExpectedHeight() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -48, 0\n"
                 + "L -44.3940446283, 36.0595537175\n"
@@ -162,7 +222,7 @@
     }
 
     @Test
-    public void parse_wideCutout_shouldBeDone() {
+    public void parse_wideCutout_topBoundShouldMatchExpectedWidth() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -72, 0\n"
                 + "L -69.9940446283, 20.0595537175\n"
@@ -177,7 +237,7 @@
     }
 
     @Test
-    public void parse_narrowCutout_shouldBeDone() {
+    public void parse_narrowCutout_topBoundShouldHaveExpectedWidth() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -24, 0\n"
                 + "L -21.9940446283, 20.0595537175\n"
@@ -192,7 +252,7 @@
     }
 
     @Test
-    public void parse_doubleCutout_shouldBeDone() {
+    public void parse_doubleCutout_topBoundShouldHaveExpectedHeight() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -72, 0\n"
                 + "L -69.9940446283, 20.0595537175\n"
@@ -217,7 +277,7 @@
     }
 
     @Test
-    public void parse_cornerCutout_shouldBeDone() {
+    public void parse_cornerCutout_topBoundShouldHaveExpectedHeight() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -48, 0\n"
                 + "C -48,48 -48,48 0,48\n"
@@ -229,7 +289,7 @@
     }
 
     @Test
-    public void parse_holeCutout_shouldBeDone() {
+    public void parse_holeCutout_shouldMatchExpectedInset() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 20.0,20.0\n"
                 + "h 136\n"
                 + "v 136\n"
@@ -259,4 +319,38 @@
         assertThat(cutoutSpecification.getSafeInset())
                 .isEqualTo(new Rect(6, 0, 8, 0));
     }
+
+    @Test
+    public void parse_bottomLeftSpec_withBindLeftMarker_shouldBeLeftBound() {
+        CutoutSpecification cutoutSpecification =
+                new CutoutSpecification.Parser(2f, 400, 200)
+                        .parse("@bottom"
+                                + "M 0,0\n"
+                                + "v -10\n"
+                                + "h 10\n"
+                                + "v 10\n"
+                                + "z\n"
+                                + "@left\n"
+                                + "@bind_left_cutout");
+
+        assertThat(cutoutSpecification.getLeftBound())
+                .isEqualTo(new Rect(0, 190, 10, 200));
+    }
+
+    @Test
+    public void parse_bottomRightSpec_withBindRightMarker_shouldBeRightBound() {
+        CutoutSpecification cutoutSpecification =
+                new CutoutSpecification.Parser(2f, 400, 200)
+                        .parse("@bottom"
+                                + "M 0,0\n"
+                                + "v -10\n"
+                                + "h 10\n"
+                                + "v 10\n"
+                                + "z\n"
+                                + "@right\n"
+                                + "@bind_right_cutout");
+
+        assertThat(cutoutSpecification.getRightBound())
+                .isEqualTo(new Rect(390, 190, 400, 200));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 248e5fe..60eda06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -112,7 +113,8 @@
     boolean mSkipPost = false;
 
     @Retention(SOURCE)
-    @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE})
+    @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
+            ACTION_UNBUBBLE, ACTION_SETTINGS})
     private @interface Action {}
     static final int ACTION_BUBBLE = 0;
     static final int ACTION_HOME = 1;
@@ -128,8 +130,6 @@
             mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
         } else {
             mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
-            Settings.Global.putInt(
-                    mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1);
         }
         closeControls(v, true);
     };
@@ -225,7 +225,9 @@
             mShortcutInfo = shortcuts.get(0);
         }
 
-        mIsBubbleable = mEntry.getBubbleMetadata() != null;
+        mIsBubbleable = mEntry.getBubbleMetadata() != null
+            && Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.NOTIFICATION_BUBBLES, 0) == 1;
         mStartedAsBubble = mEntry.isBubble();
 
         createConversationChannelIfNeeded();
@@ -382,6 +384,11 @@
         ((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
     }
 
+    private boolean bubbleImportantConversations() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                BUBBLE_IMPORTANT_CONVERSATIONS, 1) == 1;
+    }
+
     private void bindDelegate() {
         TextView delegateView = findViewById(R.id.delegate_name);
 
@@ -579,6 +586,10 @@
                     case ACTION_FAVORITE:
                         mChannelToUpdate.setImportantConversation(
                                 !mChannelToUpdate.isImportantConversation());
+                        if (mChannelToUpdate.isImportantConversation()
+                                && bubbleImportantConversations()) {
+                            mChannelToUpdate.setAllowBubbles(true);
+                        }
                         break;
                     case ACTION_MUTE:
                         if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 27b263f..138ea39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -21,6 +21,8 @@
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
@@ -55,6 +57,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Icon;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -449,6 +452,7 @@
 
     @Test
     public void testBindNotification_bubbleActionVisibleWhenCanBubble()  {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -469,7 +473,8 @@
     }
 
     @Test
-    public void testBindNotification_bubbleActionVisibleWhenCannotBubble()  {
+    public void testBindNotification_bubbleAction_noBubbleMetadata()  {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -490,6 +495,28 @@
     }
 
     @Test
+    public void testBindNotification_bubbleActionGloballyOff()  {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0);
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mLauncherApps,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mBubbleEntry,
+                null,
+                null,
+                null,
+                mIconFactory,
+                true);
+
+        View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
+        assertEquals(View.GONE, bubbleView.getVisibility());
+    }
+
+    @Test
     public void testAddToHome() throws Exception {
         when(mShortcutManager.isRequestPinShortcutSupported()).thenReturn(true);
 
@@ -550,6 +577,7 @@
 
     @Test
     public void testBubble_promotesBubble() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationChannel.setAllowBubbles(false);
         mConversationChannel.setAllowBubbles(false);
 
@@ -584,6 +612,7 @@
 
     @Test
     public void testBubble_demotesBubble() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
 
         mNotificationInfo.bindNotification(
@@ -617,6 +646,7 @@
 
     @Test
     public void testBubble_noChannelChange() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -645,7 +675,11 @@
     }
 
     @Test
-    public void testFavorite_favorite() throws Exception {
+    public void testFavorite_favorite_noBubble() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                BUBBLE_IMPORTANT_CONVERSATIONS, 0);
+        mNotificationChannel.setAllowBubbles(false);
+        mConversationChannel.setAllowBubbles(false);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -673,6 +707,44 @@
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 anyString(), anyInt(), captor.capture());
         assertTrue(captor.getValue().isImportantConversation());
+        assertFalse(captor.getValue().canBubble());
+        verify(mBubbleController, never()).onUserCreatedBubbleFromNotification(mEntry);
+    }
+
+    @Test
+    public void testFavorite_favorite_bubble() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                BUBBLE_IMPORTANT_CONVERSATIONS, 1);
+        mNotificationChannel.setAllowBubbles(false);
+        mConversationChannel.setAllowBubbles(false);
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mLauncherApps,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                null,
+                mIconFactory,
+                true);
+
+        ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+        assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
+                fave.getContentDescription().toString());
+
+        fave.performClick();
+        mTestableLooper.processAllMessages();
+
+        ArgumentCaptor<NotificationChannel> captor =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), anyInt(), captor.capture());
+        assertTrue(captor.getValue().isImportantConversation());
+        assertTrue(captor.getValue().canBubble());
     }
 
     @Test
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 207a6aa..6852fbf 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -194,8 +194,6 @@
     // time
     private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
 
-    private static final String FEATURE_ID = "LocationService";
-
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
     private final Object mLock = new Object();
@@ -245,7 +243,7 @@
     private int mBatterySaverMode;
 
     private LocationManagerService(Context context) {
-        mContext = context.createFeatureContext(FEATURE_ID);
+        mContext = context;
         mHandler = FgThread.getHandler();
         mLocalService = new LocalService();
 
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 9082dca..7f5befa 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -16,8 +16,10 @@
 
 package com.android.server;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.hardware.GeomagneticField;
 import android.hardware.Sensor;
 import android.hardware.SensorAdditionalInfo;
@@ -29,7 +31,9 @@
 import android.location.LocationManager;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Slog;
 
 public class SensorNotificationService extends SystemService
@@ -48,8 +52,6 @@
 
     private static final long MILLIS_2010_1_1 = 1262358000000l;
 
-    private static final String FEATURE_ID = "SensorNotificationService";
-
     private Context mContext;
     private SensorManager mSensorManager;
     private LocationManager mLocationManager;
@@ -59,8 +61,8 @@
     private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
 
     public SensorNotificationService(Context context) {
-        super(context.createFeatureContext(FEATURE_ID));
-        mContext = getContext();
+        super(context);
+        mContext = context;
     }
 
     public void onStart() {
diff --git a/services/core/java/com/android/server/location/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java
index b158388..8326ef9 100644
--- a/services/core/java/com/android/server/location/CountryDetectorBase.java
+++ b/services/core/java/com/android/server/location/CountryDetectorBase.java
@@ -31,15 +31,13 @@
  * @hide
  */
 public abstract class CountryDetectorBase {
-    private static final String FEATURE_ID = "CountryDetector";
-
     protected final Handler mHandler;
     protected final Context mContext;
     protected CountryListener mListener;
     protected Country mDetectedCountry;
 
-    public CountryDetectorBase(Context context) {
-        mContext = context.createFeatureContext(FEATURE_ID);
+    public CountryDetectorBase(Context ctx) {
+        mContext = ctx;
         mHandler = new Handler();
     }
 
@@ -47,7 +45,7 @@
      * Start detecting the country that the user is in.
      *
      * @return the country if it is available immediately, otherwise null should
-     * be returned.
+     *         be returned.
      */
     public abstract Country detectCountry();
 
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 05867ba..f7e1398 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -214,6 +214,8 @@
      * Updates the mSessionInfo. Returns true if the session info is changed.
      */
     boolean updateSessionInfosIfNeededLocked() {
+        // Prevent to execute this method before mBtRouteProvider is created.
+        if (mBtRouteProvider == null) return false;
         RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
 
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fd86f1d..0d402e5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3440,6 +3440,27 @@
         }
 
         @Override
+        public ParceledListSlice<ConversationChannelWrapper> getConversations(
+                boolean onlyImportant) {
+            enforceSystemOrSystemUI("getConversations");
+            ArrayList<ConversationChannelWrapper> conversations =
+                    mPreferencesHelper.getConversations(onlyImportant);
+            for (ConversationChannelWrapper conversation : conversations) {
+                LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+                        .setPackage(conversation.getPkg())
+                        .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+                        .setShortcutIds(Arrays.asList(
+                                conversation.getNotificationChannel().getConversationId()));
+                List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
+                        query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
+                if (shortcuts != null && !shortcuts.isEmpty()) {
+                    conversation.setShortcutInfo(shortcuts.get(0));
+                }
+            }
+            return new ParceledListSlice<>(conversations);
+        }
+
+        @Override
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
                 String pkg, int uid, boolean includeDeleted) {
             enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 20c8625..b8186ed 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1186,6 +1186,44 @@
         return groups;
     }
 
+    public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
+        synchronized (mPackagePreferences) {
+            ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
+
+            for (PackagePreferences p : mPackagePreferences.values()) {
+                int N = p.channels.size();
+                for (int i = 0; i < N; i++) {
+                    final NotificationChannel nc = p.channels.valueAt(i);
+                    if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
+                            && (nc.isImportantConversation() || !onlyImportant)) {
+                        ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+                        conversation.setPkg(p.pkg);
+                        conversation.setUid(p.uid);
+                        conversation.setNotificationChannel(nc);
+                        conversation.setParentChannelLabel(
+                                p.channels.get(nc.getParentChannelId()).getName());
+                        boolean blockedByGroup = false;
+                        if (nc.getGroup() != null) {
+                            NotificationChannelGroup group = p.groups.get(nc.getGroup());
+                            if (group != null) {
+                                if (group.isBlocked()) {
+                                    blockedByGroup = true;
+                                } else {
+                                    conversation.setGroupLabel(group.getName());
+                                }
+                            }
+                        }
+                        if (!blockedByGroup) {
+                            conversations.add(conversation);
+                        }
+                    }
+                }
+            }
+
+            return conversations;
+        }
+    }
+
     public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
         Objects.requireNonNull(pkg);
         synchronized (mPackagePreferences) {
@@ -1199,6 +1237,8 @@
                 final NotificationChannel nc = r.channels.valueAt(i);
                 if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
                     ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+                    conversation.setPkg(r.pkg);
+                    conversation.setUid(r.uid);
                     conversation.setNotificationChannel(nc);
                     conversation.setParentChannelLabel(
                             r.channels.get(nc.getParentChannelId()).getName());
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 761fbf8..e72ba8d 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -50,7 +50,6 @@
         implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
 
     private static final String TAG = "TwilightService";
-    private static final String FEATURE_ID = "TwilightService";
     private static final boolean DEBUG = false;
 
     private static final int MSG_START_LISTENING = 1;
@@ -74,7 +73,7 @@
     protected TwilightState mLastTwilightState;
 
     public TwilightService(Context context) {
-        super(context.createFeatureContext(FEATURE_ID));
+        super(context);
         mHandler = new Handler(Looper.getMainLooper(), this);
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0adf15c..7f9732b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -108,6 +108,7 @@
     private static final int UID_N_MR1 = 0;
     private static final UserHandle USER = UserHandle.of(0);
     private static final int UID_O = 1111;
+    private static final int UID_P = 2222;
     private static final String SYSTEM_PKG = "android";
     private static final int SYSTEM_UID = 1000;
     private static final UserHandle USER2 = UserHandle.of(10);
@@ -141,9 +142,11 @@
         upgrade.targetSdkVersion = Build.VERSION_CODES.O;
         when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
         when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
+        when(mPm.getApplicationInfoAsUser(eq(PKG_P), anyInt(), anyInt())).thenReturn(upgrade);
         when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
         when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
         when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
+        when(mPm.getPackageUidAsUser(eq(PKG_P), anyInt())).thenReturn(UID_P);
         when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
         PackageInfo info = mock(PackageInfo.class);
         info.signatures = new Signature[] {mock(Signature.class)};
@@ -2945,6 +2948,92 @@
     }
 
     @Test
+    public void testGetConversations_all() {
+        String convoId = "convo";
+        NotificationChannel messages =
+                new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+        NotificationChannel calls =
+                new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+        NotificationChannel p =
+                new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+        channel.setConversationId(messages.getId(), convoId);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        NotificationChannel diffConvo =
+                new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+        diffConvo.setConversationId(p.getId(), "different convo");
+        mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+        channel2.setConversationId(calls.getId(), convoId);
+        channel2.setImportantConversation(true);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+        List<ConversationChannelWrapper> convos = mHelper.getConversations(false);
+
+        assertEquals(3, convos.size());
+        assertTrue(conversationWrapperContainsChannel(convos, channel));
+        assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+        assertTrue(conversationWrapperContainsChannel(convos, channel2));
+    }
+
+    @Test
+    public void testGetConversations_onlyImportant() {
+        String convoId = "convo";
+        NotificationChannel messages =
+                new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+        NotificationChannel calls =
+                new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+        NotificationChannel p =
+                new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+        channel.setConversationId(messages.getId(), convoId);
+        channel.setImportantConversation(true);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        NotificationChannel diffConvo =
+                new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+        diffConvo.setConversationId(p.getId(), "different convo");
+        diffConvo.setImportantConversation(true);
+        mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+        channel2.setConversationId(calls.getId(), convoId);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+        List<ConversationChannelWrapper> convos = mHelper.getConversations(true);
+
+        assertEquals(2, convos.size());
+        assertTrue(conversationWrapperContainsChannel(convos, channel));
+        assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+        assertFalse(conversationWrapperContainsChannel(convos, channel2));
+    }
+
+    private boolean conversationWrapperContainsChannel(List<ConversationChannelWrapper> list,
+            NotificationChannel expected) {
+        for (ConversationChannelWrapper ccw : list) {
+            if (ccw.getNotificationChannel().equals(expected)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Test
     public void testGetConversations_invalidPkg() {
         assertThat(mHelper.getConversations("bad", 1)).isEmpty();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b5663bd..851b052 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -260,4 +260,9 @@
     public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
         return this;
     }
+
+    @Override
+    public SurfaceControl.Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) {
+        return this;
+    }
 }