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

Commit c5b8459c authored by Matt Pietal's avatar Matt Pietal
Browse files

Update user switching processes

Add a new callback for UserController. If the user is secure, initiate
a call to SystemUI to lockNow(), and wait for the callback. If the
callback does not arrive, crash the system after some time.

During user switching, ensure that systemui locks the device immediately
if necessary and cancels any prior attempt to dismiss.

Also, ensure that any switch is user that occurs after WM has been
notified that SystemUI is going away is stopped.

Bug: 322157041
Test: atest KeyguardViewMediatorTest
Flag: EXEMPT bugfix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:15c517ef199695c24560aa5f6b28c374ed111d68)
Merged-In: I32699e32fe1dbe94af2abe7e7ade5363a2b6cb55
Merged-In: I02df941d89467fc678c0749f4a2436ad8fee8b7b
Merged-In: I753e696474ae2a96ef8cecf9a05ad95e9dbcb2c3

Change-Id: I32699e32fe1dbe94af2abe7e7ade5363a2b6cb55
parent 0fc61ce2
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -147,6 +147,15 @@ public class KeyguardManager {
     */
    public static final String EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS = "check_dpm";

    /**
     * When switching to a secure user, system server will expect a callback when the UI has
     * completed the switch.
     *
     * @hide
     */
    public static final String LOCK_ON_USER_SWITCH_CALLBACK = "onSwitchCallback";


    /**
     *
     * Password lock type, see {@link #setLock}
+49 −18
Original line number Diff line number Diff line
@@ -138,8 +138,7 @@ const val UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS = 75L
class KeyguardUnlockAnimationController @Inject constructor(
    private val context: Context,
    private val keyguardStateController: KeyguardStateController,
    private val
    keyguardViewMediator: Lazy<KeyguardViewMediator>,
    private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
    private val keyguardViewController: KeyguardViewController,
    private val featureFlags: FeatureFlags,
    private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
@@ -198,6 +197,12 @@ class KeyguardUnlockAnimationController @Inject constructor(
     */
    var playingCannedUnlockAnimation = false

    /**
     * Whether we reached the swipe gesture threshold to dismiss keyguard, or restore it, once and
     * should ignore any future changes to the dismiss amount before the animation finishes.
     */
    var dismissAmountThresholdsReached = false

    /**
     * Remote callback provided by Launcher that allows us to control the Launcher's unlock
     * animation and smartspace.
@@ -326,7 +331,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
            addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    playingCannedUnlockAnimation = false
                    keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
                    keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
                        false /* cancelled */
                    )
                }
@@ -516,7 +521,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
            // around behind that.
            biometricUnlockControllerLazy.get().isWakeAndUnlock -> {
                setSurfaceBehindAppearAmount(1f)
                keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
                keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
                    false /* cancelled */)
            }

@@ -553,7 +558,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
                return@postDelayed
            }

            keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
            keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
                false /* cancelled */)
        }, CANNED_UNLOCK_START_DELAY)
    }
@@ -625,6 +630,10 @@ class KeyguardUnlockAnimationController @Inject constructor(
            return
        }

        if (dismissAmountThresholdsReached) {
            return
        }

        if (!keyguardStateController.isShowing) {
            return
        }
@@ -657,6 +666,11 @@ class KeyguardUnlockAnimationController @Inject constructor(
            return
        }

        // no-op if we alreaddy reached a threshold.
        if (dismissAmountThresholdsReached) {
            return
        }

        // no-op if animation is not requested yet.
        if (!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
                !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
@@ -671,7 +685,9 @@ class KeyguardUnlockAnimationController @Inject constructor(
                        !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
                        dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) {
            setSurfaceBehindAppearAmount(1f)
            keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(false /* cancelled */)
            dismissAmountThresholdsReached = true
            keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
                    false /* cancelled */)
        }
    }

