Group window relayout requests

Various places in sysui might call into
NotificationShadeWindowController. Some of these calls can lead to
relayoutWindow(), which is quite expensive.

We should group these calls, and execute them in batch.

Test: manual
Test: perfetto trace
Test: atest NotificationPanelViewControllerTest
Test: atest NotificationShadeWindowControllerImplTest
Fixes: 190382751
Bug: 210432290
Change-Id: Ib08f7ba66048eb023ee2d5e9c09461381c390f0d
This commit is contained in:
Lucas Dupin
2022-01-21 11:38:20 -08:00
parent c5900ec571
commit 593266739f
10 changed files with 139 additions and 60 deletions

View File

@@ -113,6 +113,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -318,6 +319,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// the properties of the keyguard
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
/**
* Last SIM state reported by the telephony system.
@@ -833,7 +835,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
ScreenOffAnimationController screenOffAnimationController,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor) {
InteractionJankMonitor interactionJankMonitor,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) {
super(context);
mFalsingCollector = falsingCollector;
mLockPatternUtils = lockPatternUtils;
@@ -850,6 +853,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
dumpManager.registerDumpable(getClass().getName(), this);
mDeviceConfig = deviceConfig;
mScreenOnCoordinator = screenOnCoordinator;
mNotificationShadeWindowControllerLazy = notificationShadeWindowControllerLazy;
mShowHomeOverLockscreen = mDeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
@@ -1837,10 +1841,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
params.mApps, params.mWallpapers, params.mNonApps,
params.mFinishedCallback);
mFalsingCollector.onSuccessfulUnlock();
mNotificationShadeWindowControllerLazy.get().batchApplyWindowLayoutParams(
() -> {
handleStartKeyguardExitAnimation(params.startTime,
params.fadeoutDuration,
params.mApps, params.mWallpapers, params.mNonApps,
params.mFinishedCallback);
mFalsingCollector.onSuccessfulUnlock();
});
Trace.endSection();
break;
case CANCEL_KEYGUARD_EXIT_ANIM:
@@ -2139,10 +2147,12 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mKeyguardGoingAwayRunnable.run();
} else {
// TODO(bc-unlock): Fill parameters
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */,
null /* nonApps */, null /* finishedCallback */);
mNotificationShadeWindowControllerLazy.get().batchApplyWindowLayoutParams(() -> {
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */,
null /* nonApps */, null /* finishedCallback */);
});
}
}
Trace.endSection();

View File

