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

Commit 378b540f authored by Peter Li's avatar Peter Li Committed by Android (Google) Code Review
Browse files

Merge "Add callback to report HotwordDetectionService status" into sc-dev

parents d4a14271 6f2a2638
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -10372,6 +10372,7 @@ package android.service.voice {
  public abstract static class AlwaysOnHotwordDetector.Callback implements android.service.voice.HotwordDetector.Callback {
    ctor public AlwaysOnHotwordDetector.Callback();
    method public abstract void onAvailabilityChanged(int);
    method public void onHotwordDetectionServiceInitialized(int);
    method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
  }
@@ -10419,7 +10420,11 @@ package android.service.voice {
    method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.Callback);
    method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.service.voice.HotwordDetectionService.Callback);
    method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @NonNull android.service.voice.HotwordDetectionService.Callback);
    method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
    method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
    field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1; // 0x1
    field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2; // 0x2
    field public static final int INITIALIZATION_STATUS_SUCCESS = 0; // 0x0
    field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64
    field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
  }
@@ -10441,6 +10446,7 @@ package android.service.voice {
  public static interface HotwordDetector.Callback {
    method public void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
    method public void onError();
    method public void onHotwordDetectionServiceInitialized(int);
    method public void onRecognitionPaused();
    method public void onRecognitionResumed();
    method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+46 −6
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.service.voice.HotwordDetectionService.InitializationStatus;
import android.util.Slog;

import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -260,6 +261,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
    private static final int MSG_DETECTION_PAUSE = 4;
    private static final int MSG_DETECTION_RESUME = 5;
    private static final int MSG_HOTWORD_REJECTED = 6;
    private static final int MSG_HOTWORD_STATUS_REPORTED = 7;

    private final String mText;
    private final Locale mLocale;
@@ -523,6 +525,15 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
         */
        public void onRejected(@Nullable HotwordRejectedResult result) {
        }

        /**
         * Called when the {@link HotwordDetectionService} is created by the system and given a
         * short amount of time to report it's initialization state.
         *
         * @param status Info about initialization state of {@link HotwordDetectionService}.
         */
        public void onHotwordDetectionServiceInitialized(@InitializationStatus int status) {
        }
    }

    /**
@@ -559,7 +570,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
        mTargetSdkVersion = targetSdkVersion;
        mSupportHotwordDetectionService = supportHotwordDetectionService;
        if (mSupportHotwordDetectionService) {
            updateState(options, sharedMemory);
            updateStateLocked(options, sharedMemory, mInternalCallback);
        }
        try {
            Identity identity = new Identity();
@@ -583,20 +594,34 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
     * such data to the trusted process.
     *
     * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
     * {@link HotwordDetectionService} when it was created.
     * {@link HotwordDetectionService} when it was created. In addition, if this
     * AlwaysOnHotwordDetector is in an invalid or error state.
     */
    public final void updateState(@Nullable PersistableBundle options,
            @Nullable SharedMemory sharedMemory) {
        if (DBG) {
            Slog.d(TAG, "updateState()");
        }
        synchronized (mLock) {
            if (!mSupportHotwordDetectionService) {
                throw new IllegalStateException(
                        "updateState called, but it doesn't support hotword detection service");
            }
            if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
                throw new IllegalStateException(
                        "updateState called on an invalid detector or error state");
            }
            updateStateLocked(options, sharedMemory, null /* callback */);
        }
    }

    private void updateStateLocked(@Nullable PersistableBundle options,
            @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
        if (DBG) {
            Slog.d(TAG, "updateStateLocked()");
        }
        try {
            mModelManagementService.updateState(options, sharedMemory);
            mModelManagementService.updateState(options, sharedMemory, callback);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -1147,6 +1172,18 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
            Slog.i(TAG, "onRecognitionResumed");
            mHandler.sendEmptyMessage(MSG_DETECTION_RESUME);
        }

        @Override
        public void onStatusReported(int status) {
            if (DBG) {
                Slog.d(TAG, "onStatusReported(" + status + ")");
            } else {
                Slog.i(TAG, "onStatusReported");
            }
            Message message = Message.obtain(mHandler, MSG_HOTWORD_STATUS_REPORTED);
            message.arg1 = status;
            message.sendToTarget();
        }
    }

    class MyHandler extends Handler {
@@ -1178,6 +1215,9 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
                case MSG_HOTWORD_REJECTED:
                    mExternalCallback.onRejected((HotwordRejectedResult) msg.obj);
                    break;
                case MSG_HOTWORD_STATUS_REPORTED:
                    mExternalCallback.onHotwordDetectionServiceInitialized(msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
+68 −6
Original line number Diff line number Diff line
@@ -37,10 +37,13 @@ import android.os.RemoteException;
import android.os.SharedMemory;
import android.util.Log;

import com.android.internal.app.IHotwordRecognitionStatusCallback;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
import java.util.function.IntConsumer;

/**
 * Implemented by an application that wants to offer detection for hotword. The system will
@@ -54,6 +57,39 @@ public abstract class HotwordDetectionService extends Service {
    // TODO (b/177502877): Set the Debug flag to false before shipping.
    private static final boolean DBG = true;

    private static final long UPDATE_TIMEOUT_MILLIS = 5000;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "INITIALIZATION_STATUS_" }, value = {
            INITIALIZATION_STATUS_SUCCESS,
            INITIALIZATION_STATUS_CUSTOM_ERROR_1,
            INITIALIZATION_STATUS_CUSTOM_ERROR_2,
            INITIALIZATION_STATUS_UNKNOWN,
    })
    public @interface InitializationStatus {}

    /**
     * Indicates that the updated status is successful.
     */
    public static final int INITIALIZATION_STATUS_SUCCESS = 0;

    /**
     * Indicates that the updated status is failure for some application specific reasons.
     */
    public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1;

    /**
     * Indicates that the updated status is failure for some application specific reasons.
     */
    public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2;

    /**
     * Indicates that the callback wasn’t invoked within the timeout.
     * This is used by system.
     */
    public static final int INITIALIZATION_STATUS_UNKNOWN = 100;

    /**
     * Source for the given audio stream.
     *
@@ -104,15 +140,16 @@ public abstract class HotwordDetectionService extends Service {
        }

        @Override
        public void updateState(PersistableBundle options, SharedMemory sharedMemory)
                throws RemoteException {
        public void updateState(PersistableBundle options, SharedMemory sharedMemory,
                IHotwordRecognitionStatusCallback callback) throws RemoteException {
            if (DBG) {
                Log.d(TAG, "#updateState");
            }
            mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateState,
            mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateStateInternal,
                    HotwordDetectionService.this,
                    options,
                    sharedMemory));
                    sharedMemory,
                    callback));
        }

        @Override
@@ -207,12 +244,20 @@ public abstract class HotwordDetectionService extends Service {
     * @param sharedMemory The unrestricted data blob to provide to the
     * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
     * such data to the trusted process.
     * @param callbackTimeoutMillis Timeout in milliseconds for the operation to invoke the
     * statusCallback.
     * @param statusCallback Use this to return the updated result. This is non-null only when the
     * {@link HotwordDetectionService} is being initialized; and it is null if the state is updated
     * after that.
     *
     * @hide
     */
    @SystemApi
    public void onUpdateState(@Nullable PersistableBundle options,
            @Nullable SharedMemory sharedMemory) {
    public void onUpdateState(
            @Nullable PersistableBundle options,
            @Nullable SharedMemory sharedMemory,
            @DurationMillisLong long callbackTimeoutMillis,
            @Nullable @InitializationStatus IntConsumer statusCallback) {
        // TODO: Handle the unimplemented case by throwing?
    }

@@ -268,6 +313,23 @@ public abstract class HotwordDetectionService extends Service {
        throw new UnsupportedOperationException();
    }

    private void onUpdateStateInternal(@Nullable PersistableBundle options,
            @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
        // TODO (b/183684347): Implement timeout case.
        IntConsumer intConsumer = null;
        if (callback != null) {
            intConsumer =
                    value -> {
                        try {
                            callback.onStatusReported(value);
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    };
        }
        onUpdateState(options, sharedMemory, UPDATE_TIMEOUT_MILLIS, intConsumer);
    }

    /**
     * Callback for returning the detection result.
     *
+9 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.annotation.SystemApi;
import android.media.AudioFormat;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.service.voice.HotwordDetectionService.InitializationStatus;

/**
 * Basic functionality for hotword detectors.
@@ -144,5 +145,13 @@ public interface HotwordDetector {
         *         {@link HotwordDetectionService}.
         */
        void onRejected(@Nullable HotwordRejectedResult result);

        /**
         * Called when the {@link HotwordDetectionService} is created by the system and given a
         * short amount of time to report it's initialization state.
         *
         * @param status Info about initialization state of {@link HotwordDetectionService}.
         */
        void onHotwordDetectionServiceInitialized(@InitializationStatus int status);
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.os.PersistableBundle;
import android.os.SharedMemory;
import android.service.voice.IDspHotwordDetectionCallback;

import com.android.internal.app.IHotwordRecognitionStatusCallback;

/**
 * Provide the interface to communicate with hotword detection service.
 *
@@ -41,5 +43,6 @@ oneway interface IHotwordDetectionService {
        in PersistableBundle options,
        in IDspHotwordDetectionCallback callback);

    void updateState(in PersistableBundle options, in SharedMemory sharedMemory);
    void updateState(in PersistableBundle options, in SharedMemory sharedMemory,
            in IHotwordRecognitionStatusCallback callback);
}
Loading