• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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