@@ -726,28 +742,43 @@ class KeyguardUnlockAnimationController @Inject constructor(
     * This is generally triggered by us, calling
     * [KeyguardViewMediator.finishSurfaceBehindRemoteAnimation].
     */
    fun notifyFinishedKeyguardExitAnimation(cancelled: Boolean) {
    fun notifyFinishedKeyguardExitAnimation(showKeyguard: Boolean) {
        // Cancel any pending actions.
        handler.removeCallbacksAndMessages(null)

        // Make sure we made the surface behind fully visible, just in case. It should already be
        // fully visible. If the launcher is doing its own animation, let it continue without
        // forcing it to 1f.
        // The lockscreen surface is gone, so it is now safe to re-show the smartspace.
        if (lockscreenSmartspace?.visibility == View.INVISIBLE) {
            lockscreenSmartspace?.visibility = View.VISIBLE
        }

        if (!showKeyguard) {
            // Make sure we made the surface behind fully visible, just in case. It should already
            // be fully visible. The exit animation is finished, and we should not hold the leash
            // anymore, so forcing it to 1f.
            surfaceBehindAlpha = 1f
            setSurfaceBehindAppearAmount(1f)

            try {
                launcherUnlockController?.setUnlockAmount(1f, false /* forceIfAnimating */)
            } catch (e: RemoteException) {
                Log.e(TAG, "Remote exception in notifyFinishedKeyguardExitAnimation", e)
            }
        }

        listeners.forEach { it.onUnlockAnimationFinished() }

        // Reset all state
        surfaceBehindAlphaAnimator.cancel()
        surfaceBehindEntryAnimator.cancel()

        // That target is no longer valid since the animation finished, null it out.
        surfaceBehindRemoteAnimationTarget = null
        surfaceBehindParams = null

        playingCannedUnlockAnimation = false
        dismissAmountThresholdsReached = false
        willUnlockWithInWindowLauncherAnimations = false
        willUnlockWithSmartspaceTransition = false

        // The lockscreen surface is gone, so it is now safe to re-show the smartspace.
        lockscreenSmartspace?.visibility = View.VISIBLE

        listeners.forEach { it.onUnlockAnimationFinished() }
    }

    /**
+279 −79

File changed.

Preview size limit exceeded, changes collapsed.

+3 −3
Original line number Diff line number Diff line
@@ -116,7 +116,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {

        // Also expect we've immediately asked the keyguard view mediator to finish the remote
        // animation.
        verify(keyguardViewMediator, times(1)).onKeyguardExitRemoteAnimationFinished(
        verify(keyguardViewMediator, times(1)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
            false /* cancelled */)

        verifyNoMoreInteractions(surfaceTransactionApplier)
@@ -136,7 +136,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
        )

        // Since the animation is running, we should not have finished the remote animation.
        verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
        verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
            false /* cancelled */)
    }

+25 −30
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
import static android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK;
import static android.os.PowerWhitelistManager.REASON_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -103,7 +104,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
@@ -127,6 +127,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
@@ -1580,7 +1582,8 @@ class UserController implements Handler.Callback {
                    mInjector.getWindowManager().setSwitchingUser(true);
                    // Only lock if the user has a secure keyguard PIN/Pattern/Pwd
                    if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
                        mInjector.getWindowManager().lockNow(null);
                        // Make sure the device is locked before moving on with the user switch
                        mInjector.lockDeviceNowAndWaitForKeyguardShown();
                    }
                }
            } else {
@@ -2039,22 +2042,9 @@ class UserController implements Handler.Callback {
    @VisibleForTesting
    void completeUserSwitch(int newUserId) {
        if (isUserSwitchUiEnabled()) {
            // If there is no challenge set, dismiss the keyguard right away
            if (!mInjector.getKeyguardManager().isDeviceSecure(newUserId)) {
                // Wait until the keyguard is dismissed to unfreeze
                mInjector.dismissKeyguard(
                        new Runnable() {
                            public void run() {
                                unfreezeScreen();
                            }
                        },
                        "User Switch");
                return;
            } else {
            unfreezeScreen();
        }
    }
    }

    /**
     * Tell WindowManager we're ready to unfreeze the screen, at its leisure. Note that there is
@@ -3381,23 +3371,28 @@ class UserController implements Handler.Callback {
            return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
        }

        protected void dismissKeyguard(Runnable runnable, String reason) {
            getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() {
                @Override
                public void onDismissError() throws RemoteException {
                    mHandler.post(runnable);
                }
        void lockDeviceNowAndWaitForKeyguardShown() {
            final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
            t.traceBegin("lockDeviceNowAndWaitForKeyguardShown");

                @Override
                public void onDismissSucceeded() throws RemoteException {
                    mHandler.post(runnable);
            final CountDownLatch latch = new CountDownLatch(1);
            Bundle bundle = new Bundle();
            bundle.putBinder(LOCK_ON_USER_SWITCH_CALLBACK, new IRemoteCallback.Stub() {
                public void sendResult(Bundle data) {
                    latch.countDown();
                }

                @Override
                public void onDismissCancelled() throws RemoteException {
                    mHandler.post(runnable);
            });
            getWindowManager().lockNow(bundle);
            try {
                if (!latch.await(20, TimeUnit.SECONDS)) {
                    throw new RuntimeException("User controller expected a callback while waiting "
                            + "to show the keyguard. Timed out after 20 seconds.");
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                t.traceEnd();
            }
            }, reason);
        }
    }
}
Loading