From 5d894505f794fee6b3224567e4d25f20f82f48d6 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 17 Jan 2019 10:31:00 -0800 Subject: [PATCH] Set assistant as a role - Register a role observer in VoiceInteractionManagerService. Once the role is changes map the new role setting onto the old settings. - As the assistant role is now always set, there is no need to have code in AssistUtil for the case the assistant setting is not set - Remove old config option for the default assistant. The default assistant is not configured via the roles config Bug: 110557011 Test: - Set, unset and swtiched assistant via the settings UI - for voice interaction service - for assist activity - Booted from freshly wiped device and saw assitant to be set to default Change-Id: I8596f49c6f6496e8b70cf3236aaa7d7557443a93 --- api/system-current.txt | 1 + api/test-current.txt | 5 + core/java/android/app/role/RoleManager.java | 9 ++ core/java/android/provider/Settings.java | 3 + .../com/android/internal/app/AssistUtils.java | 43 +------ core/res/res/values/config.xml | 3 - core/res/res/values/public.xml | 2 +- core/res/res/values/symbols.xml | 2 - .../role/LegacyRoleResolutionPolicy.java | 12 ++ .../server/role/RoleManagerService.java | 1 + .../java/com/android/server/SystemServer.java | 12 +- .../VoiceInteractionManagerService.java | 114 +++++++++++++++++- 12 files changed, 149 insertions(+), 58 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index 5bdcd23f3476c..cee55bc730496 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1052,6 +1052,7 @@ package android.app.role { method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List); + field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; } public interface RoleManagerCallback { diff --git a/api/test-current.txt b/api/test-current.txt index d08983103ce86..cec0ee0e19a71 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14,6 +14,10 @@ package android { field public static final String WRITE_OBB = "android.permission.WRITE_OBB"; } + public static final class R.array { + field public static final int config_defaultRoleHolders = 17235974; // 0x1070006 + } + } package android.animation { @@ -332,6 +336,7 @@ package android.app.role { method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List getRoleHolders(@NonNull String); method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); + field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; } public interface RoleManagerCallback { diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index a6abe0b30f79a..ddd531339d39e 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -171,6 +171,15 @@ public final class RoleManager { */ public static final String ROLE_CALL_COMPANION_APP = "android.app.role.CALL_COMPANION_APP"; + /** + * The name of the assistant app role. + * + * @hide + */ + @SystemApi + @TestApi + public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; + /** * The action used to request user approval of a role for an application. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 516f49ccc910a..7deea204d933b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7791,6 +7791,9 @@ public final class Settings { * or an activity that handles ACTION_ASSIST, or empty which means using the default * handling. * + *

This should be set indirectly by setting the {@link + * android.app.role.RoleManager#ROLE_ASSISTANT assistant role}. + * * @hide */ @UnsupportedAppUsage diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java index 7c371cb188781..d0102a72e703c 100644 --- a/core/java/com/android/internal/app/AssistUtils.java +++ b/core/java/com/android/internal/app/AssistUtils.java @@ -17,13 +17,10 @@ package com.android.internal.app; import android.annotation.NonNull; -import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -31,8 +28,6 @@ import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; -import com.android.internal.R; - import java.util.ArrayList; import java.util.Set; @@ -44,14 +39,6 @@ public class AssistUtils { private static final String TAG = "AssistUtils"; - /** - * Sentinel value for "no default assistant specified." - * - * Empty string is already used to represent an explicit setting of No Assistant. null cannot - * be used because we can't represent a null value in XML. - */ - private static final String UNSET = "#+UNSET"; - private final Context mContext; private final IVoiceInteractionManagerService mVoiceInteractionManagerService; @@ -186,37 +173,9 @@ public class AssistUtils { Settings.Secure.ASSISTANT, userId); if (setting != null) { return ComponentName.unflattenFromString(setting); - } - - final String defaultSetting = mContext.getResources().getString( - R.string.config_defaultAssistantComponentName); - if (defaultSetting != null && !defaultSetting.equals(UNSET)) { - return ComponentName.unflattenFromString(defaultSetting); - } - - // Fallback to keep backward compatible behavior when there is no user setting. - if (activeServiceSupportsAssistGesture()) { - return getActiveServiceComponentName(); - } - - if (UNSET.equals(defaultSetting)) { + } else { return null; } - - final SearchManager searchManager = - (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); - if (searchManager == null) { - return null; - } - final Intent intent = searchManager.getAssistIntent(false); - PackageManager pm = mContext.getPackageManager(); - ResolveInfo info = pm.resolveActivityAsUser(intent, PackageManager.MATCH_DEFAULT_ONLY, - userId); - if (info != null) { - return new ComponentName(info.activityInfo.applicationInfo.packageName, - info.activityInfo.name); - } - return null; } public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 75038c8d00fbe..e76fc74cae560 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3702,9 +3702,6 @@ true - - #+UNSET - diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 777886a9911cb..7d3711d972e6f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2986,7 +2986,7 @@ - + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8867539b02ae5..9611978b7d500 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3518,8 +3518,6 @@ - - diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java index 055c941f8b0a2..7f2dedb70514e 100644 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java @@ -18,6 +18,7 @@ package com.android.server.policy.role; import android.annotation.NonNull; import android.app.role.RoleManager; +import android.content.ComponentName; import android.content.Context; import android.os.Debug; import android.provider.Settings; @@ -90,6 +91,17 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder return CollectionUtils.singletonOrEmpty(result); } + case RoleManager.ROLE_ASSISTANT: { + String legacyAssistant = Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); + + if (legacyAssistant == null || legacyAssistant.isEmpty()) { + return Collections.emptyList(); + } else { + return Collections.singletonList( + ComponentName.unflattenFromString(legacyAssistant).getPackageName()); + } + } default: { Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName); return Collections.emptyList(); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index c0517fdc99d02..1c7596b80fd71 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -198,6 +198,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C // Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders // for a given role before adding a migration statement for it here migrateRoleIfNecessary(RoleManager.ROLE_SMS, userId); + migrateRoleIfNecessary(RoleManager.ROLE_ASSISTANT, userId); // Some vital packages state has changed since last role grant // Run grants again diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cef47caff740b..279e55e9c2487 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1546,6 +1546,12 @@ public final class SystemServer { traceEnd(); } + // Grants default permissions and defines roles + traceBeginAndSlog("StartRoleManagerService"); + mSystemServiceManager.startService(new RoleManagerService( + mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext))); + traceEnd(); + // We need to always start this service, regardless of whether the // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care // of initializing various settings. It will internally modify its behavior @@ -1996,12 +2002,6 @@ public final class SystemServer { } traceEnd(); - // Grants default permissions and defines roles - traceBeginAndSlog("StartRoleManagerService"); - mSystemServiceManager.startService(new RoleManagerService( - mSystemContext, new LegacyRoleResolutionPolicy(mSystemContext))); - traceEnd(); - // No dependency on Webview preparation in system server. But this should // be completed before allowing 3rd party final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 613c4ffceffc2..94b6a104ddaba 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -17,16 +17,18 @@ package com.android.server.voiceinteraction; import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerInternal; - -import com.android.internal.app.IVoiceActionCheckCallback; -import com.android.server.wm.ActivityTaskManagerInternal; import android.app.AppGlobals; +import android.app.role.OnRoleHoldersChangedListener; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -62,8 +64,9 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; -import com.android.internal.app.IVoiceInteractionSessionListener; +import com.android.internal.app.IVoiceActionCheckCallback; import com.android.internal.app.IVoiceInteractionManagerService; +import com.android.internal.app.IVoiceInteractionSessionListener; import com.android.internal.app.IVoiceInteractionSessionShowCallback; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.PackageMonitor; @@ -75,10 +78,12 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.soundtrigger.SoundTriggerInternal; +import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; +import java.util.concurrent.Executor; /** * SystemService that publishes an IVoiceInteractionManagerService. @@ -201,6 +206,7 @@ public class VoiceInteractionManagerService extends SystemService { VoiceInteractionManagerServiceStub() { mEnableService = shouldEnableService(mContext); + new RoleObserver(mContext.getMainExecutor()); } // TODO: VI Make sure the caller is the current user or profile @@ -1218,6 +1224,106 @@ public class VoiceInteractionManagerService extends SystemService { getActiveServiceComponentName()); } + class RoleObserver implements OnRoleHoldersChangedListener { + private PackageManager mPm = mContext.getPackageManager(); + private RoleManager mRm = mContext.getSystemService(RoleManager.class); + + RoleObserver(@NonNull @CallbackExecutor Executor executor) { + mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL); + } + + private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) { + ResolveInfo resolveInfo = mPm.resolveServiceAsUser( + new Intent(RecognitionService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA, user.getIdentifier()); + + if (resolveInfo == null || resolveInfo.serviceInfo == null) { + Log.w(TAG, "Unable to resolve default voice recognition service."); + return ""; + } + + return new ComponentName(resolveInfo.serviceInfo.packageName, + resolveInfo.serviceInfo.name).flattenToShortString(); + } + + /** + * Convert the assistant-role holder into settings. The rest of the system uses the + * settings. + * + * @param roleName the name of the role whose holders are changed + * @param user the user for this role holder change + */ + @Override + public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { + if (!roleName.equals(RoleManager.ROLE_ASSISTANT)) { + return; + } + + List roleHolders = mRm.getRoleHoldersAsUser(roleName, user); + + if (roleHolders.isEmpty()) { + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.ASSISTANT, ""); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, ""); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer(user)); + } else { + // Assistant is singleton role + String pkg = roleHolders.get(0); + + // Try to set role holder as VoiceInteractionService + List services = mPm.queryIntentServicesAsUser( + new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg), + PackageManager.GET_META_DATA, user.getIdentifier()); + + for (ResolveInfo resolveInfo : services) { + ServiceInfo serviceInfo = resolveInfo.serviceInfo; + + VoiceInteractionServiceInfo voiceInteractionServiceInfo = + new VoiceInteractionServiceInfo(mPm, serviceInfo); + if (!voiceInteractionServiceInfo.getSupportsAssist()) { + continue; + } + + String serviceComponentName = serviceInfo.getComponentName() + .flattenToShortString(); + + String serviceRecognizerName = new ComponentName(pkg, + voiceInteractionServiceInfo.getRecognitionService()) + .flattenToShortString(); + + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.ASSISTANT, serviceComponentName); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName); + + return; + } + + // If no service could be found try to set assist activity + final List activities = mPm.queryIntentActivitiesAsUser( + new Intent(Intent.ACTION_ASSIST).setPackage(pkg), + PackageManager.MATCH_DEFAULT_ONLY, user.getIdentifier()); + + for (ResolveInfo resolveInfo : activities) { + ActivityInfo activityInfo = resolveInfo.activityInfo; + + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.ASSISTANT, + activityInfo.getComponentName().flattenToShortString()); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE, ""); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE, + getDefaultRecognizer(user)); + } + } + } + } + class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super(handler);