From 5c388cd605598db18dd03e8265b2a09aff54e3b4 Mon Sep 17 00:00:00 2001 From: Ned Burns Date: Thu, 8 Aug 2019 13:43:57 -0400 Subject: [PATCH 1/2] Ensure isTopBucket() gets set when there is just one notification My descendants will vilify this CL for generations to come. We'll clean it up for R, but this is our last, best hope for fixing things in Q. Bug: 138775282 Test: manual Change-Id: I615b2f7fddca30dae67dbaab0e5d54a824a4c441 Merged-In: I615b2f7fddca30dae67dbaab0e5d54a824a4c441 (cherry picked from commit 2d35980e7201e042e253fadd0eb55e1866fc28d9) (cherry picked from commit 6c34506dd8d1f917933f31183302c86a26d474a2) --- .../collection/NotificationData.java | 9 ++- .../collection/NotificationDataTest.java | 66 ++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index 13f8f1a6a5488..fb100842f969c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -422,7 +422,14 @@ public class NotificationData { } } - Collections.sort(mSortedAndFiltered, mRankingComparator); + if (mSortedAndFiltered.size() == 1) { + // HACK: We need the comparator to run on all children in order to set the + // isHighPriority field. If there is only one child, then the comparison won't be run, + // so we have to trigger it manually. Get rid of this code as soon as possible. + mRankingComparator.compare(mSortedAndFiltered.get(0), mSortedAndFiltered.get(0)); + } else { + Collections.sort(mSortedAndFiltered, mRankingComparator); + } } public void dump(PrintWriter pw, String indent) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java index 6e0ddbf0cc463..f629757e4c683 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java @@ -23,6 +23,7 @@ import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.CATEGORY_EVENT; import static android.app.Notification.CATEGORY_MESSAGE; import static android.app.Notification.CATEGORY_REMINDER; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; @@ -62,6 +63,8 @@ import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.ArraySet; +import androidx.test.filters.SmallTest; + import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; import com.android.systemui.InitController; @@ -84,8 +87,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.test.filters.SmallTest; - @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -113,6 +114,7 @@ public class NotificationDataTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL); when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification); + when(mMockStatusBarNotification.getKey()).thenReturn("mock_key"); when(mMockPackageManager.checkUidPermission( eq(Manifest.permission.NOTIFICATION_DURING_SETUP), @@ -231,6 +233,7 @@ public class NotificationDataTest extends SysuiTestCase { Notification n = mMockStatusBarNotification.getNotification(); n.flags = Notification.FLAG_FOREGROUND_SERVICE; NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); + entry.setRow(mRow); mNotificationData.add(entry); Bundle override = new Bundle(); override.putInt(OVERRIDE_VIS_EFFECTS, 255); @@ -249,6 +252,7 @@ public class NotificationDataTest extends SysuiTestCase { n = nb.build(); when(mMockStatusBarNotification.getNotification()).thenReturn(n); NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); + entry.setRow(mRow); mNotificationData.add(entry); Bundle override = new Bundle(); override.putInt(OVERRIDE_VIS_EFFECTS, 255); @@ -262,6 +266,7 @@ public class NotificationDataTest extends SysuiTestCase { public void testIsExemptFromDndVisualSuppression_system() { initStatusBarNotification(false); NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); + entry.setRow(mRow); entry.mIsSystemNotification = true; mNotificationData.add(entry); Bundle override = new Bundle(); @@ -276,6 +281,7 @@ public class NotificationDataTest extends SysuiTestCase { public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() { initStatusBarNotification(false); NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); + entry.setRow(mRow); entry.mIsSystemNotification = true; Bundle override = new Bundle(); override.putInt(OVERRIDE_VIS_EFFECTS, NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT); @@ -528,6 +534,62 @@ public class NotificationDataTest extends SysuiTestCase { assertEquals(-1, mNotificationData.mRankingComparator.compare(a, b)); } + @Test + public void testSort_properlySetsIsTopBucket() { + + Notification notification = new Notification.Builder(mContext, "test") + .build(); + StatusBarNotification sbn = new StatusBarNotification( + "pkg", + "pkg", + 0, + "tag", + 0, + 0, + notification, + mContext.getUser(), + "", + 0); + + Bundle override = new Bundle(); + override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_DEFAULT); + mNotificationData.rankingOverrides.put(sbn.getKey(), override); + + NotificationEntry entry = new NotificationEntry(sbn); + entry.setRow(mRow); + mNotificationData.add(entry); + + assertTrue(entry.isTopBucket()); + } + + @Test + public void testSort_properlySetsIsNotTopBucket() { + Notification notification = new Notification.Builder(mContext, "test") + .build(); + StatusBarNotification sbn = new StatusBarNotification( + "pkg", + "pkg", + 0, + "tag", + 0, + 0, + notification, + mContext.getUser(), + "", + 0); + + Bundle override = new Bundle(); + override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW); + mNotificationData.rankingOverrides.put(sbn.getKey(), override); + + NotificationEntry entry = new NotificationEntry(sbn); + entry.setRow(mRow); + + mNotificationData.add(entry); + + assertFalse(entry.isTopBucket()); + } + private void initStatusBarNotification(boolean allowDuringSetup) { Bundle bundle = new Bundle(); bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup); From ec177f86e1dbfd81bc2374e6214153028ebe0c3b Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Fri, 26 Jul 2019 14:21:21 -0700 Subject: [PATCH 2/2] apply empty transaction in startActivitySync Force SurfaceFlinger to update synchronously by applying an empty transaction. Without this, SurfaceFlinger will only update after the next vsync, which may only happen after the instrumentation already starts. Since InputFlinger will only have the proper InputWindowInfo after SurfaceFlinger updates, waiting for the vsync caused some instruemntation tests to be flaky. Bug: 138263890 Test: atest android.view.cts.HoverTest Change-Id: I5457ab67ac574530dc1aa84549ca11e7e3f0d714 (cherry picked from commit 03dde38efcb3c06c6317f9fcf1265e37a66811d9) --- core/java/android/app/Instrumentation.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 41733b3af0584..9720e9f47f832 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -50,6 +50,7 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.ViewConfiguration; import android.view.Window; import android.view.WindowManagerGlobal; @@ -528,6 +529,12 @@ public class Instrumentation { } while (mWaitingActivities.contains(aw)); waitForEnterAnimationComplete(aw.activity); + + // Apply an empty transaction to ensure SF has a chance to update before + // the Activity is ready (b/138263890). + try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { + t.apply(true); + } return aw.activity; } }