Merge "Set assistant as a role"
diff --git a/api/system-current.txt b/api/system-current.txt
index 917136c..327006e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1053,6 +1053,7 @@
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<java.lang.String>);
+ 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 223ddb9..8e638fd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,10 @@
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 @@
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> 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 a6abe0b..ddd5313 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -172,6 +172,15 @@
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.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2441d42..55d12fc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7812,6 +7812,9 @@
* or an activity that handles ACTION_ASSIST, or empty which means using the default
* handling.
*
+ * <p>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 7c371cb..d0102a7 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.provider.Settings;
import android.util.Log;
-import com.android.internal.R;
-
import java.util.ArrayList;
import java.util.Set;
@@ -44,14 +39,6 @@
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 @@
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 d93cd82..49f2c84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3729,9 +3729,6 @@
<!-- Whether or not the "SMS app service" feature is enabled -->
<bool name="config_useSmsAppService">true</bool>
- <!-- Component name for default assistant on this device -->
- <string name="config_defaultAssistantComponentName">#+UNSET</string>
-
<!-- Class name for the InputEvent compatibility processor override.
Empty string means use the default compatibility processor
(android.view.InputEventCompatProcessor). -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4235341..ec1bac1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2988,7 +2988,7 @@
</public-group>
<public-group type="array" first-id="0x01070006">
- <!-- @hide @SystemApi -->
+ <!-- @hide @TestApi @SystemApi -->
<public name="config_defaultRoleHolders" />
</public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cbb4cb2..d556130 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3528,8 +3528,6 @@
<java-symbol type="bool" name="config_useSmsAppService" />
- <java-symbol type="string" name="config_defaultAssistantComponentName" />
-
<java-symbol type="id" name="transition_overlay_view_tag" />
<java-symbol type="dimen" name="rounded_corner_radius" />
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 055c941..7f2dedb 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 @@
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 @@
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 c0517fd..1c7596b 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 @@
// 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 65eaf554..5861368 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1561,6 +1561,12 @@
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
@@ -2011,12 +2017,6 @@
}
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 bb01f04..718f2d3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -17,13 +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 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;
@@ -78,6 +83,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* SystemService that publishes an IVoiceInteractionManagerService.
@@ -200,6 +206,7 @@
VoiceInteractionManagerServiceStub() {
mEnableService = shouldEnableService(mContext);
+ new RoleObserver(mContext.getMainExecutor());
}
// TODO: VI Make sure the caller is the current user or profile
@@ -1268,6 +1275,106 @@
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<String> 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<ResolveInfo> 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<ResolveInfo> 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);