[flexiglass] Always round up the remaining throttle milliseconds.

This fixes a bug where we show the user there are 0 seconds remaining
before they can try to re-authenticate during throttling.

Bonus: since we only store this value in the model for presenting it to
the user, we should store it in seconds directly rather than converting
from milliseconds when formatting the message (which happens every
second during throttling).

Fix: 314308690
Bug: 314757822
Test: Unit tests updated and still pass.
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: Ifbf4708b687c037877784b29427c56ee2f20ab37
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index d968c1b..661c345 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -27,8 +27,6 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.scene.SceneTestUtils
 import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
@@ -335,7 +333,7 @@
                     AuthenticationThrottlingModel(
                         failedAttemptCount =
                             FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
-                        remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS,
+                        remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
                     )
                 )
 
@@ -347,15 +345,12 @@
                     AuthenticationThrottlingModel(
                         failedAttemptCount =
                             FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
-                        remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS,
+                        remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
                     )
                 )
 
             // Move the clock forward to ALMOST skip the throttling, leaving one second to go:
-            val throttleTimeoutSec =
-                FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds
-                    .toInt()
-            repeat(throttleTimeoutSec - 1) { time ->
+            repeat(FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS - 1) { time ->
                 advanceTimeBy(1000)
                 assertThat(throttling)
                     .isEqualTo(
@@ -363,9 +358,8 @@
                             failedAttemptCount =
                                 FakeAuthenticationRepository
                                     .MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
-                            remainingMs =
-                                ((throttleTimeoutSec - (time + 1)).seconds.inWholeMilliseconds)
-                                    .toInt(),
+                            remainingSeconds =
+                                FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS - (time + 1),
                         )
                     )
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 04f6cd3..3e3a1a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.SceneTestUtils
 import com.google.common.truth.Truth.assertThat
-import kotlin.math.ceil
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.advanceTimeBy
@@ -268,7 +267,7 @@
                     AuthenticationThrottlingModel(
                         failedAttemptCount =
                             FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
-                        remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS,
+                        remainingSeconds = FakeAuthenticationRepository.THROTTLE_DURATION_SECONDS,
                     )
                 )
             assertTryAgainMessage(
@@ -286,8 +285,7 @@
                     .toInt()
             )
 
-            throttling?.remainingMs?.let { remainingMs ->
-                val seconds = ceil(remainingMs / 1000f).toInt()
+            throttling?.remainingSeconds?.let { seconds ->
                 repeat(seconds) { time ->
                     advanceTimeBy(1000)
                     val remainingTimeSec = seconds - time - 1
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 75d6a00..2b64d8e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.scene.SceneTestUtils
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -144,7 +145,9 @@
             }
             assertThat(message?.isUpdateAnimated).isFalse()
 
-            throttling?.remainingMs?.let { remainingMs -> advanceTimeBy(remainingMs.toLong()) }
+            throttling?.remainingSeconds?.let { remainingSeconds ->
+                advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
+            }
             assertThat(message?.isUpdateAnimated).isTrue()
         }
 
@@ -167,7 +170,9 @@
             }
             assertThat(isInputEnabled).isFalse()
 
-            throttling?.remainingMs?.let { milliseconds -> advanceTimeBy(milliseconds.toLong()) }
+            throttling?.remainingSeconds?.let { remainingSeconds ->
+                advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
+            }
             assertThat(isInputEnabled).isTrue()
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 64f2946..a217d93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -335,12 +335,12 @@
             repeat(failedAttemptCount) {
                 authenticationRepository.reportAuthenticationAttempt(false)
             }
