Fix race condition with SIM bouncer on start

The user could end up with a blank screen or a strange
looking lockscreeen.

The SIM bouncer may decide to show in two bad conditions:
1. The bouncer hasn't been bound yet - So return false
2. The device is entering doze but hasn't quite made it -
   so don't show it.

Test: atest StatusBarKeyguardViewManagerTest
Fixes: 351426938
Flag: com.android.systemui.sim_pin_race_condition_on_restart
Change-Id: I5f69563ee94aea092c2f179e66a5c51c67886fda
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 71f5511..8e2f7c1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1186,4 +1186,14 @@
   namespace: "systemui"
   description: "Enables MSDL feedback in SysUI surfaces."
   bug: "352600066"
+}
+
+flag {
+   name: "sim_pin_race_condition_on_restart"
+   namespace: "systemui"
+   description: "The SIM PIN screen may be shown incorrectly on reboot"
+   bug: "351426938"
+   metadata {
+        purpose: PURPOSE_BUGFIX
+   }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index 546a6b7..d5e1fae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -160,6 +160,12 @@
     }
 
     @Test
+    fun testShowReturnsFalseWhenDelegateIsNotSet() {
+        whenever(bouncerView.delegate).thenReturn(null)
+        assertThat(underTest.show(true)).isEqualTo(false)
+    }
+
+    @Test
     fun testShow_isResumed() {
         whenever(repository.primaryBouncerShow.value).thenReturn(true)
         whenever(keyguardSecurityModel.getSecurityMode(anyInt()))
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 3c5e571..c28bce2 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -134,7 +134,7 @@
     // TODO(b/243695312): Encapsulate all of the show logic for the bouncer.
     /** Show the bouncer if necessary and set the relevant states. */
     @JvmOverloads
-    fun show(isScrimmed: Boolean) {
+    fun show(isScrimmed: Boolean): Boolean {
         // When the scene container framework is enabled, instead of calling this, call
         // SceneInteractor#changeScene(Scenes.Bouncer, ...).
         SceneContainerFlag.assertInLegacyMode()
@@ -146,44 +146,48 @@
                     "primaryBouncerDelegate is set. Let's exit early so we don't " +
                     "set the wrong primaryBouncer state."
             )
-            return
+            return false
         }
 
-        // Reset some states as we show the bouncer.
-        repository.setKeyguardAuthenticatedBiometrics(null)
-        repository.setPrimaryStartingToHide(false)
+        try {
+            Trace.beginSection("KeyguardBouncer#show")
+            // Reset some states as we show the bouncer.
+            repository.setKeyguardAuthenticatedBiometrics(null)
+            repository.setPrimaryStartingToHide(false)
 
-        val resumeBouncer =
-            (isBouncerShowing() || repository.primaryBouncerShowingSoon.value) &&
-                needsFullscreenBouncer()
+            val resumeBouncer =
+                (isBouncerShowing() || repository.primaryBouncerShowingSoon.value) &&
+                    needsFullscreenBouncer()
 
-        Trace.beginSection("KeyguardBouncer#show")
-        repository.setPrimaryScrimmed(isScrimmed)
-        if (isScrimmed) {
-            setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+            repository.setPrimaryScrimmed(isScrimmed)
+            if (isScrimmed) {
+                setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+            }
+
+            // In this special case, we want to hide the bouncer and show it again. We want to emit
+            // show(true) again so that we can reinflate the new view.
+            if (resumeBouncer) {
+                repository.setPrimaryShow(false)
+            }
+
+            if (primaryBouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
+                // Keyguard is done.
+                return false
+            }
+
+            repository.setPrimaryShowingSoon(true)
+            if (usePrimaryBouncerPassiveAuthDelay()) {
+                Log.d(TAG, "delay bouncer, passive auth may succeed")
+                mainHandler.postDelayed(showRunnable, passiveAuthBouncerDelay)
+            } else {
+                DejankUtils.postAfterTraversal(showRunnable)
+            }
+            keyguardStateController.notifyPrimaryBouncerShowing(true)
+            primaryBouncerCallbackInteractor.dispatchStartingToShow()
+            return true
+        } finally {
+            Trace.endSection()
         }
-
-        // In this special case, we want to hide the bouncer and show it again. We want to emit
-        // show(true) again so that we can reinflate the new view.
-        if (resumeBouncer) {
-            repository.setPrimaryShow(false)
-        }
-
-        if (primaryBouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
-            // Keyguard is done.
-            return
-        }
-
-        repository.setPrimaryShowingSoon(true)
-        if (usePrimaryBouncerPassiveAuthDelay()) {
-            Log.d(TAG, "delay bouncer, passive auth may succeed")
-            mainHandler.postDelayed(showRunnable, passiveAuthBouncerDelay)
-        } else {
-            DejankUtils.postAfterTraversal(showRunnable)
-        }
-        keyguardStateController.notifyPrimaryBouncerShowing(true)
-        primaryBouncerCallbackInteractor.dispatchStartingToShow()
-        Trace.endSection()
     }
 
     /** Sets the correct bouncer states to hide the bouncer. */
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 96127b6..8f2ad40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -55,6 +55,7 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.TrustGrantFlags;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.Flags;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
@@ -708,15 +709,30 @@
      * {@link #needsFullscreenBouncer()}.
      */
     protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
-        if (needsFullscreenBouncer() && !mDozing) {
+        boolean isDozing = mDozing;
+        if (Flags.simPinRaceConditionOnRestart()) {
+            KeyguardState toState = mKeyguardTransitionInteractor.getTransitionState().getValue()
+                    .getTo();
+            isDozing = mDozing || toState == KeyguardState.DOZING || toState == KeyguardState.AOD;
+        }
+        if (needsFullscreenBouncer() && !isDozing) {
             // The keyguard might be showing (already). So we need to hide it.
             if (!primaryBouncerIsShowing()) {
-                mCentralSurfaces.hideKeyguard();
                 if (SceneContainerFlag.isEnabled()) {
+                    mCentralSurfaces.hideKeyguard();
                     mSceneInteractorLazy.get().changeScene(
                             Scenes.Bouncer, "StatusBarKeyguardViewManager.showBouncerOrKeyguard");
                 } else {
-                    mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+                    if (Flags.simPinRaceConditionOnRestart()) {
+                        if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true)) {
+                            mCentralSurfaces.hideKeyguard();
+                        } else {
+                            mCentralSurfaces.showKeyguard();
+                        }
+                    } else {
+                        mCentralSurfaces.hideKeyguard();
+                        mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+                    }
                 }
             } else {
                 Log.e(TAG, "Attempted to show the sim bouncer when it is already showing.");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 0e4d892..49e3f04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -116,6 +116,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
@@ -176,6 +177,8 @@
     private ViewRootImpl mViewRootImpl;
     @Mock
     private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Captor
     private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor;
     @Captor
@@ -223,7 +226,7 @@
                         mAlternateBouncerInteractor,
                         mUdfpsOverlayInteractor,
                         mActivityStarter,
-                        mock(KeyguardTransitionInteractor.class),
+                        mKeyguardTransitionInteractor,
                         StandardTestDispatcher(null, null),
                         () -> mock(WindowManagerLockscreenVisibilityInteractor.class),
                         () -> mock(KeyguardDismissActionInteractor.class),
@@ -1066,6 +1069,22 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART)
+    public void testShowBouncerOrKeyguard_showsKeyguardIfShowBouncerReturnsFalse() {
+        when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+                KeyguardSecurityModel.SecurityMode.SimPin);
+        when(mPrimaryBouncerInteractor.show(true)).thenReturn(false);
+        when(mKeyguardTransitionInteractor.getTransitionState().getValue().getTo())
+                .thenReturn(KeyguardState.LOCKSCREEN);
+
+        reset(mCentralSurfaces);
+        mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+        verify(mPrimaryBouncerInteractor).show(true);
+        verify(mCentralSurfaces).showKeyguard();
+    }
+
+    @Test
+    @DisableSceneContainer
     public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
                 KeyguardSecurityModel.SecurityMode.SimPin);