@@ -44,6 +44,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
@@ -98,7 +99,8 @@ public class KeyguardModule {
ScreenOffAnimationController screenOffAnimationController,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor) {
InteractionJankMonitor interactionJankMonitor,
Lazy<NotificationShadeWindowController> notificationShadeWindowController) {
return new KeyguardViewMediator(
context,
falsingCollector,
@@ -122,7 +124,8 @@ public class KeyguardModule {
screenOffAnimationController,
notificationShadeDepthController,
screenOnCoordinator,
interactionJankMonitor
interactionJankMonitor,
notificationShadeWindowController
);
}

View File

@@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
import android.graphics.Region;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
@@ -191,6 +192,14 @@ public interface NotificationShadeWindowController extends RemoteInputController
*/
default void setLightRevealScrimOpaque(boolean opaque) {}
/**
* Defer any application of window {@link WindowManager.LayoutParams} until {@code scope} is
* fully applied.
*/
default void batchApplyWindowLayoutParams(@NonNull Runnable scope) {
scope.run();
}
/**
* Custom listener to pipe data back to plugins about whether or not the status bar would be
* collapsed if not for the plugin.

View File

@@ -162,6 +162,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.QsFrameTranslateController;
@@ -741,6 +742,7 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
StatusBarWindowStateController statusBarWindowStateController,
NotificationShadeWindowController notificationShadeWindowController,
DozeLog dozeLog,
DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
LatencyTracker latencyTracker, PowerManager powerManager,
@@ -800,6 +802,7 @@ public class NotificationPanelViewController extends PanelViewController {
dozeLog,
keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController,
notificationShadeWindowController,
vibratorHelper,
statusBarKeyguardViewManager,
latencyTracker,

View File

@@ -111,6 +111,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private final SysuiColorExtractor mColorExtractor;
private final ScreenOffAnimationController mScreenOffAnimationController;
private float mFaceAuthDisplayBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
/**
* Layout params would be aggregated and dispatched all at once if this is > 0.
*
* @see #batchApplyWindowLayoutParams(Runnable)
*/
private int mDeferWindowLayoutParams;
@Inject
public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager,
@@ -437,6 +443,20 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
private void applyWindowLayoutParams() {
if (mDeferWindowLayoutParams == 0 && mLp != null && mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mNotificationShadeView, mLp);
}
}
@Override
public void batchApplyWindowLayoutParams(Runnable scope) {
mDeferWindowLayoutParams++;
scope.run();
mDeferWindowLayoutParams--;
applyWindowLayoutParams();
}
private void apply(State state) {
applyKeyguardFlags(state);
applyFocusableFlag(state);
@@ -451,9 +471,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
applyHasTopUi(state);
applyNotTouchable(state);
applyStatusBarColorSpaceAgnosticFlag(state);
if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mNotificationShadeView, mLp);
}
applyWindowLayoutParams();
if (mHasTopUi != mHasTopUiChanged) {
whitelistIpcs(() -> {
try {
@@ -739,6 +758,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
pw.println(TAG + ":");
pw.println(" mKeyguardMaxRefreshRate=" + mKeyguardMaxRefreshRate);
pw.println(" mKeyguardPreferredRefreshRate=" + mKeyguardPreferredRefreshRate);
pw.println(" mDeferWindowLayoutParams=" + mDeferWindowLayoutParams);
pw.println(mCurrentState);
if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) {
mNotificationShadeView.getViewRootImpl().dump(" ", pw);

View File

@@ -57,6 +57,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -180,6 +181,7 @@ public abstract class PanelViewController {
private boolean mExpandLatencyTracking;
private final PanelView mView;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final NotificationShadeWindowController mNotificationShadeWindowController;
protected final Resources mResources;
protected final KeyguardStateController mKeyguardStateController;
protected final SysuiStatusBarStateController mStatusBarStateController;
@@ -222,6 +224,7 @@ public abstract class PanelViewController {
DozeLog dozeLog,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController,
NotificationShadeWindowController notificationShadeWindowController,
VibratorHelper vibratorHelper,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
LatencyTracker latencyTracker,
@@ -263,6 +266,7 @@ public abstract class PanelViewController {
mResources = mView.getResources();
mKeyguardStateController = keyguardStateController;
mStatusBarStateController = statusBarStateController;
mNotificationShadeWindowController = notificationShadeWindowController;
mFlingAnimationUtils = flingAnimationUtilsBuilder
.reset()
.setMaxLengthSeconds(0.6f)
@@ -760,34 +764,36 @@ public abstract class PanelViewController {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
}
if (mExpandLatencyTracking && h != 0f) {
DejankUtils.postAfterTraversal(
() -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
mExpandLatencyTracking = false;
}
float maxPanelHeight = getMaxPanelHeight();
if (mHeightAnimator == null) {
if (mTracking) {
float overExpansionPixels = Math.max(0, h - maxPanelHeight);
setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
if (mExpandLatencyTracking && h != 0f) {
DejankUtils.postAfterTraversal(
() -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
mExpandLatencyTracking = false;
}
float maxPanelHeight = getMaxPanelHeight();
if (mHeightAnimator == null) {
if (mTracking) {
float overExpansionPixels = Math.max(0, h - maxPanelHeight);
setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
}
mExpandedHeight = Math.min(h, maxPanelHeight);
} else {
mExpandedHeight = h;
}
mExpandedHeight = Math.min(h, maxPanelHeight);
} else {
mExpandedHeight = h;
}
// If we are closing the panel and we are almost there due to a slow decelerating
// interpolator, abort the animation.
if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
mExpandedHeight = 0f;
if (mHeightAnimator != null) {
mHeightAnimator.end();
// If we are closing the panel and we are almost there due to a slow decelerating
// interpolator, abort the animation.
if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
mExpandedHeight = 0f;
if (mHeightAnimator != null) {
mHeightAnimator.end();
}
}
}
mExpandedFraction = Math.min(1f,
maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
onHeightUpdated(mExpandedHeight);
updatePanelExpansionAndVisibility();
mExpandedFraction = Math.min(1f,
maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
onHeightUpdated(mExpandedHeight);
updatePanelExpansionAndVisibility();
});
}
/**

View File

@@ -3597,26 +3597,28 @@ public class StatusBar extends CoreStartable implements
public void onStartedWakingUp() {
String tag = "StatusBar#onStartedWakingUp";
DejankUtils.startDetectingBlockingIpcs(tag);
mDeviceInteractive = true;
mWakeUpCoordinator.setWakingUp(true);
if (!mKeyguardBypassController.getBypassEnabled()) {
mHeadsUpManager.releaseAllImmediately();
}
updateVisibleToUser();
updateIsKeyguard();
mDozeServiceHost.stopDozing();
// This is intentionally below the stopDozing call above, since it avoids that we're
// unnecessarily animating the wakeUp transition. Animations should only be enabled
// once we fully woke up.
updateRevealEffect(true /* wakingUp */);
updateNotificationPanelTouchState();
mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
mDeviceInteractive = true;
mWakeUpCoordinator.setWakingUp(true);
if (!mKeyguardBypassController.getBypassEnabled()) {
mHeadsUpManager.releaseAllImmediately();
}
updateVisibleToUser();
updateIsKeyguard();
mDozeServiceHost.stopDozing();
// This is intentionally below the stopDozing call above, since it avoids that we're
// unnecessarily animating the wakeUp transition. Animations should only be enabled
// once we fully woke up.
updateRevealEffect(true /* wakingUp */);
updateNotificationPanelTouchState();
// If we are waking up during the screen off animation, we should undo making the
// expanded visible (we did that so the LightRevealScrim would be visible).
if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) {
makeExpandedInvisible();
}
// If we are waking up during the screen off animation, we should undo making the
// expanded visible (we did that so the LightRevealScrim would be visible).
if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) {
makeExpandedInvisible();
}
});
DejankUtils.stopDetectingBlockingIpcs(tag);
}

View File

@@ -51,6 +51,7 @@ import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -68,6 +69,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import dagger.Lazy;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -94,6 +97,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock ScreenOffAnimationController mScreenOffAnimationController;
private @Mock InteractionJankMonitor mInteractionJankMonitor;
private @Mock ScreenOnCoordinator mScreenOnCoordinator;
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -197,7 +201,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mScreenOffAnimationController,
() -> mNotificationShadeDepthController,
mScreenOnCoordinator,
mInteractionJankMonitor);
mInteractionJankMonitor,
mNotificationShadeWindowControllerLazy);
mViewMediator.start();
}
}

