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

Commit c6409721 authored by Peter Li's avatar Peter Li Committed by Automerger Merge Worker
Browse files

Merge "Add callback to report HotwordDetectionService status" into sc-dev am: 378b540f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13974087

Change-Id: Id279e0c945dea67e2ae94859e5b5ee2f5f785caf
parents 68b6ff5f 378b540f
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