1 /* 2 * Copyright (C) 2010 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 android.speech; 18 19 import android.Manifest; 20 import android.annotation.CallbackExecutor; 21 import android.annotation.MainThread; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.RequiresPermission; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.os.Binder; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.RemoteException; 35 import android.os.ServiceManager; 36 import android.provider.Settings; 37 import android.text.TextUtils; 38 import android.util.Log; 39 import android.util.Slog; 40 41 import java.util.Objects; 42 import java.util.Queue; 43 import java.util.concurrent.Executor; 44 import java.util.concurrent.LinkedBlockingQueue; 45 46 /** 47 * @hide 48 */ 49 class SpeechRecognizerImpl extends SpeechRecognizer { 50 /** DEBUG value to enable verbose debug prints */ 51 private static final boolean DBG = false; 52 53 /** Log messages identifier */ 54 private static final String TAG = "SpeechRecognizer"; 55 56 /** action codes */ 57 private static final int MSG_START = 1; 58 private static final int MSG_STOP = 2; 59 private static final int MSG_CANCEL = 3; 60 private static final int MSG_CHANGE_LISTENER = 4; 61 private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5; 62 private static final int MSG_CHECK_RECOGNITION_SUPPORT = 6; 63 private static final int MSG_TRIGGER_MODEL_DOWNLOAD = 7; 64 private static final int MSG_DESTROY = 8; 65 66 /** The actual RecognitionService endpoint */ 67 private IRecognitionService mService; 68 69 /** Context with which the manager was created */ 70 private final Context mContext; 71 72 /** Component to direct service intent to */ 73 private final ComponentName mServiceComponent; 74 75 /** Whether to use on-device speech recognizer. */ 76 private final boolean mOnDevice; 77 78 private IRecognitionServiceManager mManagerService; 79 80 /** Handler that will execute the main tasks */ 81 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 82 83 @Override 84 public void handleMessage(Message msg) { 85 switch (msg.what) { 86 case MSG_START -> handleStartListening((Intent) msg.obj); 87 case MSG_STOP -> handleStopMessage(); 88 case MSG_CANCEL -> handleCancelMessage(); 89 case MSG_CHANGE_LISTENER -> handleChangeListener((RecognitionListener) msg.obj); 90 case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT -> 91 handleSetTemporaryComponent((ComponentName) msg.obj); 92 case MSG_CHECK_RECOGNITION_SUPPORT -> { 93 CheckRecognitionSupportArgs args = (CheckRecognitionSupportArgs) msg.obj; 94 handleCheckRecognitionSupport( 95 args.mIntent, args.mCallbackExecutor, args.mCallback); 96 } 97 case MSG_TRIGGER_MODEL_DOWNLOAD -> { 98 ModelDownloadListenerArgs modelDownloadListenerArgs = 99 (ModelDownloadListenerArgs) msg.obj; 100 handleTriggerModelDownload( 101 modelDownloadListenerArgs.mIntent, 102 modelDownloadListenerArgs.mExecutor, 103 modelDownloadListenerArgs.mModelDownloadListener); 104 } 105 case MSG_DESTROY -> handleDestroy(); 106 } 107 } 108 }; 109 110 /** 111 * Temporary queue, saving the messages until the connection will be established, afterwards, 112 * only mHandler will receive the messages 113 */ 114 private final Queue<Message> mPendingTasks = new LinkedBlockingQueue<>(); 115 116 /** The Listener that will receive all the callbacks */ 117 private final InternalRecognitionListener mListener = new InternalRecognitionListener(); 118 119 private final IBinder mClientToken = new Binder(); 120 121 /** 122 * The right way to create a {@code SpeechRecognizer} is by using 123 * {@link #createSpeechRecognizer} static factory method 124 */ SpeechRecognizerImpl( final Context context, final ComponentName serviceComponent)125 /* package */ SpeechRecognizerImpl( 126 final Context context, 127 final ComponentName serviceComponent) { 128 this(context, serviceComponent, false); 129 } 130 131 /** 132 * The right way to create a {@code SpeechRecognizer} is by using 133 * {@link #createOnDeviceSpeechRecognizer} static factory method 134 */ SpeechRecognizerImpl(final Context context, boolean onDevice)135 /* package */ SpeechRecognizerImpl(final Context context, boolean onDevice) { 136 this(context, null, onDevice); 137 } 138 SpeechRecognizerImpl( final Context context, final ComponentName serviceComponent, final boolean onDevice)139 private SpeechRecognizerImpl( 140 final Context context, 141 final ComponentName serviceComponent, 142 final boolean onDevice) { 143 mContext = context; 144 mServiceComponent = serviceComponent; 145 mOnDevice = onDevice; 146 } 147 148 @NonNull 149 @MainThread lenientlyCreateOnDeviceSpeechRecognizer( @onNull final Context context)150 /* package */ static SpeechRecognizerImpl lenientlyCreateOnDeviceSpeechRecognizer( 151 @NonNull final Context context) { 152 if (context == null) { 153 throw new IllegalArgumentException("Context cannot be null"); 154 } 155 checkIsCalledFromMainThread(); 156 return new SpeechRecognizerImpl(context, /* onDevice */ true); 157 } 158 159 @Override 160 @MainThread setRecognitionListener(RecognitionListener listener)161 public void setRecognitionListener(RecognitionListener listener) { 162 checkIsCalledFromMainThread(); 163 if (mListener.mInternalListener == null) { 164 // This shortcut is needed because otherwise, if there's an error connecting, it never 165 // gets delivered. I.e., the onSuccess callback set up in connectToSystemService does 166 // not get called, MSG_CHANGE_LISTENER does not get executed, so the onError in the same 167 // place does not get forwarded anywhere. 168 // Thread-wise, this is safe as both this method and the handler are on the UI thread. 169 handleChangeListener(listener); 170 } else { 171 putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener)); 172 } 173 } 174 175 @Override 176 @MainThread startListening(final Intent recognizerIntent)177 public void startListening(final Intent recognizerIntent) { 178 if (recognizerIntent == null) { 179 throw new IllegalArgumentException("intent must not be null"); 180 } 181 checkIsCalledFromMainThread(); 182 183 if (DBG) { 184 Slog.i(TAG, "#startListening called"); 185 if (mService == null) { 186 Slog.i(TAG, "Connection is not established yet"); 187 } 188 } 189 190 if (mService == null) { 191 // First time connection: first establish a connection, then dispatch #startListening. 192 connectToSystemService(); 193 } 194 putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)); 195 } 196 197 @Override 198 @MainThread stopListening()199 public void stopListening() { 200 checkIsCalledFromMainThread(); 201 202 if (DBG) { 203 Slog.i(TAG, "#stopListening called"); 204 if (mService == null) { 205 Slog.i(TAG, "Connection is not established yet"); 206 } 207 } 208 209 putMessage(Message.obtain(mHandler, MSG_STOP)); 210 } 211 212 @Override 213 @MainThread cancel()214 public void cancel() { 215 checkIsCalledFromMainThread(); 216 putMessage(Message.obtain(mHandler, MSG_CANCEL)); 217 } 218 219 @Override checkRecognitionSupport( @onNull Intent recognizerIntent, @NonNull @CallbackExecutor Executor executor, @NonNull RecognitionSupportCallback supportListener)220 public void checkRecognitionSupport( 221 @NonNull Intent recognizerIntent, 222 @NonNull @CallbackExecutor Executor executor, 223 @NonNull RecognitionSupportCallback supportListener) { 224 Objects.requireNonNull(recognizerIntent, "intent must not be null"); 225 Objects.requireNonNull(supportListener, "listener must not be null"); 226 227 if (DBG) { 228 Slog.i(TAG, "#checkRecognitionSupport called"); 229 if (mService == null) { 230 Slog.i(TAG, "Connection is not established yet"); 231 } 232 } 233 234 if (mService == null) { 235 // First time connection: first establish a connection, then dispatch. 236 connectToSystemService(); 237 } 238 putMessage(Message.obtain(mHandler, MSG_CHECK_RECOGNITION_SUPPORT, 239 new CheckRecognitionSupportArgs(recognizerIntent, executor, supportListener))); 240 } 241 242 @Override triggerModelDownload(@onNull Intent recognizerIntent)243 public void triggerModelDownload(@NonNull Intent recognizerIntent) { 244 Objects.requireNonNull(recognizerIntent, "intent must not be null"); 245 if (DBG) { 246 Slog.i(TAG, "#triggerModelDownload without a listener called"); 247 if (mService == null) { 248 Slog.i(TAG, "Connection is not established yet"); 249 } 250 } 251 if (mService == null) { 252 // First time connection: first establish a connection, then dispatch. 253 connectToSystemService(); 254 } 255 putMessage(Message.obtain( 256 mHandler, MSG_TRIGGER_MODEL_DOWNLOAD, 257 new ModelDownloadListenerArgs(recognizerIntent, null, null))); 258 } 259 260 @Override triggerModelDownload( @onNull Intent recognizerIntent, @NonNull @CallbackExecutor Executor executor, @NonNull ModelDownloadListener listener)261 public void triggerModelDownload( 262 @NonNull Intent recognizerIntent, 263 @NonNull @CallbackExecutor Executor executor, 264 @NonNull ModelDownloadListener listener) { 265 Objects.requireNonNull(recognizerIntent, "intent must not be null"); 266 if (DBG) { 267 Slog.i(TAG, "#triggerModelDownload with a listener called"); 268 if (mService == null) { 269 Slog.i(TAG, "Connection is not established yet"); 270 } 271 } 272 if (mService == null) { 273 // First time connection: first establish a connection, then dispatch. 274 connectToSystemService(); 275 } 276 putMessage(Message.obtain( 277 mHandler, MSG_TRIGGER_MODEL_DOWNLOAD, 278 new ModelDownloadListenerArgs(recognizerIntent, executor, listener))); 279 } 280 281 @Override 282 @RequiresPermission(Manifest.permission.MANAGE_SPEECH_RECOGNITION) setTemporaryOnDeviceRecognizer(@ullable ComponentName componentName)283 public void setTemporaryOnDeviceRecognizer(@Nullable ComponentName componentName) { 284 mHandler.sendMessage( 285 Message.obtain(mHandler, MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT, componentName)); 286 } 287 checkIsCalledFromMainThread()288 /* package */ static void checkIsCalledFromMainThread() { 289 if (Looper.myLooper() != Looper.getMainLooper()) { 290 throw new RuntimeException( 291 "SpeechRecognizer should be used only from the application's main thread"); 292 } 293 } 294 putMessage(Message msg)295 private void putMessage(Message msg) { 296 if (mService == null) { 297 mPendingTasks.offer(msg); 298 } else { 299 mHandler.sendMessage(msg); 300 } 301 } 302 303 /** sends the actual message to the service */ handleStartListening(Intent recognizerIntent)304 private void handleStartListening(Intent recognizerIntent) { 305 if (!checkOpenConnection()) { 306 return; 307 } 308 try { 309 mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource()); 310 if (DBG) Log.d(TAG, "service start listening command succeeded"); 311 } catch (final Exception e) { 312 Log.e(TAG, "startListening() failed", e); 313 mListener.onError(ERROR_CLIENT); 314 } 315 } 316 317 /** sends the actual message to the service */ handleStopMessage()318 private void handleStopMessage() { 319 if (!checkOpenConnection()) { 320 return; 321 } 322 try { 323 mService.stopListening(mListener); 324 if (DBG) Log.d(TAG, "service stop listening command succeeded"); 325 } catch (final Exception e) { 326 Log.e(TAG, "stopListening() failed", e); 327 mListener.onError(ERROR_CLIENT); 328 } 329 } 330 331 /** sends the actual message to the service */ handleCancelMessage()332 private void handleCancelMessage() { 333 if (!checkOpenConnection()) { 334 return; 335 } 336 try { 337 mService.cancel(mListener, /*isShutdown*/ false); 338 if (DBG) Log.d(TAG, "service cancel command succeeded"); 339 } catch (final Exception e) { 340 Log.e(TAG, "cancel() failed", e); 341 mListener.onError(ERROR_CLIENT); 342 } 343 } 344 handleSetTemporaryComponent(ComponentName componentName)345 private void handleSetTemporaryComponent(ComponentName componentName) { 346 if (DBG) { 347 Log.d(TAG, "handleSetTemporaryComponent, componentName=" + componentName); 348 } 349 350 if (!maybeInitializeManagerService()) { 351 return; 352 } 353 354 try { 355 mManagerService.setTemporaryComponent(componentName); 356 } catch (final RemoteException e) { 357 e.rethrowFromSystemServer(); 358 } 359 } 360 handleCheckRecognitionSupport( Intent recognizerIntent, Executor callbackExecutor, RecognitionSupportCallback recognitionSupportCallback)361 private void handleCheckRecognitionSupport( 362 Intent recognizerIntent, 363 Executor callbackExecutor, 364 RecognitionSupportCallback recognitionSupportCallback) { 365 if (!maybeInitializeManagerService() || !checkOpenConnection()) { 366 return; 367 } 368 try { 369 mService.checkRecognitionSupport( 370 recognizerIntent, 371 mContext.getAttributionSource(), 372 new InternalSupportCallback(callbackExecutor, recognitionSupportCallback)); 373 if (DBG) Log.d(TAG, "service support command succeeded"); 374 } catch (final Exception e) { 375 Log.e(TAG, "checkRecognitionSupport() failed", e); 376 callbackExecutor.execute(() -> recognitionSupportCallback.onError(ERROR_CLIENT)); 377 } 378 } 379 handleTriggerModelDownload( Intent recognizerIntent, @Nullable Executor callbackExecutor, @Nullable ModelDownloadListener modelDownloadListener)380 private void handleTriggerModelDownload( 381 Intent recognizerIntent, 382 @Nullable Executor callbackExecutor, 383 @Nullable ModelDownloadListener modelDownloadListener) { 384 if (!maybeInitializeManagerService() || !checkOpenConnection()) { 385 return; 386 } 387 388 if (modelDownloadListener == null) { 389 // Trigger model download without a listener. 390 try { 391 mService.triggerModelDownload( 392 recognizerIntent, mContext.getAttributionSource(), null); 393 if (DBG) Log.d(TAG, "triggerModelDownload() without a listener"); 394 } catch (final Exception e) { 395 Log.e(TAG, "triggerModelDownload() without a listener failed", e); 396 mListener.onError(ERROR_CLIENT); 397 } 398 } else { 399 // Trigger model download with a listener. 400 try { 401 mService.triggerModelDownload( 402 recognizerIntent, mContext.getAttributionSource(), 403 new InternalModelDownloadListener(callbackExecutor, modelDownloadListener)); 404 if (DBG) Log.d(TAG, "triggerModelDownload() with a listener"); 405 } catch (final Exception e) { 406 Log.e(TAG, "triggerModelDownload() with a listener failed", e); 407 callbackExecutor.execute(() -> modelDownloadListener.onError(ERROR_CLIENT)); 408 } 409 } 410 } 411 checkOpenConnection()412 private boolean checkOpenConnection() { 413 if (mService != null && mService.asBinder().isBinderAlive()) { 414 return true; 415 } 416 mListener.onError(ERROR_CLIENT); 417 Log.e(TAG, "not connected to the recognition service"); 418 return false; 419 } 420 421 /** changes the listener */ handleChangeListener(RecognitionListener listener)422 private void handleChangeListener(RecognitionListener listener) { 423 if (DBG) Log.d(TAG, "handleChangeListener, listener=" + listener); 424 mListener.mInternalListener = listener; 425 } 426 427 @Override destroy()428 public void destroy() { 429 putMessage(mHandler.obtainMessage(MSG_DESTROY)); 430 } 431 handleDestroy()432 private void handleDestroy() { 433 if (mService != null) { 434 try { 435 mService.cancel(mListener, /*isShutdown*/ true); 436 } catch (final Exception e) { 437 // Not important 438 } 439 } 440 441 mService = null; 442 mPendingTasks.clear(); 443 mListener.mInternalListener = null; 444 } 445 446 /** Establishes a connection to system server proxy and initializes the session. */ connectToSystemService()447 private void connectToSystemService() { 448 if (!maybeInitializeManagerService()) { 449 return; 450 } 451 452 ComponentName componentName = getSpeechRecognizerComponentName(); 453 454 if (!mOnDevice && componentName == null) { 455 mListener.onError(ERROR_CLIENT); 456 return; 457 } 458 459 try { 460 mManagerService.createSession( 461 componentName, 462 mClientToken, 463 mOnDevice, 464 new IRecognitionServiceManagerCallback.Stub(){ 465 @Override 466 public void onSuccess(IRecognitionService service) throws RemoteException { 467 if (DBG) { 468 Log.i(TAG, "Connected to speech recognition service"); 469 } 470 mService = service; 471 while (!mPendingTasks.isEmpty()) { 472 mHandler.sendMessage(mPendingTasks.poll()); 473 } 474 } 475 476 @Override 477 public void onError(int errorCode) throws RemoteException { 478 Log.e(TAG, "Bind to system recognition service failed with error " 479 + errorCode); 480 mListener.onError(errorCode); 481 } 482 }); 483 } catch (RemoteException e) { 484 e.rethrowFromSystemServer(); 485 } 486 } 487 maybeInitializeManagerService()488 private synchronized boolean maybeInitializeManagerService() { 489 if (DBG) { 490 Log.i(TAG, "#maybeInitializeManagerService found = " + mManagerService); 491 } 492 if (mManagerService != null) { 493 return true; 494 } 495 496 IBinder service = ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE); 497 if (service == null && mOnDevice) { 498 service = (IBinder) mContext.getSystemService(Context.SPEECH_RECOGNITION_SERVICE); 499 } 500 mManagerService = IRecognitionServiceManager.Stub.asInterface(service); 501 502 if (mManagerService == null) { 503 if (mListener != null) { 504 mListener.onError(ERROR_CLIENT); 505 } 506 return false; 507 } 508 return true; 509 } 510 511 /** 512 * Returns the component name to be used for establishing a connection, based on the parameters 513 * used during initialization. 514 * 515 * <p>Note the 3 different scenarios: 516 * <ol> 517 * <li>On-device speech recognizer which is determined by the manufacturer and not 518 * changeable by the user 519 * <li>Default user-selected speech recognizer as specified by 520 * {@code Settings.Secure.VOICE_RECOGNITION_SERVICE} 521 * <li>Custom speech recognizer supplied by the client. 522 * </ol> 523 */ 524 @SuppressWarnings("NonUserGetterCalled") getSpeechRecognizerComponentName()525 private ComponentName getSpeechRecognizerComponentName() { 526 if (mOnDevice) { 527 return null; 528 } 529 530 if (mServiceComponent != null) { 531 return mServiceComponent; 532 } 533 534 String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(), 535 Settings.Secure.VOICE_RECOGNITION_SERVICE); 536 537 if (TextUtils.isEmpty(serviceComponent)) { 538 Log.e(TAG, "no selected voice recognition service"); 539 mListener.onError(ERROR_CLIENT); 540 return null; 541 } 542 543 return ComponentName.unflattenFromString(serviceComponent); 544 } 545 546 private static class CheckRecognitionSupportArgs { 547 final Intent mIntent; 548 final Executor mCallbackExecutor; 549 final RecognitionSupportCallback mCallback; 550 CheckRecognitionSupportArgs( Intent intent, Executor callbackExecutor, RecognitionSupportCallback callback)551 private CheckRecognitionSupportArgs( 552 Intent intent, 553 Executor callbackExecutor, 554 RecognitionSupportCallback callback) { 555 mIntent = intent; 556 mCallbackExecutor = callbackExecutor; 557 mCallback = callback; 558 } 559 } 560 561 private static class ModelDownloadListenerArgs { 562 final Intent mIntent; 563 final Executor mExecutor; 564 final ModelDownloadListener mModelDownloadListener; 565 ModelDownloadListenerArgs(Intent intent, Executor executor, ModelDownloadListener modelDownloadListener)566 private ModelDownloadListenerArgs(Intent intent, Executor executor, 567 ModelDownloadListener modelDownloadListener) { 568 mIntent = intent; 569 mExecutor = executor; 570 mModelDownloadListener = modelDownloadListener; 571 } 572 } 573 574 /** 575 * Internal wrapper of IRecognitionListener which will propagate the results to 576 * RecognitionListener 577 */ 578 private static class InternalRecognitionListener extends IRecognitionListener.Stub { 579 private RecognitionListener mInternalListener; 580 581 private static final int MSG_BEGINNING_OF_SPEECH = 1; 582 private static final int MSG_BUFFER_RECEIVED = 2; 583 private static final int MSG_END_OF_SPEECH = 3; 584 private static final int MSG_ERROR = 4; 585 private static final int MSG_READY_FOR_SPEECH = 5; 586 private static final int MSG_RESULTS = 6; 587 private static final int MSG_PARTIAL_RESULTS = 7; 588 private static final int MSG_RMS_CHANGED = 8; 589 private static final int MSG_ON_EVENT = 9; 590 private static final int MSG_SEGMENT_RESULTS = 10; 591 private static final int MSG_SEGMENT_END_SESSION = 11; 592 private static final int MSG_LANGUAGE_DETECTION = 12; 593 594 private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) { 595 @Override 596 public void handleMessage(Message msg) { 597 if (mInternalListener == null) { 598 return; 599 } 600 switch (msg.what) { 601 case MSG_BEGINNING_OF_SPEECH: 602 mInternalListener.onBeginningOfSpeech(); 603 break; 604 case MSG_BUFFER_RECEIVED: 605 mInternalListener.onBufferReceived((byte[]) msg.obj); 606 break; 607 case MSG_END_OF_SPEECH: 608 mInternalListener.onEndOfSpeech(); 609 break; 610 case MSG_ERROR: 611 mInternalListener.onError((Integer) msg.obj); 612 break; 613 case MSG_READY_FOR_SPEECH: 614 mInternalListener.onReadyForSpeech((Bundle) msg.obj); 615 break; 616 case MSG_RESULTS: 617 mInternalListener.onResults((Bundle) msg.obj); 618 break; 619 case MSG_PARTIAL_RESULTS: 620 mInternalListener.onPartialResults((Bundle) msg.obj); 621 break; 622 case MSG_RMS_CHANGED: 623 mInternalListener.onRmsChanged((Float) msg.obj); 624 break; 625 case MSG_ON_EVENT: 626 mInternalListener.onEvent(msg.arg1, (Bundle) msg.obj); 627 break; 628 case MSG_SEGMENT_RESULTS: 629 mInternalListener.onSegmentResults((Bundle) msg.obj); 630 break; 631 case MSG_SEGMENT_END_SESSION: 632 mInternalListener.onEndOfSegmentedSession(); 633 break; 634 case MSG_LANGUAGE_DETECTION: 635 mInternalListener.onLanguageDetection((Bundle) msg.obj); 636 break; 637 } 638 } 639 }; 640 onBeginningOfSpeech()641 public void onBeginningOfSpeech() { 642 Message.obtain(mInternalHandler, MSG_BEGINNING_OF_SPEECH).sendToTarget(); 643 } 644 onBufferReceived(final byte[] buffer)645 public void onBufferReceived(final byte[] buffer) { 646 Message.obtain(mInternalHandler, MSG_BUFFER_RECEIVED, buffer).sendToTarget(); 647 } 648 onEndOfSpeech()649 public void onEndOfSpeech() { 650 Message.obtain(mInternalHandler, MSG_END_OF_SPEECH).sendToTarget(); 651 } 652 onError(final int error)653 public void onError(final int error) { 654 Message.obtain(mInternalHandler, MSG_ERROR, error).sendToTarget(); 655 } 656 onReadyForSpeech(final Bundle noiseParams)657 public void onReadyForSpeech(final Bundle noiseParams) { 658 Message.obtain(mInternalHandler, MSG_READY_FOR_SPEECH, noiseParams).sendToTarget(); 659 } 660 onResults(final Bundle results)661 public void onResults(final Bundle results) { 662 Message.obtain(mInternalHandler, MSG_RESULTS, results).sendToTarget(); 663 } 664 onPartialResults(final Bundle results)665 public void onPartialResults(final Bundle results) { 666 Message.obtain(mInternalHandler, MSG_PARTIAL_RESULTS, results).sendToTarget(); 667 } 668 onRmsChanged(final float rmsdB)669 public void onRmsChanged(final float rmsdB) { 670 Message.obtain(mInternalHandler, MSG_RMS_CHANGED, rmsdB).sendToTarget(); 671 } 672 onSegmentResults(final Bundle bundle)673 public void onSegmentResults(final Bundle bundle) { 674 Message.obtain(mInternalHandler, MSG_SEGMENT_RESULTS, bundle).sendToTarget(); 675 } 676 onEndOfSegmentedSession()677 public void onEndOfSegmentedSession() { 678 Message.obtain(mInternalHandler, MSG_SEGMENT_END_SESSION).sendToTarget(); 679 } 680 onLanguageDetection(final Bundle results)681 public void onLanguageDetection(final Bundle results) { 682 Message.obtain(mInternalHandler, MSG_LANGUAGE_DETECTION, results).sendToTarget(); 683 } 684 onEvent(final int eventType, final Bundle params)685 public void onEvent(final int eventType, final Bundle params) { 686 Message.obtain(mInternalHandler, MSG_ON_EVENT, eventType, eventType, params) 687 .sendToTarget(); 688 } 689 } 690 691 private static class InternalSupportCallback extends IRecognitionSupportCallback.Stub { 692 private final Executor mExecutor; 693 private final RecognitionSupportCallback mCallback; 694 InternalSupportCallback(Executor executor, RecognitionSupportCallback callback)695 private InternalSupportCallback(Executor executor, RecognitionSupportCallback callback) { 696 this.mExecutor = executor; 697 this.mCallback = callback; 698 } 699 700 @Override onSupportResult(RecognitionSupport recognitionSupport)701 public void onSupportResult(RecognitionSupport recognitionSupport) throws RemoteException { 702 mExecutor.execute(() -> mCallback.onSupportResult(recognitionSupport)); 703 } 704 705 @Override onError(int errorCode)706 public void onError(int errorCode) throws RemoteException { 707 mExecutor.execute(() -> mCallback.onError(errorCode)); 708 } 709 } 710 711 private static class InternalModelDownloadListener extends IModelDownloadListener.Stub { 712 private final Executor mExecutor; 713 private final ModelDownloadListener mModelDownloadListener; 714 InternalModelDownloadListener( Executor executor, @NonNull ModelDownloadListener modelDownloadListener)715 private InternalModelDownloadListener( 716 Executor executor, 717 @NonNull ModelDownloadListener modelDownloadListener) { 718 mExecutor = executor; 719 mModelDownloadListener = modelDownloadListener; 720 } 721 722 @Override onProgress(int completedPercent)723 public void onProgress(int completedPercent) throws RemoteException { 724 mExecutor.execute(() -> mModelDownloadListener.onProgress(completedPercent)); 725 } 726 727 @Override onSuccess()728 public void onSuccess() throws RemoteException { 729 mExecutor.execute(() -> mModelDownloadListener.onSuccess()); 730 } 731 732 @Override onScheduled()733 public void onScheduled() throws RemoteException { 734 mExecutor.execute(() -> mModelDownloadListener.onScheduled()); 735 } 736 737 @Override onError(int error)738 public void onError(int error) throws RemoteException { 739 mExecutor.execute(() -> mModelDownloadListener.onError(error)); 740 } 741 } 742 } 743