Merge "Do not try to bypass when triaging notifications" into qt-r1-dev
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 787cc97..aeb8574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.content.Context;
import android.content.res.Resources;
+import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
@@ -44,6 +47,7 @@
import java.util.Stack;
import javax.inject.Inject;
+import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -59,6 +63,8 @@
public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
private static final String TAG = "NotificationViewHierarchyManager";
+ private final Handler mHandler;
+
//TODO: change this top <Entry, List<Entry>>?
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
@@ -88,9 +94,13 @@
// Used to help track down re-entrant calls to our update methods, which will cause bugs.
private boolean mPerformingUpdate;
+ // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down
+ // the problem.
+ private boolean mIsHandleDynamicPrivacyChangeScheduled;
@Inject
public NotificationViewHierarchyManager(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManager groupManager,
VisualStabilityManager visualStabilityManager,
@@ -100,6 +110,7 @@
BubbleData bubbleData,
KeyguardBypassController bypassController,
DynamicPrivacyController privacyController) {
+ mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
mBypassController = bypassController;
mGroupManager = groupManager;
@@ -438,6 +449,20 @@
@Override
public void onDynamicPrivacyChanged() {
+ if (mPerformingUpdate) {
+ Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
+ }
+ // This listener can be called from updateNotificationViews() via a convoluted listener
+ // chain, so we post here to prevent a re-entrant call. See b/136186188
+ // TODO: Refactor away the need for this
+ if (!mIsHandleDynamicPrivacyChangeScheduled) {
+ mIsHandleDynamicPrivacyChangeScheduled = true;
+ mHandler.post(this::onHandleDynamicPrivacyChanged);
+ }
+ }
+
+ private void onHandleDynamicPrivacyChanged() {
+ mIsHandleDynamicPrivacyChangeScheduled = false;
updateNotificationViews();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index ea434fc..49afae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -95,6 +95,7 @@
private boolean mPulsing;
private boolean mDozing;
private boolean mDocked;
+ private boolean mBlockUpdates;
private int mIconColor;
private float mDozeAmount;
private boolean mBouncerShowingScrimmed;
@@ -107,8 +108,22 @@
new KeyguardMonitor.Callback() {
@Override
public void onKeyguardShowingChanged() {
+ boolean force = false;
+ boolean wasShowing = mKeyguardShowing;
mKeyguardShowing = mKeyguardMonitor.isShowing();
- update();
+ if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
+ mBlockUpdates = false;
+ force = true;
+ }
+ update(force);
+ }
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ if (!mKeyguardMonitor.isKeyguardFadingAway() && mBlockUpdates) {
+ mBlockUpdates = false;
+ update(true /* force */);
+ }
}
};
private final DockManager.DockEventListener mDockEventListener =
@@ -261,7 +276,11 @@
mIsFaceUnlockState = state == STATE_SCANNING_FACE;
mLastState = state;
- if (lastState != state || mForceUpdate) {
+ boolean shouldUpdate = lastState != state || mForceUpdate;
+ if (mBlockUpdates && canBlockUpdates()) {
+ shouldUpdate = false;
+ }
+ if (shouldUpdate) {
mForceUpdate = false;
@LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState,
state, mPulsing, mDozing);
@@ -330,6 +349,10 @@
return true;
}
+ private boolean canBlockUpdates() {
+ return mKeyguardShowing || mKeyguardMonitor.isKeyguardFadingAway();
+ }
+
private void updateClickability() {
if (mAccessibilityController == null) {
return;
@@ -536,11 +559,17 @@
/**
* We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
* icon on top of the black front scrim.
+ * @param wakeAndUnlock are we wake and unlocking
+ * @param isUnlock are we currently unlocking
*/
- public void onBiometricAuthModeChanged(boolean wakeAndUnlock) {
+ public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
if (wakeAndUnlock) {
mWakeAndUnlockRunning = true;
}
+ if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) {
+ // We don't want the icon to change while we are unlocking
+ mBlockUpdates = true;
+ }
update();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e756d3a..55f61fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3834,7 +3834,8 @@
public void notifyBiometricAuthModeChanged() {
updateDozing();
updateScrimController();
- mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock());
+ mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock(),
+ mBiometricUnlockController.isBiometricUnlock());
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 6dc2c8c..462b65f37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -814,7 +814,7 @@
*/
public boolean shouldSubtleWindowAnimationsForUnlock() {
return mStatusBar.mKeyguardBypassController.getBypassEnabled()
- && mStatusBar.mState == StatusBarState.KEYGUARD;
+ && mStatusBar.mState == StatusBarState.KEYGUARD && !mBouncer.isAnimatingAway();
}
public boolean isGoingToNotificationShade() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 667521b..9405418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -272,10 +272,11 @@
/**
* Called when the biometric authentication mode changes.
* @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
+ * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} ()
*/
- public void onBiometricAuthModeChanged(boolean wakeAndUnlock) {
+ public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
if (mLockIcon != null) {
- mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock);
+ mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index 01498e6..f61b556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -50,5 +50,6 @@
interface Callback {
void onKeyguardShowingChanged();
+ default void onKeyguardFadingAwayChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index b53ff0e..68d0070 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -141,14 +141,24 @@
}
public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) {
- mKeyguardFadingAway = true;
+ setKeyguardFadingAway(true);
mKeyguardFadingAwayDelay = delay;
mKeyguardFadingAwayDuration = fadeoutDuration;
}
+ private void setKeyguardFadingAway(boolean keyguardFadingAway) {
+ if (mKeyguardFadingAway != keyguardFadingAway) {
+ mKeyguardFadingAway = keyguardFadingAway;
+ ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).onKeyguardFadingAwayChanged();
+ }
+ }
+ }
+
public void notifyKeyguardDoneFading() {
- mKeyguardFadingAway = false;
mKeyguardGoingAway = false;
+ setKeyguardFadingAway(false);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 010b85e..58fb53a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,12 +20,16 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -79,13 +83,19 @@
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private ShadeController mShadeController;
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
private NotificationViewHierarchyManager mViewHierarchyManager;
private NotificationTestHelper mHelper;
+ private boolean mMadeReentrantCall = false;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ mTestableLooper = TestableLooper.get(this);
+ Assert.sMainLooper = mTestableLooper.getLooper();
+ mHandler = Handler.createAsync(mTestableLooper.getLooper());
+
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
mLockscreenUserManager);
@@ -98,7 +108,7 @@
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
- mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+ mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
() -> mShadeController, new BubbleData(mContext),
mock(KeyguardBypassController.class),
@@ -215,9 +225,60 @@
verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
}
+ @Test
+ public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
+ // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
+ mMadeReentrantCall = false;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews()
+ mViewHierarchyManager.updateNotificationViews();
+
+ // THEN onNotificationViewUpdateFinished() is only called once
+ verify(mListContainer).onNotificationViewUpdateFinished();
+
+ // WHEN we drain the looper
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called a second time (for the reentrant call)
+ verify(mListContainer, times(2)).onNotificationViewUpdateFinished();
+ }
+
+ @Test
+ public void testMultipleReentrantCallsToOnDynamicPrivacyChangedOnlyPostOnce() {
+ // GIVEN a ListContainer that will make many re-entrant calls to updateNotificationViews()
+ mMadeReentrantCall = false;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews() and drain the looper
+ mViewHierarchyManager.updateNotificationViews();
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ clearInvocations(mListContainer);
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called only one more time
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ }
+
private class FakeListContainer implements NotificationListContainer {
final LinearLayout mLayout = new LinearLayout(mContext);
final List<View> mRows = Lists.newArrayList();
+ private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications;
@Override
public void setChildTransferInProgress(boolean childTransferInProgress) {}
@@ -266,7 +327,11 @@
}
@Override
- public void setMaxDisplayedNotifications(int maxNotifications) {}
+ public void setMaxDisplayedNotifications(int maxNotifications) {
+ if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ }
@Override
public ViewGroup getViewParentForNotification(NotificationEntry entry) {
@@ -301,5 +366,7 @@
return false;
}
+ @Override
+ public void onNotificationViewUpdateFinished() { }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2052b157..eba4fb6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2523,11 +2523,11 @@
}
final int resource;
- if (onWallpaper) {
- resource = R.anim.lock_screen_behind_enter_wallpaper;
- } else if (subtleAnimation) {
+ if (subtleAnimation) {
resource = R.anim.lock_screen_behind_enter_subtle;
- } else {
+ } else if (onWallpaper) {
+ resource = R.anim.lock_screen_behind_enter_wallpaper;
+ } else {
resource = R.anim.lock_screen_behind_enter;
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 6127303..553b0ff 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -176,6 +176,8 @@
mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token);
mTransferTouchFromToken = null;
+ // syncInputWindows here to ensure the input channel isn't removed before the transfer.
+ mTransaction.syncInputWindows();
mTransaction.apply();
}