-            val remainingTimeMs = 30_000
-            authenticationRepository.setThrottleDuration(remainingTimeMs)
+            val remainingTimeSeconds = 30
+            authenticationRepository.setThrottleDuration(remainingTimeSeconds * 1000)
             authenticationRepository.throttling.value =
                 AuthenticationThrottlingModel(
                     failedAttemptCount = failedAttemptCount,
-                    remainingMs = remainingTimeMs,
+                    remainingSeconds = remainingTimeSeconds,
                 )
         } else {
             authenticationRepository.reportAuthenticationAttempt(true)
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 1ba0220..4e67771 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
+import kotlin.math.ceil
 import kotlin.math.max
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
@@ -283,7 +284,7 @@
                 if (remainingMs > 0) {
                     AuthenticationThrottlingModel(
                         failedAttemptCount = failedAttemptCount.await(),
-                        remainingMs = remainingMs.toInt(),
+                        remainingSeconds = ceil(remainingMs / 1000f).toInt(),
                     )
                 } else {
                     null // Throttling ended.
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt
index d0d398e..8392528 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt
@@ -23,10 +23,13 @@
     val failedAttemptCount: Int = 0,
 
     /**
-     * Remaining amount of time, in milliseconds, before another authentication attempt can be done.
-     * If not throttling this will be `0`.
+     * Remaining amount of time, in seconds, before another authentication attempt can be done. If
+     * not throttling this will be `0`.
      *
      * This number is changed throughout the timeout.
+     *
+     * Note: this isn't precise (in milliseconds), but rounded up to ensure "at most" this amount of
+     * seconds remains.
      */
-    val remainingMs: Int = 0,
+    val remainingSeconds: Int = 0,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 1122877..677f60d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.async
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -259,7 +258,7 @@
             throttlingModel != null ->
                 applicationContext.getString(
                     com.android.internal.R.string.lockscreen_too_many_failed_attempts_countdown,
-                    throttlingModel.remainingMs.milliseconds.inWholeSeconds,
+                    throttlingModel.remainingSeconds,
                 )
             message != null -> message
             else -> ""
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 58fa857..fefc3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.user.ui.viewmodel.UserViewModel
 import dagger.Module
 import dagger.Provides
-import kotlin.math.ceil
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.SupervisorJob
@@ -205,7 +204,7 @@
                             applicationContext.getString(
                                 authMethodViewModel.throttlingMessageId,
                                 throttling.failedAttemptCount,
-                                ceil(throttling.remainingMs / 1000f).toInt(),
+                                throttling.remainingSeconds,
                             )
                         } else {
                             null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 094616f..aa0d7b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -35,7 +35,7 @@
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.DismissCallbackRegistry
@@ -62,7 +62,6 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
-import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -108,17 +107,18 @@
 
     suspend fun TestScope.init() {
         userRepository.setSelectedUserInfo(PRIMARY_USER)
-        val featureFlags = FakeFeatureFlags().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
+        val featureFlags =
+            FakeFeatureFlagsClassic().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
         primaryBouncerInteractor =
             PrimaryBouncerInteractor(
                 bouncerRepository,
-                Mockito.mock(BouncerView::class.java),
-                Mockito.mock(Handler::class.java),
-                Mockito.mock(KeyguardStateController::class.java),
-                Mockito.mock(KeyguardSecurityModel::class.java),
-                Mockito.mock(PrimaryBouncerCallbackInteractor::class.java),
-                Mockito.mock(FalsingCollector::class.java),
-                Mockito.mock(DismissCallbackRegistry::class.java),
+                mock(BouncerView::class.java),
+                mock(Handler::class.java),
+                mock(KeyguardStateController::class.java),
+                mock(KeyguardSecurityModel::class.java),
+                mock(PrimaryBouncerCallbackInteractor::class.java),
+                mock(FalsingCollector::class.java),
+                mock(DismissCallbackRegistry::class.java),
                 context,
                 keyguardUpdateMonitor,
                 fakeTrustRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 4fdea97..0327087 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -166,7 +166,8 @@
                 AuthenticationPatternCoordinate(0, 2),
             )
         const val MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING = 5
-        const val THROTTLE_DURATION_MS = 30000
+        const val THROTTLE_DURATION_SECONDS = 30
+        const val THROTTLE_DURATION_MS = THROTTLE_DURATION_SECONDS * 1000
         const val HINTING_PIN_LENGTH = 6
         val DEFAULT_PIN = buildList { repeat(HINTING_PIN_LENGTH) { add(it + 1) } }