Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 461cfbb7 authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Improve systemui unlockscreen jank issue

- In VisualStabilityCoordinator, ensure sure setting
  mPipelineRunAllowedis false when the notification is not visible
  after unlock to avoid unnecessary building notification list that
  blocks the UI thread
- Send notifyRendererOfExpensiveFrame to raise CPU work load when
  drawing notification shade window
- Refining WindowRootView#applyMargins performance to layout at once
  after all children's margins has updated

Flag: com.android.systemui.check_lockscreen_gone_transition
Bug: 358301118
Test: atest VisualStabilityCoordinatorTest
Test: run PTS cuj test for testing sysui unlock screen by local command
     ./sysui_unlockscreen.sh -i 5 -w
Change-Id: I61f1dad2dc9485f043c53a8900f1fae2e8ea1187
parent dcb7cdae
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1446,3 +1446,13 @@ flag {
       purpose: PURPOSE_BUGFIX
   }
}

flag {
   name: "check_lockscreen_gone_transition"
   namespace: "systemui"
   description: "Run notification pipeline when the lockscreen is not in gone transition for avoiding janky frames during unlocking animation"
   bug: "358301118"
   metadata {
       purpose: PURPOSE_BUGFIX
   }
}
+33 −5
Original line number Diff line number Diff line
@@ -20,8 +20,6 @@ import static com.google.common.truth.Truth.assertThat;

import static junit.framework.Assert.assertFalse;

import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;

import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -31,12 +29,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;

import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.dump.DumpManager;
@@ -67,9 +69,6 @@ import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;

import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.test.TestScope;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -79,6 +78,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;

import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.test.TestScope;

