Fix launched time. Add notified status.

Move launched time updating logic to where tutorial is launched. We need
it for for context edu. Add isNotified for oobe scheduling.

Drive-by renaming.

Bug: 378865553
Flag: NONE bug fix
Test: TutorialSchedulerInteractorTest
Change-Id: Iae15120a6e9c26c1ea849d5c9ce43eef8e8ed6f5
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
index 8bb6962..4630674 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
@@ -22,13 +22,12 @@
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
 import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
-import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,7 +37,7 @@
 class TutorialSchedulerRepositoryTest : SysuiTestCase() {
 
     private lateinit var underTest: TutorialSchedulerRepository
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     @Before
@@ -46,45 +45,57 @@
         underTest =
             TutorialSchedulerRepository(
                 context,
-                testScope.backgroundScope,
+                kosmos.backgroundScope,
                 "TutorialSchedulerRepositoryTest",
             )
     }
 
-    @After
-    fun clear() {
-        testScope.launch { underTest.clear() }
+    @Test
+    fun initialState() = runTestAndClear {
+        assertThat(underTest.wasEverConnected(KEYBOARD)).isFalse()
+        assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
+        assertThat(underTest.isNotified(KEYBOARD)).isFalse()
+        assertThat(underTest.isNotified(TOUCHPAD)).isFalse()
+        assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isFalse()
+        assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse()
     }
 
     @Test
-    fun initialState() =
-        testScope.runTest {
-            assertThat(underTest.wasEverConnected(KEYBOARD)).isFalse()
-            assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
-            assertThat(underTest.isLaunched(KEYBOARD)).isFalse()
-            assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
-        }
+    fun connectKeyboard() = runTestAndClear {
+        val now = Instant.now()
+        underTest.setFirstConnectionTime(KEYBOARD, now)
+
+        assertThat(underTest.wasEverConnected(KEYBOARD)).isTrue()
+        assertThat(underTest.getFirstConnectionTime(KEYBOARD)!!.epochSecond)
+            .isEqualTo(now.epochSecond)
+        assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
+    }
 
     @Test
-    fun connectKeyboard() =
-        testScope.runTest {
-            val now = Instant.now()
-            underTest.updateFirstConnectionTime(KEYBOARD, now)
+    fun launchKeyboard() = runTestAndClear {
+        val now = Instant.now()
+        underTest.setScheduledTutorialLaunchTime(KEYBOARD, now)
 
-            assertThat(underTest.wasEverConnected(KEYBOARD)).isTrue()
-            assertThat(underTest.firstConnectionTime(KEYBOARD)!!.epochSecond)
-                .isEqualTo(now.epochSecond)
-            assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
-        }
+        assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isTrue()
+        assertThat(underTest.getScheduledTutorialLaunchTime(KEYBOARD)!!.epochSecond)
+            .isEqualTo(now.epochSecond)
+        assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse()
+    }
 
     @Test
-    fun launchKeyboard() =
-        testScope.runTest {
-            val now = Instant.now()
-            underTest.updateLaunchTime(KEYBOARD, now)
+    fun notifyKeyboard() = runTestAndClear {
+        underTest.setNotified(KEYBOARD)
 
-            assertThat(underTest.isLaunched(KEYBOARD)).isTrue()
-            assertThat(underTest.launchTime(KEYBOARD)!!.epochSecond).isEqualTo(now.epochSecond)
-            assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
+        assertThat(underTest.isNotified(KEYBOARD)).isTrue()
+        assertThat(underTest.isNotified(TOUCHPAD)).isFalse()
+    }
+
+    private fun runTestAndClear(block: suspend () -> Unit) =
+        testScope.runTest {
+            try {
+                block()
+            } finally {
+                underTest.clear()
+            }
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
index bcac086..886aa51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
 import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
 import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.kosmos.backgroundScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.settings.userTracker
@@ -34,14 +35,9 @@
 import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.hours
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -65,7 +61,6 @@
     private val testScope = kosmos.testScope
     private val keyboardRepository = FakeKeyboardRepository()
     private val touchpadRepository = FakeTouchpadRepository()
-    private lateinit var dataStoreScope: CoroutineScope
     private lateinit var repository: TutorialSchedulerRepository
     @Mock private lateinit var notificationManager: NotificationManager
     @Captor private lateinit var notificationCaptor: ArgumentCaptor<Notification>
@@ -73,11 +68,10 @@
 
     @Before
     fun setup() {
-        dataStoreScope = CoroutineScope(Dispatchers.Unconfined)
         repository =
             TutorialSchedulerRepository(
                 context,
-                dataStoreScope,
+                kosmos.backgroundScope,
                 dataStoreName = "TutorialNotificationCoordinatorTest",
             )
         val interactor =
@@ -87,6 +81,7 @@
                 repository,
                 kosmos.inputDeviceTutorialLogger,
                 kosmos.commandRegistry,
+                testScope.backgroundScope,
             )
         underTest =
             TutorialNotificationCoordinator(
@@ -100,52 +95,51 @@
         underTest.start()
     }
 
-    @After
-    fun clear() {
-        runBlocking { repository.clear() }
-        dataStoreScope.cancel()
+    @Test
+    fun showKeyboardNotification() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        testScope.advanceTimeBy(LAUNCH_DELAY)
+        verifyNotification(
+            R.string.launch_keyboard_tutorial_notification_title,
+            R.string.launch_keyboard_tutorial_notification_content,
+        )
     }
 
     @Test
-    fun showKeyboardNotification() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-            advanceTimeBy(LAUNCH_DELAY)
-            verifyNotification(
-                R.string.launch_keyboard_tutorial_notification_title,
-                R.string.launch_keyboard_tutorial_notification_content,
-            )
-        }
+    fun showTouchpadNotification() = runTestAndClear {
+        touchpadRepository.setIsAnyTouchpadConnected(true)
+        testScope.advanceTimeBy(LAUNCH_DELAY)
+        verifyNotification(
+            R.string.launch_touchpad_tutorial_notification_title,
+            R.string.launch_touchpad_tutorial_notification_content,
+        )
+    }
 
     @Test
-    fun showTouchpadNotification() =
-        testScope.runTest {
-            touchpadRepository.setIsAnyTouchpadConnected(true)
-            advanceTimeBy(LAUNCH_DELAY)
-            verifyNotification(
-                R.string.launch_touchpad_tutorial_notification_title,
-                R.string.launch_touchpad_tutorial_notification_content,
-            )
-        }
+    fun showKeyboardTouchpadNotification() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        touchpadRepository.setIsAnyTouchpadConnected(true)
+        testScope.advanceTimeBy(LAUNCH_DELAY)
+        verifyNotification(
+            R.string.launch_keyboard_touchpad_tutorial_notification_title,
+            R.string.launch_keyboard_touchpad_tutorial_notification_content,
+        )
+    }
 
     @Test
-    fun showKeyboardTouchpadNotification() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-            touchpadRepository.setIsAnyTouchpadConnected(true)
-            advanceTimeBy(LAUNCH_DELAY)
-            verifyNotification(
-                R.string.launch_keyboard_touchpad_tutorial_notification_title,
-                R.string.launch_keyboard_touchpad_tutorial_notification_content,
-            )
-        }
+    fun doNotShowNotification() = runTestAndClear {
+        testScope.advanceTimeBy(LAUNCH_DELAY)
+        verify(notificationManager, never())
+            .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), any(), any())
+    }
 
-    @Test
-    fun doNotShowNotification() =
+    private fun runTestAndClear(block: suspend () -> Unit) =
         testScope.runTest {
-            advanceTimeBy(LAUNCH_DELAY)
-            verify(notificationManager, never())
-                .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), any(), any())
+            try {
+                block()
+            } finally {
+                repository.clear()
+            }
         }
 
     private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
index 2efa2f3..722451e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
@@ -30,16 +30,11 @@
 import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.hours
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -52,18 +47,16 @@
     private lateinit var underTest: TutorialSchedulerInteractor
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private lateinit var dataStoreScope: CoroutineScope
     private val keyboardRepository = FakeKeyboardRepository()
     private val touchpadRepository = FakeTouchpadRepository()
     private lateinit var schedulerRepository: TutorialSchedulerRepository
 
     @Before
     fun setup() {
-        dataStoreScope = CoroutineScope(Dispatchers.Unconfined)
         schedulerRepository =
             TutorialSchedulerRepository(
                 context,
-                dataStoreScope,
+                testScope.backgroundScope,
                 dataStoreName = "TutorialSchedulerInteractorTest",
             )
         underTest =
@@ -73,98 +66,95 @@
                 schedulerRepository,
                 kosmos.inputDeviceTutorialLogger,
                 kosmos.commandRegistry,
+                testScope.backgroundScope,
             )
     }
 
-    @After
-    fun clear() {
-        runBlocking { schedulerRepository.clear() }
-        dataStoreScope.cancel()
+    @Test
+    fun connectKeyboard_delayElapse_notifyForKeyboard() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        testScope.advanceTimeBy(LAUNCH_DELAY)
+        notifyAndAssert(TutorialType.KEYBOARD)
     }
 
     @Test
-    fun connectKeyboard_delayElapse_launchForKeyboard() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-            advanceTimeBy(LAUNCH_DELAY)
+    fun connectBothDevices_delayElapse_notifyForBoth() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        touchpadRepository.setIsAnyTouchpadConnected(true)
+        testScope.advanceTimeBy(LAUNCH_DELAY)
 
-            launchAndAssert(TutorialType.KEYBOARD)
-        }
+        notifyAndAssert(TutorialType.BOTH)
+    }
 
     @Test
-    fun connectBothDevices_delayElapse_launchForBoth() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-            touchpadRepository.setIsAnyTouchpadConnected(true)
-            advanceTimeBy(LAUNCH_DELAY)
+    fun connectBothDevice_delayNotElapse_notifyNothing() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        touchpadRepository.setIsAnyTouchpadConnected(true)
+        testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
 
-            launchAndAssert(TutorialType.BOTH)
-        }
+        notifyAndAssert(TutorialType.NONE)
+    }
 
     @Test
-    fun connectBothDevice_delayNotElapse_launchNothing() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-            touchpadRepository.setIsAnyTouchpadConnected(true)
-            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+    fun nothingConnect_delayElapse_notifyNothing() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(false)
+        touchpadRepository.setIsAnyTouchpadConnected(false)
+        testScope.advanceTimeBy(LAUNCH_DELAY)
 
-            launchAndAssert(TutorialType.NONE)
-        }
+        notifyAndAssert(TutorialType.NONE)
+    }
 
     @Test
-    fun nothingConnect_delayElapse_launchNothing() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(false)
-            touchpadRepository.setIsAnyTouchpadConnected(false)
-            advanceTimeBy(LAUNCH_DELAY)
+    fun connectKeyboard_thenTouchpad_delayElapse_notifyForBoth() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+        touchpadRepository.setIsAnyTouchpadConnected(true)
+        testScope.advanceTimeBy(REMAINING_TIME)
 
-            launchAndAssert(TutorialType.NONE)
-        }
+        notifyAndAssert(TutorialType.BOTH)
+    }
 
     @Test
-    fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
-            touchpadRepository.setIsAnyTouchpadConnected(true)
-            advanceTimeBy(REMAINING_TIME)
+    fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_notifyNothing() = runTestAndClear {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+        touchpadRepository.setIsAnyTouchpadConnected(true)
+        keyboardRepository.setIsAnyKeyboardConnected(false)
+        testScope.advanceTimeBy(REMAINING_TIME)
 
-            launchAndAssert(TutorialType.BOTH)
+        notifyAndAssert(TutorialType.NONE)
+    }
+
+    private fun runTestAndClear(block: suspend () -> Unit) =
+        testScope.runTest {
+            try {
+                block()
+            } finally {
+                schedulerRepository.clear()
+            }
         }
 
-    @Test
-    fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() =
-        testScope.runTest {
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
-            touchpadRepository.setIsAnyTouchpadConnected(true)
-            keyboardRepository.setIsAnyKeyboardConnected(false)
-            advanceTimeBy(REMAINING_TIME)
-            launchAndAssert(TutorialType.NONE)
-        }
-
-    private suspend fun launchAndAssert(expectedTutorial: TutorialType) =
+    private fun notifyAndAssert(expectedTutorial: TutorialType) =
         testScope.backgroundScope.launch {
             val actualTutorial = underTest.tutorials.first()
             assertThat(actualTutorial).isEqualTo(expectedTutorial)
 
-            // TODO: need to update after we move launch into the tutorial
             when (expectedTutorial) {
                 TutorialType.KEYBOARD -> {
-                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
-                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
+                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue()
+                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse()
                 }
                 TutorialType.TOUCHPAD -> {
-                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
-                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
+                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse()
+                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue()
                 }
                 TutorialType.BOTH -> {
-                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
-                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
+                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue()
+                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue()
                 }
                 TutorialType.NONE -> {
-                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
-                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
+                    assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse()
+                    assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse()
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
index e264635..21002c6 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.education.domain.interactor
 
 import android.os.SystemProperties
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.contextualeducation.GestureType
@@ -56,7 +57,6 @@
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.merge
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Allow listening to new contextual education triggered */
 @SysUISingleton
@@ -278,7 +278,8 @@
         }
 
     private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean {
-        val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false
+        val oobeLaunchTime =
+            tutorialRepository.getScheduledTutorialLaunchTime(deviceType) ?: return false
         return clock
             .instant()
             .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds))
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt
index 1dbe83a..c436ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt
@@ -20,14 +20,17 @@
 
 data class DeviceSchedulerInfo(
     var launchTime: Instant? = null,
-    var firstConnectionTime: Instant? = null
+    var firstConnectionTime: Instant? = null,
+    var isNotified: Boolean = false,
 ) {
     constructor(
         launchTimeSec: Long?,
-        firstConnectionTimeSec: Long?
+        firstConnectionTimeSec: Long?,
+        isNotified: Boolean = false,
     ) : this(
         launchTimeSec?.let { Instant.ofEpochSecond(it) },
-        firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) }
+        firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) },
+        isNotified,
     )
 
     val wasEverConnected: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
index a89ec70..526e7db 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import androidx.datastore.core.DataStore
 import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
 import androidx.datastore.preferences.core.edit
 import androidx.datastore.preferences.core.longPreferencesKey
 import androidx.datastore.preferences.preferencesDataStore
@@ -47,28 +48,36 @@
     private val Context.dataStore: DataStore<Preferences> by
         preferencesDataStore(name = dataStoreName, scope = backgroundScope)
 
