• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.internal.app;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageManager;
26 import android.os.Bundle;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.provider.Settings;
31 import android.util.Log;
32 
33 import java.util.ArrayList;
34 import java.util.Set;
35 
36 /**
37  * Utility method for dealing with the assistant aspects of
38  * {@link com.android.internal.app.IVoiceInteractionManagerService IVoiceInteractionManagerService}.
39  */
40 public class AssistUtils {
41 
42     private static final String TAG = "AssistUtils";
43 
44     /** bundle key: how was the assistant invoked? */
45     public static final String INVOCATION_TYPE_KEY = "invocation_type";
46     /** value for INVOCATION_TYPE_KEY: no data */
47     public static final int INVOCATION_TYPE_UNKNOWN = 0;
48     /** value for INVOCATION_TYPE_KEY: on-screen swipe gesture */
49     public static final int INVOCATION_TYPE_GESTURE = 1;
50     /** value for INVOCATION_TYPE_KEY: device-specific physical gesture */
51     public static final int INVOCATION_TYPE_PHYSICAL_GESTURE = 2;
52     /** value for INVOCATION_TYPE_KEY: voice hotword */
53     public static final int INVOCATION_TYPE_VOICE = 3;
54     /** value for INVOCATION_TYPE_KEY: search bar affordance */
55     public static final int INVOCATION_TYPE_QUICK_SEARCH_BAR = 4;
56     /** value for INVOCATION_TYPE_KEY: long press on home navigation button */
57     public static final int INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS = 5;
58     /** value for INVOCATION_TYPE_KEY: long press on physical power button */
59     public static final int INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS = 6;
60     /** value for INVOCATION_TYPE_KEY: press on physcial assistant button */
61     public static final int INVOCATION_TYPE_ASSIST_BUTTON = 7;
62     /** value for INVOCATION_TYPE_KEY: long press on nav handle */
63     public static final int INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS = 8;
64     /** value for INVOCATION_TYPE_KEY: sysui launcher */
65     public static final int INVOCATION_TYPE_LAUNCHER_SYSTEM_SHORTCUT = 9;
66 
67     private final Context mContext;
68     private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
69 
70     @UnsupportedAppUsage
AssistUtils(Context context)71     public AssistUtils(Context context) {
72         mContext = context;
73         mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface(
74                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
75     }
76 
77     /**
78      * Shows the session for the currently active service. Used to start a new session from system
79      * affordances.
80      *
81      * @param args the bundle to pass as arguments to the voice interaction session
82      * @param sourceFlags flags indicating the source of this show
83      * @param showCallback optional callback to be notified when the session was shown
84      * @param activityToken optional token of activity that needs to be on top
85      *
86      * @deprecated Use {@link #showSessionForActiveService(Bundle, int, String,
87      *             IVoiceInteractionSessionShowCallback, IBinder)} instead
88      */
89     @Deprecated
showSessionForActiveService(@ullable Bundle args, int sourceFlags, @Nullable IVoiceInteractionSessionShowCallback showCallback, @Nullable IBinder activityToken)90     public boolean showSessionForActiveService(@Nullable Bundle args, int sourceFlags,
91             @Nullable IVoiceInteractionSessionShowCallback showCallback,
92             @Nullable IBinder activityToken) {
93         return showSessionForActiveServiceInternal(args, sourceFlags, /* attributionTag */ null,
94                 showCallback, activityToken);
95     }
96 
97     /**
98      * Shows the session for the currently active service. Used to start a new session from system
99      * affordances.
100      *
101      * @param args the bundle to pass as arguments to the voice interaction session
102      * @param sourceFlags flags indicating the source of this show
103      * @param attributionTag the attribution tag of the calling context or {@code null} for default
104      *                       attribution
105      * @param showCallback optional callback to be notified when the session was shown
106      * @param activityToken optional token of activity that needs to be on top
107      */
showSessionForActiveService(@ullable Bundle args, int sourceFlags, @Nullable String attributionTag, @Nullable IVoiceInteractionSessionShowCallback showCallback, @Nullable IBinder activityToken)108     public boolean showSessionForActiveService(@Nullable Bundle args, int sourceFlags,
109             @Nullable String attributionTag,
110             @Nullable IVoiceInteractionSessionShowCallback showCallback,
111             @Nullable IBinder activityToken) {
112         return showSessionForActiveServiceInternal(args, sourceFlags, attributionTag, showCallback,
113                 activityToken);
114     }
115 
showSessionForActiveServiceInternal(@ullable Bundle args, int sourceFlags, @Nullable String attributionTag, @Nullable IVoiceInteractionSessionShowCallback showCallback, @Nullable IBinder activityToken)116     private boolean showSessionForActiveServiceInternal(@Nullable Bundle args, int sourceFlags,
117             @Nullable String attributionTag,
118             @Nullable IVoiceInteractionSessionShowCallback showCallback,
119             @Nullable IBinder activityToken) {
120         try {
121             if (mVoiceInteractionManagerService != null) {
122                 return mVoiceInteractionManagerService.showSessionForActiveService(args,
123                         sourceFlags, attributionTag, showCallback, activityToken);
124             }
125         } catch (RemoteException e) {
126             Log.w(TAG, "Failed to call showSessionForActiveService", e);
127         }
128         return false;
129     }
130 
131     /**
132      * Checks the availability of a set of voice actions for the current active voice service.
133      *
134      * @param voiceActions A set of supported voice actions to be checked.
135      * @param callback     The callback which will deliver a set of supported voice actions. If
136      *                     no voice actions are supported for the given voice action set, then null
137      *                     or empty set is provided.
138      */
getActiveServiceSupportedActions(@onNull Set<String> voiceActions, @NonNull IVoiceActionCheckCallback callback)139     public void getActiveServiceSupportedActions(@NonNull Set<String> voiceActions,
140             @NonNull IVoiceActionCheckCallback callback) {
141         try {
142             if (mVoiceInteractionManagerService != null) {
143                 mVoiceInteractionManagerService
144                         .getActiveServiceSupportedActions(new ArrayList<>(voiceActions), callback);
145             }
146         } catch (RemoteException e) {
147             Log.w(TAG, "Failed to call activeServiceSupportedActions", e);
148             try {
149                 callback.onComplete(null);
150             } catch (RemoteException re) {
151             }
152         }
153     }
154 
launchVoiceAssistFromKeyguard()155     public void launchVoiceAssistFromKeyguard() {
156         try {
157             if (mVoiceInteractionManagerService != null) {
158                 mVoiceInteractionManagerService.launchVoiceAssistFromKeyguard();
159             }
160         } catch (RemoteException e) {
161             Log.w(TAG, "Failed to call launchVoiceAssistFromKeyguard", e);
162         }
163     }
164 
activeServiceSupportsAssistGesture()165     public boolean activeServiceSupportsAssistGesture() {
166         try {
167             return mVoiceInteractionManagerService != null
168                     && mVoiceInteractionManagerService.activeServiceSupportsAssist();
169         } catch (RemoteException e) {
170             Log.w(TAG, "Failed to call activeServiceSupportsAssistGesture", e);
171             return false;
172         }
173     }
174 
activeServiceSupportsLaunchFromKeyguard()175     public boolean activeServiceSupportsLaunchFromKeyguard() {
176         try {
177             return mVoiceInteractionManagerService != null
178                     && mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard();
179         } catch (RemoteException e) {
180             Log.w(TAG, "Failed to call activeServiceSupportsLaunchFromKeyguard", e);
181             return false;
182         }
183     }
184 
getActiveServiceComponentName()185     public ComponentName getActiveServiceComponentName() {
186         try {
187             if (mVoiceInteractionManagerService != null) {
188                 return mVoiceInteractionManagerService.getActiveServiceComponentName();
189             } else {
190                 return null;
191             }
192         } catch (RemoteException e) {
193             Log.w(TAG, "Failed to call getActiveServiceComponentName", e);
194             return null;
195         }
196     }
197 
isSessionRunning()198     public boolean isSessionRunning() {
199         try {
200             return mVoiceInteractionManagerService != null
201                     && mVoiceInteractionManagerService.isSessionRunning();
202         } catch (RemoteException e) {
203             Log.w(TAG, "Failed to call isSessionRunning", e);
204             return false;
205         }
206     }
207 
hideCurrentSession()208     public void hideCurrentSession() {
209         try {
210             if (mVoiceInteractionManagerService != null) {
211                 mVoiceInteractionManagerService.hideCurrentSession();
212             }
213         } catch (RemoteException e) {
214             Log.w(TAG, "Failed to call hideCurrentSession", e);
215         }
216     }
217 
onLockscreenShown()218     public void onLockscreenShown() {
219         try {
220             if (mVoiceInteractionManagerService != null) {
221                 mVoiceInteractionManagerService.onLockscreenShown();
222             }
223         } catch (RemoteException e) {
224             Log.w(TAG, "Failed to call onLockscreenShown", e);
225         }
226     }
227 
registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener)228     public void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener) {
229         try {
230             if (mVoiceInteractionManagerService != null) {
231                 mVoiceInteractionManagerService.registerVoiceInteractionSessionListener(listener);
232             }
233         } catch (RemoteException e) {
234             Log.w(TAG, "Failed to register voice interaction listener", e);
235         }
236     }
237 
238     /**
239      * Allows subscription to {@link android.service.voice.VisualQueryDetectionService} service
240      * status.
241      *
242      * @param listener to receive visual service start/stop events.
243      */
subscribeVisualQueryRecognitionStatus(IVisualQueryRecognitionStatusListener listener)244     public void subscribeVisualQueryRecognitionStatus(IVisualQueryRecognitionStatusListener
245             listener) {
246         try {
247             if (mVoiceInteractionManagerService != null) {
248                 mVoiceInteractionManagerService.subscribeVisualQueryRecognitionStatus(listener);
249             }
250         } catch (RemoteException e) {
251             Log.w(TAG, "Failed to register visual query detection start listener", e);
252         }
253     }
254 
255     /**
256      * Enables visual detection service.
257      *
258      * @param listener to receive visual attention gained/lost events.
259      */
enableVisualQueryDetection( IVisualQueryDetectionAttentionListener listener)260     public void enableVisualQueryDetection(
261             IVisualQueryDetectionAttentionListener listener) {
262         try {
263             if (mVoiceInteractionManagerService != null) {
264                 mVoiceInteractionManagerService.enableVisualQueryDetection(listener);
265             }
266         } catch (RemoteException e) {
267             Log.w(TAG, "Failed to register visual query detection attention listener", e);
268         }
269     }
270 
271     /**
272      * Disables visual query detection.
273      */
disableVisualQueryDetection()274     public void disableVisualQueryDetection() {
275         try {
276             if (mVoiceInteractionManagerService != null) {
277                 mVoiceInteractionManagerService.disableVisualQueryDetection();
278             }
279         } catch (RemoteException e) {
280             Log.w(TAG, "Failed to register visual query detection attention listener", e);
281         }
282     }
283 
284     @UnsupportedAppUsage
getAssistComponentForUser(int userId)285     public ComponentName getAssistComponentForUser(int userId) {
286         final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
287                 Settings.Secure.ASSISTANT, userId);
288         if (setting != null) {
289             return ComponentName.unflattenFromString(setting);
290         } else {
291             return null;
292         }
293     }
294 
isPreinstalledAssistant(Context context, ComponentName assistant)295     public static boolean isPreinstalledAssistant(Context context, ComponentName assistant) {
296         if (assistant == null) {
297             return false;
298         }
299         ApplicationInfo applicationInfo;
300         try {
301             applicationInfo = context.getPackageManager().getApplicationInfo(
302                     assistant.getPackageName(), 0);
303         } catch (PackageManager.NameNotFoundException e) {
304             return false;
305         }
306         return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp();
307     }
308 
isDisclosureEnabled(Context context)309     public static boolean isDisclosureEnabled(Context context) {
310         return Settings.Secure.getInt(context.getContentResolver(),
311                 Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0;
312     }
313 
314     /**
315      * @return if the disclosure animation should trigger for the given assistant.
316      *
317      * Third-party assistants will always need to disclose, while the user can configure this for
318      * pre-installed assistants.
319      */
shouldDisclose(Context context, ComponentName assistant)320     public static boolean shouldDisclose(Context context, ComponentName assistant) {
321         if (!allowDisablingAssistDisclosure(context)) {
322             return true;
323         }
324 
325         return isDisclosureEnabled(context) || !isPreinstalledAssistant(context, assistant);
326     }
327 
allowDisablingAssistDisclosure(Context context)328     public static boolean allowDisablingAssistDisclosure(Context context) {
329         return context.getResources().getBoolean(
330                 com.android.internal.R.bool.config_allowDisablingAssistDisclosure);
331     }
332 }
333