1 /* 2 * Copyright (C) 2021 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 com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED; 20 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE; 21 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED; 22 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__ON_DISCONNECTED; 23 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE; 24 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL; 25 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR; 26 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP; 27 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER; 28 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.app.AppOpsManager; 33 import android.compat.annotation.ChangeId; 34 import android.compat.annotation.Disabled; 35 import android.content.ComponentName; 36 import android.content.ContentCaptureOptions; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.hardware.soundtrigger.IRecognitionStatusCallback; 40 import android.hardware.soundtrigger.SoundTrigger; 41 import android.media.AudioFormat; 42 import android.media.AudioManagerInternal; 43 import android.media.permission.Identity; 44 import android.os.Binder; 45 import android.os.Bundle; 46 import android.os.IBinder; 47 import android.os.IRemoteCallback; 48 import android.os.ParcelFileDescriptor; 49 import android.os.PersistableBundle; 50 import android.os.RemoteException; 51 import android.os.ServiceManager; 52 import android.os.SharedMemory; 53 import android.os.SystemProperties; 54 import android.provider.DeviceConfig; 55 import android.service.voice.HotwordDetectionService; 56 import android.service.voice.HotwordDetectionServiceFailure; 57 import android.service.voice.HotwordDetector; 58 import android.service.voice.IDetectorSessionStorageService; 59 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; 60 import android.service.voice.ISandboxedDetectionService; 61 import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback; 62 import android.service.voice.SoundTriggerFailure; 63 import android.service.voice.VisualQueryDetectionService; 64 import android.service.voice.VisualQueryDetectionServiceFailure; 65 import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; 66 import android.speech.IRecognitionServiceManager; 67 import android.util.Slog; 68 import android.util.SparseArray; 69 import android.view.contentcapture.IContentCaptureManager; 70 71 import com.android.internal.annotations.GuardedBy; 72 import com.android.internal.app.IHotwordRecognitionStatusCallback; 73 import com.android.internal.app.IVisualQueryDetectionAttentionListener; 74 import com.android.internal.infra.AndroidFuture; 75 import com.android.internal.infra.ServiceConnector; 76 import com.android.server.LocalServices; 77 import com.android.server.pm.permission.PermissionManagerServiceInternal; 78 import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener; 79 80 import java.io.PrintWriter; 81 import java.time.Instant; 82 import java.util.concurrent.ScheduledFuture; 83 import java.util.concurrent.ScheduledThreadPoolExecutor; 84 import java.util.concurrent.TimeUnit; 85 import java.util.function.Consumer; 86 import java.util.function.Function; 87 88 /** 89 * A class that provides the communication with the {@link HotwordDetectionService} and 90 * {@link VisualQueryDetectionService}. 91 */ 92 final class HotwordDetectionConnection { 93 private static final String TAG = "HotwordDetectionConnection"; 94 static final boolean DEBUG = false; 95 96 /** 97 * Implementors of the HotwordDetectionService must not augment the phrase IDs which are 98 * supplied via HotwordDetectionService 99 * #onDetect(AlwaysOnHotwordDetector.EventPayload, long, HotwordDetectionService.Callback). 100 * 101 * <p>The HotwordDetectedResult#getHotwordPhraseId() must match one of the phrase IDs 102 * from the AlwaysOnHotwordDetector.EventPayload#getKeyphraseRecognitionExtras() list. 103 * </p> 104 * 105 * <p>This behavior change is made to ensure the HotwordDetectionService honors what 106 * it receives from the android.hardware.soundtrigger.SoundTriggerModule, and it 107 * cannot signal to the client application a phrase which was not originally detected. 108 * </p> 109 */ 110 @ChangeId 111 @Disabled 112 public static final long ENFORCE_HOTWORD_PHRASE_ID = 215066299L; 113 114 private static final String KEY_RESTART_PERIOD_IN_SECONDS = "restart_period_in_seconds"; 115 private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour 116 private static final int MAX_ISOLATED_PROCESS_NUMBER = 10; 117 118 private static final boolean SYSPROP_VISUAL_QUERY_SERVICE_ENABLED = 119 SystemProperties.getBoolean("ro.hotword.visual_query_service_enabled", false); 120 121 /** 122 * Indicates the {@link HotwordDetectionService} is created. 123 */ 124 private static final int DETECTION_SERVICE_TYPE_HOTWORD = 1; 125 126 /** 127 * Indicates the {@link VisualQueryDetectionService} is created. 128 */ 129 private static final int DETECTION_SERVICE_TYPE_VISUAL_QUERY = 2; 130 131 // TODO: This may need to be a Handler(looper) 132 private final ScheduledThreadPoolExecutor mScheduledExecutorService = 133 new ScheduledThreadPoolExecutor(1); 134 @Nullable private final ScheduledFuture<?> mCancellationTaskFuture; 135 private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied; 136 @NonNull private final ServiceConnectionFactory mHotwordDetectionServiceConnectionFactory; 137 @NonNull private final ServiceConnectionFactory mVisualQueryDetectionServiceConnectionFactory; 138 private int mDetectorType; 139 /** 140 * Time after which each HotwordDetectionService process is stopped and replaced by a new one. 141 * 0 indicates no restarts. 142 */ 143 private final int mReStartPeriodSeconds; 144 145 final Object mLock; 146 final int mVoiceInteractionServiceUid; 147 final ComponentName mHotwordDetectionComponentName; 148 final ComponentName mVisualQueryDetectionComponentName; 149 final int mUser; 150 final Context mContext; 151 volatile HotwordDetectionServiceIdentity mIdentity; 152 //TODO: Consider rename this to SandboxedDetectionIdentity 153 private Instant mLastRestartInstant; 154 155 private ScheduledFuture<?> mDebugHotwordLoggingTimeoutFuture = null; 156 157 /** Identity used for attributing app ops when delivering data to the Interactor. */ 158 @GuardedBy("mLock") 159 @Nullable 160 private final Identity mVoiceInteractorIdentity; 161 private int mRestartCount = 0; 162 @NonNull private ServiceConnection mRemoteHotwordDetectionService; 163 @NonNull private ServiceConnection mRemoteVisualQueryDetectionService; 164 @GuardedBy("mLock") 165 @Nullable private IBinder mAudioFlinger; 166 167 @Nullable private IHotwordRecognitionStatusCallback mHotwordRecognitionCallback; 168 @GuardedBy("mLock") 169 private boolean mDebugHotwordLogging = false; 170 171 private DetectorRemoteExceptionListener mRemoteExceptionListener; 172 173 /** 174 * For multiple detectors feature, we only support one AlwaysOnHotwordDetector and one 175 * SoftwareHotwordDetector at the same time. We use SparseArray with detector type as the key 176 * to record the detectors. 177 */ 178 @GuardedBy("mLock") 179 private final SparseArray<DetectorSession> mDetectorSessions = 180 new SparseArray<>(); 181 HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid, Identity voiceInteractorIdentity, ComponentName hotwordDetectionServiceName, ComponentName visualQueryDetectionServiceName, int userId, boolean bindInstantServiceAllowed, int detectorType, DetectorRemoteExceptionListener listener)182 HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid, 183 Identity voiceInteractorIdentity, ComponentName hotwordDetectionServiceName, 184 ComponentName visualQueryDetectionServiceName, int userId, 185 boolean bindInstantServiceAllowed, int detectorType, 186 DetectorRemoteExceptionListener listener) { 187 mLock = lock; 188 mContext = context; 189 mVoiceInteractionServiceUid = voiceInteractionServiceUid; 190 mVoiceInteractorIdentity = voiceInteractorIdentity; 191 mHotwordDetectionComponentName = hotwordDetectionServiceName; 192 mVisualQueryDetectionComponentName = visualQueryDetectionServiceName; 193 mUser = userId; 194 mDetectorType = detectorType; 195 mRemoteExceptionListener = listener; 196 mReStartPeriodSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_VOICE_INTERACTION, 197 KEY_RESTART_PERIOD_IN_SECONDS, 0); 198 199 final Intent hotwordDetectionServiceIntent = 200 new Intent(HotwordDetectionService.SERVICE_INTERFACE); 201 hotwordDetectionServiceIntent.setComponent(mHotwordDetectionComponentName); 202 203 final Intent visualQueryDetectionServiceIntent = 204 new Intent(VisualQueryDetectionService.SERVICE_INTERFACE); 205 visualQueryDetectionServiceIntent.setComponent(mVisualQueryDetectionComponentName); 206 207 initAudioFlinger(); 208 209 mHotwordDetectionServiceConnectionFactory = 210 new ServiceConnectionFactory(hotwordDetectionServiceIntent, 211 bindInstantServiceAllowed, DETECTION_SERVICE_TYPE_HOTWORD); 212 213 mVisualQueryDetectionServiceConnectionFactory = 214 new ServiceConnectionFactory(visualQueryDetectionServiceIntent, 215 bindInstantServiceAllowed, DETECTION_SERVICE_TYPE_VISUAL_QUERY); 216 217 218 mLastRestartInstant = Instant.now(); 219 220 if (mReStartPeriodSeconds <= 0) { 221 mCancellationTaskFuture = null; 222 } else { 223 mScheduledExecutorService.setRemoveOnCancelPolicy(true); 224 // TODO: we need to be smarter here, e.g. schedule it a bit more often, 225 // but wait until the current session is closed. 226 mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> { 227 Slog.v(TAG, "Time to restart the process, TTL has passed"); 228 synchronized (mLock) { 229 restartProcessLocked(); 230 if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { 231 HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType, 232 HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE, 233 mVoiceInteractionServiceUid); 234 } 235 } 236 }, mReStartPeriodSeconds, mReStartPeriodSeconds, TimeUnit.SECONDS); 237 } 238 } 239 initAudioFlinger()240 private void initAudioFlinger() { 241 if (DEBUG) { 242 Slog.d(TAG, "initAudioFlinger"); 243 } 244 final IBinder audioFlinger = ServiceManager.waitForService("media.audio_flinger"); 245 if (audioFlinger == null) { 246 setAudioFlinger(null); 247 throw new IllegalStateException("Service media.audio_flinger wasn't found."); 248 } 249 if (DEBUG) { 250 Slog.d(TAG, "Obtained audio_flinger binder."); 251 } 252 try { 253 audioFlinger.linkToDeath(mAudioServerDeathRecipient, /* flags= */ 0); 254 } catch (RemoteException e) { 255 Slog.w(TAG, "Audio server died before we registered a DeathRecipient; " 256 + "retrying init.", e); 257 initAudioFlinger(); 258 return; 259 } 260 261 setAudioFlinger(audioFlinger); 262 } 263 setAudioFlinger(@ullable IBinder audioFlinger)264 private void setAudioFlinger(@Nullable IBinder audioFlinger) { 265 synchronized (mLock) { 266 mAudioFlinger = audioFlinger; 267 } 268 } 269 audioServerDied()270 private void audioServerDied() { 271 Slog.w(TAG, "Audio server died; restarting the HotwordDetectionService."); 272 // TODO: Check if this needs to be scheduled on a different thread. 273 initAudioFlinger(); 274 synchronized (mLock) { 275 // We restart the process instead of simply sending over the new binder, to avoid race 276 // conditions with audio reading in the service. 277 restartProcessLocked(); 278 if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { 279 HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType, 280 HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED, 281 mVoiceInteractionServiceUid); 282 } 283 } 284 } 285 286 @SuppressWarnings("GuardedBy") cancelLocked()287 void cancelLocked() { 288 Slog.v(TAG, "cancelLocked"); 289 clearDebugHotwordLoggingTimeoutLocked(); 290 mRemoteExceptionListener = null; 291 runForEachDetectorSessionLocked((session) -> { 292 session.destroyLocked(); 293 }); 294 mDetectorSessions.clear(); 295 mDebugHotwordLogging = false; 296 unbindVisualQueryDetectionService(); 297 unbindHotwordDetectionService(); 298 if (mCancellationTaskFuture != null) { 299 mCancellationTaskFuture.cancel(/* mayInterruptIfRunning= */ true); 300 } 301 if (mAudioFlinger != null) { 302 mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0); 303 } 304 } 305 306 @SuppressWarnings("GuardedBy") unbindVisualQueryDetectionService()307 private void unbindVisualQueryDetectionService() { 308 if (mRemoteVisualQueryDetectionService != null) { 309 mRemoteVisualQueryDetectionService.unbind(); 310 mRemoteVisualQueryDetectionService = null; 311 } 312 resetDetectionProcessIdentityIfEmptyLocked(); 313 } 314 315 @SuppressWarnings("GuardedBy") unbindHotwordDetectionService()316 private void unbindHotwordDetectionService() { 317 if (mRemoteHotwordDetectionService != null) { 318 mRemoteHotwordDetectionService.unbind(); 319 mRemoteHotwordDetectionService = null; 320 } 321 resetDetectionProcessIdentityIfEmptyLocked(); 322 } 323 324 // TODO(b/266669849): Clean up SuppressWarnings for calling methods. 325 @GuardedBy("mLock") resetDetectionProcessIdentityIfEmptyLocked()326 private void resetDetectionProcessIdentityIfEmptyLocked() { 327 if (mRemoteHotwordDetectionService == null && mRemoteVisualQueryDetectionService == null) { 328 LocalServices.getService(PermissionManagerServiceInternal.class) 329 .setHotwordDetectionServiceProvider(null); 330 if (mIdentity != null) { 331 removeServiceUidForAudioPolicy(mIdentity.getIsolatedUid()); 332 } 333 mIdentity = null; 334 } 335 } 336 337 @SuppressWarnings("GuardedBy") updateStateLocked(PersistableBundle options, SharedMemory sharedMemory, @NonNull IBinder token)338 void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory, 339 @NonNull IBinder token) { 340 final DetectorSession session = getDetectorSessionByTokenLocked(token); 341 if (session == null) { 342 Slog.v(TAG, "Not found the detector by token"); 343 return; 344 } 345 session.updateStateLocked(options, sharedMemory, mLastRestartInstant); 346 } 347 348 /** 349 * This method is only used by SoftwareHotwordDetector. 350 */ startListeningFromMicLocked( AudioFormat audioFormat, IMicrophoneHotwordDetectionVoiceInteractionCallback callback)351 void startListeningFromMicLocked( 352 AudioFormat audioFormat, 353 IMicrophoneHotwordDetectionVoiceInteractionCallback callback) { 354 if (DEBUG) { 355 Slog.d(TAG, "startListeningFromMicLocked"); 356 } 357 // We only support one Dsp trusted hotword detector and one software hotword detector at 358 // the same time, so we can reuse original single software trusted hotword mechanism. 359 final SoftwareTrustedHotwordDetectorSession session = 360 getSoftwareTrustedHotwordDetectorSessionLocked(); 361 if (session == null) { 362 return; 363 } 364 session.startListeningFromMicLocked(audioFormat, callback); 365 } 366 setVisualQueryDetectionAttentionListenerLocked( @ullable IVisualQueryDetectionAttentionListener listener)367 public void setVisualQueryDetectionAttentionListenerLocked( 368 @Nullable IVisualQueryDetectionAttentionListener listener) { 369 final VisualQueryDetectorSession session = getVisualQueryDetectorSessionLocked(); 370 if (session == null) { 371 return; 372 } 373 session.setVisualQueryDetectionAttentionListenerLocked(listener); 374 } 375 376 /** 377 * This method is only used by VisualQueryDetector. 378 */ startPerceivingLocked(IVisualQueryDetectionVoiceInteractionCallback callback)379 boolean startPerceivingLocked(IVisualQueryDetectionVoiceInteractionCallback callback) { 380 if (DEBUG) { 381 Slog.d(TAG, "startPerceivingLocked"); 382 } 383 final VisualQueryDetectorSession session = getVisualQueryDetectorSessionLocked(); 384 if (session == null) { 385 return false; 386 } 387 return session.startPerceivingLocked(callback); 388 } 389 390 /** 391 * This method is only used by VisaulQueryDetector. 392 */ stopPerceivingLocked()393 boolean stopPerceivingLocked() { 394 if (DEBUG) { 395 Slog.d(TAG, "stopPerceivingLocked"); 396 } 397 final VisualQueryDetectorSession session = getVisualQueryDetectorSessionLocked(); 398 if (session == null) { 399 return false; 400 } 401 return session.stopPerceivingLocked(); 402 } 403 startListeningFromExternalSourceLocked( ParcelFileDescriptor audioStream, AudioFormat audioFormat, @Nullable PersistableBundle options, @NonNull IBinder token, IMicrophoneHotwordDetectionVoiceInteractionCallback callback)404 public void startListeningFromExternalSourceLocked( 405 ParcelFileDescriptor audioStream, 406 AudioFormat audioFormat, 407 @Nullable PersistableBundle options, 408 @NonNull IBinder token, 409 IMicrophoneHotwordDetectionVoiceInteractionCallback callback) { 410 if (DEBUG) { 411 Slog.d(TAG, "startListeningFromExternalSourceLocked"); 412 } 413 final DetectorSession session = getDetectorSessionByTokenLocked(token); 414 if (session == null) { 415 Slog.v(TAG, "Not found the detector by token"); 416 return; 417 } 418 session.startListeningFromExternalSourceLocked(audioStream, audioFormat, options, callback); 419 } 420 421 /** 422 * This method is only used by SoftwareHotwordDetector. 423 */ stopListeningFromMicLocked()424 void stopListeningFromMicLocked() { 425 if (DEBUG) { 426 Slog.d(TAG, "stopListeningFromMicLocked"); 427 } 428 final SoftwareTrustedHotwordDetectorSession session = 429 getSoftwareTrustedHotwordDetectorSessionLocked(); 430 if (session == null) { 431 return; 432 } 433 session.stopListeningFromMicLocked(); 434 } 435 triggerHardwareRecognitionEventForTestLocked( SoundTrigger.KeyphraseRecognitionEvent event, IHotwordRecognitionStatusCallback callback)436 void triggerHardwareRecognitionEventForTestLocked( 437 SoundTrigger.KeyphraseRecognitionEvent event, 438 IHotwordRecognitionStatusCallback callback) { 439 if (DEBUG) { 440 Slog.d(TAG, "triggerHardwareRecognitionEventForTestLocked"); 441 } 442 detectFromDspSource(event, callback); 443 } 444 detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent, IHotwordRecognitionStatusCallback externalCallback)445 private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent, 446 IHotwordRecognitionStatusCallback externalCallback) { 447 if (DEBUG) { 448 Slog.d(TAG, "detectFromDspSource"); 449 } 450 // We only support one Dsp trusted hotword detector and one software hotword detector at 451 // the same time, so we can reuse original single Dsp trusted hotword mechanism. 452 synchronized (mLock) { 453 final DspTrustedHotwordDetectorSession session = 454 getDspTrustedHotwordDetectorSessionLocked(); 455 if (session == null || !session.isSameCallback(externalCallback)) { 456 Slog.v(TAG, "Not found the Dsp detector by callback"); 457 return; 458 } 459 session.detectFromDspSourceLocked(recognitionEvent, externalCallback); 460 } 461 } 462 forceRestart()463 void forceRestart() { 464 Slog.v(TAG, "Requested to restart the service internally. Performing the restart"); 465 synchronized (mLock) { 466 restartProcessLocked(); 467 } 468 } 469 470 @SuppressWarnings("GuardedBy") setDebugHotwordLoggingLocked(boolean logging)471 void setDebugHotwordLoggingLocked(boolean logging) { 472 Slog.v(TAG, "setDebugHotwordLoggingLocked: " + logging); 473 clearDebugHotwordLoggingTimeoutLocked(); 474 mDebugHotwordLogging = logging; 475 runForEachDetectorSessionLocked((session) -> { 476 session.setDebugHotwordLoggingLocked(logging); 477 }); 478 479 if (logging) { 480 // Reset mDebugHotwordLogging to false after one hour 481 mDebugHotwordLoggingTimeoutFuture = mScheduledExecutorService.schedule(() -> { 482 Slog.v(TAG, "Timeout to reset mDebugHotwordLogging to false"); 483 synchronized (mLock) { 484 mDebugHotwordLogging = false; 485 runForEachDetectorSessionLocked((session) -> { 486 session.setDebugHotwordLoggingLocked(false); 487 }); 488 } 489 }, RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 490 } 491 } 492 setDetectorType(int detectorType)493 void setDetectorType(int detectorType) { 494 mDetectorType = detectorType; 495 } 496 clearDebugHotwordLoggingTimeoutLocked()497 private void clearDebugHotwordLoggingTimeoutLocked() { 498 if (mDebugHotwordLoggingTimeoutFuture != null) { 499 mDebugHotwordLoggingTimeoutFuture.cancel(/* mayInterruptIfRunning= */ true); 500 mDebugHotwordLoggingTimeoutFuture = null; 501 } 502 } 503 504 @SuppressWarnings("GuardedBy") restartProcessLocked()505 private void restartProcessLocked() { 506 // TODO(b/244598068): Check HotwordAudioStreamManager first 507 Slog.v(TAG, "Restarting hotword detection process"); 508 509 ServiceConnection oldHotwordConnection = mRemoteHotwordDetectionService; 510 ServiceConnection oldVisualQueryDetectionConnection = mRemoteVisualQueryDetectionService; 511 HotwordDetectionServiceIdentity previousIdentity = mIdentity; 512 513 mLastRestartInstant = Instant.now(); 514 // Recreate connection to reset the cache. 515 mRestartCount++; 516 517 if (oldHotwordConnection != null) { 518 mRemoteHotwordDetectionService = 519 mHotwordDetectionServiceConnectionFactory.createLocked(); 520 } 521 522 if (oldVisualQueryDetectionConnection != null) { 523 mRemoteVisualQueryDetectionService = 524 mVisualQueryDetectionServiceConnectionFactory.createLocked(); 525 } 526 527 Slog.v(TAG, "Started the new process, dispatching processRestarted to detector"); 528 runForEachDetectorSessionLocked((session) -> { 529 HotwordDetectionConnection.ServiceConnection newRemoteService = 530 (session instanceof VisualQueryDetectorSession) 531 ? mRemoteVisualQueryDetectionService : mRemoteHotwordDetectionService; 532 session.updateRemoteSandboxedDetectionServiceLocked(newRemoteService); 533 session.informRestartProcessLocked(); 534 }); 535 if (DEBUG) { 536 Slog.i(TAG, "processRestarted is dispatched done, unbinding from the old process"); 537 } 538 539 if (oldHotwordConnection != null) { 540 oldHotwordConnection.ignoreConnectionStatusEvents(); 541 oldHotwordConnection.unbind(); 542 } 543 544 if (oldVisualQueryDetectionConnection != null) { 545 oldVisualQueryDetectionConnection.ignoreConnectionStatusEvents(); 546 oldVisualQueryDetectionConnection.unbind(); 547 } 548 549 // TODO(b/266670431): Handles identity resetting for the new process to make sure the 550 // correct identity is provided. 551 552 if (previousIdentity != null) { 553 removeServiceUidForAudioPolicy(previousIdentity.getIsolatedUid()); 554 } 555 } 556 557 static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub { 558 private final HotwordDetectionConnection mHotwordDetectionConnection; 559 private final IHotwordRecognitionStatusCallback mExternalCallback; 560 private final Identity mVoiceInteractorIdentity; 561 private final Context mContext; 562 SoundTriggerCallback(Context context, IHotwordRecognitionStatusCallback callback, HotwordDetectionConnection connection, Identity voiceInteractorIdentity)563 SoundTriggerCallback(Context context, IHotwordRecognitionStatusCallback callback, 564 HotwordDetectionConnection connection, Identity voiceInteractorIdentity) { 565 mContext = context; 566 mHotwordDetectionConnection = connection; 567 mExternalCallback = callback; 568 mVoiceInteractorIdentity = voiceInteractorIdentity; 569 } 570 571 @Override onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent)572 public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) 573 throws RemoteException { 574 if (DEBUG) { 575 Slog.d(TAG, "onKeyphraseDetected recognitionEvent : " + recognitionEvent); 576 } 577 final boolean useHotwordDetectionService = mHotwordDetectionConnection != null; 578 if (useHotwordDetectionService) { 579 HotwordMetricsLogger.writeKeyphraseTriggerEvent( 580 HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP, 581 HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER, 582 mVoiceInteractorIdentity.uid); 583 mHotwordDetectionConnection.detectFromDspSource( 584 recognitionEvent, mExternalCallback); 585 } else { 586 // We have to attribute ops here, since we configure all st clients as trusted to 587 // enable a partial exemption. 588 // TODO (b/292012931) remove once trusted uniformly required. 589 int result = mContext.getSystemService(AppOpsManager.class) 590 .noteOpNoThrow(AppOpsManager.OP_RECORD_AUDIO_HOTWORD, 591 mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName, 592 mVoiceInteractorIdentity.attributionTag, 593 "Non-HDS keyphrase recognition to VoiceInteractionService"); 594 595 if (result != AppOpsManager.MODE_ALLOWED) { 596 Slog.w(TAG, "onKeyphraseDetected suppressed, permission check returned: " 597 + result); 598 mExternalCallback.onRecognitionPaused(); 599 } else { 600 HotwordMetricsLogger.writeKeyphraseTriggerEvent( 601 HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR, 602 HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER, 603 mVoiceInteractorIdentity.uid); 604 mExternalCallback.onKeyphraseDetected(recognitionEvent, null); 605 } 606 } 607 } 608 609 @Override onGenericSoundTriggerDetected( SoundTrigger.GenericRecognitionEvent recognitionEvent)610 public void onGenericSoundTriggerDetected( 611 SoundTrigger.GenericRecognitionEvent recognitionEvent) 612 throws RemoteException { 613 mExternalCallback.onGenericSoundTriggerDetected(recognitionEvent); 614 } 615 616 @Override onPreempted()617 public void onPreempted() throws RemoteException { 618 mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( 619 SoundTriggerFailure.ERROR_CODE_UNEXPECTED_PREEMPTION, 620 "Unexpected startRecognition on already started ST session")); 621 } 622 623 @Override onModuleDied()624 public void onModuleDied() throws RemoteException { 625 mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( 626 SoundTriggerFailure.ERROR_CODE_MODULE_DIED, 627 "STHAL died")); 628 } 629 630 @Override onResumeFailed(int status)631 public void onResumeFailed(int status) throws RemoteException { 632 mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( 633 SoundTriggerFailure.ERROR_CODE_RECOGNITION_RESUME_FAILED, 634 "STService recognition resume failed with: " + status)); 635 } 636 637 @Override onPauseFailed(int status)638 public void onPauseFailed(int status) throws RemoteException { 639 mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure( 640 SoundTriggerFailure.ERROR_CODE_RECOGNITION_RESUME_FAILED, 641 "STService recognition pause failed with: " + status)); 642 } 643 644 @Override onRecognitionPaused()645 public void onRecognitionPaused() throws RemoteException { 646 mExternalCallback.onRecognitionPaused(); 647 } 648 649 @Override onRecognitionResumed()650 public void onRecognitionResumed() throws RemoteException { 651 mExternalCallback.onRecognitionResumed(); 652 } 653 } 654 dump(String prefix, PrintWriter pw)655 public void dump(String prefix, PrintWriter pw) { 656 synchronized (mLock) { 657 pw.print(prefix); pw.print("mReStartPeriodSeconds="); pw.println(mReStartPeriodSeconds); 658 pw.print(prefix); pw.print("bound for HotwordDetectionService="); 659 pw.println(mRemoteHotwordDetectionService != null 660 && mRemoteHotwordDetectionService.isBound()); 661 pw.print(prefix); pw.print("bound for VisualQueryDetectionService="); 662 pw.println(mRemoteVisualQueryDetectionService != null 663 && mRemoteHotwordDetectionService != null 664 && mRemoteHotwordDetectionService.isBound()); 665 pw.print(prefix); pw.print("mRestartCount="); 666 pw.println(mRestartCount); 667 pw.print(prefix); pw.print("mLastRestartInstant="); pw.println(mLastRestartInstant); 668 pw.print(prefix); pw.println("DetectorSession(s):"); 669 pw.print(prefix); pw.print("Num of DetectorSession(s)="); 670 pw.println(mDetectorSessions.size()); 671 runForEachDetectorSessionLocked((session) -> { 672 session.dumpLocked(prefix, pw); 673 }); 674 } 675 } 676 677 private class ServiceConnectionFactory { 678 private final Intent mIntent; 679 private final int mBindingFlags; 680 private final int mDetectionServiceType; 681 ServiceConnectionFactory(@onNull Intent intent, boolean bindInstantServiceAllowed, int detectionServiceType)682 ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed, 683 int detectionServiceType) { 684 mIntent = intent; 685 mDetectionServiceType = detectionServiceType; 686 int flags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0; 687 if (SYSPROP_VISUAL_QUERY_SERVICE_ENABLED 688 && mVisualQueryDetectionComponentName != null 689 && mHotwordDetectionComponentName != null) { 690 flags |= Context.BIND_SHARED_ISOLATED_PROCESS; 691 } 692 mBindingFlags = flags; 693 } 694 createLocked()695 ServiceConnection createLocked() { 696 ServiceConnection connection = 697 new ServiceConnection(mContext, mIntent, mBindingFlags, mUser, 698 ISandboxedDetectionService.Stub::asInterface, 699 mRestartCount % MAX_ISOLATED_PROCESS_NUMBER, mDetectionServiceType); 700 connection.connect(); 701 702 updateAudioFlinger(connection, mAudioFlinger); 703 updateContentCaptureManager(connection); 704 updateSpeechService(connection); 705 updateServiceIdentity(connection); 706 updateStorageService(connection); 707 return connection; 708 } 709 } 710 711 class ServiceConnection extends ServiceConnector.Impl<ISandboxedDetectionService> { 712 private final Object mLock = new Object(); 713 714 private final Intent mIntent; 715 private final int mBindingFlags; 716 private final int mInstanceNumber; 717 718 private boolean mRespectServiceConnectionStatusChanged = true; 719 private boolean mIsBound = false; 720 private boolean mIsLoggedFirstConnect = false; 721 private final int mDetectionServiceType; 722 ServiceConnection(@onNull Context context, @NonNull Intent serviceIntent, int bindingFlags, int userId, @Nullable Function<IBinder, ISandboxedDetectionService> binderAsInterface, int instanceNumber, int detectionServiceType)723 ServiceConnection(@NonNull Context context, 724 @NonNull Intent serviceIntent, int bindingFlags, int userId, 725 @Nullable Function<IBinder, ISandboxedDetectionService> binderAsInterface, 726 int instanceNumber, int detectionServiceType) { 727 super(context, serviceIntent, bindingFlags, userId, binderAsInterface); 728 this.mIntent = serviceIntent; 729 this.mBindingFlags = bindingFlags; 730 this.mInstanceNumber = instanceNumber; 731 this.mDetectionServiceType = detectionServiceType; 732 } 733 734 @Override // from ServiceConnector.Impl onServiceConnectionStatusChanged(ISandboxedDetectionService service, boolean connected)735 protected void onServiceConnectionStatusChanged(ISandboxedDetectionService service, 736 boolean connected) { 737 if (DEBUG) { 738 Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected); 739 } 740 synchronized (mLock) { 741 if (!mRespectServiceConnectionStatusChanged) { 742 Slog.v(TAG, "Ignored onServiceConnectionStatusChanged event"); 743 return; 744 } 745 mIsBound = connected; 746 747 if (!connected) { 748 if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { 749 HotwordMetricsLogger.writeDetectorEvent(mDetectorType, 750 HOTWORD_DETECTOR_EVENTS__EVENT__ON_DISCONNECTED, 751 mVoiceInteractionServiceUid); 752 } 753 } else if (!mIsLoggedFirstConnect) { 754 mIsLoggedFirstConnect = true; 755 if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { 756 HotwordMetricsLogger.writeDetectorEvent(mDetectorType, 757 HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED, 758 mVoiceInteractionServiceUid); 759 } 760 } 761 } 762 } 763 764 @Override getAutoDisconnectTimeoutMs()765 protected long getAutoDisconnectTimeoutMs() { 766 return -1; 767 } 768 769 @Override binderDied()770 public void binderDied() { 771 super.binderDied(); 772 Slog.w(TAG, "binderDied mDetectionServiceType = " + mDetectionServiceType); 773 synchronized (mLock) { 774 if (!mRespectServiceConnectionStatusChanged) { 775 Slog.v(TAG, "Ignored #binderDied event"); 776 return; 777 } 778 } 779 //TODO(b265535257): report error to either service only. 780 synchronized (HotwordDetectionConnection.this.mLock) { 781 runForEachDetectorSessionLocked(this::reportBinderDiedLocked); 782 } 783 // Can improve to log exit reason if needed 784 if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { 785 HotwordMetricsLogger.writeKeyphraseTriggerEvent( 786 mDetectorType, 787 HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH, 788 mVoiceInteractionServiceUid); 789 } 790 } 791 792 @Override bindService( @onNull android.content.ServiceConnection serviceConnection)793 protected boolean bindService( 794 @NonNull android.content.ServiceConnection serviceConnection) { 795 try { 796 if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { 797 HotwordMetricsLogger.writeDetectorEvent(mDetectorType, 798 HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE, 799 mVoiceInteractionServiceUid); 800 } 801 boolean bindResult = mContext.bindIsolatedService( 802 mIntent, 803 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | mBindingFlags, 804 "hotword_detector_" + mInstanceNumber, 805 mExecutor, 806 serviceConnection); 807 if (!bindResult) { 808 Slog.w(TAG, 809 "bindService failure mDetectionServiceType = " + mDetectionServiceType); 810 synchronized (HotwordDetectionConnection.this.mLock) { 811 runForEachDetectorSessionLocked(this::reportBindServiceFailureLocked); 812 } 813 if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { 814 HotwordMetricsLogger.writeDetectorEvent(mDetectorType, 815 HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL, 816 mVoiceInteractionServiceUid); 817 } 818 } 819 return bindResult; 820 } catch (IllegalArgumentException e) { 821 if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) { 822 HotwordMetricsLogger.writeDetectorEvent(mDetectorType, 823 HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL, 824 mVoiceInteractionServiceUid); 825 } 826 Slog.wtf(TAG, "Can't bind to the hotword detection service!", e); 827 return false; 828 } 829 } 830 isBound()831 boolean isBound() { 832 synchronized (mLock) { 833 return mIsBound; 834 } 835 } 836 ignoreConnectionStatusEvents()837 void ignoreConnectionStatusEvents() { 838 synchronized (mLock) { 839 mRespectServiceConnectionStatusChanged = false; 840 } 841 } 842 reportBinderDiedLocked(DetectorSession detectorSession)843 private void reportBinderDiedLocked(DetectorSession detectorSession) { 844 if (mDetectionServiceType == DETECTION_SERVICE_TYPE_HOTWORD && ( 845 detectorSession instanceof DspTrustedHotwordDetectorSession 846 || detectorSession instanceof SoftwareTrustedHotwordDetectorSession)) { 847 detectorSession.reportErrorLocked(new HotwordDetectionServiceFailure( 848 HotwordDetectionServiceFailure.ERROR_CODE_BINDING_DIED, 849 "Detection service is dead.")); 850 } else if (mDetectionServiceType == DETECTION_SERVICE_TYPE_VISUAL_QUERY 851 && detectorSession instanceof VisualQueryDetectorSession) { 852 detectorSession.reportErrorLocked(new VisualQueryDetectionServiceFailure( 853 VisualQueryDetectionServiceFailure.ERROR_CODE_BINDING_DIED, 854 "Detection service is dead.")); 855 } else { 856 detectorSession.reportErrorLocked( 857 "Detection service is dead with unknown detection service type."); 858 } 859 } 860 reportBindServiceFailureLocked(DetectorSession detectorSession)861 private void reportBindServiceFailureLocked(DetectorSession detectorSession) { 862 if (mDetectionServiceType == DETECTION_SERVICE_TYPE_HOTWORD && ( 863 detectorSession instanceof DspTrustedHotwordDetectorSession 864 || detectorSession instanceof SoftwareTrustedHotwordDetectorSession)) { 865 detectorSession.reportErrorLocked(new HotwordDetectionServiceFailure( 866 HotwordDetectionServiceFailure.ERROR_CODE_BIND_FAILURE, 867 "Bind detection service failure.")); 868 } else if (mDetectionServiceType == DETECTION_SERVICE_TYPE_VISUAL_QUERY 869 && detectorSession instanceof VisualQueryDetectorSession) { 870 detectorSession.reportErrorLocked(new VisualQueryDetectionServiceFailure( 871 VisualQueryDetectionServiceFailure.ERROR_CODE_BIND_FAILURE, 872 "Bind detection service failure.")); 873 } else { 874 detectorSession.reportErrorLocked( 875 "Bind detection service failure with unknown detection service type."); 876 } 877 } 878 } 879 880 @SuppressWarnings("GuardedBy") createDetectorLocked( @ullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @NonNull IBinder token, @NonNull IHotwordRecognitionStatusCallback callback, int detectorType)881 void createDetectorLocked( 882 @Nullable PersistableBundle options, 883 @Nullable SharedMemory sharedMemory, 884 @NonNull IBinder token, 885 @NonNull IHotwordRecognitionStatusCallback callback, 886 int detectorType) { 887 // We only support one Dsp trusted hotword detector and one software hotword detector at 888 // the same time, remove existing one. 889 DetectorSession removeSession = mDetectorSessions.get(detectorType); 890 if (removeSession != null) { 891 removeSession.destroyLocked(); 892 mDetectorSessions.remove(detectorType); 893 } 894 final DetectorSession session; 895 if (detectorType == HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP) { 896 if (mRemoteHotwordDetectionService == null) { 897 mRemoteHotwordDetectionService = 898 mHotwordDetectionServiceConnectionFactory.createLocked(); 899 } 900 session = new DspTrustedHotwordDetectorSession(mRemoteHotwordDetectionService, 901 mLock, mContext, token, callback, mVoiceInteractionServiceUid, 902 mVoiceInteractorIdentity, mScheduledExecutorService, mDebugHotwordLogging, 903 mRemoteExceptionListener); 904 } else if (detectorType == HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { 905 if (mRemoteVisualQueryDetectionService == null) { 906 mRemoteVisualQueryDetectionService = 907 mVisualQueryDetectionServiceConnectionFactory.createLocked(); 908 } 909 session = new VisualQueryDetectorSession( 910 mRemoteVisualQueryDetectionService, mLock, mContext, token, callback, 911 mVoiceInteractionServiceUid, mVoiceInteractorIdentity, 912 mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener); 913 } else { 914 if (mRemoteHotwordDetectionService == null) { 915 mRemoteHotwordDetectionService = 916 mHotwordDetectionServiceConnectionFactory.createLocked(); 917 } 918 session = new SoftwareTrustedHotwordDetectorSession( 919 mRemoteHotwordDetectionService, mLock, mContext, token, callback, 920 mVoiceInteractionServiceUid, mVoiceInteractorIdentity, 921 mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener); 922 } 923 mHotwordRecognitionCallback = callback; 924 mDetectorSessions.put(detectorType, session); 925 session.initialize(options, sharedMemory); 926 } 927 928 @SuppressWarnings("GuardedBy") destroyDetectorLocked(@onNull IBinder token)929 void destroyDetectorLocked(@NonNull IBinder token) { 930 final DetectorSession session = getDetectorSessionByTokenLocked(token); 931 if (session == null) { 932 return; 933 } 934 session.destroyLocked(); 935 final int index = mDetectorSessions.indexOfValue(session); 936 if (index < 0 || index > mDetectorSessions.size() - 1) { 937 return; 938 } 939 mDetectorSessions.removeAt(index); 940 if (session instanceof VisualQueryDetectorSession) { 941 unbindVisualQueryDetectionService(); 942 } 943 // Handle case where all hotword detector sessions are destroyed with only the visual 944 // detector session left 945 boolean allHotwordDetectionServiceSessionsRemoved = mDetectorSessions.size() == 0 946 || (mDetectorSessions.size() == 1 && mDetectorSessions.get(0) 947 instanceof VisualQueryDetectorSession); 948 if (allHotwordDetectionServiceSessionsRemoved) { 949 unbindHotwordDetectionService(); 950 } 951 } 952 953 @SuppressWarnings("GuardedBy") getDetectorSessionByTokenLocked(IBinder token)954 private DetectorSession getDetectorSessionByTokenLocked(IBinder token) { 955 if (token == null) { 956 return null; 957 } 958 for (int i = 0; i < mDetectorSessions.size(); i++) { 959 final DetectorSession session = mDetectorSessions.valueAt(i); 960 if (!session.isDestroyed() && session.isSameToken(token)) { 961 return session; 962 } 963 } 964 return null; 965 } 966 967 @SuppressWarnings("GuardedBy") getDspTrustedHotwordDetectorSessionLocked()968 private DspTrustedHotwordDetectorSession getDspTrustedHotwordDetectorSessionLocked() { 969 final DetectorSession session = mDetectorSessions.get( 970 HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP); 971 if (session == null || session.isDestroyed()) { 972 Slog.v(TAG, "Not found the Dsp detector"); 973 return null; 974 } 975 return (DspTrustedHotwordDetectorSession) session; 976 } 977 978 @SuppressWarnings("GuardedBy") getSoftwareTrustedHotwordDetectorSessionLocked()979 private SoftwareTrustedHotwordDetectorSession getSoftwareTrustedHotwordDetectorSessionLocked() { 980 final DetectorSession session = mDetectorSessions.get( 981 HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE); 982 if (session == null || session.isDestroyed()) { 983 Slog.v(TAG, "Not found the software detector"); 984 return null; 985 } 986 return (SoftwareTrustedHotwordDetectorSession) session; 987 } 988 989 @SuppressWarnings("GuardedBy") getVisualQueryDetectorSessionLocked()990 private VisualQueryDetectorSession getVisualQueryDetectorSessionLocked() { 991 final DetectorSession session = mDetectorSessions.get( 992 HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR); 993 if (session == null || session.isDestroyed()) { 994 Slog.v(TAG, "Not found the visual query detector"); 995 return null; 996 } 997 return (VisualQueryDetectorSession) session; 998 } runForEachDetectorSessionLocked( @onNull Consumer<DetectorSession> action)999 private void runForEachDetectorSessionLocked( 1000 @NonNull Consumer<DetectorSession> action) { 1001 for (int i = 0; i < mDetectorSessions.size(); i++) { 1002 DetectorSession session = mDetectorSessions.valueAt(i); 1003 action.accept(session); 1004 } 1005 } 1006 updateAudioFlinger(ServiceConnection connection, IBinder audioFlinger)1007 private static void updateAudioFlinger(ServiceConnection connection, IBinder audioFlinger) { 1008 // TODO: Consider using a proxy that limits the exposed API surface. 1009 connection.run(service -> service.updateAudioFlinger(audioFlinger)); 1010 } 1011 updateContentCaptureManager(ServiceConnection connection)1012 private static void updateContentCaptureManager(ServiceConnection connection) { 1013 IBinder b = ServiceManager 1014 .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); 1015 IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b); 1016 connection.run( 1017 service -> service.updateContentCaptureManager(binderService, 1018 new ContentCaptureOptions(null))); 1019 } 1020 updateSpeechService(ServiceConnection connection)1021 private static void updateSpeechService(ServiceConnection connection) { 1022 IBinder b = ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE); 1023 IRecognitionServiceManager binderService = IRecognitionServiceManager.Stub.asInterface(b); 1024 connection.run(service -> { 1025 service.updateRecognitionServiceManager(binderService); 1026 }); 1027 } 1028 updateServiceIdentity(ServiceConnection connection)1029 private void updateServiceIdentity(ServiceConnection connection) { 1030 connection.run(service -> service.ping(new IRemoteCallback.Stub() { 1031 @Override 1032 public void sendResult(Bundle bundle) throws RemoteException { 1033 // TODO: Exit if the service has been unbound already (though there's a very low 1034 // chance this happens). 1035 if (DEBUG) { 1036 Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid()); 1037 } 1038 // TODO: Have the provider point to the current state stored in 1039 // VoiceInteractionManagerServiceImpl. 1040 final int uid = Binder.getCallingUid(); 1041 LocalServices.getService(PermissionManagerServiceInternal.class) 1042 .setHotwordDetectionServiceProvider(() -> uid); 1043 mIdentity = new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid); 1044 addServiceUidForAudioPolicy(uid); 1045 } 1046 })); 1047 } 1048 updateStorageService(ServiceConnection connection)1049 private void updateStorageService(ServiceConnection connection) { 1050 connection.run(service -> { 1051 service.registerRemoteStorageService(new IDetectorSessionStorageService.Stub() { 1052 @Override 1053 public void openFile(String filename, AndroidFuture future) 1054 throws RemoteException { 1055 Slog.v(TAG, "BinderCallback#onFileOpen"); 1056 try { 1057 mHotwordRecognitionCallback.onOpenFile(filename, future); 1058 } catch (RemoteException e) { 1059 e.rethrowFromSystemServer(); 1060 } 1061 } 1062 }); 1063 }); 1064 } 1065 addServiceUidForAudioPolicy(int uid)1066 private void addServiceUidForAudioPolicy(int uid) { 1067 mScheduledExecutorService.execute(() -> { 1068 AudioManagerInternal audioManager = 1069 LocalServices.getService(AudioManagerInternal.class); 1070 if (audioManager != null) { 1071 audioManager.addAssistantServiceUid(uid); 1072 } 1073 }); 1074 } 1075 removeServiceUidForAudioPolicy(int uid)1076 private void removeServiceUidForAudioPolicy(int uid) { 1077 mScheduledExecutorService.execute(() -> { 1078 AudioManagerInternal audioManager = 1079 LocalServices.getService(AudioManagerInternal.class); 1080 if (audioManager != null) { 1081 audioManager.removeAssistantServiceUid(uid); 1082 } 1083 }); 1084 } 1085 } 1086