PM-24688: Use the realtime elapse time to determine vault lock timeouts (#5684)

This commit is contained in:
David Perez
2025-08-13 10:04:19 -05:00
committed by GitHub
parent 1e2bc4aa70
commit 3ed63ef5eb
9 changed files with 55 additions and 6 deletions

View File

@@ -3,6 +3,8 @@ package com.x8bit.bitwarden.data.platform.manager.di
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import androidx.core.content.getSystemService 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.ToastManager
import com.bitwarden.core.data.manager.toast.ToastManagerImpl import com.bitwarden.core.data.manager.toast.ToastManagerImpl
import com.bitwarden.data.manager.DispatcherManager import com.bitwarden.data.manager.DispatcherManager
@@ -198,6 +200,10 @@ object PlatformManagerModule {
toastManager = toastManager, toastManager = toastManager,
) )
@Provides
@Singleton
fun provideRealtimeManager(): RealtimeManager = RealtimeManagerImpl()
@Provides @Provides
@Singleton @Singleton
fun provideToastManager( fun provideToastManager(

View File

@@ -7,6 +7,7 @@ import android.content.IntentFilter
import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitOrgCryptoRequest
import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.InitUserCryptoMethod
import com.bitwarden.core.InitUserCryptoRequest 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.repository.util.bufferedMutableSharedFlow
import com.bitwarden.core.data.util.asSuccess import com.bitwarden.core.data.util.asSuccess
import com.bitwarden.core.data.util.concurrentMapOf import com.bitwarden.core.data.util.concurrentMapOf
@@ -77,6 +78,7 @@ private const val MAXIMUM_INVALID_UNLOCK_ATTEMPTS = 5
@Suppress("TooManyFunctions", "LongParameterList") @Suppress("TooManyFunctions", "LongParameterList")
class VaultLockManagerImpl( class VaultLockManagerImpl(
private val clock: Clock, private val clock: Clock,
private val realtimeManager: RealtimeManager,
private val authDiskSource: AuthDiskSource, private val authDiskSource: AuthDiskSource,
private val authSdkSource: AuthSdkSource, private val authSdkSource: AuthSdkSource,
private val vaultSdkSource: VaultSdkSource, private val vaultSdkSource: VaultSdkSource,
@@ -613,7 +615,7 @@ class VaultLockManagerImpl(
handleTimeoutAction(userId = userId, vaultTimeoutAction = vaultTimeoutAction) handleTimeoutAction(userId = userId, vaultTimeoutAction = vaultTimeoutAction)
}, },
vaultTimeoutAction = vaultTimeoutAction, vaultTimeoutAction = vaultTimeoutAction,
startTimeMs = clock.millis(), startTimeMs = realtimeManager.elapsedRealtimeMs,
durationMs = delayMs, durationMs = delayMs,
) )
} }
@@ -674,11 +676,12 @@ class VaultLockManagerImpl(
private inner class ScreenStateBroadcastReceiver : BroadcastReceiver() { private inner class ScreenStateBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
userIdTimerJobMap.map { (userId, data) -> userIdTimerJobMap.map { (userId, data) ->
val durationSoFarMs = (realtimeManager.elapsedRealtimeMs - data.startTimeMs)
.coerceAtLeast(minimumValue = 0L)
handleTimeoutActionWithDelay( handleTimeoutActionWithDelay(
userId = userId, userId = userId,
vaultTimeoutAction = data.vaultTimeoutAction, vaultTimeoutAction = data.vaultTimeoutAction,
delayMs = data.durationMs - (clock.millis() - data.startTimeMs) delayMs = data.durationMs - durationSoFarMs,
.coerceAtLeast(minimumValue = 0L),
) )
} }
} }

View File