@SmallTest
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@@ -516,6 +518,32 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
        verifyStabilityManagerWasInvalidated(times(1));
    }

    @Test
    @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
    public void testNotLockscreenInGoneTransition_invalidationCalled() {
        // GIVEN visual stability is being maintained b/c animation is playing
        mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
                mTestScope, new TransitionStep(
                        KeyguardState.LOCKSCREEN,
                        KeyguardState.GONE,
                        1f,
                        TransitionState.RUNNING),  /* validateStep = */ false);
        mTestScope.getTestScheduler().runCurrent();
        assertFalse(mNotifStabilityManager.isPipelineRunAllowed());

        // WHEN the animation has stopped playing
        mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
                mTestScope, new TransitionStep(
                        KeyguardState.LOCKSCREEN,
                        KeyguardState.GONE,
                        1f,
                        TransitionState.FINISHED),  /* validateStep = */ false);
        mTestScope.getTestScheduler().runCurrent();

        // invalidate is called, b/c we were previously suppressing the pipeline from running
        verifyStabilityManagerWasInvalidated(times(1));
    }

    @Test
    public void testNeverSuppressPipelineRunFromPanelCollapse_noInvalidationCalled() {
        // GIVEN animation is playing
+11 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updateMargins
import com.android.systemui.Flags
import com.android.systemui.compose.ComposeInitializer
import com.android.systemui.res.R

@@ -103,6 +104,8 @@ open class WindowRootView(

    private fun applyMargins() {
        val count = childCount
        val hasFlagsEnabled = Flags.checkLockscreenGoneTransition()
        var hasChildMarginUpdated = false
        for (i in 0 until count) {
            val child = getChildAt(i)
            if (child.layoutParams is LayoutParams) {
@@ -113,11 +116,18 @@ open class WindowRootView(
                            layoutParams.leftMargin != leftInset)
                ) {
                    layoutParams.updateMargins(left = leftInset, right = rightInset)
                    hasChildMarginUpdated = true
                    if (!hasFlagsEnabled) {
                        child.requestLayout()
                    }
                }
            }
        }
        if (hasFlagsEnabled && hasChildMarginUpdated) {
            // Request layout at once after all children's margins has updated
            requestLayout()
        }
    }

    /**
     * Returns `true` if this view is the true root of the view-hierarchy; `false` otherwise.
+10 −3
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import androidx.annotation.WorkerThread;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -802,11 +803,17 @@ public class NotificationLockscreenUserManagerImpl implements

    private void notifyNotificationStateChanged() {
        if (!Looper.getMainLooper().isCurrentThread()) {
            if (Flags.checkLockscreenGoneTransition()) {
                for (NotificationStateChangedListener listener : mNotifStateChangedListeners) {
                    mMainExecutor.execute(listener::onNotificationStateChanged);
                }
            } else {
                mMainExecutor.execute(() -> {
                    for (NotificationStateChangedListener listener : mNotifStateChangedListeners) {
                        listener.onNotificationStateChanged();
                    }
                });
            }
        } else {
            for (NotificationStateChangedListener listener : mNotifStateChangedListeners) {
                listener.onNotificationStateChanged();
+28 −2
Original line number Diff line number Diff line
@@ -22,15 +22,18 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
@@ -85,6 +88,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
    private boolean mNotifPanelLaunchingActivity;
    private boolean mCommunalShowing = false;
    private boolean mLockscreenShowing = false;
    private boolean mLockscreenInGoneTransition = false;

    private boolean mPipelineRunAllowed;
    private boolean mReorderingAllowed;
@@ -158,6 +162,13 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
                            KeyguardState.LOCKSCREEN),
                    this::onLockscreenKeyguardStateTransitionValueChanged);
        }
        if (Flags.checkLockscreenGoneTransition()) {
            mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.isInTransition(
                            Edge.create(KeyguardState.LOCKSCREEN, Scenes.Gone),
                            Edge.create(KeyguardState.LOCKSCREEN, KeyguardState.GONE)),
                    this::onLockscreenInGoneTransitionChanged);
        }


        pipeline.setVisualStabilityManager(mNotifStabilityManager);
    }
@@ -239,7 +250,9 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
    private void updateAllowedStates(String field, boolean value) {
        boolean wasPipelineRunAllowed = mPipelineRunAllowed;
        boolean wasReorderingAllowed = mReorderingAllowed;
        mPipelineRunAllowed = !isPanelCollapsingOrLaunchingActivity();
        // No need to run notification pipeline when the lockscreen is in fading animation.
        mPipelineRunAllowed = !(isPanelCollapsingOrLaunchingActivity()
                || (Flags.checkLockscreenGoneTransition() && mLockscreenInGoneTransition));
        mReorderingAllowed = isReorderingAllowed();
        if (wasPipelineRunAllowed != mPipelineRunAllowed
                || wasReorderingAllowed != mReorderingAllowed) {
@@ -330,7 +343,6 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
                    updateAllowedStates("fullyDozed", fullyDozed);
                }
            };

    final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
        @Override
        public void onFinishedGoingToSleep() {
@@ -353,6 +365,9 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
        pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
        pw.println("  notifPanelCollapsing: " + mNotifPanelCollapsing);
        pw.println("  launchingNotifActivity: " + mNotifPanelLaunchingActivity);
        if (Flags.checkLockscreenGoneTransition()) {
            pw.println("  lockscreenInGoneTransition: " + mLockscreenInGoneTransition);
        }
        pw.println("reorderingAllowed: " + mReorderingAllowed);
        pw.println("  sleepy: " + mSleepy);
        pw.println("  fullyDozed: " + mFullyDozed);
@@ -401,4 +416,15 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
        mLockscreenShowing = isShowing;
        updateAllowedStates("lockscreenShowing", isShowing);
    }

    private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
        if (!Flags.checkLockscreenGoneTransition()) {
            return;
        }
        if (inGoneTransition == mLockscreenInGoneTransition) {
            return;
        }
        mLockscreenInGoneTransition = inGoneTransition;
        updateAllowedStates("lockscreenInGoneTransition", mLockscreenInGoneTransition);
    }
}
Loading