View File

@@ -23,7 +23,6 @@ import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.ViewGroupFadeHelper.reset;
import static com.google.common.truth.Truth.assertThat;
@@ -38,6 +37,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -120,6 +120,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.QsFrameTranslateController;
@@ -365,6 +366,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock
private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
private NotificationShadeWindowController mNotificationShadeWindowController;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -490,7 +493,10 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
.thenReturn(true);
when(mInteractionJankMonitor.end(anyInt()))
.thenReturn(true);
reset(mView);
doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
mMainHandler = new Handler(Looper.getMainLooper());
@@ -505,6 +511,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mCommunalStateController, mKeyguardStateController,
mStatusBarStateController,
mStatusBarWindowStateController,
mNotificationShadeWindowController,
mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
mCommunalSourceMonitor, mMetricsLogger, mActivityManager, mConfigurationController,

View File

@@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -225,4 +226,17 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
assertThat((mLayoutParameters.getValue().flags & FLAG_NOT_FOCUSABLE) != 0).isTrue();
assertThat((mLayoutParameters.getValue().flags & FLAG_ALT_FOCUSABLE_IM) == 0).isTrue();
}
@Test
public void batchApplyWindowLayoutParams_doesNotDispatchEvents() {
mNotificationShadeWindowController.setForceDozeBrightness(true);
verify(mWindowManager).updateViewLayout(any(), any());
clearInvocations(mWindowManager);
mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
mNotificationShadeWindowController.setForceDozeBrightness(false);
verify(mWindowManager, never()).updateViewLayout(any(), any());
});
verify(mWindowManager).updateViewLayout(any(), any());
}
}