1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.packageinstaller.role.model; 18 19 import android.app.ActivityManager; 20 import android.app.Application; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.content.pm.ServiceInfo; 26 import android.content.res.Resources; 27 import android.content.res.XmlResourceParser; 28 import android.os.UserHandle; 29 import android.provider.Settings; 30 import android.service.voice.VoiceInteractionService; 31 import android.util.ArraySet; 32 import android.util.AttributeSet; 33 import android.util.Log; 34 import android.util.Xml; 35 36 import androidx.annotation.NonNull; 37 import androidx.annotation.Nullable; 38 39 import com.android.packageinstaller.permission.utils.Utils; 40 import com.android.packageinstaller.role.utils.UserUtils; 41 import com.android.permissioncontroller.R; 42 43 import org.xmlpull.v1.XmlPullParserException; 44 45 import java.io.IOException; 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.Set; 49 50 /** 51 * Class for behavior of the assistant role. 52 */ 53 public class AssistantRoleBehavior implements RoleBehavior { 54 55 private static final String LOG_TAG = AssistantRoleBehavior.class.getSimpleName(); 56 57 private static final Intent ASSIST_SERVICE_PROBE = 58 new Intent(VoiceInteractionService.SERVICE_INTERFACE); 59 private static final Intent ASSIST_ACTIVITY_PROBE = new Intent(Intent.ACTION_ASSIST); 60 61 @Override isAvailableAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)62 public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user, 63 @NonNull Context context) { 64 return !UserUtils.isWorkProfile(user, context); 65 } 66 67 @Nullable 68 @Override getFallbackHolder(@onNull Role role, @NonNull Context context)69 public String getFallbackHolder(@NonNull Role role, @NonNull Context context) { 70 return ExclusiveDefaultHolderMixin.getDefaultHolder(role, "config_defaultAssistant", 71 context); 72 } 73 74 @Override isVisibleAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)75 public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, 76 @NonNull Context context) { 77 return VisibilityMixin.isVisible("config_showDefaultAssistant", context); 78 } 79 80 @Nullable 81 @Override getManageIntentAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)82 public Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user, 83 @NonNull Context context) { 84 return new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS); 85 } 86 87 @Nullable 88 @Override getConfirmationMessage(@onNull Role role, @NonNull String packageName, @NonNull Context context)89 public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName, 90 @NonNull Context context) { 91 return context.getString(R.string.assistant_confirmation_message); 92 } 93 94 @Nullable 95 @Override getQualifyingPackagesAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)96 public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user, 97 @NonNull Context context) { 98 Context userContext = UserUtils.getUserContext(context, user); 99 ActivityManager userActivityManager = userContext.getSystemService(ActivityManager.class); 100 PackageManager userPackageManager = userContext.getPackageManager(); 101 Set<String> availableAssistants = new ArraySet<>(); 102 103 if (!userActivityManager.isLowRamDevice()) { 104 List<ResolveInfo> services = userPackageManager.queryIntentServices( 105 ASSIST_SERVICE_PROBE, PackageManager.GET_META_DATA 106 | PackageManager.MATCH_DIRECT_BOOT_AWARE 107 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 108 int numServices = services.size(); 109 for (int i = 0; i < numServices; i++) { 110 ResolveInfo service = services.get(i); 111 112 if (isAssistantVoiceInteractionService(userPackageManager, service.serviceInfo)) { 113 availableAssistants.add(service.serviceInfo.packageName); 114 } 115 } 116 } 117 118 List<ResolveInfo> activities = userPackageManager.queryIntentActivities( 119 ASSIST_ACTIVITY_PROBE, PackageManager.MATCH_DEFAULT_ONLY 120 | PackageManager.MATCH_DIRECT_BOOT_AWARE 121 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 122 int numActivities = activities.size(); 123 for (int i = 0; i < numActivities; i++) { 124 availableAssistants.add(activities.get(i).activityInfo.packageName); 125 } 126 127 return new ArrayList<>(availableAssistants); 128 } 129 130 @Nullable 131 @Override isPackageQualified(@onNull Role role, @NonNull String packageName, @NonNull Context context)132 public Boolean isPackageQualified(@NonNull Role role, @NonNull String packageName, 133 @NonNull Context context) { 134 ActivityManager activityManager = context.getSystemService(ActivityManager.class); 135 PackageManager packageManager = context.getPackageManager(); 136 137 boolean hasAssistantService = false; 138 if (!activityManager.isLowRamDevice()) { 139 Intent pkgServiceProbe = new Intent(ASSIST_SERVICE_PROBE).setPackage(packageName); 140 List<ResolveInfo> services = packageManager.queryIntentServices(pkgServiceProbe, 141 PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE 142 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 143 hasAssistantService = !services.isEmpty(); 144 int numServices = services.size(); 145 for (int i = 0; i < numServices; i++) { 146 ResolveInfo service = services.get(i); 147 148 if (isAssistantVoiceInteractionService(packageManager, service.serviceInfo)) { 149 return true; 150 } 151 } 152 } 153 154 Intent pkgActivityProbe = new Intent(ASSIST_ACTIVITY_PROBE).setPackage(packageName); 155 boolean hasAssistantActivity = !packageManager.queryIntentActivities(pkgActivityProbe, 156 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE 157 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE).isEmpty(); 158 if (!hasAssistantActivity) { 159 Log.w(LOG_TAG, "Package " + packageName + " not qualified for " + role.getName() 160 + " due to " + (hasAssistantService ? "unqualified" : "missing") 161 + " service and missing activity"); 162 } 163 164 return hasAssistantActivity; 165 } 166 isAssistantVoiceInteractionService(@onNull PackageManager pm, @NonNull ServiceInfo si)167 private boolean isAssistantVoiceInteractionService(@NonNull PackageManager pm, 168 @NonNull ServiceInfo si) { 169 if (!android.Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) { 170 return false; 171 } 172 173 try (XmlResourceParser parser = si.loadXmlMetaData(pm, 174 VoiceInteractionService.SERVICE_META_DATA)) { 175 if (parser == null) { 176 return false; 177 } 178 179 int type; 180 do { 181 type = parser.next(); 182 } while (type != XmlResourceParser.END_DOCUMENT && type != XmlResourceParser.START_TAG); 183 184 String sessionService = null; 185 String recognitionService = null; 186 boolean supportsAssist = false; 187 188 AttributeSet attrs = Xml.asAttributeSet(parser); 189 int numAttrs = attrs.getAttributeCount(); 190 for (int i = 0; i < numAttrs; i++) { 191 switch (attrs.getAttributeNameResource(i)) { 192 case android.R.attr.sessionService: 193 sessionService = attrs.getAttributeValue(i); 194 break; 195 case android.R.attr.recognitionService: 196 recognitionService = attrs.getAttributeValue(i); 197 break; 198 case android.R.attr.supportsAssist: 199 supportsAssist = attrs.getAttributeBooleanValue(i, false); 200 break; 201 } 202 } 203 204 if (sessionService == null || recognitionService == null || !supportsAssist) { 205 return false; 206 } 207 } catch (XmlPullParserException | IOException | Resources.NotFoundException ignored) { 208 return false; 209 } 210 211 return true; 212 } 213 214 @Override onHolderChangedAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)215 public void onHolderChangedAsUser(@NonNull Role role, @NonNull UserHandle user, 216 @NonNull Context context) { 217 Utils.updateUserSensitive((Application) context.getApplicationContext(), user); 218 } 219 } 220