Run update note task shortcut in a service if user is different

Shortcut Manager injects the main user, and do not allow us to set what user should have their shortcuts modified. To workaround that until we add support to custom users, we are launching a service and triggering the update task from that user's process.

Running a different service may delay the update by a fraction of a second, in case the user jump into the home screen just after.

Test: atest NoteTaskControllerTest

Fixes: b/276889922
Change-Id: If503df7af038c51829e9267af7a9d0aafd7da986
This commit is contained in:
Marcello Galhardo
2023-04-06 18:24:57 +00:00
parent 708988976d
commit c2beb67c03
5 changed files with 104 additions and 1 deletions

View File

@@ -979,6 +979,8 @@
</intent-filter>
</activity>
<service android:name=".notetask.NoteTaskControllerUpdateService" />
<activity
android:name=".notetask.shortcut.LaunchNoteTaskActivity"
android:exported="true"

View File

@@ -284,7 +284,15 @@ constructor(
/** @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 {

View File

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

View File

@@ -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 @@ import dagger.multibindings.IntoMap
@Module(includes = [NoteTaskQuickAffordanceModule::class])
interface NoteTaskModule {
@[Binds IntoMap ClassKey(NoteTaskControllerUpdateService::class)]
fun NoteTaskControllerUpdateService.bindNoteTaskControllerUpdateService(): Service
@[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity

View File

@@ -620,6 +620,38 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
}
}
// 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() {