diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt index e52d5e25767c4b6dc448fe4a64b15357c35e5a8d..5d94053ba090f1db43cd022dedb0236384c94ab5 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt @@ -40,6 +40,7 @@ import org.briarproject.mailbox.android.ui.StartupFailureActivity.Companion.EXTR import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure import org.briarproject.mailbox.android.ui.WipeCompleteActivity import org.briarproject.mailbox.core.lifecycle.LifecycleManager +import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.LIFECYCLE_REUSE import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.SUCCESS @@ -138,6 +139,7 @@ class MailboxService : Service() { SUCCESS -> started = true SERVICE_ERROR -> showStartupFailure(StartupFailure.SERVICE_ERROR) LIFECYCLE_REUSE -> showStartupFailure(StartupFailure.LIFECYCLE_REUSE) + CLOCK_ERROR -> showStartupFailure(StartupFailure.CLOCK_ERROR) } } // Register for device shutdown broadcasts diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFailureActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFailureActivity.kt index c73032b3cb7bc0222e55f0ef7513c86a4f2be210..866665aa389a75cf929a8cca4e6d83ddaabfc8ad 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFailureActivity.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFailureActivity.kt @@ -25,6 +25,7 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import dagger.hilt.android.AndroidEntryPoint import org.briarproject.mailbox.R +import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure.CLOCK_ERROR import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure.LIFECYCLE_REUSE import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure.SERVICE_ERROR @@ -36,7 +37,7 @@ class StartupFailureActivity : AppCompatActivity() { } enum class StartupFailure { - SERVICE_ERROR, LIFECYCLE_REUSE + SERVICE_ERROR, LIFECYCLE_REUSE, CLOCK_ERROR } override fun onCreate(savedInstanceState: Bundle?) { @@ -51,6 +52,7 @@ class StartupFailureActivity : AppCompatActivity() { val errorRes = when (i.getSerializableExtra(EXTRA_START_RESULT) as StartupFailure) { SERVICE_ERROR -> R.string.startup_failed_service_error LIFECYCLE_REUSE -> R.string.startup_failed_lifecycle_reuse + CLOCK_ERROR -> R.string.startup_failed_clock_error } val msg: TextView = findViewById(R.id.errorMessage) msg.text = getString(errorRes) diff --git a/mailbox-android/src/main/res/values/strings.xml b/mailbox-android/src/main/res/values/strings.xml index bcffbb7d70fa972c7d0f97604d18c57d15b7ef3f..d6cdf41f190048688dd6d2ebfa62f90e0b0854fb 100644 --- a/mailbox-android/src/main/res/values/strings.xml +++ b/mailbox-android/src/main/res/values/strings.xml @@ -77,4 +77,6 @@ <string name="startup_failed_activity_title">Mailbox Startup Failure</string> <string name="startup_failed_service_error">Mailbox was unable to start a required component.\n\nPlease upgrade to the latest version of the app and try again.</string> <string name="startup_failed_lifecycle_reuse">Mailbox has already stopped.\n\nPlease close the app and try again.</string> + <string name="startup_failed_clock_error">Mailbox was unable to start because your device\'s clock is wrong.\n\nPlease set your device\'s clock to the right time and try again.</string> + </resources> diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManager.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManager.kt index dfc769bf70a7a5f901efec26148093c49622a35d..4f148a988b5b921b14a65af4b23bbfd74d35b952 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManager.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManager.kt @@ -34,7 +34,7 @@ interface LifecycleManager { * The result of calling [startServices]. */ enum class StartResult { - SERVICE_ERROR, LIFECYCLE_REUSE, SUCCESS + SERVICE_ERROR, LIFECYCLE_REUSE, CLOCK_ERROR, SUCCESS } /** diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManagerImpl.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManagerImpl.kt index 60cd7b27d3671f9fe38b84fa3265c87e010921c2..826921b92bc8945743b4e0491ddb3c895ee1876f 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManagerImpl.kt +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/lifecycle/LifecycleManagerImpl.kt @@ -32,10 +32,14 @@ import org.briarproject.mailbox.core.lifecycle.LifecycleManager.LifecycleState.S import org.briarproject.mailbox.core.lifecycle.LifecycleManager.LifecycleState.WIPING import org.briarproject.mailbox.core.lifecycle.LifecycleManager.OpenDatabaseHook import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult +import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.LIFECYCLE_REUSE import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.SUCCESS import org.briarproject.mailbox.core.setup.WipeManager +import org.briarproject.mailbox.core.system.Clock +import org.briarproject.mailbox.core.system.Clock.MAX_REASONABLE_TIME_MS +import org.briarproject.mailbox.core.system.Clock.MIN_REASONABLE_TIME_MS import org.briarproject.mailbox.core.system.System import org.briarproject.mailbox.core.util.LogUtils.info import org.briarproject.mailbox.core.util.LogUtils.logDuration @@ -56,6 +60,7 @@ internal class LifecycleManagerImpl @Inject constructor( private val db: Database, private val wipeManager: WipeManager, private val system: System, + private val clock: Clock, ) : LifecycleManager, MigrationListener { @@ -105,6 +110,11 @@ internal class LifecycleManagerImpl @Inject constructor( LOG.warn { "Invalid state: ${state.value}" } return LIFECYCLE_REUSE } + val now = clock.currentTimeMillis() + if (now < MIN_REASONABLE_TIME_MS || now > MAX_REASONABLE_TIME_MS) { + LOG.warn { "System clock is unreasonable: $now" } + return CLOCK_ERROR + } this.wipeHook = wipeHook return try { LOG.info("Opening database") diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/Clock.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/Clock.java index 43aacdcbf4700173a6b41db51ca04ce9d86c91b7..eabfdad4987455340fe10216571ddb53d9cc4d6c 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/Clock.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/system/Clock.java @@ -21,4 +21,20 @@ package org.briarproject.mailbox.core.system; public interface Clock { long currentTimeMillis(); + + /** + * The minimum reasonable value for the system clock, in milliseconds + * since the Unix epoch. + * <p/> + * 1 Jan 2023, 00:00:00 UTC + */ + long MIN_REASONABLE_TIME_MS = 1_672_531_200_000L; + + /** + * The maximum reasonable value for the system clock, in milliseconds + * since the Unix epoch. + * <p/> + * 1 Jan 2121, 00:00:00 UTC + */ + long MAX_REASONABLE_TIME_MS = 4_765_132_800_000L; } diff --git a/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/IntegrationTest.kt b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/IntegrationTest.kt index dee124dc9797296b3d0d5e78f1228da9a60c7e5f..fe93dc739d78741daa404b20f2727e96d6339d78 100644 --- a/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/IntegrationTest.kt +++ b/mailbox-core/src/test/java/org/briarproject/mailbox/core/server/IntegrationTest.kt @@ -15,6 +15,7 @@ import org.briarproject.mailbox.core.TestModule import org.briarproject.mailbox.core.TestUtils.getNewRandomContact import org.briarproject.mailbox.core.TestUtils.getNewRandomId import org.briarproject.mailbox.core.contacts.Contact +import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.SUCCESS import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll @@ -26,6 +27,7 @@ import org.junit.jupiter.api.io.TempDir import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.File +import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -80,7 +82,7 @@ abstract class IntegrationTest(private val installJsonFeature: Boolean = true) { testComponent = DaggerTestComponent.builder().testModule(TestModule(tempDir)).build() testComponent.injectCoreEagerSingletons() assertFalse(setupManager.hasDb) - lifecycleManager.startServices() + assertEquals(SUCCESS, lifecycleManager.startServices()) lifecycleManager.waitForStartup() baseUrl = "http://127.0.0.1:${testComponent.getWebServerManager().port}" } diff --git a/mailbox-lib/src/main/java/org/briarproject/mailbox/lib/AbstractMailbox.kt b/mailbox-lib/src/main/java/org/briarproject/mailbox/lib/AbstractMailbox.kt index 8b22df0c9d1686afae4edd917ff2da0955ade85a..983e9bc37b20def223529d1067644bfeafbbe4a2 100644 --- a/mailbox-lib/src/main/java/org/briarproject/mailbox/lib/AbstractMailbox.kt +++ b/mailbox-lib/src/main/java/org/briarproject/mailbox/lib/AbstractMailbox.kt @@ -26,6 +26,7 @@ import org.briarproject.mailbox.core.CoreEagerSingletons import org.briarproject.mailbox.core.MailboxLibEagerSingletons import org.briarproject.mailbox.core.db.TransactionManager import org.briarproject.mailbox.core.lifecycle.LifecycleManager +import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.SUCCESS import org.briarproject.mailbox.core.server.WebServerManager import org.briarproject.mailbox.core.setup.QrCodeEncoder import org.briarproject.mailbox.core.setup.SetupManager @@ -82,7 +83,8 @@ abstract class AbstractMailbox(protected val customDataDir: File? = null) { fun startLifecycle() { LOG.info { "Starting lifecycle" } - lifecycleManager.startServices() + val startResult = lifecycleManager.startServices() + if (startResult != SUCCESS) error(startResult.name) LOG.info { "Waiting for startup" } lifecycleManager.waitForStartup() LOG.info { "Startup finished" }