Merge "More accurate notification panel logs." into rvc-dev

This commit is contained in:
Will Brockman
2020-06-15 15:44:29 +00:00
committed by Android (Google) Code Review
3 changed files with 146 additions and 45 deletions

View File

@@ -29,6 +29,7 @@ import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.NotificationVisibility;
@@ -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;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.NotificationListener; 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.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -58,6 +60,7 @@ import javax.inject.Inject;
*/ */
public class NotificationLogger implements StateListener { public class NotificationLogger implements StateListener {
private static final String TAG = "NotificationLogger"; private static final String TAG = "NotificationLogger";
private static final boolean DEBUG = false;
/** The minimum delay in ms between reports of notification visibility. */ /** The minimum delay in ms between reports of notification visibility. */
private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
@@ -79,7 +82,12 @@ public class NotificationLogger implements StateListener {
private long mLastVisibilityReportUptimeMs; private long mLastVisibilityReportUptimeMs;
private NotificationListContainer mListContainer; private NotificationListContainer mListContainer;
private final Object mDozingLock = new Object(); 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 = protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
new OnChildLocationsChangedListener() { new OnChildLocationsChangedListener() {
@@ -247,33 +255,44 @@ public class NotificationLogger implements StateListener {
} }
public void stopNotificationLogging() { public void stopNotificationLogging() {
// Report all notifications as invisible and turn down the if (mLogging) {
// reporter. mLogging = false;
if (!mCurrentlyVisibleNotifications.isEmpty()) { if (DEBUG) {
logNotificationVisibilityChanges( Log.i(TAG, "stopNotificationLogging: log notifications invisible");
Collections.emptyList(), mCurrentlyVisibleNotifications); }
recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); // Report all notifications as invisible and turn down the
// reporter.
if (!mCurrentlyVisibleNotifications.isEmpty()) {
logNotificationVisibilityChanges(
Collections.emptyList(), mCurrentlyVisibleNotifications);
recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
}
mHandler.removeCallbacks(mVisibilityReporter);
mListContainer.setChildLocationsChangedListener(null);
} }
mHandler.removeCallbacks(mVisibilityReporter);
mListContainer.setChildLocationsChangedListener(null);
} }
public void startNotificationLogging() { public void startNotificationLogging() {
mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener); if (!mLogging) {
// Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't mLogging = true;
// cause the scroller to emit child location events. Hence generate if (DEBUG) {
// one ourselves to guarantee that we're reporting visible Log.i(TAG, "startNotificationLogging");
// notifications. }
// (Note that in cases where the scroller does emit events, this mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
// additional event doesn't break anything.) // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
mNotificationLocationsChangedListener.onChildLocationsChanged(); // cause the scroller to emit child location events. Hence generate
mNotificationPanelLogger.logPanelShown(mListContainer.hasPulsingNotifications(), // one ourselves to guarantee that we're reporting visible
mEntryManager.getVisibleNotifications()); // notifications.
// (Note that in cases where the scroller does emit events, this
// additional event doesn't break anything.)
mNotificationLocationsChangedListener.onChildLocationsChanged();
}
} }
private void setDozing(boolean dozing) { private void setDozing(boolean dozing) {
synchronized (mDozingLock) { synchronized (mDozingLock) {
mDozing = dozing; mDozing = dozing;
maybeUpdateLoggingStatus();
} }
} }
@@ -343,19 +362,12 @@ public class NotificationLogger implements StateListener {
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
} }
// TODO: Call NotificationEntryManager to do this, once it exists.
synchronized (mDozingLock) { // TODO: Consider not catching all runtime exceptions here.
// setNotificationsShown should only be called if we are confident that try {
// the user has seen the notification, aka not when ambient display is on mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
if (!mDozing) { } catch (RuntimeException e) {
// TODO: Call NotificationEntryManager to do this, once it exists. Log.d(TAG, "failed setNotificationsShown: ", e);
// TODO: Consider not catching all runtime exceptions here.
try {
mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
} catch (RuntimeException e) {
Log.d(TAG, "failed setNotificationsShown: ", e);
}
}
} }
} }
recycleAllVisibilityObjects(newlyVisibleAr); recycleAllVisibilityObjects(newlyVisibleAr);
@@ -400,14 +412,64 @@ public class NotificationLogger implements StateListener {
@Override @Override
public void onStateChanged(int newState) { 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 @Override
public void onDozingChanged(boolean isDozing) { public void onDozingChanged(boolean isDozing) {
if (DEBUG) {
Log.i(TAG, "onDozingChanged: new=" + isDozing);
}
setDozing(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. * Called when the notification is expanded / collapsed.
*/ */

View File

@@ -1772,6 +1772,9 @@ public class StatusBar extends SystemUI implements DemoMode,
} }
public void setPanelExpanded(boolean isExpanded) { public void setPanelExpanded(boolean isExpanded) {
if (mPanelExpanded != isExpanded) {
mNotificationLogger.onPanelExpandedChanged(isExpanded);
}
mPanelExpanded = isExpanded; mPanelExpanded = isExpanded;
updateHideIconsForBouncer(false /* animate */); updateHideIconsForBouncer(false /* animate */);
mNotificationShadeWindowController.setPanelExpanded(isExpanded); mNotificationShadeWindowController.setPanelExpanded(isExpanded);
@@ -2878,7 +2881,6 @@ public class StatusBar extends SystemUI implements DemoMode,
} }
// Visibility reporting // Visibility reporting
protected void handleVisibleToUserChanged(boolean visibleToUser) { protected void handleVisibleToUserChanged(boolean visibleToUser) {
if (visibleToUser) { if (visibleToUser) {
handleVisibleToUserChangedImpl(visibleToUser); handleVisibleToUserChangedImpl(visibleToUser);
@@ -2900,12 +2902,12 @@ public class StatusBar extends SystemUI implements DemoMode,
} }
} }
/** // Visibility reporting
* 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. void handleVisibleToUserChangedImpl(boolean visibleToUser) {
*/
private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
if (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 pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
boolean clearNotificationEffects = boolean clearNotificationEffects =
!mPresenter.isPresenterFullyCollapsed() && !mPresenter.isPresenterFullyCollapsed() &&

View File

@@ -20,6 +20,8 @@ import static com.android.systemui.statusbar.notification.stack.NotificationSect
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; 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.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -42,6 +44,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -163,28 +166,61 @@ public class NotificationLoggerTest extends SysuiTestCase {
mUiBgExecutor.runAllReady(); mUiBgExecutor.runAllReady();
Mockito.reset(mBarService); 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(); mUiBgExecutor.runAllReady();
// The visibility objects are recycled by NotificationLogger, so we can't use specific // The visibility objects are recycled by NotificationLogger, so we can't use specific
// matchers here. // matchers here.
verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any()); 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 @Test
public void testLogPanelShownOnLoggingStart() { public void testLogPanelShownOnWake() {
when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry)); when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
mLogger.startNotificationLogging(); setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertEquals(false, mNotificationPanelLoggerFake.get(0).isLockscreen); assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
assertEquals(TEST_PACKAGE_NAME, n.packageName); assertEquals(TEST_PACKAGE_NAME, n.packageName);
assertEquals(TEST_UID, n.uid); assertEquals(TEST_UID, n.uid);
assertEquals(1, n.instanceId); assertEquals(1, n.instanceId);
assertEquals(false, n.isGroupSummary); assertFalse(n.isGroupSummary);
assertEquals(1 + BUCKET_ALERTING, n.section); assertEquals(1 + BUCKET_ALERTING, n.section);
} }
@Test
public void testLogPanelShownOnShadePull() {
when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
mLogger.onPanelExpandedChanged(true);
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
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);
assertFalse(n.isGroupSummary);
assertEquals(1 + BUCKET_ALERTING, n.section);
}
@Test @Test
public void testLogPanelShownHandlesNullInstanceIds() { public void testLogPanelShownHandlesNullInstanceIds() {
// Construct a NotificationEntry like mEntry, but with a null instance id. // Construct a NotificationEntry like mEntry, but with a null instance id.
@@ -198,7 +234,8 @@ public class NotificationLoggerTest extends SysuiTestCase {
entry.setRow(mRow); entry.setRow(mRow);
when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry)); when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
mLogger.startNotificationLogging(); setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];