Merge "Delay possible re-entrant call to updateNotificationViews()" into qt-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
bce828f157
@@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar;
|
package com.android.systemui.statusbar;
|
||||||
|
|
||||||
|
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -43,6 +46,7 @@ import java.util.List;
|
|||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
@@ -58,6 +62,8 @@ import dagger.Lazy;
|
|||||||
public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
|
public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
|
||||||
private static final String TAG = "NotificationViewHierarchyManager";
|
private static final String TAG = "NotificationViewHierarchyManager";
|
||||||
|
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
//TODO: change this top <Entry, List<Entry>>?
|
//TODO: change this top <Entry, List<Entry>>?
|
||||||
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
|
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
|
||||||
mTmpChildOrderMap = new HashMap<>();
|
mTmpChildOrderMap = new HashMap<>();
|
||||||
@@ -86,9 +92,13 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
|||||||
|
|
||||||
// Used to help track down re-entrant calls to our update methods, which will cause bugs.
|
// Used to help track down re-entrant calls to our update methods, which will cause bugs.
|
||||||
private boolean mPerformingUpdate;
|
private boolean mPerformingUpdate;
|
||||||
|
// Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down
|
||||||
|
// the problem.
|
||||||
|
private boolean mIsHandleDynamicPrivacyChangeScheduled;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NotificationViewHierarchyManager(Context context,
|
public NotificationViewHierarchyManager(Context context,
|
||||||
|
@Named(MAIN_HANDLER_NAME) Handler mainHandler,
|
||||||
NotificationLockscreenUserManager notificationLockscreenUserManager,
|
NotificationLockscreenUserManager notificationLockscreenUserManager,
|
||||||
NotificationGroupManager groupManager,
|
NotificationGroupManager groupManager,
|
||||||
VisualStabilityManager visualStabilityManager,
|
VisualStabilityManager visualStabilityManager,
|
||||||
@@ -97,6 +107,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
|||||||
Lazy<ShadeController> shadeController,
|
Lazy<ShadeController> shadeController,
|
||||||
BubbleData bubbleData,
|
BubbleData bubbleData,
|
||||||
DynamicPrivacyController privacyController) {
|
DynamicPrivacyController privacyController) {
|
||||||
|
mHandler = mainHandler;
|
||||||
mLockscreenUserManager = notificationLockscreenUserManager;
|
mLockscreenUserManager = notificationLockscreenUserManager;
|
||||||
mGroupManager = groupManager;
|
mGroupManager = groupManager;
|
||||||
mVisualStabilityManager = visualStabilityManager;
|
mVisualStabilityManager = visualStabilityManager;
|
||||||
@@ -435,6 +446,20 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDynamicPrivacyChanged() {
|
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();
|
updateNotificationViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,16 @@ import static junit.framework.Assert.assertTrue;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
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.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
import android.testing.TestableLooper;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -78,13 +82,19 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
|
|||||||
@Mock private VisualStabilityManager mVisualStabilityManager;
|
@Mock private VisualStabilityManager mVisualStabilityManager;
|
||||||
@Mock private ShadeController mShadeController;
|
@Mock private ShadeController mShadeController;
|
||||||
|
|
||||||
|
private TestableLooper mTestableLooper;
|
||||||
|
private Handler mHandler;
|
||||||
private NotificationViewHierarchyManager mViewHierarchyManager;
|
private NotificationViewHierarchyManager mViewHierarchyManager;
|
||||||
private NotificationTestHelper mHelper;
|
private NotificationTestHelper mHelper;
|
||||||
|
private boolean mMadeReentrantCall = false;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
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(NotificationEntryManager.class, mEntryManager);
|
||||||
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
|
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
|
||||||
mLockscreenUserManager);
|
mLockscreenUserManager);
|
||||||
@@ -97,7 +107,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
|
|||||||
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
|
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
|
||||||
|
|
||||||
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
|
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
|
||||||
mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
|
mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
|
||||||
mock(StatusBarStateControllerImpl.class), mEntryManager,
|
mock(StatusBarStateControllerImpl.class), mEntryManager,
|
||||||
() -> mShadeController, new BubbleData(mContext), mock(DynamicPrivacyController.class));
|
() -> mShadeController, new BubbleData(mContext), mock(DynamicPrivacyController.class));
|
||||||
Dependency.get(InitController.class).executePostInitTasks();
|
Dependency.get(InitController.class).executePostInitTasks();
|
||||||
@@ -212,9 +222,60 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
|
|||||||
verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
|
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 {
|
private class FakeListContainer implements NotificationListContainer {
|
||||||
final LinearLayout mLayout = new LinearLayout(mContext);
|
final LinearLayout mLayout = new LinearLayout(mContext);
|
||||||
final List<View> mRows = Lists.newArrayList();
|
final List<View> mRows = Lists.newArrayList();
|
||||||
|
private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setChildTransferInProgress(boolean childTransferInProgress) {}
|
public void setChildTransferInProgress(boolean childTransferInProgress) {}
|
||||||
@@ -263,7 +324,11 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMaxDisplayedNotifications(int maxNotifications) {}
|
public void setMaxDisplayedNotifications(int maxNotifications) {
|
||||||
|
if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
|
||||||
|
mViewHierarchyManager.onDynamicPrivacyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ViewGroup getViewParentForNotification(NotificationEntry entry) {
|
public ViewGroup getViewParentForNotification(NotificationEntry entry) {
|
||||||
@@ -298,5 +363,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNotificationViewUpdateFinished() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user