@@ -1,6 +1,7 @@
package com.x8bit.bitwarden.data.vault.manager.di package com.x8bit.bitwarden.data.vault.manager.di
import android.content.Context import android.content.Context
import com.bitwarden.core.data.manager.realtime.RealtimeManager
import com.bitwarden.data.manager.DispatcherManager import com.bitwarden.data.manager.DispatcherManager
import com.bitwarden.network.service.CiphersService import com.bitwarden.network.service.CiphersService
import com.bitwarden.network.service.DownloadService import com.bitwarden.network.service.DownloadService
@@ -73,6 +74,7 @@ object VaultManagerModule {
fun provideVaultLockManager( fun provideVaultLockManager(
@ApplicationContext context: Context, @ApplicationContext context: Context,
clock: Clock, clock: Clock,
realtimeManager: RealtimeManager,
authDiskSource: AuthDiskSource, authDiskSource: AuthDiskSource,
authSdkSource: AuthSdkSource, authSdkSource: AuthSdkSource,
vaultSdkSource: VaultSdkSource, vaultSdkSource: VaultSdkSource,
@@ -85,6 +87,7 @@ object VaultManagerModule {
VaultLockManagerImpl( VaultLockManagerImpl(
context = context, context = context,
clock = clock, clock = clock,
realtimeManager = realtimeManager,
authDiskSource = authDiskSource, authDiskSource = authDiskSource,
authSdkSource = authSdkSource, authSdkSource = authSdkSource,
vaultSdkSource = vaultSdkSource, vaultSdkSource = vaultSdkSource,

View File

@@ -8,6 +8,7 @@ import app.cash.turbine.test
import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitOrgCryptoRequest
import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.InitUserCryptoMethod
import com.bitwarden.core.InitUserCryptoRequest 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.asFailure
import com.bitwarden.core.data.util.asSuccess import com.bitwarden.core.data.util.asSuccess
import com.bitwarden.crypto.HashPurpose import com.bitwarden.crypto.HashPurpose
@@ -100,10 +101,14 @@ class VaultLockManagerTest {
} }
private val testDispatcher = UnconfinedTestDispatcher() private val testDispatcher = UnconfinedTestDispatcher()
private val fakeDispatcherManager = FakeDispatcherManager(unconfined = testDispatcher) private val fakeDispatcherManager = FakeDispatcherManager(unconfined = testDispatcher)
private val realtimeManager: RealtimeManager = mockk {
every { elapsedRealtimeMs } returns FIXED_CLOCK.millis()
}
private val vaultLockManager: VaultLockManager = VaultLockManagerImpl( private val vaultLockManager: VaultLockManager = VaultLockManagerImpl(
context = context, context = context,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
realtimeManager = realtimeManager,
authDiskSource = fakeAuthDiskSource, authDiskSource = fakeAuthDiskSource,
authSdkSource = authSdkSource, authSdkSource = authSdkSource,
vaultSdkSource = vaultSdkSource, vaultSdkSource = vaultSdkSource,

View File

@@ -1,7 +1,7 @@
package com.bitwarden.authenticator.data.auth.repository package com.bitwarden.authenticator.data.auth.repository
import android.os.SystemClock
import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource
import com.bitwarden.core.data.manager.realtime.RealtimeManager
import javax.inject.Inject import javax.inject.Inject
/** /**
@@ -9,7 +9,7 @@ import javax.inject.Inject
*/ */
class AuthRepositoryImpl @Inject constructor( class AuthRepositoryImpl @Inject constructor(
private val authDiskSource: AuthDiskSource, private val authDiskSource: AuthDiskSource,
private val elapsedRealtimeMillisProvider: () -> Long = { SystemClock.elapsedRealtime() }, private val realtimeManager: RealtimeManager,
) : AuthRepository { ) : AuthRepository {
/** /**
@@ -17,7 +17,7 @@ class AuthRepositoryImpl @Inject constructor(
*/ */
override fun updateLastActiveTime() { override fun updateLastActiveTime() {
authDiskSource.storeLastActiveTimeMillis( authDiskSource.storeLastActiveTimeMillis(
lastActiveTimeMillis = elapsedRealtimeMillisProvider(), lastActiveTimeMillis = realtimeManager.elapsedRealtimeMs,
) )
} }
} }

View File

@@ -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.datasource.disk.AuthDiskSource
import com.bitwarden.authenticator.data.auth.repository.AuthRepository import com.bitwarden.authenticator.data.auth.repository.AuthRepository
import com.bitwarden.authenticator.data.auth.repository.AuthRepositoryImpl import com.bitwarden.authenticator.data.auth.repository.AuthRepositoryImpl
import com.bitwarden.core.data.manager.realtime.RealtimeManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@@ -18,7 +19,9 @@ object AuthRepositoryModule {
@Provides @Provides
fun provideAuthRepository( fun provideAuthRepository(
authDiskSource: AuthDiskSource, authDiskSource: AuthDiskSource,
realtimeManager: RealtimeManager,
): AuthRepository = AuthRepositoryImpl( ): AuthRepository = AuthRepositoryImpl(
authDiskSource = authDiskSource, authDiskSource = authDiskSource,
realtimeManager = realtimeManager,
) )
} }

View File

@@ -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.manager.imports.ImportManagerImpl
import com.bitwarden.authenticator.data.platform.repository.DebugMenuRepository import com.bitwarden.authenticator.data.platform.repository.DebugMenuRepository
import com.bitwarden.authenticator.data.platform.repository.SettingsRepository 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.ToastManager
import com.bitwarden.core.data.manager.toast.ToastManagerImpl import com.bitwarden.core.data.manager.toast.ToastManagerImpl
import com.bitwarden.data.manager.DispatcherManager import com.bitwarden.data.manager.DispatcherManager
@@ -49,6 +51,10 @@ object PlatformManagerModule {
toastManager = toastManager, toastManager = toastManager,
) )
@Provides
@Singleton
fun provideRealtimeManager(): RealtimeManager = RealtimeManagerImpl()
@Provides @Provides
@Singleton @Singleton
fun provideToastManager( fun provideToastManager(

View File

@@ -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
}

View File

@@ -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()
}