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);