1 /* 2 * Copyright (C) 2014 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.server.voiceinteraction; 18 19 import static android.app.ActivityManager.START_ASSISTANT_HIDDEN_SESSION; 20 import static android.app.ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION; 21 import static android.app.ActivityManager.START_VOICE_HIDDEN_SESSION; 22 import static android.app.ActivityManager.START_VOICE_NOT_ACTIVE_SESSION; 23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; 24 import static android.service.voice.VoiceInteractionSession.KEY_SHOW_SESSION_ID; 25 26 import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; 27 28 import android.Manifest; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.app.ActivityManager; 32 import android.app.ActivityTaskManager; 33 import android.app.AppGlobals; 34 import android.app.ApplicationExitInfo; 35 import android.app.IActivityManager; 36 import android.app.IActivityTaskManager; 37 import android.content.BroadcastReceiver; 38 import android.content.ComponentName; 39 import android.content.ContentResolver; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.IntentFilter; 43 import android.content.ServiceConnection; 44 import android.content.pm.ApplicationInfo; 45 import android.content.pm.PackageManager; 46 import android.content.pm.PackageManagerInternal; 47 import android.content.pm.ParceledListSlice; 48 import android.content.pm.ServiceInfo; 49 import android.database.ContentObserver; 50 import android.hardware.soundtrigger.IRecognitionStatusCallback; 51 import android.hardware.soundtrigger.SoundTrigger; 52 import android.media.AudioFormat; 53 import android.media.permission.Identity; 54 import android.net.Uri; 55 import android.os.Bundle; 56 import android.os.Handler; 57 import android.os.IBinder; 58 import android.os.ParcelFileDescriptor; 59 import android.os.PersistableBundle; 60 import android.os.RemoteCallback; 61 import android.os.RemoteException; 62 import android.os.ServiceManager; 63 import android.os.SharedMemory; 64 import android.os.SystemProperties; 65 import android.os.UserHandle; 66 import android.provider.Settings; 67 import android.service.voice.HotwordDetector; 68 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; 69 import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback; 70 import android.service.voice.IVoiceInteractionService; 71 import android.service.voice.IVoiceInteractionSession; 72 import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback; 73 import android.service.voice.VoiceInteractionService; 74 import android.service.voice.VoiceInteractionServiceInfo; 75 import android.system.OsConstants; 76 import android.text.TextUtils; 77 import android.util.PrintWriterPrinter; 78 import android.util.Slog; 79 import android.view.IWindowManager; 80 81 import com.android.internal.app.IHotwordRecognitionStatusCallback; 82 import com.android.internal.app.IVisualQueryDetectionAttentionListener; 83 import com.android.internal.app.IVoiceActionCheckCallback; 84 import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener; 85 import com.android.internal.app.IVoiceInteractionSessionShowCallback; 86 import com.android.internal.app.IVoiceInteractor; 87 import com.android.internal.util.function.pooled.PooledLambda; 88 import com.android.server.LocalServices; 89 import com.android.server.wm.ActivityAssistInfo; 90 import com.android.server.wm.ActivityTaskManagerInternal; 91 import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens; 92 93 import java.io.FileDescriptor; 94 import java.io.PrintWriter; 95 import java.util.ArrayList; 96 import java.util.List; 97 import java.util.Objects; 98 99 class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback { 100 final static String TAG = "VoiceInteractionServiceManager"; 101 static final boolean DEBUG = false; 102 103 final static String CLOSE_REASON_VOICE_INTERACTION = "voiceinteraction"; 104 105 /** The delay time for retrying to request DirectActions. */ 106 private static final long REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS = 200; 107 private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED = 108 SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false); 109 110 final boolean mValid; 111 112 final Context mContext; 113 final Handler mHandler; 114 final Handler mDirectActionsHandler; 115 final VoiceInteractionManagerService.VoiceInteractionManagerServiceStub mServiceStub; 116 final int mUser; 117 final ComponentName mComponent; 118 final IActivityManager mAm; 119 final IActivityTaskManager mAtm; 120 final PackageManagerInternal mPackageManagerInternal; 121 final VoiceInteractionServiceInfo mInfo; 122 final ComponentName mSessionComponentName; 123 final IWindowManager mIWindowManager; 124 final ComponentName mHotwordDetectionComponentName; 125 final ComponentName mVisualQueryDetectionComponentName; 126 boolean mBound = false; 127 IVoiceInteractionService mService; 128 volatile HotwordDetectionConnection mHotwordDetectionConnection; 129 130 VoiceInteractionSessionConnection mActiveSession; 131 int mDisabledShowContext; 132 133 final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 134 @Override 135 public void onReceive(Context context, Intent intent) { 136 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { 137 String reason = intent.getStringExtra("reason"); 138 if (!CLOSE_REASON_VOICE_INTERACTION.equals(reason) 139 && !TextUtils.equals("dream", reason) 140 && !SYSTEM_DIALOG_REASON_ASSIST.equals(reason)) { 141 synchronized (mServiceStub) { 142 if (mActiveSession != null && mActiveSession.mSession != null) { 143 try { 144 mActiveSession.mSession.closeSystemDialogs(); 145 } catch (RemoteException e) { 146 } 147 } 148 } 149 } 150 } 151 } 152 }; 153 154 final ServiceConnection mConnection = new ServiceConnection() { 155 @Override 156 public void onServiceConnected(ComponentName name, IBinder service) { 157 if (DEBUG) { 158 Slog.d(TAG, "onServiceConnected to " + name + " for user(" + mUser + ")"); 159 } 160 synchronized (mServiceStub) { 161 mService = IVoiceInteractionService.Stub.asInterface(service); 162 try { 163 mService.ready(); 164 } catch (RemoteException e) { 165 } 166 } 167 } 168 169 @Override 170 public void onServiceDisconnected(ComponentName name) { 171 if (DEBUG) { 172 Slog.d(TAG, "onServiceDisconnected to " + name); 173 } 174 synchronized (mServiceStub) { 175 mService = null; 176 resetHotwordDetectionConnectionLocked(); 177 } 178 } 179 180 @Override 181 public void onBindingDied(ComponentName name) { 182 Slog.d(TAG, "onBindingDied to " + name); 183 String packageName = name.getPackageName(); 184 ParceledListSlice<ApplicationExitInfo> plistSlice = null; 185 try { 186 plistSlice = mAm.getHistoricalProcessExitReasons(packageName, 0, 1, mUser); 187 } catch (RemoteException e) { 188 // do nothing. The local binder so it can not throw it. 189 } 190 if (plistSlice == null) { 191 return; 192 } 193 List<ApplicationExitInfo> list = plistSlice.getList(); 194 if (list.isEmpty()) { 195 return; 196 } 197 // TODO(b/229956310): Refactor the logic of PackageMonitor and onBindingDied 198 ApplicationExitInfo info = list.get(0); 199 if (info.getReason() == ApplicationExitInfo.REASON_USER_REQUESTED 200 && info.getSubReason() == ApplicationExitInfo.SUBREASON_STOP_APP) { 201 // only handle user stopped the application from the task manager 202 mServiceStub.handleUserStop(packageName, mUser); 203 } 204 } 205 }; 206 207 final ArrayList< 208 IVoiceInteractionAccessibilitySettingsListener> mAccessibilitySettingsListeners = 209 new ArrayList<IVoiceInteractionAccessibilitySettingsListener>(); 210 VoiceInteractionManagerServiceImpl(Context context, Handler handler, VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub, int userHandle, ComponentName service)211 VoiceInteractionManagerServiceImpl(Context context, Handler handler, 212 VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub, 213 int userHandle, ComponentName service) { 214 mContext = context; 215 mHandler = handler; 216 mDirectActionsHandler = new Handler(true); 217 mServiceStub = stub; 218 mUser = userHandle; 219 mComponent = service; 220 mAm = ActivityManager.getService(); 221 mAtm = ActivityTaskManager.getService(); 222 mPackageManagerInternal = Objects.requireNonNull( 223 LocalServices.getService(PackageManagerInternal.class)); 224 VoiceInteractionServiceInfo info; 225 try { 226 info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); 227 } catch (PackageManager.NameNotFoundException e) { 228 Slog.w(TAG, "Voice interaction service not found: " + service, e); 229 mInfo = null; 230 mSessionComponentName = null; 231 mHotwordDetectionComponentName = null; 232 mVisualQueryDetectionComponentName = null; 233 mIWindowManager = null; 234 mValid = false; 235 return; 236 } 237 mInfo = info; 238 if (mInfo.getParseError() != null) { 239 Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); 240 mSessionComponentName = null; 241 mHotwordDetectionComponentName = null; 242 mVisualQueryDetectionComponentName = null; 243 mIWindowManager = null; 244 mValid = false; 245 return; 246 } 247 mValid = true; 248 mSessionComponentName = new ComponentName(service.getPackageName(), 249 mInfo.getSessionService()); 250 final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService(); 251 mHotwordDetectionComponentName = hotwordDetectionServiceName != null 252 ? new ComponentName(service.getPackageName(), hotwordDetectionServiceName) : null; 253 final String visualQueryDetectionServiceName = mInfo.getVisualQueryDetectionService(); 254 mVisualQueryDetectionComponentName = visualQueryDetectionServiceName != null ? new 255 ComponentName(service.getPackageName(), visualQueryDetectionServiceName) : null; 256 mIWindowManager = IWindowManager.Stub.asInterface( 257 ServiceManager.getService(Context.WINDOW_SERVICE)); 258 IntentFilter filter = new IntentFilter(); 259 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 260 mContext.registerReceiver(mBroadcastReceiver, filter, null, handler, 261 Context.RECEIVER_EXPORTED); 262 new AccessibilitySettingsContentObserver().register(mContext.getContentResolver()); 263 } 264 grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent)265 public void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) { 266 final int grantRecipientAppId = UserHandle.getAppId(grantRecipientUid); 267 final int grantRecipientUserId = UserHandle.getUserId(grantRecipientUid); 268 final int voiceInteractionUid = mInfo.getServiceInfo().applicationInfo.uid; 269 mPackageManagerInternal.grantImplicitAccess( 270 grantRecipientUserId, intent, grantRecipientAppId, voiceInteractionUid, 271 /* direct= */ true); 272 } 273 showSessionLocked(@ullable Bundle args, int flags, @Nullable String attributionTag, @Nullable IVoiceInteractionSessionShowCallback showCallback, @Nullable IBinder activityToken)274 public boolean showSessionLocked(@Nullable Bundle args, int flags, 275 @Nullable String attributionTag, 276 @Nullable IVoiceInteractionSessionShowCallback showCallback, 277 @Nullable IBinder activityToken) { 278 final int sessionId = mServiceStub.getNextShowSessionId(); 279 final Bundle newArgs = args == null ? new Bundle() : args; 280 newArgs.putInt(KEY_SHOW_SESSION_ID, sessionId); 281 282 try { 283 if (mService != null) { 284 mService.prepareToShowSession(newArgs, flags); 285 } 286 } catch (RemoteException e) { 287 Slog.w(TAG, "RemoteException while calling prepareToShowSession", e); 288 } 289 290 if (mActiveSession == null) { 291 mActiveSession = new VoiceInteractionSessionConnection(mServiceStub, 292 mSessionComponentName, mUser, mContext, this, 293 mInfo.getServiceInfo().applicationInfo.uid, mHandler); 294 } 295 if (!mActiveSession.mBound) { 296 try { 297 if (mService != null) { 298 Bundle failedArgs = new Bundle(); 299 failedArgs.putInt(KEY_SHOW_SESSION_ID, sessionId); 300 mService.showSessionFailed(failedArgs); 301 } 302 } catch (RemoteException e) { 303 Slog.w(TAG, "RemoteException while calling showSessionFailed", e); 304 } 305 } 306 307 List<ActivityAssistInfo> allVisibleActivities = 308 LocalServices.getService(ActivityTaskManagerInternal.class) 309 .getTopVisibleActivities(); 310 311 List<ActivityAssistInfo> visibleActivities = null; 312 if (activityToken != null) { 313 visibleActivities = new ArrayList(); 314 int activitiesCount = allVisibleActivities.size(); 315 for (int i = 0; i < activitiesCount; i++) { 316 ActivityAssistInfo info = allVisibleActivities.get(i); 317 if (info.getActivityToken() == activityToken) { 318 visibleActivities.add(info); 319 break; 320 } 321 } 322 } else { 323 visibleActivities = allVisibleActivities; 324 } 325 return mActiveSession.showLocked(newArgs, flags, attributionTag, mDisabledShowContext, 326 showCallback, visibleActivities); 327 } 328 getActiveServiceSupportedActions(List<String> commands, IVoiceActionCheckCallback callback)329 public void getActiveServiceSupportedActions(List<String> commands, 330 IVoiceActionCheckCallback callback) { 331 if (mService == null) { 332 Slog.w(TAG, "Not bound to voice interaction service " + mComponent); 333 try { 334 callback.onComplete(null); 335 } catch (RemoteException e) { 336 } 337 return; 338 } 339 try { 340 mService.getActiveServiceSupportedActions(commands, callback); 341 } catch (RemoteException e) { 342 Slog.w(TAG, "RemoteException while calling getActiveServiceSupportedActions", e); 343 } 344 } 345 hideSessionLocked()346 public boolean hideSessionLocked() { 347 if (mActiveSession != null) { 348 return mActiveSession.hideLocked(); 349 } 350 return false; 351 } 352 deliverNewSessionLocked(IBinder token, IVoiceInteractionSession session, IVoiceInteractor interactor)353 public boolean deliverNewSessionLocked(IBinder token, 354 IVoiceInteractionSession session, IVoiceInteractor interactor) { 355 if (mActiveSession == null || token != mActiveSession.mToken) { 356 Slog.w(TAG, "deliverNewSession does not match active session"); 357 return false; 358 } 359 mActiveSession.deliverNewSessionLocked(session, interactor); 360 return true; 361 } 362 startVoiceActivityLocked(@ullable String callingFeatureId, int callingPid, int callingUid, IBinder token, Intent intent, String resolvedType)363 public int startVoiceActivityLocked(@Nullable String callingFeatureId, int callingPid, 364 int callingUid, IBinder token, Intent intent, String resolvedType) { 365 try { 366 if (mActiveSession == null || token != mActiveSession.mToken) { 367 Slog.w(TAG, "startVoiceActivity does not match active session"); 368 return START_VOICE_NOT_ACTIVE_SESSION; 369 } 370 if (!mActiveSession.mShown) { 371 Slog.w(TAG, "startVoiceActivity not allowed on hidden session"); 372 return START_VOICE_HIDDEN_SESSION; 373 } 374 intent = new Intent(intent); 375 intent.addCategory(Intent.CATEGORY_VOICE); 376 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 377 return mAtm.startVoiceActivity(mComponent.getPackageName(), callingFeatureId, 378 callingPid, callingUid, intent, resolvedType, mActiveSession.mSession, 379 mActiveSession.mInteractor, 0, null, null, mUser); 380 } catch (RemoteException e) { 381 throw new IllegalStateException("Unexpected remote error", e); 382 } 383 } 384 startAssistantActivityLocked(@ullable String callingFeatureId, int callingPid, int callingUid, IBinder token, Intent intent, String resolvedType, @NonNull Bundle bundle)385 public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid, 386 int callingUid, IBinder token, Intent intent, String resolvedType, 387 @NonNull Bundle bundle) { 388 try { 389 if (mActiveSession == null || token != mActiveSession.mToken) { 390 Slog.w(TAG, "startAssistantActivity does not match active session"); 391 return START_ASSISTANT_NOT_ACTIVE_SESSION; 392 } 393 if (!mActiveSession.mShown) { 394 Slog.w(TAG, "startAssistantActivity not allowed on hidden session"); 395 return START_ASSISTANT_HIDDEN_SESSION; 396 } 397 intent = new Intent(intent); 398 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 399 // TODO: make the key public hidden 400 bundle.putInt("android.activity.activityType", ACTIVITY_TYPE_ASSISTANT); 401 return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId, 402 callingPid, callingUid, intent, resolvedType, bundle, mUser); 403 } catch (RemoteException e) { 404 throw new IllegalStateException("Unexpected remote error", e); 405 } 406 } 407 requestDirectActionsLocked(@onNull IBinder token, int taskId, @NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback, @NonNull RemoteCallback callback)408 public void requestDirectActionsLocked(@NonNull IBinder token, int taskId, 409 @NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback, 410 @NonNull RemoteCallback callback) { 411 if (mActiveSession == null || token != mActiveSession.mToken) { 412 Slog.w(TAG, "requestDirectActionsLocked does not match active session"); 413 callback.sendResult(null); 414 return; 415 } 416 final ActivityTokens tokens = LocalServices.getService(ActivityTaskManagerInternal.class) 417 .getAttachedNonFinishingActivityForTask(taskId, null); 418 if (tokens == null || tokens.getAssistToken() != assistToken) { 419 Slog.w(TAG, "Unknown activity to query for direct actions"); 420 mDirectActionsHandler.sendMessageDelayed(PooledLambda.obtainMessage( 421 VoiceInteractionManagerServiceImpl::retryRequestDirectActions, 422 VoiceInteractionManagerServiceImpl.this, token, taskId, assistToken, 423 cancellationCallback, callback), REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS); 424 } else { 425 grantImplicitAccessLocked(tokens.getUid(), /* intent= */ null); 426 try { 427 tokens.getApplicationThread().requestDirectActions(tokens.getActivityToken(), 428 mActiveSession.mInteractor, cancellationCallback, callback); 429 } catch (RemoteException e) { 430 Slog.w("Unexpected remote error", e); 431 callback.sendResult(null); 432 } 433 } 434 } 435 retryRequestDirectActions(@onNull IBinder token, int taskId, @NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback, @NonNull RemoteCallback callback)436 private void retryRequestDirectActions(@NonNull IBinder token, int taskId, 437 @NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback, 438 @NonNull RemoteCallback callback) { 439 synchronized (mServiceStub) { 440 if (mActiveSession == null || token != mActiveSession.mToken) { 441 Slog.w(TAG, "retryRequestDirectActions does not match active session"); 442 callback.sendResult(null); 443 return; 444 } 445 final ActivityTokens tokens = LocalServices.getService( 446 ActivityTaskManagerInternal.class) 447 .getAttachedNonFinishingActivityForTask(taskId, null); 448 if (tokens == null || tokens.getAssistToken() != assistToken) { 449 Slog.w(TAG, "Unknown activity to query for direct actions during retrying"); 450 callback.sendResult(null); 451 } else { 452 try { 453 tokens.getApplicationThread().requestDirectActions(tokens.getActivityToken(), 454 mActiveSession.mInteractor, cancellationCallback, callback); 455 } catch (RemoteException e) { 456 Slog.w("Unexpected remote error", e); 457 callback.sendResult(null); 458 } 459 } 460 } 461 } 462 performDirectActionLocked(@onNull IBinder token, @NonNull String actionId, @Nullable Bundle arguments, int taskId, IBinder assistToken, @Nullable RemoteCallback cancellationCallback, @NonNull RemoteCallback resultCallback)463 void performDirectActionLocked(@NonNull IBinder token, @NonNull String actionId, 464 @Nullable Bundle arguments, int taskId, IBinder assistToken, 465 @Nullable RemoteCallback cancellationCallback, 466 @NonNull RemoteCallback resultCallback) { 467 if (mActiveSession == null || token != mActiveSession.mToken) { 468 Slog.w(TAG, "performDirectActionLocked does not match active session"); 469 resultCallback.sendResult(null); 470 return; 471 } 472 final ActivityTokens tokens = LocalServices.getService(ActivityTaskManagerInternal.class) 473 .getAttachedNonFinishingActivityForTask(taskId, null); 474 if (tokens == null || tokens.getAssistToken() != assistToken) { 475 Slog.w(TAG, "Unknown activity to perform a direct action"); 476 resultCallback.sendResult(null); 477 } else { 478 try { 479 tokens.getApplicationThread().performDirectAction(tokens.getActivityToken(), 480 actionId, arguments, cancellationCallback, 481 resultCallback); 482 } catch (RemoteException e) { 483 Slog.w("Unexpected remote error", e); 484 resultCallback.sendResult(null); 485 } 486 } 487 } 488 setKeepAwakeLocked(IBinder token, boolean keepAwake)489 public void setKeepAwakeLocked(IBinder token, boolean keepAwake) { 490 try { 491 if (mActiveSession == null || token != mActiveSession.mToken) { 492 Slog.w(TAG, "setKeepAwake does not match active session"); 493 return; 494 } 495 mAtm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake); 496 } catch (RemoteException e) { 497 throw new IllegalStateException("Unexpected remote error", e); 498 } 499 } 500 closeSystemDialogsLocked(IBinder token)501 public void closeSystemDialogsLocked(IBinder token) { 502 try { 503 if (mActiveSession == null || token != mActiveSession.mToken) { 504 Slog.w(TAG, "closeSystemDialogs does not match active session"); 505 return; 506 } 507 mAm.closeSystemDialogs(CLOSE_REASON_VOICE_INTERACTION); 508 } catch (RemoteException e) { 509 throw new IllegalStateException("Unexpected remote error", e); 510 } 511 } 512 finishLocked(IBinder token, boolean finishTask)513 public void finishLocked(IBinder token, boolean finishTask) { 514 if (mActiveSession == null || (!finishTask && token != mActiveSession.mToken)) { 515 Slog.w(TAG, "finish does not match active session"); 516 return; 517 } 518 mActiveSession.cancelLocked(finishTask); 519 mActiveSession = null; 520 } 521 setDisabledShowContextLocked(int callingUid, int flags)522 public void setDisabledShowContextLocked(int callingUid, int flags) { 523 int activeUid = mInfo.getServiceInfo().applicationInfo.uid; 524 if (callingUid != activeUid) { 525 throw new SecurityException("Calling uid " + callingUid 526 + " does not match active uid " + activeUid); 527 } 528 mDisabledShowContext = flags; 529 } 530 getDisabledShowContextLocked(int callingUid)531 public int getDisabledShowContextLocked(int callingUid) { 532 int activeUid = mInfo.getServiceInfo().applicationInfo.uid; 533 if (callingUid != activeUid) { 534 throw new SecurityException("Calling uid " + callingUid 535 + " does not match active uid " + activeUid); 536 } 537 return mDisabledShowContext; 538 } 539 getUserDisabledShowContextLocked(int callingUid)540 public int getUserDisabledShowContextLocked(int callingUid) { 541 int activeUid = mInfo.getServiceInfo().applicationInfo.uid; 542 if (callingUid != activeUid) { 543 throw new SecurityException("Calling uid " + callingUid 544 + " does not match active uid " + activeUid); 545 } 546 return mActiveSession != null ? mActiveSession.getUserDisabledShowContextLocked() : 0; 547 } 548 supportsLocalVoiceInteraction()549 public boolean supportsLocalVoiceInteraction() { 550 return mInfo.getSupportsLocalInteraction(); 551 } 552 getApplicationInfo()553 public ApplicationInfo getApplicationInfo() { 554 return mInfo.getServiceInfo().applicationInfo; 555 } 556 startListeningVisibleActivityChangedLocked(@onNull IBinder token)557 public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) { 558 if (DEBUG) { 559 Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token); 560 } 561 if (mActiveSession == null || token != mActiveSession.mToken) { 562 Slog.w(TAG, "startListeningVisibleActivityChangedLocked does not match" 563 + " active session"); 564 return; 565 } 566 mActiveSession.startListeningVisibleActivityChangedLocked(); 567 } 568 stopListeningVisibleActivityChangedLocked(@onNull IBinder token)569 public void stopListeningVisibleActivityChangedLocked(@NonNull IBinder token) { 570 if (DEBUG) { 571 Slog.d(TAG, "stopListeningVisibleActivityChangedLocked: token=" + token); 572 } 573 if (mActiveSession == null || token != mActiveSession.mToken) { 574 Slog.w(TAG, "stopListeningVisibleActivityChangedLocked does not match" 575 + " active session"); 576 return; 577 } 578 mActiveSession.stopListeningVisibleActivityChangedLocked(); 579 } 580 notifyActivityDestroyedLocked(@onNull IBinder activityToken)581 public void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) { 582 if (DEBUG) { 583 Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken); 584 } 585 if (mActiveSession == null || !mActiveSession.mShown) { 586 if (DEBUG) { 587 Slog.d(TAG, "notifyActivityDestroyedLocked not allowed on no session or" 588 + " hidden session"); 589 } 590 return; 591 } 592 mActiveSession.notifyActivityDestroyedLocked(activityToken); 593 } 594 notifyActivityEventChangedLocked(@onNull IBinder activityToken, int type)595 public void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) { 596 if (DEBUG) { 597 Slog.d(TAG, "notifyActivityEventChangedLocked type=" + type); 598 } 599 if (mActiveSession == null || !mActiveSession.mShown) { 600 if (DEBUG) { 601 Slog.d(TAG, "notifyActivityEventChangedLocked not allowed on no session or" 602 + " hidden session"); 603 } 604 return; 605 } 606 mActiveSession.notifyActivityEventChangedLocked(activityToken, type); 607 } 608 updateStateLocked( @ullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @NonNull IBinder token)609 public void updateStateLocked( 610 @Nullable PersistableBundle options, 611 @Nullable SharedMemory sharedMemory, 612 @NonNull IBinder token) { 613 Slog.v(TAG, "updateStateLocked"); 614 615 if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) { 616 Slog.w(TAG, "Can't set sharedMemory to be read-only"); 617 throw new IllegalStateException("Can't set sharedMemory to be read-only"); 618 } 619 620 if (mHotwordDetectionConnection == null) { 621 Slog.w(TAG, "update State, but no hotword detection connection"); 622 throw new IllegalStateException("Hotword detection connection not found"); 623 } 624 synchronized (mHotwordDetectionConnection.mLock) { 625 mHotwordDetectionConnection.updateStateLocked(options, sharedMemory, token); 626 } 627 } 628 verifyDetectorForHotwordDetectionLocked( @ullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback, int detectorType)629 private void verifyDetectorForHotwordDetectionLocked( 630 @Nullable SharedMemory sharedMemory, 631 IHotwordRecognitionStatusCallback callback, 632 int detectorType) { 633 Slog.v(TAG, "verifyDetectorForHotwordDetectionLocked"); 634 int voiceInteractionServiceUid = mInfo.getServiceInfo().applicationInfo.uid; 635 if (mHotwordDetectionComponentName == null) { 636 Slog.w(TAG, "Hotword detection service name not found"); 637 logDetectorCreateEventIfNeeded(callback, detectorType, false, 638 voiceInteractionServiceUid); 639 throw new IllegalStateException("Hotword detection service name not found"); 640 } 641 ServiceInfo hotwordDetectionServiceInfo = getServiceInfoLocked( 642 mHotwordDetectionComponentName, mUser); 643 if (hotwordDetectionServiceInfo == null) { 644 Slog.w(TAG, "Hotword detection service info not found"); 645 logDetectorCreateEventIfNeeded(callback, detectorType, false, 646 voiceInteractionServiceUid); 647 throw new IllegalStateException("Hotword detection service info not found"); 648 } 649 if (!isIsolatedProcessLocked(hotwordDetectionServiceInfo)) { 650 Slog.w(TAG, "Hotword detection service not in isolated process"); 651 logDetectorCreateEventIfNeeded(callback, detectorType, false, 652 voiceInteractionServiceUid); 653 throw new IllegalStateException("Hotword detection service not in isolated process"); 654 } 655 if (!Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals( 656 hotwordDetectionServiceInfo.permission)) { 657 Slog.w(TAG, "Hotword detection service does not require permission " 658 + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); 659 logDetectorCreateEventIfNeeded(callback, detectorType, false, 660 voiceInteractionServiceUid); 661 throw new SecurityException("Hotword detection service does not require permission " 662 + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); 663 } 664 if (mContext.getPackageManager().checkPermission( 665 Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE, 666 mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) { 667 Slog.w(TAG, "Voice interaction service should not hold permission " 668 + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); 669 logDetectorCreateEventIfNeeded(callback, detectorType, false, 670 voiceInteractionServiceUid); 671 throw new SecurityException("Voice interaction service should not hold permission " 672 + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); 673 } 674 675 if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) { 676 Slog.w(TAG, "Can't set sharedMemory to be read-only"); 677 logDetectorCreateEventIfNeeded(callback, detectorType, false, 678 voiceInteractionServiceUid); 679 throw new IllegalStateException("Can't set sharedMemory to be read-only"); 680 } 681 682 logDetectorCreateEventIfNeeded(callback, detectorType, true, 683 voiceInteractionServiceUid); 684 } 685 verifyDetectorForVisualQueryDetectionLocked(@ullable SharedMemory sharedMemory)686 private void verifyDetectorForVisualQueryDetectionLocked(@Nullable SharedMemory sharedMemory) { 687 Slog.v(TAG, "verifyDetectorForVisualQueryDetectionLocked"); 688 689 if (mVisualQueryDetectionComponentName == null) { 690 Slog.w(TAG, "Visual query detection service name not found"); 691 throw new IllegalStateException("Visual query detection service name not found"); 692 } 693 ServiceInfo visualQueryDetectionServiceInfo = getServiceInfoLocked( 694 mVisualQueryDetectionComponentName, mUser); 695 if (visualQueryDetectionServiceInfo == null) { 696 Slog.w(TAG, "Visual query detection service info not found"); 697 throw new IllegalStateException("Visual query detection service name not found"); 698 } 699 if (!isIsolatedProcessLocked(visualQueryDetectionServiceInfo)) { 700 Slog.w(TAG, "Visual query detection service not in isolated process"); 701 throw new IllegalStateException("Visual query detection not in isolated process"); 702 } 703 if (!Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE.equals( 704 visualQueryDetectionServiceInfo.permission)) { 705 Slog.w(TAG, "Visual query detection does not require permission " 706 + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE); 707 throw new SecurityException("Visual query detection does not require permission " 708 + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE); 709 } 710 if (mContext.getPackageManager().checkPermission( 711 Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE, 712 mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) { 713 Slog.w(TAG, "Voice interaction service should not hold permission " 714 + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE); 715 throw new SecurityException("Voice interaction service should not hold permission " 716 + Manifest.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE); 717 } 718 if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) { 719 Slog.w(TAG, "Can't set sharedMemory to be read-only"); 720 throw new IllegalStateException("Can't set sharedMemory to be read-only"); 721 } 722 } 723 initAndVerifyDetectorLocked( @onNull Identity voiceInteractorIdentity, @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @NonNull IBinder token, IHotwordRecognitionStatusCallback callback, int detectorType)724 public void initAndVerifyDetectorLocked( 725 @NonNull Identity voiceInteractorIdentity, 726 @Nullable PersistableBundle options, 727 @Nullable SharedMemory sharedMemory, 728 @NonNull IBinder token, 729 IHotwordRecognitionStatusCallback callback, 730 int detectorType) { 731 732 if (detectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { 733 verifyDetectorForHotwordDetectionLocked(sharedMemory, callback, detectorType); 734 } else { 735 verifyDetectorForVisualQueryDetectionLocked(sharedMemory); 736 } 737 if (SYSPROP_VISUAL_QUERY_SERVICE_ENABLED && !verifyProcessSharingLocked()) { 738 Slog.w(TAG, "Sandboxed detection service not in shared isolated process"); 739 throw new IllegalStateException("VisualQueryDetectionService or HotworDetectionService " 740 + "not in a shared isolated process. Please make sure to set " 741 + "android:allowSharedIsolatedProcess and android:isolatedProcess to be true " 742 + "and android:externalService to be false in the manifest file"); 743 } 744 745 if (mHotwordDetectionConnection == null) { 746 mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext, 747 mInfo.getServiceInfo().applicationInfo.uid, voiceInteractorIdentity, 748 mHotwordDetectionComponentName, mVisualQueryDetectionComponentName, mUser, 749 /* bindInstantServiceAllowed= */ false, detectorType, 750 (token1, detectorType1) -> { 751 try { 752 mService.detectorRemoteExceptionOccurred(token1, detectorType1); 753 } catch (RemoteException e) { 754 Slog.w(TAG, "Fail to notify client detector remote " 755 + "exception occurred."); 756 } 757 }); 758 registerAccessibilityDetectionSettingsListenerLocked( 759 mHotwordDetectionConnection.mAccessibilitySettingsListener); 760 } else if (detectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { 761 // TODO: Logger events should be handled in session instead. Temporary adding the 762 // checking to prevent confusion so VisualQueryDetection events won't be logged if the 763 // connection is instantiated by the VisualQueryDetector. 764 mHotwordDetectionConnection.setDetectorType(detectorType); 765 } 766 mHotwordDetectionConnection.createDetectorLocked(options, sharedMemory, token, callback, 767 detectorType); 768 } 769 destroyDetectorLocked(IBinder token)770 public void destroyDetectorLocked(IBinder token) { 771 Slog.v(TAG, "destroyDetectorLocked"); 772 773 if (mHotwordDetectionConnection == null) { 774 Slog.w(TAG, "destroy detector callback, but no hotword detection connection"); 775 return; 776 } 777 mHotwordDetectionConnection.destroyDetectorLocked(token); 778 } 779 logDetectorCreateEventIfNeeded(IHotwordRecognitionStatusCallback callback, int detectorType, boolean isCreated, int voiceInteractionServiceUid)780 private void logDetectorCreateEventIfNeeded(IHotwordRecognitionStatusCallback callback, 781 int detectorType, boolean isCreated, int voiceInteractionServiceUid) { 782 if (callback != null) { 783 HotwordMetricsLogger.writeDetectorCreateEvent(detectorType, isCreated, 784 voiceInteractionServiceUid); 785 } 786 } 787 shutdownHotwordDetectionServiceLocked()788 public void shutdownHotwordDetectionServiceLocked() { 789 if (DEBUG) { 790 Slog.d(TAG, "shutdownHotwordDetectionServiceLocked"); 791 } 792 if (mHotwordDetectionConnection == null) { 793 Slog.w(TAG, "shutdown, but no hotword detection connection"); 794 return; 795 } 796 mHotwordDetectionConnection.cancelLocked(); 797 unregisterAccessibilityDetectionSettingsListenerLocked( 798 mHotwordDetectionConnection.mAccessibilitySettingsListener); 799 mHotwordDetectionConnection = null; 800 } 801 setVisualQueryDetectionAttentionListenerLocked( @ullable IVisualQueryDetectionAttentionListener listener)802 public void setVisualQueryDetectionAttentionListenerLocked( 803 @Nullable IVisualQueryDetectionAttentionListener listener) { 804 if (mHotwordDetectionConnection == null) { 805 return; 806 } 807 mHotwordDetectionConnection.setVisualQueryDetectionAttentionListenerLocked(listener); 808 } 809 startPerceivingLocked(IVisualQueryDetectionVoiceInteractionCallback callback)810 public boolean startPerceivingLocked(IVisualQueryDetectionVoiceInteractionCallback callback) { 811 if (DEBUG) { 812 Slog.d(TAG, "startPerceivingLocked"); 813 } 814 815 if (mHotwordDetectionConnection == null) { 816 // TODO: callback.onError(); 817 return false; 818 } 819 820 return mHotwordDetectionConnection.startPerceivingLocked(callback); 821 } 822 stopPerceivingLocked()823 public boolean stopPerceivingLocked() { 824 if (DEBUG) { 825 Slog.d(TAG, "stopPerceivingLocked"); 826 } 827 828 if (mHotwordDetectionConnection == null) { 829 Slog.w(TAG, "stopPerceivingLocked() called but connection isn't established"); 830 return false; 831 } 832 833 return mHotwordDetectionConnection.stopPerceivingLocked(); 834 } 835 startListeningFromMicLocked( AudioFormat audioFormat, IMicrophoneHotwordDetectionVoiceInteractionCallback callback)836 public void startListeningFromMicLocked( 837 AudioFormat audioFormat, 838 IMicrophoneHotwordDetectionVoiceInteractionCallback callback) { 839 if (DEBUG) { 840 Slog.d(TAG, "startListeningFromMicLocked"); 841 } 842 843 if (mHotwordDetectionConnection == null) { 844 // TODO: callback.onError(); 845 return; 846 } 847 848 mHotwordDetectionConnection.startListeningFromMicLocked(audioFormat, callback); 849 } 850 startListeningFromExternalSourceLocked( ParcelFileDescriptor audioStream, AudioFormat audioFormat, @Nullable PersistableBundle options, @NonNull IBinder token, IMicrophoneHotwordDetectionVoiceInteractionCallback callback)851 public void startListeningFromExternalSourceLocked( 852 ParcelFileDescriptor audioStream, 853 AudioFormat audioFormat, 854 @Nullable PersistableBundle options, 855 @NonNull IBinder token, 856 IMicrophoneHotwordDetectionVoiceInteractionCallback callback) { 857 if (DEBUG) { 858 Slog.d(TAG, "startListeningFromExternalSourceLocked"); 859 } 860 861 if (mHotwordDetectionConnection == null) { 862 // TODO: callback.onError(); 863 return; 864 } 865 866 if (audioStream == null) { 867 Slog.w(TAG, "External source is null for hotword detector"); 868 throw new IllegalStateException("External source is null for hotword detector"); 869 } 870 871 mHotwordDetectionConnection.startListeningFromExternalSourceLocked(audioStream, audioFormat, 872 options, token, callback); 873 } 874 startListeningFromWearableLocked( ParcelFileDescriptor audioStream, AudioFormat audioFormat, PersistableBundle options, WearableHotwordDetectionCallback callback)875 public void startListeningFromWearableLocked( 876 ParcelFileDescriptor audioStream, 877 AudioFormat audioFormat, 878 PersistableBundle options, 879 WearableHotwordDetectionCallback callback) { 880 if (DEBUG) { 881 Slog.d(TAG, "startListeningFromWearable"); 882 } 883 if (mHotwordDetectionConnection == null) { 884 callback.onError( 885 "Unable to start listening from wearable because the hotword detection" 886 + " connection is null."); 887 return; 888 } 889 mHotwordDetectionConnection.startListeningFromWearableLocked( 890 audioStream, audioFormat, options, callback); 891 } 892 stopListeningFromMicLocked()893 public void stopListeningFromMicLocked() { 894 if (DEBUG) { 895 Slog.d(TAG, "stopListeningFromMicLocked"); 896 } 897 898 if (mHotwordDetectionConnection == null) { 899 Slog.w(TAG, "stopListeningFromMicLocked() called but connection isn't established"); 900 return; 901 } 902 903 mHotwordDetectionConnection.stopListeningFromMicLocked(); 904 } 905 triggerHardwareRecognitionEventForTestLocked( SoundTrigger.KeyphraseRecognitionEvent event, IHotwordRecognitionStatusCallback callback)906 public void triggerHardwareRecognitionEventForTestLocked( 907 SoundTrigger.KeyphraseRecognitionEvent event, 908 IHotwordRecognitionStatusCallback callback) { 909 if (DEBUG) { 910 Slog.d(TAG, "triggerHardwareRecognitionEventForTestLocked"); 911 } 912 if (mHotwordDetectionConnection == null) { 913 Slog.w(TAG, "triggerHardwareRecognitionEventForTestLocked() called but connection" 914 + " isn't established"); 915 return; 916 } 917 mHotwordDetectionConnection.triggerHardwareRecognitionEventForTestLocked(event, callback); 918 } 919 createSoundTriggerCallbackLocked( Context context, IHotwordRecognitionStatusCallback callback, Identity voiceInteractorIdentity)920 public IRecognitionStatusCallback createSoundTriggerCallbackLocked( 921 Context context, IHotwordRecognitionStatusCallback callback, 922 Identity voiceInteractorIdentity) { 923 if (DEBUG) { 924 Slog.d(TAG, "createSoundTriggerCallbackLocked"); 925 } 926 return new HotwordDetectionConnection.SoundTriggerCallback(context, callback, 927 mHotwordDetectionConnection, voiceInteractorIdentity); 928 } 929 getServiceInfoLocked(@onNull ComponentName componentName, int userHandle)930 private static ServiceInfo getServiceInfoLocked(@NonNull ComponentName componentName, 931 int userHandle) { 932 try { 933 return AppGlobals.getPackageManager().getServiceInfo(componentName, 934 PackageManager.GET_META_DATA 935 | PackageManager.MATCH_DIRECT_BOOT_AWARE 936 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); 937 } catch (RemoteException e) { 938 if (DEBUG) { 939 Slog.w(TAG, "getServiceInfoLocked RemoteException : " + e); 940 } 941 } 942 return null; 943 } 944 isIsolatedProcessLocked(@onNull ServiceInfo serviceInfo)945 boolean isIsolatedProcessLocked(@NonNull ServiceInfo serviceInfo) { 946 return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0 947 && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0; 948 } 949 verifyProcessSharingLocked()950 boolean verifyProcessSharingLocked() { 951 // only check this if both VQDS and HDS are declared in the app 952 ServiceInfo hotwordInfo = getServiceInfoLocked(mHotwordDetectionComponentName, mUser); 953 ServiceInfo visualQueryInfo = 954 getServiceInfoLocked(mVisualQueryDetectionComponentName, mUser); 955 if (hotwordInfo == null || visualQueryInfo == null) { 956 return true; 957 } 958 // Enforce shared isolated option is used when VisualQueryDetectionservice is enabled 959 return (hotwordInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0 960 && (visualQueryInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0; 961 } 962 963 forceRestartHotwordDetector()964 void forceRestartHotwordDetector() { 965 if (mHotwordDetectionConnection == null) { 966 Slog.w(TAG, "Failed to force-restart hotword detection: no hotword detection active"); 967 return; 968 } 969 mHotwordDetectionConnection.forceRestart(); 970 } 971 setDebugHotwordLoggingLocked(boolean logging)972 void setDebugHotwordLoggingLocked(boolean logging) { 973 if (mHotwordDetectionConnection == null) { 974 Slog.w(TAG, "Failed to set temporary debug logging: no hotword detection active"); 975 return; 976 } 977 mHotwordDetectionConnection.setDebugHotwordLoggingLocked(logging); 978 } 979 resetHotwordDetectionConnectionLocked()980 void resetHotwordDetectionConnectionLocked() { 981 if (DEBUG) { 982 Slog.d(TAG, "resetHotwordDetectionConnectionLocked"); 983 } 984 if (mHotwordDetectionConnection == null) { 985 if (DEBUG) { 986 Slog.w(TAG, "reset, but no hotword detection connection"); 987 } 988 return; 989 } 990 mHotwordDetectionConnection.cancelLocked(); 991 unregisterAccessibilityDetectionSettingsListenerLocked( 992 mHotwordDetectionConnection.mAccessibilitySettingsListener); 993 mHotwordDetectionConnection = null; 994 } 995 dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args)996 public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { 997 if (!mValid) { 998 pw.print(" NOT VALID: "); 999 if (mInfo == null) { 1000 pw.println("no info"); 1001 } else { 1002 pw.println(mInfo.getParseError()); 1003 } 1004 return; 1005 } 1006 pw.print(" mUser="); pw.println(mUser); 1007 pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); 1008 pw.print(" Session service="); pw.println(mInfo.getSessionService()); 1009 pw.println(" Service info:"); 1010 mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), " "); 1011 pw.print(" Recognition service="); pw.println(mInfo.getRecognitionService()); 1012 pw.print(" Hotword detection service="); pw.println(mInfo.getHotwordDetectionService()); 1013 pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); 1014 pw.print(" Supports assist="); pw.println(mInfo.getSupportsAssist()); 1015 pw.print(" Supports launch from keyguard="); 1016 pw.println(mInfo.getSupportsLaunchFromKeyguard()); 1017 if (mDisabledShowContext != 0) { 1018 pw.print(" mDisabledShowContext="); 1019 pw.println(Integer.toHexString(mDisabledShowContext)); 1020 } 1021 pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); 1022 if (mHotwordDetectionConnection != null) { 1023 pw.println(" Hotword detection connection:"); 1024 mHotwordDetectionConnection.dump(" ", pw); 1025 } else { 1026 pw.println(" No Hotword detection connection"); 1027 } 1028 if (mActiveSession != null) { 1029 pw.println(" Active session:"); 1030 mActiveSession.dump(" ", pw); 1031 } 1032 } 1033 getAccessibilityDetectionEnabled()1034 boolean getAccessibilityDetectionEnabled() { 1035 return Settings.Secure.getIntForUser( 1036 mContext.getContentResolver(), 1037 Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0, 1038 mUser) == 1; 1039 } 1040 registerAccessibilityDetectionSettingsListenerLocked( IVoiceInteractionAccessibilitySettingsListener listener)1041 void registerAccessibilityDetectionSettingsListenerLocked( 1042 IVoiceInteractionAccessibilitySettingsListener listener) { 1043 if (DEBUG) { 1044 Slog.d(TAG, "registerAccessibilityDetectionSettingsListener"); 1045 } 1046 mAccessibilitySettingsListeners.add(listener); 1047 } 1048 unregisterAccessibilityDetectionSettingsListenerLocked( IVoiceInteractionAccessibilitySettingsListener listener)1049 void unregisterAccessibilityDetectionSettingsListenerLocked( 1050 IVoiceInteractionAccessibilitySettingsListener listener) { 1051 if (DEBUG) { 1052 Slog.d(TAG, "unregisterAccessibilityDetectionSettingsListener"); 1053 } 1054 mAccessibilitySettingsListeners.remove(listener); 1055 } 1056 startLocked()1057 void startLocked() { 1058 Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); 1059 intent.setComponent(mComponent); 1060 mBound = mContext.bindServiceAsUser(intent, mConnection, 1061 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE 1062 | Context.BIND_INCLUDE_CAPABILITIES 1063 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser)); 1064 if (!mBound) { 1065 Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); 1066 } 1067 } 1068 launchVoiceAssistFromKeyguard()1069 public void launchVoiceAssistFromKeyguard() { 1070 if (mService == null) { 1071 Slog.w(TAG, "Not bound to voice interaction service " + mComponent); 1072 return; 1073 } 1074 try { 1075 mService.launchVoiceAssistFromKeyguard(); 1076 } catch (RemoteException e) { 1077 Slog.w(TAG, "RemoteException while calling launchVoiceAssistFromKeyguard", e); 1078 } 1079 } 1080 shutdownLocked()1081 void shutdownLocked() { 1082 // If there is an active session, cancel it to allow it to clean up its window and other 1083 // state. 1084 if (mActiveSession != null) { 1085 mActiveSession.cancelLocked(false); 1086 mActiveSession = null; 1087 } 1088 try { 1089 if (mService != null) { 1090 mService.shutdown(); 1091 } 1092 } catch (RemoteException e) { 1093 Slog.w(TAG, "RemoteException in shutdown", e); 1094 } 1095 if (mHotwordDetectionConnection != null) { 1096 mHotwordDetectionConnection.cancelLocked(); 1097 unregisterAccessibilityDetectionSettingsListenerLocked( 1098 mHotwordDetectionConnection.mAccessibilitySettingsListener); 1099 mHotwordDetectionConnection = null; 1100 } 1101 if (mBound) { 1102 mContext.unbindService(mConnection); 1103 mBound = false; 1104 } 1105 if (mValid) { 1106 mContext.unregisterReceiver(mBroadcastReceiver); 1107 } 1108 } 1109 notifySoundModelsChangedLocked()1110 void notifySoundModelsChangedLocked() { 1111 if (mService == null) { 1112 Slog.w(TAG, "Not bound to voice interaction service " + mComponent); 1113 return; 1114 } 1115 try { 1116 mService.soundModelsChanged(); 1117 } catch (RemoteException e) { 1118 Slog.w(TAG, "RemoteException while calling soundModelsChanged", e); 1119 } 1120 } 1121 1122 @Override sessionConnectionGone(VoiceInteractionSessionConnection connection)1123 public void sessionConnectionGone(VoiceInteractionSessionConnection connection) { 1124 synchronized (mServiceStub) { 1125 finishLocked(connection.mToken, false); 1126 } 1127 } 1128 1129 @Override onSessionShown(VoiceInteractionSessionConnection connection)1130 public void onSessionShown(VoiceInteractionSessionConnection connection) { 1131 mServiceStub.onSessionShown(); 1132 } 1133 1134 @Override onSessionHidden(VoiceInteractionSessionConnection connection)1135 public void onSessionHidden(VoiceInteractionSessionConnection connection) { 1136 mServiceStub.onSessionHidden(); 1137 // Notifies visibility change here can cause duplicate events, it is added to make sure 1138 // client always get the callback even if session is unexpectedly closed. 1139 mServiceStub.setSessionWindowVisible(connection.mToken, false); 1140 } 1141 1142 interface DetectorRemoteExceptionListener { onDetectorRemoteException(@onNull IBinder token, int detectorType)1143 void onDetectorRemoteException(@NonNull IBinder token, int detectorType); 1144 } 1145 1146 private final class AccessibilitySettingsContentObserver extends ContentObserver { 1147 private Uri mAccessibilitySettingsEnabledUri = Settings.Secure.getUriFor( 1148 Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED); 1149 AccessibilitySettingsContentObserver()1150 AccessibilitySettingsContentObserver() { 1151 super(null); 1152 } 1153 register(ContentResolver contentResolver)1154 public void register(ContentResolver contentResolver) { 1155 contentResolver.registerContentObserver( 1156 mAccessibilitySettingsEnabledUri, false, this, UserHandle.USER_ALL); 1157 } 1158 1159 @Override onChange(boolean selfChange, Uri uri)1160 public void onChange(boolean selfChange, Uri uri) { 1161 Slog.i(TAG, "OnChange called with uri:" + uri); 1162 if (mAccessibilitySettingsEnabledUri.equals(uri)) { 1163 boolean enable = Settings.Secure.getIntForUser( 1164 mContext.getContentResolver(), 1165 Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0, 1166 mUser) == 1; 1167 Slog.i(TAG, "Notifying listeners with Accessibility setting set to " 1168 + enable); 1169 mAccessibilitySettingsListeners.forEach( 1170 listener -> { 1171 try { 1172 listener.onAccessibilityDetectionChanged(enable); 1173 } catch (RemoteException e) { 1174 e.rethrowFromSystemServer(); 1175 } 1176 } 1177 ); 1178 1179 } 1180 } 1181 } 1182 } 1183