Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0a17400 authored by Sandeep Siddhartha's avatar Sandeep Siddhartha Committed by Android (Google) Code Review
Browse files

Merge "Add Service API to perform Hotword recognition" into klp-dev

parents ca915793 637cc458
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -195,6 +195,8 @@ LOCAL_SRC_FILES += \
	core/java/android/view/IWindowSession.aidl \
	core/java/android/speech/IRecognitionListener.aidl \
	core/java/android/speech/IRecognitionService.aidl \
        core/java/android/speech/hotword/IHotwordRecognitionListener.aidl \
	core/java/android/speech/hotword/IHotwordRecognitionService.aidl \
	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
	core/java/android/speech/tts/ITextToSpeechService.aidl \
	core/java/com/android/internal/app/IAppOpsCallback.aidl \
+60 −0
Original line number Diff line number Diff line
/*
 * 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
+258 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+409 −0

File added.

Preview size limit exceeded, changes collapsed.

+60 −0
Original line number Diff line number Diff line
/*
 * 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);
}
Loading