From 3ed63ef5eb59399927c4e10f66c5f80c3ac52ec4 Mon Sep 17 00:00:00 2001 From: David Perez Date: Wed, 13 Aug 2025 10:04:19 -0500 Subject: [PATCH] PM-24688: Use the realtime elapse time to determine vault lock timeouts (#5684) --- .../platform/manager/di/PlatformManagerModule.kt | 6 ++++++ .../data/vault/manager/VaultLockManagerImpl.kt | 9 ++++++--- .../data/vault/manager/di/VaultManagerModule.kt | 3 +++ .../data/vault/manager/VaultLockManagerTest.kt | 5 +++++ .../data/auth/repository/AuthRepositoryImpl.kt | 6 +++--- .../data/auth/repository/di/AuthRepositoryModule.kt | 3 +++ .../platform/manager/di/PlatformManagerModule.kt | 6 ++++++ .../core/data/manager/realtime/RealtimeManager.kt | 11 +++++++++++ .../data/manager/realtime/RealtimeManagerImpl.kt | 12 ++++++++++++ 9 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManager.kt create mode 100644 core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManagerImpl.kt diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt index efbb751d36..ddfff45e9c 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt @@ -3,6 +3,8 @@ package com.x8bit.bitwarden.data.platform.manager.di import android.app.Application import android.content.Context import androidx.core.content.getSystemService +import com.bitwarden.core.data.manager.realtime.RealtimeManager +import com.bitwarden.core.data.manager.realtime.RealtimeManagerImpl import com.bitwarden.core.data.manager.toast.ToastManager import com.bitwarden.core.data.manager.toast.ToastManagerImpl import com.bitwarden.data.manager.DispatcherManager @@ -198,6 +200,10 @@ object PlatformManagerModule { toastManager = toastManager, ) + @Provides + @Singleton + fun provideRealtimeManager(): RealtimeManager = RealtimeManagerImpl() + @Provides @Singleton fun provideToastManager( diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt index 1938450278..6509579ee9 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt @@ -7,6 +7,7 @@ import android.content.IntentFilter import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.InitUserCryptoRequest +import com.bitwarden.core.data.manager.realtime.RealtimeManager import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow import com.bitwarden.core.data.util.asSuccess import com.bitwarden.core.data.util.concurrentMapOf @@ -77,6 +78,7 @@ private const val MAXIMUM_INVALID_UNLOCK_ATTEMPTS = 5 @Suppress("TooManyFunctions", "LongParameterList") class VaultLockManagerImpl( private val clock: Clock, + private val realtimeManager: RealtimeManager, private val authDiskSource: AuthDiskSource, private val authSdkSource: AuthSdkSource, private val vaultSdkSource: VaultSdkSource, @@ -613,7 +615,7 @@ class VaultLockManagerImpl( handleTimeoutAction(userId = userId, vaultTimeoutAction = vaultTimeoutAction) }, vaultTimeoutAction = vaultTimeoutAction, - startTimeMs = clock.millis(), + startTimeMs = realtimeManager.elapsedRealtimeMs, durationMs = delayMs, ) } @@ -674,11 +676,12 @@ class VaultLockManagerImpl( private inner class ScreenStateBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { userIdTimerJobMap.map { (userId, data) -> + val durationSoFarMs = (realtimeManager.elapsedRealtimeMs - data.startTimeMs) + .coerceAtLeast(minimumValue = 0L) handleTimeoutActionWithDelay( userId = userId, vaultTimeoutAction = data.vaultTimeoutAction, - delayMs = data.durationMs - (clock.millis() - data.startTimeMs) - .coerceAtLeast(minimumValue = 0L), + delayMs = data.durationMs - durationSoFarMs, ) } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/di/VaultManagerModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/di/VaultManagerModule.kt index 46148d3960..d737952697 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/di/VaultManagerModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/di/VaultManagerModule.kt @@ -1,6 +1,7 @@ package com.x8bit.bitwarden.data.vault.manager.di import android.content.Context +import com.bitwarden.core.data.manager.realtime.RealtimeManager import com.bitwarden.data.manager.DispatcherManager import com.bitwarden.network.service.CiphersService import com.bitwarden.network.service.DownloadService @@ -73,6 +74,7 @@ object VaultManagerModule { fun provideVaultLockManager( @ApplicationContext context: Context, clock: Clock, + realtimeManager: RealtimeManager, authDiskSource: AuthDiskSource, authSdkSource: AuthSdkSource, vaultSdkSource: VaultSdkSource, @@ -85,6 +87,7 @@ object VaultManagerModule { VaultLockManagerImpl( context = context, clock = clock, + realtimeManager = realtimeManager, authDiskSource = authDiskSource, authSdkSource = authSdkSource, vaultSdkSource = vaultSdkSource, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt index 62dabc134f..d4b9b1c581 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt @@ -8,6 +8,7 @@ import app.cash.turbine.test import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.InitUserCryptoRequest +import com.bitwarden.core.data.manager.realtime.RealtimeManager import com.bitwarden.core.data.util.asFailure import com.bitwarden.core.data.util.asSuccess import com.bitwarden.crypto.HashPurpose @@ -100,10 +101,14 @@ class VaultLockManagerTest { } private val testDispatcher = UnconfinedTestDispatcher() private val fakeDispatcherManager = FakeDispatcherManager(unconfined = testDispatcher) + private val realtimeManager: RealtimeManager = mockk { + every { elapsedRealtimeMs } returns FIXED_CLOCK.millis() + } private val vaultLockManager: VaultLockManager = VaultLockManagerImpl( context = context, clock = FIXED_CLOCK, + realtimeManager = realtimeManager, authDiskSource = fakeAuthDiskSource, authSdkSource = authSdkSource, vaultSdkSource = vaultSdkSource, diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/AuthRepositoryImpl.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/AuthRepositoryImpl.kt index a40ef11792..1560cbc5ae 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/AuthRepositoryImpl.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/AuthRepositoryImpl.kt @@ -1,7 +1,7 @@ package com.bitwarden.authenticator.data.auth.repository -import android.os.SystemClock import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource +import com.bitwarden.core.data.manager.realtime.RealtimeManager import javax.inject.Inject /** @@ -9,7 +9,7 @@ import javax.inject.Inject */ class AuthRepositoryImpl @Inject constructor( private val authDiskSource: AuthDiskSource, - private val elapsedRealtimeMillisProvider: () -> Long = { SystemClock.elapsedRealtime() }, + private val realtimeManager: RealtimeManager, ) : AuthRepository { /** @@ -17,7 +17,7 @@ class AuthRepositoryImpl @Inject constructor( */ override fun updateLastActiveTime() { authDiskSource.storeLastActiveTimeMillis( - lastActiveTimeMillis = elapsedRealtimeMillisProvider(), + lastActiveTimeMillis = realtimeManager.elapsedRealtimeMs, ) } } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/di/AuthRepositoryModule.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/di/AuthRepositoryModule.kt index 96bfb403a1..cd7ba5bd40 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/di/AuthRepositoryModule.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/auth/repository/di/AuthRepositoryModule.kt @@ -3,6 +3,7 @@ package com.bitwarden.authenticator.data.auth.repository.di import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource import com.bitwarden.authenticator.data.auth.repository.AuthRepository import com.bitwarden.authenticator.data.auth.repository.AuthRepositoryImpl +import com.bitwarden.core.data.manager.realtime.RealtimeManager import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -18,7 +19,9 @@ object AuthRepositoryModule { @Provides fun provideAuthRepository( authDiskSource: AuthDiskSource, + realtimeManager: RealtimeManager, ): AuthRepository = AuthRepositoryImpl( authDiskSource = authDiskSource, + realtimeManager = realtimeManager, ) } diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/manager/di/PlatformManagerModule.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/manager/di/PlatformManagerModule.kt index 0ae8d708de..ec6e4e5d41 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/manager/di/PlatformManagerModule.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/manager/di/PlatformManagerModule.kt @@ -20,6 +20,8 @@ import com.bitwarden.authenticator.data.platform.manager.imports.ImportManager import com.bitwarden.authenticator.data.platform.manager.imports.ImportManagerImpl import com.bitwarden.authenticator.data.platform.repository.DebugMenuRepository import com.bitwarden.authenticator.data.platform.repository.SettingsRepository +import com.bitwarden.core.data.manager.realtime.RealtimeManager +import com.bitwarden.core.data.manager.realtime.RealtimeManagerImpl import com.bitwarden.core.data.manager.toast.ToastManager import com.bitwarden.core.data.manager.toast.ToastManagerImpl import com.bitwarden.data.manager.DispatcherManager @@ -49,6 +51,10 @@ object PlatformManagerModule { toastManager = toastManager, ) + @Provides + @Singleton + fun provideRealtimeManager(): RealtimeManager = RealtimeManagerImpl() + @Provides @Singleton fun provideToastManager( diff --git a/core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManager.kt b/core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManager.kt new file mode 100644 index 0000000000..862f262df3 --- /dev/null +++ b/core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManager.kt @@ -0,0 +1,11 @@ +package com.bitwarden.core.data.manager.realtime + +/** + * An manager interface for accessing the system realtime clock. + */ +interface RealtimeManager { + /** + * Returns milliseconds since the device has booted up, this includes time spent in sleep. + */ + val elapsedRealtimeMs: Long +} diff --git a/core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManagerImpl.kt b/core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManagerImpl.kt new file mode 100644 index 0000000000..f980beb898 --- /dev/null +++ b/core/src/main/kotlin/com/bitwarden/core/data/manager/realtime/RealtimeManagerImpl.kt @@ -0,0 +1,12 @@ +package com.bitwarden.core.data.manager.realtime + +import android.os.SystemClock +import com.bitwarden.annotation.OmitFromCoverage + +/** + * The default implementation of the [RealtimeManager]. + */ +@OmitFromCoverage +class RealtimeManagerImpl : RealtimeManager { + override val elapsedRealtimeMs: Long get() = SystemClock.elapsedRealtime() +}