diff options
author | Sandeep Siddhartha <sansid@google.com> | 2013-08-23 16:40:08 -0700 |
---|---|---|
committer | Sandeep Siddhartha <sansid@google.com> | 2013-08-26 17:32:16 -0700 |
commit | 637cc458096f2f9ad53728dfb9ab0511c08b4837 (patch) | |
tree | 2e3df3f494e4ea5026a70d04e652d5f1a01ad73f /core | |
parent | ff796e5a24481febd8c07b1a6a3c3eda0e3fd88b (diff) | |
download | frameworks_base-637cc458096f2f9ad53728dfb9ab0511c08b4837.zip frameworks_base-637cc458096f2f9ad53728dfb9ab0511c08b4837.tar.gz frameworks_base-637cc458096f2f9ad53728dfb9ab0511c08b4837.tar.bz2 |
Add Service API to perform Hotword recognition
Change-Id: I855330b255a12cce309aa11e0b7cde5b8e061043
Diffstat (limited to 'core')
5 files changed, 834 insertions, 0 deletions
diff --git a/core/java/android/speech/hotword/HotwordRecognitionListener.java b/core/java/android/speech/hotword/HotwordRecognitionListener.java new file mode 100644 index 0000000..8e62373 --- /dev/null +++ b/core/java/android/speech/hotword/HotwordRecognitionListener.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.speech.hotword; + +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Bundle; + +/** + * Used for receiving notifications from the HotwordRecognitionService when the + * hotword recognition related events occur. + * All the callbacks are executed on the application main thread. + * {@hide} + */ +public interface HotwordRecognitionListener { + /** + * Called when the service starts listening for hotword. + */ + void onHotwordRecognitionStarted(); + + /** + * Called when the service stops listening for hotword. + */ + void onHotwordRecognitionStopped(); + + /** + * Called on an event of interest to the client. + * + * @param eventType the event type. + * @param eventBundle a Bundle containing the hotword event(s). + */ + void onHotwordEvent(int eventType, Bundle eventBundle); + + /** + * Called back when hotword is detected. + * The action tells the client what action to take, post hotword-detection. + */ + void onHotwordRecognized(PendingIntent intent); + + /** + * Called when the HotwordRecognitionService encounters an error. + * + * @param errorCode the error code describing the error that was encountered. + */ + void onHotwordError(int errorCode); +}
\ No newline at end of file diff --git a/core/java/android/speech/hotword/HotwordRecognitionService.java b/core/java/android/speech/hotword/HotwordRecognitionService.java new file mode 100644 index 0000000..4a89593 --- /dev/null +++ b/core/java/android/speech/hotword/HotwordRecognitionService.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.speech.hotword; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +/** + * This class provides a base class for hotword detection service implementations. + * This class should be extended only if you wish to implement a new hotword recognizer. + * {@hide} + */ +public abstract class HotwordRecognitionService extends Service { + /** + * The {@link Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = + "android.speech.hotword.HotwordRecognitionService"; + + /** Log messages identifier */ + private static final String TAG = "HotwordRecognitionService"; + + /** Debugging flag */ + // TODO: Turn off. + private static final boolean DBG = true; + + private static final int MSG_START_RECOGNITION = 1; + private static final int MSG_STOP_RECOGNITION = 2; + + /** + * The current callback of an application that invoked the + * {@link HotwordRecognitionService#onStartHotwordRecognition(Callback)} method + */ + private Callback mCurrentCallback = null; + + // Handle the client dying. + private final IBinder.DeathRecipient mCallbackDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + if (DBG) Log.i(TAG, "HotwordRecognitionService listener died"); + mCurrentCallback = null; + } + }; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_START_RECOGNITION: + dispatchStartRecognition((IHotwordRecognitionListener) msg.obj); + break; + case MSG_STOP_RECOGNITION: + dispatchStopRecognition((IHotwordRecognitionListener) msg.obj); + break; + } + } + }; + + /** Binder of the hotword recognition service */ + private RecognitionServiceBinder mBinder = new RecognitionServiceBinder(this); + + private void dispatchStartRecognition(IHotwordRecognitionListener listener) { + if (mCurrentCallback == null) { + if (DBG) Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder()); + try { + listener.asBinder().linkToDeath(mCallbackDeathRecipient, 0); + } catch (RemoteException e) { + if (DBG) Log.d(TAG, "listener died before linkToDeath()"); + } + mCurrentCallback = new Callback(listener); + HotwordRecognitionService.this.onStartHotwordRecognition(mCurrentCallback); + } else { + try { + listener.onHotwordError(HotwordRecognizer.ERROR_RECOGNIZER_BUSY); + } catch (RemoteException e) { + if (DBG) Log.d(TAG, "onError call from startRecognition failed"); + } + if (DBG) Log.d(TAG, "concurrent startRecognition received - ignoring this call"); + } + } + + private void dispatchStopRecognition(IHotwordRecognitionListener listener) { + try { + if (mCurrentCallback == null) { + listener.onHotwordError(HotwordRecognizer.ERROR_CLIENT); + Log.w(TAG, "stopRecognition called with no preceding startRecognition - ignoring"); + } else if (mCurrentCallback.mListener.asBinder() != listener.asBinder()) { + listener.onHotwordError(HotwordRecognizer.ERROR_RECOGNIZER_BUSY); + Log.w(TAG, "stopRecognition called by a different caller - ignoring"); + } else { // the correct state + HotwordRecognitionService.this.onStopHotwordRecognition(mCurrentCallback); + mCurrentCallback = null; + } + } catch (RemoteException e) { // occurs if onError fails + if (DBG) Log.d(TAG, "onError call from stopRecognition failed"); + } + } + + @Override + public IBinder onBind(final Intent intent) { + if (DBG) Log.d(TAG, "onBind, intent=" + intent); + return mBinder; + } + + @Override + public void onDestroy() { + if (DBG) Log.d(TAG, "onDestroy"); + if (mCurrentCallback != null) { + mCurrentCallback.mListener.asBinder().unlinkToDeath(mCallbackDeathRecipient, 0); + mCurrentCallback = null; + } + mBinder.clearReference(); + super.onDestroy(); + } + + /** + * Checks whether the caller has sufficient permissions + * + * @param listener to send the error message to in case of error + * @return {@code true} if the caller has enough permissions, {@code false} otherwise + */ + private boolean checkPermissions(IHotwordRecognitionListener listener) { + if (DBG) Log.d(TAG, "checkPermissions"); + if (checkCallingOrSelfPermission( + android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { + return true; + } + try { + Log.e(TAG, "Recognition service called without RECORD_AUDIO permissions"); + listener.onHotwordError(HotwordRecognizer.ERROR_INSUFFICIENT_PERMISSIONS); + } catch (RemoteException e) { + Log.e(TAG, "onHotwordError(ERROR_INSUFFICIENT_PERMISSIONS) message failed", e); + } + return false; + } + + /** + * Notifies the service to start a recognition. + * + * @param callback that receives the callbacks from the service. + */ + public abstract void onStartHotwordRecognition(Callback callback); + + /** + * Notifies the service to stop recognition. + * + * @param callback that receives the callbacks from the service. + */ + public abstract void onStopHotwordRecognition(Callback callback); + + /** Binder of the hotword recognition service */ + private static class RecognitionServiceBinder extends IHotwordRecognitionService.Stub { + private HotwordRecognitionService mInternalService; + + public RecognitionServiceBinder(HotwordRecognitionService service) { + mInternalService = service; + } + + public void startHotwordRecognition(IHotwordRecognitionListener listener) { + if (DBG) Log.d(TAG, "startRecognition called by: " + listener.asBinder()); + if (mInternalService != null && mInternalService.checkPermissions(listener)) { + mInternalService.mHandler.sendMessage( + Message.obtain(mInternalService.mHandler, MSG_START_RECOGNITION, listener)); + } + } + + public void stopHotwordRecognition(IHotwordRecognitionListener listener) { + if (DBG) Log.d(TAG, "stopRecognition called by: " + listener.asBinder()); + if (mInternalService != null) { + mInternalService.mHandler.sendMessage( + Message.obtain(mInternalService.mHandler, MSG_STOP_RECOGNITION, listener)); + } + } + + private void clearReference() { + mInternalService = null; + } + } + + /** + * This class acts passes on the callbacks received from the Hotword service + * to the listener. + */ + public static class Callback { + private final IHotwordRecognitionListener mListener; + + private Callback(IHotwordRecognitionListener listener) { + mListener = listener; + } + + /** + * Called when the service starts listening for hotword. + */ + public void onHotwordRecognitionStarted() throws RemoteException { + mListener.onHotwordRecognitionStarted(); + } + + /** + * Called when the service starts listening for hotword. + */ + public void onHotwordRecognitionStopped() throws RemoteException { + mListener.onHotwordRecognitionStopped(); + } + + /** + * Called on an event of interest to the client. + * + * @param eventType the event type. Event types are defined in {@link HotwordRecognizer}. + * @param eventBundle a Bundle containing the hotword event(s). + */ + public void onHotwordEvent(int eventType, Bundle eventBundle) throws RemoteException { + mListener.onHotwordEvent(eventType, eventBundle); + } + + /** + * Called back when hotword is detected. + * The action tells the client what action to take, post hotword-detection. + */ + public void onHotwordRecognized(PendingIntent intent) throws RemoteException { + mListener.onHotwordRecognized(intent); + } + + /** + * Called when the HotwordRecognitionService encounters an error. + * + * @param errorCode the error code describing the error that was encountered. + * Error codes are defined in {@link HotwordRecognizer}. + */ + public void onError(int errorCode) throws RemoteException { + mListener.onHotwordError(errorCode); + } + } +} diff --git a/core/java/android/speech/hotword/HotwordRecognizer.java b/core/java/android/speech/hotword/HotwordRecognizer.java new file mode 100644 index 0000000..bdb59f7 --- /dev/null +++ b/core/java/android/speech/hotword/HotwordRecognizer.java @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.speech.hotword; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * This class provides access to the Hotword recognition service. + * This class's methods must be invoked on the main application thread. + * {@hide} + */ +public class HotwordRecognizer { + /** DEBUG value to enable verbose debug prints */ + // TODO: Turn off. + private final static boolean DBG = true; + + /** Log messages identifier */ + private static final String TAG = "HotwordRecognizer"; + + /** + * Key used to retrieve a string to be displayed to the user passed to the + * {@link android.speech.hotword.HotwordRecognitionListener#onHotwordEvent(int, Bundle)} method. + */ + public static final String PROMPT_TEXT = "prompt_text"; + + /** + * Event type used to indicate to the user that the hotword service has changed + * its state. + */ + public static final int EVENT_TYPE_STATE_CHANGED = 1; + + /** Audio recording error. */ + public static final int ERROR_AUDIO = 1; + + /** RecognitionService busy. */ + public static final int ERROR_RECOGNIZER_BUSY = 2; + + /** Insufficient permissions */ + public static final int ERROR_INSUFFICIENT_PERMISSIONS = 3; + + /** Client-side errors */ + public static final int ERROR_CLIENT = 4; + + /** The service timed out */ + public static final int ERROR_TIMEOUT = 5; + + /** The service received concurrent start calls */ + public static final int WARNING_SERVICE_ALREADY_STARTED = 6; + + /** action codes */ + private static final int MSG_START = 1; + private static final int MSG_STOP = 2; + private final static int MSG_CHANGE_LISTENER = 3; + + /** The underlying HotwordRecognitionService endpoint */ + private IHotwordRecognitionService mService; + + /** The connection to the actual service */ + private Connection mConnection; + + /** Context with which the manager was created */ + private final Context mContext; + + /** Component to direct service intent to */ + private final ComponentName mServiceComponent; + + /** Handler that will execute the main tasks */ + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_START: + handleStartRecognition(); + break; + case MSG_STOP: + handleStopRecognition(); + break; + case MSG_CHANGE_LISTENER: + handleChangeListener((HotwordRecognitionListener) msg.obj); + break; + } + } + }; + + /** + * Temporary queue, saving the messages until the connection will be established, afterwards, + * only mHandler will receive the messages + */ + private final Queue<Message> mPendingTasks = new LinkedList<Message>(); + + /** The Listener that will receive all the callbacks */ + private final InternalListener mListener = new InternalListener(); + + /** + * Checks whether a hotword recognition service is available on the system. If this method + * returns {@code false}, {@link HotwordRecognizer#createHotwordRecognizer(Context)} will + * fail. + * + * @param context with which {@code HotwordRecognizer} will be created + * @return {@code true} if recognition is available, {@code false} otherwise + */ + public static boolean isHotwordRecognitionAvailable(final Context context) { + final List<ResolveInfo> list = context.getPackageManager().queryIntentServices( + new Intent(HotwordRecognitionService.SERVICE_INTERFACE), 0); + return list != null && list.size() != 0; + } + + /** + * Factory method to create a new {@code HotwordRecognizer}. Please note that + * {@link #setRecognitionListener(HotwordRecognitionListener)} + * should be called before dispatching any command to the created {@code HotwordRecognizer}, + * otherwise no notifications will be received. + * + * @param context in which to create {@code HotwordRecognizer} + * @return a new {@code HotwordRecognizer} + */ + public static HotwordRecognizer createHotwordRecognizer(final Context context) { + return createHotwordRecognizer(context, null); + } + + + /** + * Factory method to create a new {@code HotwordRecognizer}. Please note that + * {@link #setRecognitionListener(HotwordRecognitionListener)} + * should be called before dispatching any command to the created {@code HotwordRecognizer}, + * otherwise no notifications will be received. + * + * Use this version of the method to specify a specific service to direct this + * {@link HotwordRecognizer} to. Normally you would not use this; use + * {@link #createHotwordRecognizer(Context)} instead to use the system default recognition + * service. + * + * @param context in which to create {@code HotwordRecognizer} + * @param serviceComponent the {@link ComponentName} of a specific service to direct this + * {@code HotwordRecognizer} to + * @return a new {@code HotwordRecognizer} + */ + public static HotwordRecognizer createHotwordRecognizer( + final Context context, final ComponentName serviceComponent) { + if (context == null) { + throw new IllegalArgumentException("Context cannot be null)"); + } + checkIsCalledFromMainThread(); + return new HotwordRecognizer(context, serviceComponent); + } + + /** + * Sets the listener that will receive all the callbacks. The previous unfinished commands will + * be executed with the old listener, while any following command will be executed with the new + * listener. + * + * @param listener listener that will receive all the callbacks from the created + * {@link HotwordRecognizer}, this must not be null. + */ + public void setRecognitionListener(HotwordRecognitionListener listener) { + checkIsCalledFromMainThread(); + putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener)); + } + + /** + * Starts recognizing hotword. Please note that + * {@link #setRecognitionListener(HotwordRecognitionListener)} should be called beforehand, + * otherwise no notifications will be received. + */ + public void startRecognition() { + checkIsCalledFromMainThread(); + if (mConnection == null) { // first time connection + mConnection = new Connection(); + + Intent serviceIntent = new Intent(HotwordRecognitionService.SERVICE_INTERFACE); + + + if (mServiceComponent == null) { + // TODO: Resolve the ComponentName here and use it. + String serviceComponent = null; + if (TextUtils.isEmpty(serviceComponent)) { + Log.e(TAG, "no selected voice recognition service"); + mListener.onHotwordError(ERROR_CLIENT); + return; + } + serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent)); + } else { + serviceIntent.setComponent(mServiceComponent); + } + + if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) { + Log.e(TAG, "bind to recognition service failed"); + mConnection = null; + mService = null; + mListener.onHotwordError(ERROR_CLIENT); + return; + } + } else { + mListener.onHotwordError(WARNING_SERVICE_ALREADY_STARTED); + return; + } + putMessage(Message.obtain(mHandler, MSG_START)); + } + + /** + * Stops recognizing hotword. Please note that + * {@link #setRecognitionListener(HotwordRecognitionListener)} should be called beforehand, + * otherwise no notifications will be received. + */ + public void stopRecognition() { + checkIsCalledFromMainThread(); + putMessage(Message.obtain(mHandler, MSG_STOP)); + } + + // Private constructor. + private HotwordRecognizer(Context context, ComponentName serviceComponent) { + mContext = context; + mServiceComponent = serviceComponent; + } + + /** + * Destroys the {@code HotwordRecognizer} object. + */ + public void destroy() { + if (mConnection != null) { + mContext.unbindService(mConnection); + } + mPendingTasks.clear(); + mService = null; + mConnection = null; + mListener.mInternalListener = null; + } + + private void handleStartRecognition() { + if (!checkOpenConnection()) { + return; + } + try { + mService.startHotwordRecognition(mListener); + if (DBG) Log.d(TAG, "service startRecognition command succeeded"); + } catch (final RemoteException e) { + Log.e(TAG, "startRecognition() failed", e); + mListener.onHotwordError(ERROR_CLIENT); + } + } + + + private void handleStopRecognition() { + if (!checkOpenConnection()) { + return; + } + try { + mService.stopHotwordRecognition(mListener); + if (DBG) Log.d(TAG, "service stopRecognition command succeeded"); + } catch (final RemoteException e) { + Log.e(TAG, "stopRecognition() failed", e); + mListener.onHotwordError(ERROR_CLIENT); + } + } + + /** changes the listener */ + private void handleChangeListener(HotwordRecognitionListener listener) { + if (DBG) Log.d(TAG, "handleChangeListener, listener=" + listener); + mListener.mInternalListener = listener; + } + + private boolean checkOpenConnection() { + if (mService != null) { + return true; + } + mListener.onHotwordError(ERROR_CLIENT); + Log.e(TAG, "not connected to the recognition service"); + return false; + } + + private static void checkIsCalledFromMainThread() { + if (Looper.myLooper() != Looper.getMainLooper()) { + throw new RuntimeException( + "HotwordRecognizer should be used only from the application's main thread"); + } + } + + private void putMessage(Message msg) { + if (mService == null) { + mPendingTasks.offer(msg); + } else { + mHandler.sendMessage(msg); + } + } + + /** + * Basic ServiceConnection that records the mService variable. + * Additionally, on creation it invokes + * {@link IHotwordRecognitionService#startHotwordRecognition(IHotwordRecognitionListener)}. + */ + private class Connection implements ServiceConnection { + + public void onServiceConnected(final ComponentName name, final IBinder service) { + // always done on the application main thread, so no need to send message to mHandler + mService = IHotwordRecognitionService.Stub.asInterface(service); + if (DBG) Log.d(TAG, "onServiceConnected - Success"); + while (!mPendingTasks.isEmpty()) { + mHandler.sendMessage(mPendingTasks.poll()); + } + } + + public void onServiceDisconnected(final ComponentName name) { + // always done on the application main thread, so no need to send message to mHandler + mService = null; + mConnection = null; + mPendingTasks.clear(); + if (DBG) Log.d(TAG, "onServiceDisconnected - Success"); + } + } + + /** + * Internal wrapper of IHotwordRecognitionListener which will propagate the results to + * HotwordRecognitionListener. + */ + private class InternalListener extends IHotwordRecognitionListener.Stub { + private HotwordRecognitionListener mInternalListener; + + private final static int MSG_ON_START = 1; + private final static int MSG_ON_STOP = 2; + private final static int MSG_ON_EVENT = 3; + private final static int MSG_ON_RECOGNIZED = 4; + private final static int MSG_ON_ERROR = 5; + + private final Handler mInternalHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (mInternalListener == null) { + return; + } + switch (msg.what) { + case MSG_ON_START: + mInternalListener.onHotwordRecognitionStarted(); + break; + case MSG_ON_STOP: + mInternalListener.onHotwordRecognitionStopped(); + break; + case MSG_ON_EVENT: + mInternalListener.onHotwordEvent(msg.arg1, (Bundle) msg.obj); + break; + case MSG_ON_RECOGNIZED: + mInternalListener.onHotwordRecognized((PendingIntent) msg.obj); + break; + case MSG_ON_ERROR: + mInternalListener.onHotwordError((Integer) msg.obj); + break; + } + } + }; + + @Override + public void onHotwordRecognitionStarted() throws RemoteException { + Message.obtain(mInternalHandler, MSG_ON_START).sendToTarget(); + } + + @Override + public void onHotwordRecognitionStopped() throws RemoteException { + Message.obtain(mInternalHandler, MSG_ON_STOP).sendToTarget(); + } + + @Override + public void onHotwordEvent(final int eventType, final Bundle params) { + Message.obtain(mInternalHandler, MSG_ON_EVENT, eventType, eventType, params) + .sendToTarget(); + } + + @Override + public void onHotwordRecognized(PendingIntent intent) throws RemoteException { + Message.obtain(mInternalHandler, MSG_ON_RECOGNIZED, intent) + .sendToTarget(); + } + + @Override + public void onHotwordError(final int error) { + Message.obtain(mInternalHandler, MSG_ON_ERROR, error).sendToTarget(); + } + } +} diff --git a/core/java/android/speech/hotword/IHotwordRecognitionListener.aidl b/core/java/android/speech/hotword/IHotwordRecognitionListener.aidl new file mode 100644 index 0000000..49c5233 --- /dev/null +++ b/core/java/android/speech/hotword/IHotwordRecognitionListener.aidl @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.speech.hotword; + +import android.app.PendingIntent; +import android.os.Bundle; + +/** + * Listener for hotword detection events. + * This indicates when the hotword was detected, and also notifies the + * client of the intermediate events that may be used to show visual feedback + * to the user. + * {@hide} + */ +oneway interface IHotwordRecognitionListener { + /** + * Called when the service starts listening for hotword. + */ + void onHotwordRecognitionStarted(); + + /** + * Called when the service starts listening for hotword. + */ + void onHotwordRecognitionStopped(); + + /** + * Called on an event of interest to the client. + * + * @param eventType the event type. + * @param eventBundle a Bundle containing the hotword event(s). + */ + void onHotwordEvent(in int eventType, in Bundle eventBundle); + + /** + * Called back when hotword is detected. + * The action tells the client what action to take, post hotword-detection. + */ + void onHotwordRecognized(in PendingIntent intent); + + /** + * Called when the HotwordRecognitionService encounters an error. + * + * @param errorCode the error code describing the error that was encountered. + */ + void onHotwordError(in int errorCode); +} diff --git a/core/java/android/speech/hotword/IHotwordRecognitionService.aidl b/core/java/android/speech/hotword/IHotwordRecognitionService.aidl new file mode 100644 index 0000000..331d81c --- /dev/null +++ b/core/java/android/speech/hotword/IHotwordRecognitionService.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.speech.hotword; + +import android.speech.hotword.IHotwordRecognitionListener; + +/** + * A service interface to Hotword recognition. + * Call startHotwordDetection with a listener when you want to begin detecting + * hotword; + * The service would automatically stop detection when hotword is detected; + * So it's a create-once use-once service. + * The service doesn't support nested calls to start detection and disallows them. + * {@hide} + */ +oneway interface IHotwordRecognitionService { + /** + * Start hotword recognition. + * The clients should rely on the callback to figure out if the detection was + * started. + * + * @param listener a listener to notify of hotword events. + */ + void startHotwordRecognition(in IHotwordRecognitionListener listener); + + /** + * Stop hotword recognition. + * Stops the recognition only if it was started by the same caller. + * + * @param listener a listener to notify of hotword events. + */ + void stopHotwordRecognition(in IHotwordRecognitionListener listener); +} |