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