diff options
10 files changed, 201 insertions, 23 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 7af7a4adc748..f6014e5fdf80 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -95,7 +95,7 @@ interface INotificationManager void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel); NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId); NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, boolean returnParentIfNoConversationChannel, String conversationId); - void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId); + void createConversationNotificationChannelForPackage(String pkg, int uid, String triggeringKey, in NotificationChannel parentChannel, String conversationId); NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted); void deleteNotificationChannel(String pkg, String channelId); void deleteConversationNotificationChannels(String pkg, int uid, String conversationId); diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 389040cea864..4f400a8e9cb2 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -16,6 +16,9 @@ package android.service.notification; +import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; +import static android.util.FeatureFlagUtils.*; + import android.annotation.NonNull; import android.app.Notification; import android.app.NotificationManager; @@ -29,6 +32,8 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -436,6 +441,19 @@ public class StatusBarNotification implements Parcelable { return logMaker; } + /** + * @hide + */ + public String getShortcutId(Context context) { + String conversationId = getNotification().getShortcutId(); + if (isEnabled(context, NOTIF_CONVO_BYPASS_SHORTCUT_REQ) + && getNotification().getNotificationStyle() == Notification.MessagingStyle.class + && TextUtils.isEmpty(conversationId)) { + conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID; + } + return conversationId; + } + private String getGroupLogTag() { return shortenTag(getGroup()); } 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 6d3f3f49a8e8..64bdd9799ad6 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 @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.notification.row; import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; -import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; @@ -103,7 +102,6 @@ public class NotificationConversationInfo extends LinearLayout implements private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; - private int mStartingChannelImportance; private boolean mStartedAsBubble; private boolean mIsBubbleable; @@ -212,11 +210,10 @@ public class NotificationConversationInfo extends LinearLayout implements mLauncherApps = launcherApps; mConversationId = mNotificationChannel.getConversationId(); if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) { - mConversationId = mSbn.getNotification().getShortcutId(); + mConversationId = mSbn.getShortcutId(mContext); } - // TODO: flag this when flag exists if (TextUtils.isEmpty(mConversationId)) { - mConversationId = mSbn.getId() + mSbn.getTag() + PLACEHOLDER_CONVERSATION_ID; + throw new IllegalArgumentException("Does not have required information"); } // TODO: consider querying this earlier in the notification pipeline and passing it in LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery() @@ -243,10 +240,9 @@ public class NotificationConversationInfo extends LinearLayout implements // a custom channel if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) { try { - // TODO: associate this key with this channel service side so the customization - // isn't forgotten on the next update mINotificationManager.createConversationNotificationChannelForPackage( - mPackageName, mAppUid, mNotificationChannel, mConversationId); + mPackageName, mAppUid, mSbn.getKey(), mNotificationChannel, + mConversationId); mNotificationChannel = mINotificationManager.getConversationNotificationChannel( mContext.getOpPackageName(), UserHandle.getUserId(mAppUid), mPackageName, mNotificationChannel.getId(), false, mConversationId); @@ -363,7 +359,6 @@ public class NotificationConversationInfo extends LinearLayout implements image.setImageDrawable(mLauncherApps.getShortcutBadgedIconDrawable(mShortcutInfo, mContext.getResources().getDisplayMetrics().densityDpi)); } else { - // TODO: flag this behavior if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) { // TODO: maybe use a generic group icon, or a composite of recent senders image.setImageDrawable(mPm.getDefaultActivityIcon()); @@ -391,7 +386,6 @@ public class NotificationConversationInfo extends LinearLayout implements if (mShortcutInfo != null) { name.setText(mShortcutInfo.getShortLabel()); } else { - // TODO: flag this behavior Bundle extras = mSbn.getNotification().extras; String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE); if (TextUtils.isEmpty(nameString)) { 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 61f0b265e874..9ae477ee717d 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 @@ -797,7 +797,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { null, true); verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage( - anyString(), anyInt(), any(), eq(CONVERSATION_ID)); + anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); } @Test @@ -817,7 +817,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { null, true); verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( - anyString(), anyInt(), any(), eq(CONVERSATION_ID)); + anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); } @Test diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java index eaf120ee2755..0e14364b4280 100644 --- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java @@ -15,7 +15,10 @@ */ package com.android.server.notification; +import android.app.Notification; +import android.app.NotificationChannel; import android.content.Context; +import android.util.FeatureFlagUtils; import android.util.Slog; /** @@ -26,8 +29,10 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor private static final boolean DBG = false; private RankingConfig mConfig; + private Context mContext; public void initialize(Context ctx, NotificationUsageStats usageStats) { + mContext = ctx; if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); } @@ -41,11 +46,11 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor if (DBG) Slog.d(TAG, "missing config"); return null; } - - record.updateNotificationChannel(mConfig.getConversationNotificationChannel( + NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel( record.sbn.getPackageName(), record.sbn.getUid(), record.getChannel().getId(), - record.getNotification().getShortcutId(), true, false)); + record.sbn.getShortcutId(mContext), true, false); + record.updateNotificationChannel(updatedChannel); return null; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 1d493647304f..385d84a93fbd 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -208,6 +208,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.FeatureFlagUtils; import android.util.IntArray; import android.util.Log; import android.util.Pair; @@ -3038,7 +3039,7 @@ public class NotificationManagerService extends SystemService { @Override public void createConversationNotificationChannelForPackage(String pkg, int uid, - NotificationChannel parentChannel, String conversationId) { + String triggeringKey, NotificationChannel parentChannel, String conversationId) { enforceSystemOrSystemUI("only system can call this"); Preconditions.checkNotNull(parentChannel); Preconditions.checkNotNull(conversationId); @@ -3049,6 +3050,8 @@ public class NotificationManagerService extends SystemService { conversationChannel.setConversationId(parentId, conversationId); createNotificationChannelsImpl( pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel))); + mRankingHandler.requestSort(); + handleSavePolicyFile(); } @Override @@ -5257,9 +5260,10 @@ public class NotificationManagerService extends SystemService { if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) { channelId = (new Notification.TvExtender(notification)).getChannelId(); } - // TODO: flag this behavior String shortcutId = notification.getShortcutId(); - if (shortcutId == null + if (FeatureFlagUtils.isEnabled(getContext(), + FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ) + && shortcutId == null && notification.getNotificationStyle() == Notification.MessagingStyle.class) { shortcutId = id + tag + NotificationChannel.PLACEHOLDER_CONVERSATION_ID; } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 185d75c754a2..b0c1863ea31d 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; @@ -40,6 +41,7 @@ import android.service.notification.RankingHelperProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.FeatureFlagUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; @@ -139,6 +141,8 @@ public class PreferencesHelper implements RankingConfig { private boolean mAreChannelsBypassingDnd; private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS; + private boolean mAllowInvalidShortcuts = false; + private static final String BADGING_FORCED_TRUE = "force_badging_true_for_bug"; // STOPSHIP (b/142218092) this should be removed before ship @@ -164,6 +168,8 @@ public class PreferencesHelper implements RankingConfig { updateBadgingEnabled(); updateBubblesEnabled(); syncChannelsBypassingDnd(mContext.getUserId()); + mAllowInvalidShortcuts = FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ); } public void readXml(XmlPullParser parser, boolean forRestore, int userId) @@ -266,7 +272,14 @@ public class PreferencesHelper implements RankingConfig { } channel.setImportanceLockedByCriticalDeviceFunction( r.defaultAppLockedImportance); - r.channels.put(id, channel); + boolean isInvalidShortcutChannel = + channel.getConversationId() != null && + channel.getConversationId().contains( + PLACEHOLDER_CONVERSATION_ID); + if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts + && !isInvalidShortcutChannel)) { + r.channels.put(id, channel); + } } } // Delegate diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java index f5af3ec76788..d16c232afea9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java @@ -18,6 +18,7 @@ package com.android.server.notification; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; @@ -30,6 +31,7 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.NotificationChannel; import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import com.android.server.UiServiceTestCase; @@ -52,6 +54,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { public void testExtractsUpdatedChannel() { NotificationChannelExtractor extractor = new NotificationChannelExtractor(); extractor.setConfig(mConfig); + extractor.initialize(mContext, null); NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); final Notification.Builder builder = new Notification.Builder(getContext()) @@ -71,4 +74,62 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertNull(extractor.process(r)); assertEquals(updatedChannel, r.getChannel()); } + + @Test + public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + + NotificationChannelExtractor extractor = new NotificationChannelExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); + final Notification.Builder builder = new Notification.Builder(getContext()) + .setContentTitle("foo") + .setStyle(new Notification.MessagingStyle("name")) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0, + 0, n, UserHandle.ALL, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + + NotificationChannel updatedChannel = + new NotificationChannel("a", "", IMPORTANCE_HIGH); + when(mConfig.getConversationNotificationChannel( + any(), anyInt(), eq("a"), eq(r.sbn.getShortcutId(mContext)), eq(true), eq(false))) + .thenReturn(updatedChannel); + + assertNull(extractor.process(r)); + assertEquals(updatedChannel, r.getChannel()); + } + + @Test + public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + + NotificationChannelExtractor extractor = new NotificationChannelExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); + final Notification.Builder builder = new Notification.Builder(getContext()) + .setContentTitle("foo") + .setStyle(new Notification.MessagingStyle("name")) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0, + 0, n, UserHandle.ALL, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + + NotificationChannel updatedChannel = + new NotificationChannel("a", "", IMPORTANCE_HIGH); + when(mConfig.getConversationNotificationChannel( + any(), anyInt(), eq("a"), eq(null), eq(true), eq(false))) + .thenReturn(updatedChannel); + + assertNull(extractor.process(r)); + assertEquals(updatedChannel, r.getChannel()); + } } 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 2ac464251c73..c6c64c9e0f6b 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5834,7 +5834,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList( orig))); - mBinderService.createConversationNotificationChannelForPackage(PKG, mUid, orig, "friend"); + mBinderService.createConversationNotificationChannelForPackage( + PKG, mUid, "key", orig, "friend"); NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel( PKG, 0, PKG, original.getId(), false, "friend"); @@ -5869,9 +5870,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { String conversationId = "friend"; mBinderService.createConversationNotificationChannelForPackage( - PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel), conversationId); + PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(msgParcel), + conversationId); mBinderService.createConversationNotificationChannelForPackage( - PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel), + PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(callParcel), conversationId); NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel( 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 6f1657441fb5..c1c74da50c7c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -22,6 +22,7 @@ import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ; import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT; @@ -2858,4 +2859,84 @@ public class PreferencesHelperTest extends UiServiceTestCase { // good } } + + @Test + public void testPlaceholderConversationId_flagOn() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } + + @Test + public void testPlaceholderConversationId_flagOff() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } + + @Test + public void testNormalConversationId_flagOff() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } + + @Test + public void testNoConversationId_flagOff() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } } |