Merge "Run update note task shortcut in a service if user is different" into udc-dev
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 24de487..4652ef1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -982,6 +982,8 @@
             </intent-filter>
         </activity>
 
+        <service android:name=".notetask.NoteTaskControllerUpdateService" />
+
         <activity
             android:name=".notetask.shortcut.LaunchNoteTaskActivity"
             android:exported="true"
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index aab898e..8aec0c6 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -284,7 +284,15 @@
 
     /** @see OnRoleHoldersChangedListener */
     fun onRoleHoldersChanged(roleName: String, user: UserHandle) {
-        if (roleName == ROLE_NOTES) updateNoteTaskAsUser(user)
+        if (roleName != ROLE_NOTES) return
+
+        if (user == userTracker.userHandle) {
+            updateNoteTaskAsUser(user)
+        } else {
+            // TODO(b/278729185): Replace fire and forget service with a bounded service.
+            val intent = NoteTaskControllerUpdateService.createIntent(context)
+            context.startServiceAsUser(intent, user)
+        }
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
new file mode 100644
index 0000000..26b35cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notetask
+
+import android.content.Context
+import android.content.Intent
+import androidx.lifecycle.LifecycleService
+import javax.inject.Inject
+
+/**
+ * A fire & forget service for updating note task shortcuts.
+ *
+ * The main use is to update shortcuts in different user by launching it using `startServiceAsUser`.
+ * The service will open with access to a context from that user, trigger
+ * [NoteTaskController.updateNoteTaskAsUser] and [stopSelf] immediately.
+ *
+ * The fire and forget approach was created due to its simplicity but may use unnecessary resources
+ * by recreating the services. We will investigate its impacts and consider to move to a bounded
+ * services - the implementation is more complex as a bounded service is asynchronous by default.
+ *
+ * TODO(b/278729185): Replace fire and forget service with a bounded service.
+ */
+@InternalNoteTaskApi
+class NoteTaskControllerUpdateService
+@Inject
+constructor(
+    val controller: NoteTaskController,
+) : LifecycleService() {
+
+    override fun onCreate() {
+        super.onCreate()
+        // TODO(b/278729185): Replace fire and forget service with a bounded service.
+        controller.updateNoteTaskAsUser(user)
+        stopSelf()
+    }
+
+    companion object {
+        fun createIntent(context: Context): Intent =
+            Intent(context, NoteTaskControllerUpdateService::class.java)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 1839dfd..a166393 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
+@file:OptIn(InternalNoteTaskApi::class)
+
 package com.android.systemui.notetask
 
 import android.app.Activity
+import android.app.Service
 import android.app.role.RoleManager
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
@@ -34,6 +37,9 @@
 @Module(includes = [NoteTaskQuickAffordanceModule::class])
 interface NoteTaskModule {
 
+    @[Binds IntoMap ClassKey(NoteTaskControllerUpdateService::class)]
+    fun NoteTaskControllerUpdateService.bindNoteTaskControllerUpdateService(): Service
+
     @[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
     fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 7dc622b..55f221d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -620,6 +620,38 @@
         }
     }
 
+    // region onRoleHoldersChanged
+    @Test
+    fun onRoleHoldersChanged_notNotesRole_doNothing() {
+        val user = UserHandle.of(0)
+
+        createNoteTaskController(isEnabled = true).onRoleHoldersChanged("NOT_NOTES", user)
+
+        verifyZeroInteractions(context)
+    }
+
+    @Test
+    fun onRoleHoldersChanged_notesRole_sameUser_shouldUpdateShortcuts() {
+        val user = userTracker.userHandle
+        val controller = spy(createNoteTaskController())
+        doNothing().whenever(controller).updateNoteTaskAsUser(any())
+
+        controller.onRoleHoldersChanged(ROLE_NOTES, user)
+
+        verify(controller).updateNoteTaskAsUser(user)
+    }
+
+    @Test
+    fun onRoleHoldersChanged_notesRole_differentUser_shouldUpdateShortcutsInUserProcess() {
+        // FakeUserTracker will default to UserHandle.SYSTEM.
+        val user = UserHandle.CURRENT
+
+        createNoteTaskController(isEnabled = true).onRoleHoldersChanged(ROLE_NOTES, user)
+
+        verify(context).startServiceAsUser(any(), eq(user))
+    }
+    // endregion
+
     // region updateNoteTaskAsUser
     @Test
     fun updateNoteTaskAsUser_withNotesRole_withShortcuts_shouldUpdateShortcuts() {