-    suspend fun isLaunched(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isLaunched
+    suspend fun setScheduledTutorialLaunchTime(device: DeviceType, time: Instant) {
+        applicationContext.dataStore.edit { pref -> pref[getLaunchKey(device)] = time.epochSecond }
+    }
 
-    suspend fun launchTime(deviceType: DeviceType): Instant? = loadData()[deviceType]!!.launchTime
+    suspend fun isScheduledTutorialLaunched(deviceType: DeviceType): Boolean =
+        loadData()[deviceType]!!.isLaunched
+
+    suspend fun getScheduledTutorialLaunchTime(deviceType: DeviceType): Instant? =
+        loadData()[deviceType]!!.launchTime
+
+    suspend fun setFirstConnectionTime(device: DeviceType, time: Instant) {
+        applicationContext.dataStore.edit { pref -> pref[getConnectKey(device)] = time.epochSecond }
+    }
+
+    suspend fun setNotified(device: DeviceType) {
+        applicationContext.dataStore.edit { pref -> pref[getNotificationKey(device)] = true }
+    }
+
+    suspend fun isNotified(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isNotified
 
     suspend fun wasEverConnected(deviceType: DeviceType): Boolean =
         loadData()[deviceType]!!.wasEverConnected
 
-    suspend fun firstConnectionTime(deviceType: DeviceType): Instant? =
+    suspend fun getFirstConnectionTime(deviceType: DeviceType): Instant? =
         loadData()[deviceType]!!.firstConnectionTime
 
     private suspend fun loadData(): Map<DeviceType, DeviceSchedulerInfo> {
         return applicationContext.dataStore.data.map { pref -> getSchedulerInfo(pref) }.first()
     }
 
-    suspend fun updateFirstConnectionTime(device: DeviceType, time: Instant) {
-        applicationContext.dataStore.edit { pref -> pref[getConnectKey(device)] = time.epochSecond }
-    }
-
-    suspend fun updateLaunchTime(device: DeviceType, time: Instant) {
-        applicationContext.dataStore.edit { pref -> pref[getLaunchKey(device)] = time.epochSecond }
-    }
-
     private fun getSchedulerInfo(pref: Preferences): Map<DeviceType, DeviceSchedulerInfo> {
         return mapOf(
             DeviceType.KEYBOARD to getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
@@ -79,7 +88,8 @@
     private fun getDeviceSchedulerInfo(pref: Preferences, device: DeviceType): DeviceSchedulerInfo {
         val launchTime = pref[getLaunchKey(device)]
         val connectionTime = pref[getConnectKey(device)]
-        return DeviceSchedulerInfo(launchTime, connectionTime)
+        val isNotified = pref[getNotificationKey(device)] == true
+        return DeviceSchedulerInfo(launchTime, connectionTime, isNotified)
     }
 
     private fun getLaunchKey(device: DeviceType) =
@@ -88,6 +98,9 @@
     private fun getConnectKey(device: DeviceType) =
         longPreferencesKey(device.name + CONNECT_TIME_SUFFIX)
 
+    private fun getNotificationKey(device: DeviceType) =
+        booleanPreferencesKey(device.name + NOTIFIED_SUFFIX)
+
     suspend fun clear() {
         applicationContext.dataStore.edit { it.clear() }
     }
@@ -96,6 +109,7 @@
         const val DATASTORE_NAME = "TutorialScheduler"
         const val LAUNCH_TIME_SUFFIX = "_LAUNCH_TIME"
         const val CONNECT_TIME_SUFFIX = "_CONNECT_TIME"
+        const val NOTIFIED_SUFFIX = "_NOTIFIED"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index 7758dcc..419eefe 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.inputdevice.tutorial.domain.interactor
 
 import android.os.SystemProperties
-import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
 import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
@@ -35,6 +35,7 @@
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.hours
 import kotlin.time.toKotlinDuration
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -43,6 +44,7 @@
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 
 /**
@@ -58,6 +60,7 @@
     private val repo: TutorialSchedulerRepository,
     private val logger: InputDeviceTutorialLogger,
     commandRegistry: CommandRegistry,
+    @Background private val backgroundScope: CoroutineScope,
 ) {
     init {
         commandRegistry.registerCommand(COMMAND) { TutorialCommand() }
@@ -70,14 +73,14 @@
         )
 
     private val touchpadScheduleFlow = flow {
-        if (!repo.isLaunched(TOUCHPAD)) {
+        if (!repo.isNotified(TOUCHPAD)) {
             schedule(TOUCHPAD)
             emit(TOUCHPAD)
         }
     }
 
     private val keyboardScheduleFlow = flow {
-        if (!repo.isLaunched(KEYBOARD)) {
+        if (!repo.isNotified(KEYBOARD)) {
             schedule(KEYBOARD)
             emit(KEYBOARD)
         }
@@ -88,9 +91,9 @@
             logger.d("Waiting for $deviceType to connect")
             waitForDeviceConnection(deviceType)
             logger.logDeviceFirstConnection(deviceType)
-            repo.updateFirstConnectionTime(deviceType, Instant.now())
+            repo.setFirstConnectionTime(deviceType, Instant.now())
         }
-        val remainingTime = remainingTime(start = repo.firstConnectionTime(deviceType)!!)
+        val remainingTime = remainingTime(start = repo.getFirstConnectionTime(deviceType)!!)
         logger.d("Tutorial is scheduled in ${remainingTime.inWholeSeconds} seconds")
         delay(remainingTime)
         waitForDeviceConnection(deviceType)
@@ -100,18 +103,17 @@
         isAnyDeviceConnected[deviceType]!!.filter { it }.first()
 
     // Only for testing notifications. This should behave independently from scheduling
-    @VisibleForTesting val commandTutorials = MutableStateFlow(TutorialType.NONE)
+    val commandTutorials = MutableStateFlow(TutorialType.NONE)
 
     // Merging two flows ensures that tutorial is launched consecutively to avoid race condition
     val tutorials: Flow<TutorialType> =
         merge(touchpadScheduleFlow, keyboardScheduleFlow).map {
             val tutorialType = resolveTutorialType(it)
 
-            // TODO: notifying time is not oobe launching time - move these updates into oobe
             if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
-                repo.updateLaunchTime(KEYBOARD, Instant.now())
+                repo.setNotified(KEYBOARD)
             if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
-                repo.updateLaunchTime(TOUCHPAD, Instant.now())
+                repo.setNotified(TOUCHPAD)
 
             logger.logTutorialLaunched(tutorialType)
             tutorialType
@@ -121,10 +123,10 @@
         // Resolve the type of tutorial depending on which device are connected when the tutorial is
         // launched. E.g. when the keyboard is connected for [LAUNCH_DELAY], both keyboard and
         // touchpad are connected, we launch the tutorial for both.
-        if (repo.isLaunched(deviceType)) return TutorialType.NONE
+        if (repo.isNotified(deviceType)) return TutorialType.NONE
         val otherDevice = if (deviceType == KEYBOARD) TOUCHPAD else KEYBOARD
         val isOtherDeviceConnected = isAnyDeviceConnected[otherDevice]!!.first()
-        if (!repo.isLaunched(otherDevice) && isOtherDeviceConnected) return TutorialType.BOTH
+        if (!repo.isNotified(otherDevice) && isOtherDeviceConnected) return TutorialType.BOTH
         return if (deviceType == KEYBOARD) TutorialType.KEYBOARD else TutorialType.TOUCHPAD
     }
 
@@ -133,6 +135,15 @@
         return LAUNCH_DELAY.minus(elapsed).toKotlinDuration()
     }
 
+    fun updateLaunchInfo(tutorialType: TutorialType) {
+        backgroundScope.launch {
+            if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
+                repo.setScheduledTutorialLaunchTime(KEYBOARD, Instant.now())
+            if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
+                repo.setScheduledTutorialLaunchTime(TOUCHPAD, Instant.now())
+        }
+    }
+
     inner class TutorialCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
             if (args.isEmpty()) {
@@ -147,10 +158,20 @@
                     }
                 "info" ->
                     runBlocking {
-                        pw.println("Keyboard connect time = ${repo.firstConnectionTime(KEYBOARD)}")
-                        pw.println("         launch time = ${repo.launchTime(KEYBOARD)}")
-                        pw.println("Touchpad connect time = ${repo.firstConnectionTime(TOUCHPAD)}")
-                        pw.println("         launch time = ${repo.launchTime(TOUCHPAD)}")
+                        pw.println(
+                            "Keyboard connect time = ${repo.getFirstConnectionTime(KEYBOARD)}"
+                        )
+                        pw.println("         notified = ${repo.isNotified(KEYBOARD)}")
+                        pw.println(
+                            "         launch time = ${repo.getScheduledTutorialLaunchTime(KEYBOARD)}"
+                        )
+                        pw.println(
+                            "Touchpad connect time = ${repo.getFirstConnectionTime(TOUCHPAD)}"
+                        )
+                        pw.println("         notified = ${repo.isNotified(TOUCHPAD)}")
+                        pw.println(
+                            "         launch time = ${repo.getScheduledTutorialLaunchTime(TOUCHPAD)}"
+                        )
                     }
                 "notify" -> {
                     if (args.size != 2) help(pw)
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
index 67b307f..639e9b1 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
@@ -33,6 +33,8 @@
 import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext
 import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialMetricsLogger
 import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType
 import com.android.systemui.inputdevice.tutorial.ui.composable.ActionKeyTutorialScreen
 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel
 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel.Factory.ViewModelFactoryAssistedProvider
@@ -51,6 +53,7 @@
 constructor(
     private val viewModelFactoryAssistedProvider: ViewModelFactoryAssistedProvider,
     private val touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
+    private val schedulerInteractor: TutorialSchedulerInteractor,
     private val logger: InputDeviceTutorialLogger,
     private val metricsLogger: KeyboardTouchpadTutorialMetricsLogger,
 ) : ComponentActivity() {
@@ -93,15 +96,28 @@
         setContent {
             PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) }
         }
-        // TODO(b/376692701): Update launchTime when the activity is launched by Companion App
         if (savedInstanceState == null) {
-            metricsLogger.logPeripheralTutorialLaunched(
-                intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY),
-                intent.getStringExtra(INTENT_TUTORIAL_SCOPE_KEY),
-            )
             logger.logOpenTutorial(TutorialContext.KEYBOARD_TOUCHPAD_TUTORIAL)
+
+            val entryPointExtra = intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY)
+            val tutorialTypeExtra = intent.getStringExtra(INTENT_TUTORIAL_SCOPE_KEY)
+            metricsLogger.logPeripheralTutorialLaunched(entryPointExtra, tutorialTypeExtra)
+            // We only update launched info when the tutorial is triggered by the scheduler
+            if (entryPointExtra.equals(INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER))
+                updateLaunchInfo(tutorialTypeExtra)
         }
     }
+
+    private fun updateLaunchInfo(tutorialTypeExtra: String?) {
+        val type =
+            when (tutorialTypeExtra) {
+                INTENT_TUTORIAL_SCOPE_KEYBOARD -> TutorialType.KEYBOARD
+                INTENT_TUTORIAL_SCOPE_TOUCHPAD -> TutorialType.TOUCHPAD
+                INTENT_TUTORIAL_SCOPE_ALL -> TutorialType.BOTH
+                else -> TutorialType.NONE
+            }
+        schedulerInteractor.updateLaunchInfo(type)
+    }
 }
 
 @Composable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
index d7fcb6a..74e8257 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
@@ -357,7 +357,10 @@
     fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
         testScope.runTest {
             setUpForDeviceConnection()
-            tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+            tutorialSchedulerRepository.setScheduledTutorialLaunchTime(
+                DeviceType.TOUCHPAD,
+                eduClock.instant(),
+            )
 
             val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
             val originalValue = model!!.signalCount
@@ -372,7 +375,10 @@
     fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
         testScope.runTest {
             setUpForDeviceConnection()
-            tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+            tutorialSchedulerRepository.setScheduledTutorialLaunchTime(
+                DeviceType.TOUCHPAD,
+                eduClock.instant(),
+            )
 
             val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
             val originalValue = model!!.signalCount
@@ -398,8 +404,14 @@
         }
 
     private suspend fun setUpForInitialDelayElapse() {
-        tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
-        tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, eduClock.instant())
+        tutorialSchedulerRepository.setScheduledTutorialLaunchTime(
+            DeviceType.TOUCHPAD,
+            eduClock.instant(),
+        )
+        tutorialSchedulerRepository.setScheduledTutorialLaunchTime(
+            DeviceType.KEYBOARD,
+            eduClock.instant(),
+        )
         eduClock.offset(initialDelayElapsedDuration)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 580f631..692b9c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -151,8 +151,14 @@
         }
 
     private suspend fun setUpForInitialDelayElapse() {
-        tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
-        tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, eduClock.instant())
+        tutorialSchedulerRepository.setScheduledTutorialLaunchTime(
+            DeviceType.TOUCHPAD,
+            eduClock.instant(),
+        )
+        tutorialSchedulerRepository.setScheduledTutorialLaunchTime(
+            DeviceType.KEYBOARD,
+            eduClock.instant(),
+        )
         eduClock.offset(initialDelayElapsedDuration)
     }