• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IntDef;
21 import android.annotation.MainThread;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.annotation.TestApi;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.ResolveInfo;
30 import android.os.Binder;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.IBinder;
34 import android.os.Looper;
35 import android.os.Message;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.provider.Settings;
39 import android.text.TextUtils;
40 import android.util.Log;
41 import android.util.Slog;
42 
43 import com.android.internal.R;
44 
45 import java.lang.annotation.Documented;
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.util.List;
49 import java.util.Queue;
50 import java.util.concurrent.LinkedBlockingQueue;
51 
52 /**
53  * This class provides access to the speech recognition service. This service allows access to the
54  * speech recognizer. Do not instantiate this class directly, instead, call
55  * {@link SpeechRecognizer#createSpeechRecognizer(Context)}, or
56  * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be
57  * invoked only from the main application thread.
58  *
59  * <p>The implementation of this API is likely to stream audio to remote servers to perform speech
60  * recognition. As such this API is not intended to be used for continuous recognition, which would
61  * consume a significant amount of battery and bandwidth.
62  *
63  * <p>Please note that the application must have {@link android.Manifest.permission#RECORD_AUDIO}
64  * permission to use this class.
65  */
66 public class SpeechRecognizer {
67     /** DEBUG value to enable verbose debug prints */
68     private static final boolean DBG = false;
69 
70     /** Log messages identifier */
71     private static final String TAG = "SpeechRecognizer";
72 
73     /**
74      * Key used to retrieve an {@code ArrayList<String>} from the {@link Bundle} passed to the
75      * {@link RecognitionListener#onResults(Bundle)} and
76      * {@link RecognitionListener#onPartialResults(Bundle)} methods. These strings are the possible
77      * recognition results, where the first element is the most likely candidate.
78      */
79     public static final String RESULTS_RECOGNITION = "results_recognition";
80 
81     /**
82      * Key used to retrieve a float array from the {@link Bundle} passed to the
83      * {@link RecognitionListener#onResults(Bundle)} and
84      * {@link RecognitionListener#onPartialResults(Bundle)} methods. The array should be
85      * the same size as the ArrayList provided in {@link #RESULTS_RECOGNITION}, and should contain
86      * values ranging from 0.0 to 1.0, or -1 to represent an unavailable confidence score.
87      * <p>
88      * Confidence values close to 1.0 indicate high confidence (the speech recognizer is confident
89      * that the recognition result is correct), while values close to 0.0 indicate low confidence.
90      * <p>
91      * This value is optional and might not be provided.
92      */
93     public static final String CONFIDENCE_SCORES = "confidence_scores";
94 
95     /**
96      * The reason speech recognition failed.
97      *
98      * @hide
99      */
100     @Documented
101     @Retention(RetentionPolicy.SOURCE)
102     @IntDef(prefix = {"ERROR_"}, value = {
103             ERROR_NETWORK_TIMEOUT,
104             ERROR_NETWORK,
105             ERROR_AUDIO,
106             ERROR_SERVER,
107             ERROR_CLIENT,
108             ERROR_SPEECH_TIMEOUT,
109             ERROR_NO_MATCH,
110             ERROR_RECOGNIZER_BUSY,
111             ERROR_INSUFFICIENT_PERMISSIONS,
112             ERROR_TOO_MANY_REQUESTS,
113             ERROR_SERVER_DISCONNECTED,
114             ERROR_LANGUAGE_NOT_SUPPORTED,
115             ERROR_LANGUAGE_UNAVAILABLE
116     })
117     public @interface RecognitionError {}
118 
119     /** Network operation timed out. */
120     public static final int ERROR_NETWORK_TIMEOUT = 1;
121 
122     /** Other network related errors. */
123     public static final int ERROR_NETWORK = 2;
124 
125     /** Audio recording error. */
126     public static final int ERROR_AUDIO = 3;
127 
128     /** Server sends error status. */
129     public static final int ERROR_SERVER = 4;
130 
131     /** Other client side errors. */
132     public static final int ERROR_CLIENT = 5;
133 
134     /** No speech input */
135     public static final int ERROR_SPEECH_TIMEOUT = 6;
136 
137     /** No recognition result matched. */
138     public static final int ERROR_NO_MATCH = 7;
139 
140     /** RecognitionService busy. */
141     public static final int ERROR_RECOGNIZER_BUSY = 8;
142 
143     /** Insufficient permissions */
144     public static final int ERROR_INSUFFICIENT_PERMISSIONS = 9;
145 
146     /** Too many requests from the same client. */
147     public static final int ERROR_TOO_MANY_REQUESTS = 10;
148 
149     /** Server has been disconnected, e.g. because the app has crashed. */
150     public static final int ERROR_SERVER_DISCONNECTED = 11;
151 
152     /** Requested language is not available to be used with the current recognizer. */
153     public static final int ERROR_LANGUAGE_NOT_SUPPORTED = 12;
154 
155     /** Requested language is supported, but not available currently (e.g. not downloaded yet). */
156     public static final int ERROR_LANGUAGE_UNAVAILABLE = 13;
157 
158     /** action codes */
159     private static final int MSG_START = 1;
160     private static final int MSG_STOP = 2;
161     private static final int MSG_CANCEL = 3;
162     private static final int MSG_CHANGE_LISTENER = 4;
163     private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5;
164 
165     /** The actual RecognitionService endpoint */
166     private IRecognitionService mService;
167 
168     /** Context with which the manager was created */
169     private final Context mContext;
170 
171     /** Component to direct service intent to */
172     private final ComponentName mServiceComponent;
173 
174     /** Whether to use on-device speech recognizer. */
175     private final boolean mOnDevice;
176 
177     private IRecognitionServiceManager mManagerService;
178 
179     /** Handler that will execute the main tasks */
180     private Handler mHandler = new Handler(Looper.getMainLooper()) {
181 
182         @Override
183         public void handleMessage(Message msg) {
184             switch (msg.what) {
185                 case MSG_START:
186                     handleStartListening((Intent) msg.obj);
187                     break;
188                 case MSG_STOP:
189                     handleStopMessage();
190                     break;
191                 case MSG_CANCEL:
192                     handleCancelMessage();
193                     break;
194                 case MSG_CHANGE_LISTENER:
195                     handleChangeListener((RecognitionListener) msg.obj);
196                     break;
197                 case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT:
198                     handleSetTemporaryComponent((ComponentName) msg.obj);
199                     break;
200             }
201         }
202     };
203 
204     /**
205      * Temporary queue, saving the messages until the connection will be established, afterwards,
206      * only mHandler will receive the messages
207      */
208     private final Queue<Message> mPendingTasks = new LinkedBlockingQueue<>();
209 
210     /** The Listener that will receive all the callbacks */
211     private final InternalListener mListener = new InternalListener();
212 
213     private final IBinder mClientToken = new Binder();
214 
215     /**
216      * The right way to create a {@code SpeechRecognizer} is by using
217      * {@link #createSpeechRecognizer} static factory method
218      */
SpeechRecognizer(final Context context, final ComponentName serviceComponent)219     private SpeechRecognizer(final Context context, final ComponentName serviceComponent) {
220         mContext = context;
221         mServiceComponent = serviceComponent;
222         mOnDevice = false;
223     }
224 
225     /**
226      * The right way to create a {@code SpeechRecognizer} is by using
227      * {@link #createOnDeviceSpeechRecognizer} static factory method
228      */
SpeechRecognizer(final Context context, boolean onDevice)229     private SpeechRecognizer(final Context context, boolean onDevice) {
230         mContext = context;
231         mServiceComponent = null;
232         mOnDevice = onDevice;
233     }
234 
235     /**
236      * Checks whether a speech recognition service is available on the system. If this method
237      * returns {@code false}, {@link SpeechRecognizer#createSpeechRecognizer(Context)} will
238      * fail.
239      *
240      * @param context with which {@code SpeechRecognizer} will be created
241      * @return {@code true} if recognition is available, {@code false} otherwise
242      */
isRecognitionAvailable(@onNull final Context context)243     public static boolean isRecognitionAvailable(@NonNull final Context context) {
244         // TODO(b/176578753): make sure this works well with system speech recognizers.
245         final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
246                 new Intent(RecognitionService.SERVICE_INTERFACE), 0);
247         return list != null && list.size() != 0;
248     }
249 
250     /**
251      * Checks whether an on-device speech recognition service is available on the system. If this
252      * method returns {@code false},
253      * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)} will
254      * fail.
255      *
256      * @param context with which on-device {@code SpeechRecognizer} will be created
257      * @return {@code true} if on-device recognition is available, {@code false} otherwise
258      */
isOnDeviceRecognitionAvailable(@onNull final Context context)259     public static boolean isOnDeviceRecognitionAvailable(@NonNull final Context context) {
260         ComponentName componentName =
261                 ComponentName.unflattenFromString(
262                         context.getString(R.string.config_defaultOnDeviceSpeechRecognitionService));
263         return componentName != null;
264     }
265 
266     /**
267      * Factory method to create a new {@code SpeechRecognizer}. Please note that
268      * {@link #setRecognitionListener(RecognitionListener)} should be called before dispatching any
269      * command to the created {@code SpeechRecognizer}, otherwise no notifications will be
270      * received.
271      *
272      * <p>For apps targeting Android 11 (API level 30) interaction with a speech recognition
273      * service requires <queries> element to be added to the manifest file:
274      * <pre>{@code
275      * <queries>
276      *   <intent>
277      *     <action
278      *        android:name="android.speech.RecognitionService" />
279      *   </intent>
280      * </queries>
281      * }</pre>
282      *
283      * @param context in which to create {@code SpeechRecognizer}
284      * @return a new {@code SpeechRecognizer}
285      */
286     @MainThread
createSpeechRecognizer(final Context context)287     public static SpeechRecognizer createSpeechRecognizer(final Context context) {
288         return createSpeechRecognizer(context, null);
289     }
290 
291     /**
292      * Factory method to create a new {@code SpeechRecognizer}. Please note that
293      * {@link #setRecognitionListener(RecognitionListener)} should be called before dispatching any
294      * command to the created {@code SpeechRecognizer}, otherwise no notifications will be
295      * received.
296      * Use this version of the method to specify a specific service to direct this
297      * {@link SpeechRecognizer} to.
298      *
299      * <p><strong>Important</strong>: before calling this method, please check via
300      * {@link android.content.pm.PackageManager#queryIntentServices(Intent, int)} that {@code
301      * serviceComponent} actually exists and provides
302      * {@link RecognitionService#SERVICE_INTERFACE}. Normally you would not use this; call
303      * {@link #createSpeechRecognizer(Context)} to use the system default recognition
304      * service instead or {@link #createOnDeviceSpeechRecognizer(Context)} to use on-device
305      * recognition.</p>
306      *
307      * <p>For apps targeting Android 11 (API level 30) interaction with a speech recognition
308      * service requires <queries> element to be added to the manifest file:
309      * <pre>{@code
310      * <queries>
311      *   <intent>
312      *     <action
313      *        android:name="android.speech.RecognitionService" />
314      *   </intent>
315      * </queries>
316      * }</pre>
317      *
318      * @param context in which to create {@code SpeechRecognizer}
319      * @param serviceComponent the {@link ComponentName} of a specific service to direct this
320      *        {@code SpeechRecognizer} to
321      * @return a new {@code SpeechRecognizer}
322      */
323     @MainThread
createSpeechRecognizer(final Context context, final ComponentName serviceComponent)324     public static SpeechRecognizer createSpeechRecognizer(final Context context,
325             final ComponentName serviceComponent) {
326         if (context == null) {
327             throw new IllegalArgumentException("Context cannot be null");
328         }
329         checkIsCalledFromMainThread();
330         return new SpeechRecognizer(context, serviceComponent);
331     }
332 
333     /**
334      * Factory method to create a new {@code SpeechRecognizer}.
335      *
336      * <p>Please note that {@link #setRecognitionListener(RecognitionListener)} should be called
337      * before dispatching any command to the created {@code SpeechRecognizer}, otherwise no
338      * notifications will be received.
339      *
340      * @param context in which to create {@code SpeechRecognizer}
341      * @return a new on-device {@code SpeechRecognizer}.
342      * @throws UnsupportedOperationException iff {@link #isOnDeviceRecognitionAvailable(Context)}
343      *                                       is false
344      */
345     @NonNull
346     @MainThread
createOnDeviceSpeechRecognizer(@onNull final Context context)347     public static SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull final Context context) {
348         if (!isOnDeviceRecognitionAvailable(context)) {
349             throw new UnsupportedOperationException("On-device recognition is not available");
350         }
351         return lenientlyCreateOnDeviceSpeechRecognizer(context);
352     }
353 
354     /**
355      * Helper method to create on-device SpeechRecognizer in tests even when the device does not
356      * support on-device speech recognition.
357      *
358      * @hide
359      */
360     @TestApi
361     @NonNull
362     @MainThread
createOnDeviceTestingSpeechRecognizer( @onNull final Context context)363     public static SpeechRecognizer createOnDeviceTestingSpeechRecognizer(
364             @NonNull final Context context) {
365         return lenientlyCreateOnDeviceSpeechRecognizer(context);
366     }
367 
368     @NonNull
369     @MainThread
lenientlyCreateOnDeviceSpeechRecognizer( @onNull final Context context)370     private static SpeechRecognizer lenientlyCreateOnDeviceSpeechRecognizer(
371             @NonNull final Context context) {
372         if (context == null) {
373             throw new IllegalArgumentException("Context cannot be null");
374         }
375         checkIsCalledFromMainThread();
376         return new SpeechRecognizer(context, /* onDevice */ true);
377     }
378 
379     /**
380      * Sets the listener that will receive all the callbacks. The previous unfinished commands will
381      * be executed with the old listener, while any following command will be executed with the new
382      * listener.
383      *
384      * @param listener listener that will receive all the callbacks from the created
385      *        {@link SpeechRecognizer}, this must not be null.
386      */
387     @MainThread
setRecognitionListener(RecognitionListener listener)388     public void setRecognitionListener(RecognitionListener listener) {
389         checkIsCalledFromMainThread();
390         putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener));
391     }
392 
393     /**
394      * Starts listening for speech. Please note that
395      * {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise
396      * no notifications will be received.
397      *
398      * @param recognizerIntent contains parameters for the recognition to be performed. The intent
399      *        may also contain optional extras, see {@link RecognizerIntent}. If these values are
400      *        not set explicitly, default values will be used by the recognizer.
401      */
402     @MainThread
startListening(final Intent recognizerIntent)403     public void startListening(final Intent recognizerIntent) {
404         if (recognizerIntent == null) {
405             throw new IllegalArgumentException("intent must not be null");
406         }
407         checkIsCalledFromMainThread();
408 
409         if (DBG) {
410             Slog.i(TAG, "#startListening called");
411             if (mService == null) {
412                 Slog.i(TAG, "Connection is not established yet");
413             }
414         }
415 
416         if (mService == null) {
417             // First time connection: first establish a connection, then dispatch #startListening.
418             connectToSystemService();
419         }
420         putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
421     }
422 
423     /**
424      * Stops listening for speech. Speech captured so far will be recognized as if the user had
425      * stopped speaking at this point.
426      *
427      * <p>Note that in the default case, this does not need to be called, as the speech endpointer
428      * will automatically stop the recognizer listening when it determines speech has completed.
429      * However, you can manipulate endpointer parameters directly using the intent extras defined in
430      * {@link RecognizerIntent}, in which case you may sometimes want to manually call this method
431      * to stop listening sooner.
432      *
433      * <p>Upon invocation clients must wait until {@link RecognitionListener#onResults} or
434      * {@link RecognitionListener#onError} are invoked before calling
435      * {@link SpeechRecognizer#startListening} again. Otherwise such an attempt would be rejected by
436      * recognition service.
437      *
438      * <p>Please note that
439      * {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise
440      * no notifications will be received.
441      */
442     @MainThread
stopListening()443     public void stopListening() {
444         checkIsCalledFromMainThread();
445 
446         if (DBG) {
447             Slog.i(TAG, "#stopListening called");
448             if (mService == null) {
449                 Slog.i(TAG, "Connection is not established yet");
450             }
451         }
452 
453         putMessage(Message.obtain(mHandler, MSG_STOP));
454     }
455 
456     /**
457      * Cancels the speech recognition. Please note that
458      * {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise
459      * no notifications will be received.
460      */
461     @MainThread
cancel()462     public void cancel() {
463         checkIsCalledFromMainThread();
464         putMessage(Message.obtain(mHandler, MSG_CANCEL));
465     }
466 
467     /**
468      * Sets a temporary component to power on-device speech recognizer.
469      *
470      * <p>This is only expected to be called in tests, system would reject calls from client apps.
471      *
472      * @param componentName name of the component to set temporary replace speech recognizer. {@code
473      *        null} value resets the recognizer to default.
474      *
475      * @hide
476      */
477     @TestApi
478     @RequiresPermission(Manifest.permission.MANAGE_SPEECH_RECOGNITION)
setTemporaryOnDeviceRecognizer(@ullable ComponentName componentName)479     public void setTemporaryOnDeviceRecognizer(@Nullable ComponentName componentName) {
480         mHandler.sendMessage(
481                 Message.obtain(mHandler, MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT, componentName));
482     }
483 
checkIsCalledFromMainThread()484     private static void checkIsCalledFromMainThread() {
485         if (Looper.myLooper() != Looper.getMainLooper()) {
486             throw new RuntimeException(
487                     "SpeechRecognizer should be used only from the application's main thread");
488         }
489     }
490 
putMessage(Message msg)491     private void putMessage(Message msg) {
492         if (mService == null) {
493             mPendingTasks.offer(msg);
494         } else {
495             mHandler.sendMessage(msg);
496         }
497     }
498 
499     /** sends the actual message to the service */
handleStartListening(Intent recognizerIntent)500     private void handleStartListening(Intent recognizerIntent) {
501         if (!checkOpenConnection()) {
502             return;
503         }
504         try {
505             mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource());
506             if (DBG) Log.d(TAG, "service start listening command succeded");
507         } catch (final RemoteException e) {
508             Log.e(TAG, "startListening() failed", e);
509             mListener.onError(ERROR_CLIENT);
510         }
511     }
512 
513     /** sends the actual message to the service */
handleStopMessage()514     private void handleStopMessage() {
515         if (!checkOpenConnection()) {
516             return;
517         }
518         try {
519             mService.stopListening(mListener);
520             if (DBG) Log.d(TAG, "service stop listening command succeded");
521         } catch (final RemoteException e) {
522             Log.e(TAG, "stopListening() failed", e);
523             mListener.onError(ERROR_CLIENT);
524         }
525     }
526 
527     /** sends the actual message to the service */
handleCancelMessage()528     private void handleCancelMessage() {
529         if (!checkOpenConnection()) {
530             return;
531         }
532         try {
533             mService.cancel(mListener, /*isShutdown*/ false);
534             if (DBG) Log.d(TAG, "service cancel command succeded");
535         } catch (final RemoteException e) {
536             Log.e(TAG, "cancel() failed", e);
537             mListener.onError(ERROR_CLIENT);
538         }
539     }
540 
handleSetTemporaryComponent(ComponentName componentName)541     private void handleSetTemporaryComponent(ComponentName componentName) {
542         if (DBG) {
543             Log.d(TAG, "handleSetTemporaryComponent, componentName=" + componentName);
544         }
545 
546         if (!maybeInitializeManagerService()) {
547             return;
548         }
549 
550         try {
551             mManagerService.setTemporaryComponent(componentName);
552         } catch (final RemoteException e) {
553             e.rethrowFromSystemServer();
554         }
555     }
556 
checkOpenConnection()557     private boolean checkOpenConnection() {
558         if (mService != null) {
559             return true;
560         }
561         mListener.onError(ERROR_CLIENT);
562         Log.e(TAG, "not connected to the recognition service");
563         return false;
564     }
565 
566     /** changes the listener */
handleChangeListener(RecognitionListener listener)567     private void handleChangeListener(RecognitionListener listener) {
568         if (DBG) Log.d(TAG, "handleChangeListener, listener=" + listener);
569         mListener.mInternalListener = listener;
570     }
571 
572     /** Destroys the {@code SpeechRecognizer} object. */
destroy()573     public void destroy() {
574         if (mService != null) {
575             try {
576                 mService.cancel(mListener, /*isShutdown*/ true);
577             } catch (final RemoteException e) {
578                 // Not important
579             }
580         }
581 
582         mService = null;
583         mPendingTasks.clear();
584         mListener.mInternalListener = null;
585     }
586 
587     /** Establishes a connection to system server proxy and initializes the session. */
connectToSystemService()588     private void connectToSystemService() {
589         if (!maybeInitializeManagerService()) {
590             return;
591         }
592 
593         ComponentName componentName = getSpeechRecognizerComponentName();
594 
595         if (!mOnDevice && componentName == null) {
596             mListener.onError(ERROR_CLIENT);
597             return;
598         }
599 
600         try {
601             mManagerService.createSession(
602                     componentName,
603                     mClientToken,
604                     mOnDevice,
605                     new IRecognitionServiceManagerCallback.Stub(){
606                         @Override
607                         public void onSuccess(IRecognitionService service) throws RemoteException {
608                             if (DBG) {
609                                 Log.i(TAG, "Connected to speech recognition service");
610                             }
611                             mService = service;
612                             while (!mPendingTasks.isEmpty()) {
613                                 mHandler.sendMessage(mPendingTasks.poll());
614                             }
615                         }
616 
617                         @Override
618                         public void onError(int errorCode) throws RemoteException {
619                             Log.e(TAG, "Bind to system recognition service failed with error "
620                                     + errorCode);
621                             mListener.onError(errorCode);
622                         }
623                     });
624         } catch (RemoteException e) {
625             e.rethrowFromSystemServer();
626         }
627     }
628 
maybeInitializeManagerService()629     private boolean maybeInitializeManagerService() {
630         if (mManagerService != null) {
631             return true;
632         }
633 
634         mManagerService = IRecognitionServiceManager.Stub.asInterface(
635                 ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
636 
637         if (mManagerService == null && mListener != null) {
638             mListener.onError(ERROR_CLIENT);
639             return false;
640         }
641         return true;
642     }
643 
644     /**
645      * Returns the component name to be used for establishing a connection, based on the parameters
646      * used during initialization.
647      *
648      * <p>Note the 3 different scenarios:
649      * <ol>
650      *     <li>On-device speech recognizer which is determined by the manufacturer and not
651      *     changeable by the user
652      *     <li>Default user-selected speech recognizer as specified by
653      *     {@code Settings.Secure.VOICE_RECOGNITION_SERVICE}
654      *     <li>Custom speech recognizer supplied by the client.
655      */
getSpeechRecognizerComponentName()656     private ComponentName getSpeechRecognizerComponentName() {
657         if (mOnDevice) {
658             return null;
659         }
660 
661         if (mServiceComponent != null) {
662             return mServiceComponent;
663         }
664 
665         String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
666                 Settings.Secure.VOICE_RECOGNITION_SERVICE);
667 
668         if (TextUtils.isEmpty(serviceComponent)) {
669             Log.e(TAG, "no selected voice recognition service");
670             mListener.onError(ERROR_CLIENT);
671             return null;
672         }
673 
674         return ComponentName.unflattenFromString(serviceComponent);
675     }
676 
677     /**
678      * Internal wrapper of IRecognitionListener which will propagate the results to
679      * RecognitionListener
680      */
681     private static class InternalListener extends IRecognitionListener.Stub {
682         private RecognitionListener mInternalListener;
683 
684         private static final int MSG_BEGINNING_OF_SPEECH = 1;
685         private static final int MSG_BUFFER_RECEIVED = 2;
686         private static final int MSG_END_OF_SPEECH = 3;
687         private static final int MSG_ERROR = 4;
688         private static final int MSG_READY_FOR_SPEECH = 5;
689         private static final int MSG_RESULTS = 6;
690         private static final int MSG_PARTIAL_RESULTS = 7;
691         private static final int MSG_RMS_CHANGED = 8;
692         private static final int MSG_ON_EVENT = 9;
693 
694         private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
695             @Override
696             public void handleMessage(Message msg) {
697                 if (mInternalListener == null) {
698                     return;
699                 }
700                 switch (msg.what) {
701                     case MSG_BEGINNING_OF_SPEECH:
702                         mInternalListener.onBeginningOfSpeech();
703                         break;
704                     case MSG_BUFFER_RECEIVED:
705                         mInternalListener.onBufferReceived((byte[]) msg.obj);
706                         break;
707                     case MSG_END_OF_SPEECH:
708                         mInternalListener.onEndOfSpeech();
709                         break;
710                     case MSG_ERROR:
711                         mInternalListener.onError((Integer) msg.obj);
712                         break;
713                     case MSG_READY_FOR_SPEECH:
714                         mInternalListener.onReadyForSpeech((Bundle) msg.obj);
715                         break;
716                     case MSG_RESULTS:
717                         mInternalListener.onResults((Bundle) msg.obj);
718                         break;
719                     case MSG_PARTIAL_RESULTS:
720                         mInternalListener.onPartialResults((Bundle) msg.obj);
721                         break;
722                     case MSG_RMS_CHANGED:
723                         mInternalListener.onRmsChanged((Float) msg.obj);
724                         break;
725                     case MSG_ON_EVENT:
726                         mInternalListener.onEvent(msg.arg1, (Bundle) msg.obj);
727                         break;
728                 }
729             }
730         };
731 
onBeginningOfSpeech()732         public void onBeginningOfSpeech() {
733             Message.obtain(mInternalHandler, MSG_BEGINNING_OF_SPEECH).sendToTarget();
734         }
735 
onBufferReceived(final byte[] buffer)736         public void onBufferReceived(final byte[] buffer) {
737             Message.obtain(mInternalHandler, MSG_BUFFER_RECEIVED, buffer).sendToTarget();
738         }
739 
onEndOfSpeech()740         public void onEndOfSpeech() {
741             Message.obtain(mInternalHandler, MSG_END_OF_SPEECH).sendToTarget();
742         }
743 
onError(final int error)744         public void onError(final int error) {
745             Message.obtain(mInternalHandler, MSG_ERROR, error).sendToTarget();
746         }
747 
onReadyForSpeech(final Bundle noiseParams)748         public void onReadyForSpeech(final Bundle noiseParams) {
749             Message.obtain(mInternalHandler, MSG_READY_FOR_SPEECH, noiseParams).sendToTarget();
750         }
751 
onResults(final Bundle results)752         public void onResults(final Bundle results) {
753             Message.obtain(mInternalHandler, MSG_RESULTS, results).sendToTarget();
754         }
755 
onPartialResults(final Bundle results)756         public void onPartialResults(final Bundle results) {
757             Message.obtain(mInternalHandler, MSG_PARTIAL_RESULTS, results).sendToTarget();
758         }
759 
onRmsChanged(final float rmsdB)760         public void onRmsChanged(final float rmsdB) {
761             Message.obtain(mInternalHandler, MSG_RMS_CHANGED, rmsdB).sendToTarget();
762         }
763 
onEvent(final int eventType, final Bundle params)764         public void onEvent(final int eventType, final Bundle params) {
765             Message.obtain(mInternalHandler, MSG_ON_EVENT, eventType, eventType, params)
766                     .sendToTarget();
767         }
768     }
769 }
770