Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +95 −33 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.util.Log; import androidx.annotation.Nullable; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; Loading @@ -36,6 +37,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; Loading @@ -58,6 +60,7 @@ import javax.inject.Inject; */ public class NotificationLogger implements StateListener { private static final String TAG = "NotificationLogger"; private static final boolean DEBUG = false; /** The minimum delay in ms between reports of notification visibility. */ private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; Loading @@ -79,7 +82,12 @@ public class NotificationLogger implements StateListener { private long mLastVisibilityReportUptimeMs; private NotificationListContainer mListContainer; private final Object mDozingLock = new Object(); private boolean mDozing; @GuardedBy("mDozingLock") private Boolean mDozing = null; // Use null to indicate state is not yet known @GuardedBy("mDozingLock") private Boolean mLockscreen = null; // Use null to indicate state is not yet known private Boolean mPanelExpanded = null; // Use null to indicate state is not yet known private boolean mLogging = false; protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener = new OnChildLocationsChangedListener() { Loading Loading @@ -247,6 +255,11 @@ public class NotificationLogger implements StateListener { } public void stopNotificationLogging() { if (mLogging) { mLogging = false; if (DEBUG) { Log.i(TAG, "stopNotificationLogging: log notifications invisible"); } // Report all notifications as invisible and turn down the // reporter. if (!mCurrentlyVisibleNotifications.isEmpty()) { Loading @@ -257,8 +270,14 @@ public class NotificationLogger implements StateListener { mHandler.removeCallbacks(mVisibilityReporter); mListContainer.setChildLocationsChangedListener(null); } } public void startNotificationLogging() { if (!mLogging) { mLogging = true; if (DEBUG) { Log.i(TAG, "startNotificationLogging"); } mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener); // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't // cause the scroller to emit child location events. Hence generate Loading @@ -267,13 +286,13 @@ public class NotificationLogger implements StateListener { // (Note that in cases where the scroller does emit events, this // additional event doesn't break anything.) mNotificationLocationsChangedListener.onChildLocationsChanged(); mNotificationPanelLogger.logPanelShown(mListContainer.hasPulsingNotifications(), mEntryManager.getVisibleNotifications()); } } private void setDozing(boolean dozing) { synchronized (mDozingLock) { mDozing = dozing; maybeUpdateLoggingStatus(); } } Loading Loading @@ -343,11 +362,6 @@ public class NotificationLogger implements StateListener { for (int i = 0; i < N; i++) { newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; } synchronized (mDozingLock) { // setNotificationsShown should only be called if we are confident that // the user has seen the notification, aka not when ambient display is on if (!mDozing) { // TODO: Call NotificationEntryManager to do this, once it exists. // TODO: Consider not catching all runtime exceptions here. try { Loading @@ -356,8 +370,6 @@ public class NotificationLogger implements StateListener { Log.d(TAG, "failed setNotificationsShown: ", e); } } } } recycleAllVisibilityObjects(newlyVisibleAr); recycleAllVisibilityObjects(noLongerVisibleAr); }); Loading Loading @@ -400,14 +412,64 @@ public class NotificationLogger implements StateListener { @Override public void onStateChanged(int newState) { // don't care about state change if (DEBUG) { Log.i(TAG, "onStateChanged: new=" + newState); } synchronized (mDozingLock) { mLockscreen = (newState == StatusBarState.KEYGUARD || newState == StatusBarState.SHADE_LOCKED); } } @Override public void onDozingChanged(boolean isDozing) { if (DEBUG) { Log.i(TAG, "onDozingChanged: new=" + isDozing); } setDozing(isDozing); } @GuardedBy("mDozingLock") private void maybeUpdateLoggingStatus() { if (mPanelExpanded == null || mDozing == null) { if (DEBUG) { Log.i(TAG, "Panel status unclear: panelExpandedKnown=" + (mPanelExpanded == null) + " dozingKnown=" + (mDozing == null)); } return; } // Once we know panelExpanded and Dozing, turn logging on & off when appropriate boolean lockscreen = mLockscreen == null ? false : mLockscreen; if (mPanelExpanded && !mDozing) { mNotificationPanelLogger.logPanelShown(lockscreen, mEntryManager.getVisibleNotifications()); if (DEBUG) { Log.i(TAG, "Notification panel shown, lockscreen=" + lockscreen); } startNotificationLogging(); } else { if (DEBUG) { Log.i(TAG, "Notification panel hidden, lockscreen=" + lockscreen); } stopNotificationLogging(); } } /** * Called by StatusBar to notify the logger that the panel expansion has changed. * The panel may be showing any of the normal notification panel, the AOD, or the bouncer. * @param isExpanded True if the panel is expanded. */ public void onPanelExpandedChanged(boolean isExpanded) { if (DEBUG) { Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded); } mPanelExpanded = isExpanded; synchronized (mDozingLock) { maybeUpdateLoggingStatus(); } } /** * Called when the notification is expanded / collapsed. */ Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +8 −6 Original line number Diff line number Diff line Loading @@ -1772,6 +1772,9 @@ public class StatusBar extends SystemUI implements DemoMode, } public void setPanelExpanded(boolean isExpanded) { if (mPanelExpanded != isExpanded) { mNotificationLogger.onPanelExpandedChanged(isExpanded); } mPanelExpanded = isExpanded; updateHideIconsForBouncer(false /* animate */); mNotificationShadeWindowController.setPanelExpanded(isExpanded); Loading Loading @@ -2878,7 +2881,6 @@ public class StatusBar extends SystemUI implements DemoMode, } // Visibility reporting protected void handleVisibleToUserChanged(boolean visibleToUser) { if (visibleToUser) { handleVisibleToUserChangedImpl(visibleToUser); Loading @@ -2900,12 +2902,12 @@ public class StatusBar extends SystemUI implements DemoMode, } } /** * The LEDs are turned off when the notification panel is shown, even just a little bit. * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */ private void handleVisibleToUserChangedImpl(boolean visibleToUser) { // Visibility reporting void handleVisibleToUserChangedImpl(boolean visibleToUser) { if (visibleToUser) { /* The LEDs are turned off when the notification panel is shown, even just a little bit. * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */ boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp(); boolean clearNotificationEffects = !mPresenter.isPresenterFullyCollapsed() && Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +43 −6 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static com.android.systemui.statusbar.notification.stack.NotificationSect import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; Loading @@ -42,6 +44,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; Loading Loading @@ -163,28 +166,61 @@ public class NotificationLoggerTest extends SysuiTestCase { mUiBgExecutor.runAllReady(); Mockito.reset(mBarService); mLogger.stopNotificationLogging(); setStateAsleep(); mLogger.onDozingChanged(false); // Wake to lockscreen mLogger.onDozingChanged(true); // And go back to sleep, turning off logging mUiBgExecutor.runAllReady(); // The visibility objects are recycled by NotificationLogger, so we can't use specific // matchers here. verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any()); } private void setStateAsleep() { mLogger.onPanelExpandedChanged(true); mLogger.onDozingChanged(true); mLogger.onStateChanged(StatusBarState.KEYGUARD); } private void setStateAwake() { mLogger.onPanelExpandedChanged(false); mLogger.onDozingChanged(false); mLogger.onStateChanged(StatusBarState.SHADE); } @Test public void testLogPanelShownOnWake() { when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry)); setStateAsleep(); mLogger.onDozingChanged(false); // Wake to lockscreen assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; assertEquals(TEST_PACKAGE_NAME, n.packageName); assertEquals(TEST_UID, n.uid); assertEquals(1, n.instanceId); assertFalse(n.isGroupSummary); assertEquals(1 + BUCKET_ALERTING, n.section); } @Test public void testLogPanelShownOnLoggingStart() { public void testLogPanelShownOnShadePull() { when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry)); mLogger.startNotificationLogging(); setStateAwake(); // Now expand panel mLogger.onPanelExpandedChanged(true); assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertEquals(false, mNotificationPanelLoggerFake.get(0).isLockscreen); assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; assertEquals(TEST_PACKAGE_NAME, n.packageName); assertEquals(TEST_UID, n.uid); assertEquals(1, n.instanceId); assertEquals(false, n.isGroupSummary); assertFalse(n.isGroupSummary); assertEquals(1 + BUCKET_ALERTING, n.section); } @Test public void testLogPanelShownHandlesNullInstanceIds() { // Construct a NotificationEntry like mEntry, but with a null instance id. Loading @@ -198,7 +234,8 @@ public class NotificationLoggerTest extends SysuiTestCase { entry.setRow(mRow); when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry)); mLogger.startNotificationLogging(); setStateAsleep(); mLogger.onDozingChanged(false); // Wake to lockscreen assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +95 −33 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.util.Log; import androidx.annotation.Nullable; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; Loading @@ -36,6 +37,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; Loading @@ -58,6 +60,7 @@ import javax.inject.Inject; */ public class NotificationLogger implements StateListener { private static final String TAG = "NotificationLogger"; private static final boolean DEBUG = false; /** The minimum delay in ms between reports of notification visibility. */ private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; Loading @@ -79,7 +82,12 @@ public class NotificationLogger implements StateListener { private long mLastVisibilityReportUptimeMs; private NotificationListContainer mListContainer; private final Object mDozingLock = new Object(); private boolean mDozing; @GuardedBy("mDozingLock") private Boolean mDozing = null; // Use null to indicate state is not yet known @GuardedBy("mDozingLock") private Boolean mLockscreen = null; // Use null to indicate state is not yet known private Boolean mPanelExpanded = null; // Use null to indicate state is not yet known private boolean mLogging = false; protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener = new OnChildLocationsChangedListener() { Loading Loading @@ -247,6 +255,11 @@ public class NotificationLogger implements StateListener { } public void stopNotificationLogging() { if (mLogging) { mLogging = false; if (DEBUG) { Log.i(TAG, "stopNotificationLogging: log notifications invisible"); } // Report all notifications as invisible and turn down the // reporter. if (!mCurrentlyVisibleNotifications.isEmpty()) { Loading @@ -257,8 +270,14 @@ public class NotificationLogger implements StateListener { mHandler.removeCallbacks(mVisibilityReporter); mListContainer.setChildLocationsChangedListener(null); } } public void startNotificationLogging() { if (!mLogging) { mLogging = true; if (DEBUG) { Log.i(TAG, "startNotificationLogging"); } mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener); // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't // cause the scroller to emit child location events. Hence generate Loading @@ -267,13 +286,13 @@ public class NotificationLogger implements StateListener { // (Note that in cases where the scroller does emit events, this // additional event doesn't break anything.) mNotificationLocationsChangedListener.onChildLocationsChanged(); mNotificationPanelLogger.logPanelShown(mListContainer.hasPulsingNotifications(), mEntryManager.getVisibleNotifications()); } } private void setDozing(boolean dozing) { synchronized (mDozingLock) { mDozing = dozing; maybeUpdateLoggingStatus(); } } Loading Loading @@ -343,11 +362,6 @@ public class NotificationLogger implements StateListener { for (int i = 0; i < N; i++) { newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; } synchronized (mDozingLock) { // setNotificationsShown should only be called if we are confident that // the user has seen the notification, aka not when ambient display is on if (!mDozing) { // TODO: Call NotificationEntryManager to do this, once it exists. // TODO: Consider not catching all runtime exceptions here. try { Loading @@ -356,8 +370,6 @@ public class NotificationLogger implements StateListener { Log.d(TAG, "failed setNotificationsShown: ", e); } } } } recycleAllVisibilityObjects(newlyVisibleAr); recycleAllVisibilityObjects(noLongerVisibleAr); }); Loading Loading @@ -400,14 +412,64 @@ public class NotificationLogger implements StateListener { @Override public void onStateChanged(int newState) { // don't care about state change if (DEBUG) { Log.i(TAG, "onStateChanged: new=" + newState); } synchronized (mDozingLock) { mLockscreen = (newState == StatusBarState.KEYGUARD || newState == StatusBarState.SHADE_LOCKED); } } @Override public void onDozingChanged(boolean isDozing) { if (DEBUG) { Log.i(TAG, "onDozingChanged: new=" + isDozing); } setDozing(isDozing); } @GuardedBy("mDozingLock") private void maybeUpdateLoggingStatus() { if (mPanelExpanded == null || mDozing == null) { if (DEBUG) { Log.i(TAG, "Panel status unclear: panelExpandedKnown=" + (mPanelExpanded == null) + " dozingKnown=" + (mDozing == null)); } return; } // Once we know panelExpanded and Dozing, turn logging on & off when appropriate boolean lockscreen = mLockscreen == null ? false : mLockscreen; if (mPanelExpanded && !mDozing) { mNotificationPanelLogger.logPanelShown(lockscreen, mEntryManager.getVisibleNotifications()); if (DEBUG) { Log.i(TAG, "Notification panel shown, lockscreen=" + lockscreen); } startNotificationLogging(); } else { if (DEBUG) { Log.i(TAG, "Notification panel hidden, lockscreen=" + lockscreen); } stopNotificationLogging(); } } /** * Called by StatusBar to notify the logger that the panel expansion has changed. * The panel may be showing any of the normal notification panel, the AOD, or the bouncer. * @param isExpanded True if the panel is expanded. */ public void onPanelExpandedChanged(boolean isExpanded) { if (DEBUG) { Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded); } mPanelExpanded = isExpanded; synchronized (mDozingLock) { maybeUpdateLoggingStatus(); } } /** * Called when the notification is expanded / collapsed. */ Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +8 −6 Original line number Diff line number Diff line Loading @@ -1772,6 +1772,9 @@ public class StatusBar extends SystemUI implements DemoMode, } public void setPanelExpanded(boolean isExpanded) { if (mPanelExpanded != isExpanded) { mNotificationLogger.onPanelExpandedChanged(isExpanded); } mPanelExpanded = isExpanded; updateHideIconsForBouncer(false /* animate */); mNotificationShadeWindowController.setPanelExpanded(isExpanded); Loading Loading @@ -2878,7 +2881,6 @@ public class StatusBar extends SystemUI implements DemoMode, } // Visibility reporting protected void handleVisibleToUserChanged(boolean visibleToUser) { if (visibleToUser) { handleVisibleToUserChangedImpl(visibleToUser); Loading @@ -2900,12 +2902,12 @@ public class StatusBar extends SystemUI implements DemoMode, } } /** * The LEDs are turned off when the notification panel is shown, even just a little bit. * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */ private void handleVisibleToUserChangedImpl(boolean visibleToUser) { // Visibility reporting void handleVisibleToUserChangedImpl(boolean visibleToUser) { if (visibleToUser) { /* The LEDs are turned off when the notification panel is shown, even just a little bit. * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */ boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp(); boolean clearNotificationEffects = !mPresenter.isPresenterFullyCollapsed() && Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +43 −6 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static com.android.systemui.statusbar.notification.stack.NotificationSect import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; Loading @@ -42,6 +44,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; Loading Loading @@ -163,28 +166,61 @@ public class NotificationLoggerTest extends SysuiTestCase { mUiBgExecutor.runAllReady(); Mockito.reset(mBarService); mLogger.stopNotificationLogging(); setStateAsleep(); mLogger.onDozingChanged(false); // Wake to lockscreen mLogger.onDozingChanged(true); // And go back to sleep, turning off logging mUiBgExecutor.runAllReady(); // The visibility objects are recycled by NotificationLogger, so we can't use specific // matchers here. verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any()); } private void setStateAsleep() { mLogger.onPanelExpandedChanged(true); mLogger.onDozingChanged(true); mLogger.onStateChanged(StatusBarState.KEYGUARD); } private void setStateAwake() { mLogger.onPanelExpandedChanged(false); mLogger.onDozingChanged(false); mLogger.onStateChanged(StatusBarState.SHADE); } @Test public void testLogPanelShownOnWake() { when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry)); setStateAsleep(); mLogger.onDozingChanged(false); // Wake to lockscreen assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; assertEquals(TEST_PACKAGE_NAME, n.packageName); assertEquals(TEST_UID, n.uid); assertEquals(1, n.instanceId); assertFalse(n.isGroupSummary); assertEquals(1 + BUCKET_ALERTING, n.section); } @Test public void testLogPanelShownOnLoggingStart() { public void testLogPanelShownOnShadePull() { when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry)); mLogger.startNotificationLogging(); setStateAwake(); // Now expand panel mLogger.onPanelExpandedChanged(true); assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertEquals(false, mNotificationPanelLoggerFake.get(0).isLockscreen); assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; assertEquals(TEST_PACKAGE_NAME, n.packageName); assertEquals(TEST_UID, n.uid); assertEquals(1, n.instanceId); assertEquals(false, n.isGroupSummary); assertFalse(n.isGroupSummary); assertEquals(1 + BUCKET_ALERTING, n.section); } @Test public void testLogPanelShownHandlesNullInstanceIds() { // Construct a NotificationEntry like mEntry, but with a null instance id. Loading @@ -198,7 +234,8 @@ public class NotificationLoggerTest extends SysuiTestCase { entry.setRow(mRow); when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry)); mLogger.startNotificationLogging(); setStateAsleep(); mLogger.onDozingChanged(false); // Wake to lockscreen assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; Loading