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

Commit 98900f7f authored by Ivan Chiang's avatar Ivan Chiang Committed by Android (Google) Code Review
Browse files

Merge "Hotword: Add Executor into HotwordDetector."

parents 89bd9fed b1ae847b
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -13174,9 +13174,12 @@ package android.service.voice {
  }
  public class VoiceInteractionService extends android.app.Service {
    method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.service.voice.HotwordDetector.Callback);
    method @Deprecated @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(@NonNull String, @NonNull java.util.Locale, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(@NonNull String, @NonNull java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.service.voice.HotwordDetector.Callback);
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.HotwordDetector.Callback);
    method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.VisualQueryDetector createVisualQueryDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VisualQueryDetector.Callback);
  }
+21 −26
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package android.service.voice;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +25,7 @@ import android.media.AudioFormat;
import android.media.permission.Identity;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -38,6 +37,7 @@ import android.util.Slog;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionManagerService;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

@@ -57,7 +57,7 @@ abstract class AbstractDetector implements HotwordDetector {
    protected final Object mLock = new Object();

    private final IVoiceInteractionManagerService mManagerService;
    private final Handler mHandler;
    private final Executor mExecutor;
    private final HotwordDetector.Callback mCallback;
    private Consumer<AbstractDetector> mOnDestroyListener;
    private final AtomicBoolean mIsDetectorActive;
@@ -68,11 +68,12 @@ abstract class AbstractDetector implements HotwordDetector {

    AbstractDetector(
            IVoiceInteractionManagerService managerService,
            Executor executor,
            HotwordDetector.Callback callback) {
        mManagerService = managerService;
        // TODO: this needs to be supplied from above
        mHandler = new Handler(Looper.getMainLooper());
        mCallback = callback;
        mExecutor = executor != null ? executor : new HandlerExecutor(
                new Handler(Looper.getMainLooper()));
        mIsDetectorActive = new AtomicBoolean(true);
    }

@@ -106,7 +107,7 @@ abstract class AbstractDetector implements HotwordDetector {
                    audioFormat,
                    options,
                    mToken,
                    new BinderCallback(mHandler, mCallback));
                    new BinderCallback(mExecutor, mCallback));
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
@@ -205,13 +206,13 @@ abstract class AbstractDetector implements HotwordDetector {

    private static class BinderCallback
            extends IMicrophoneHotwordDetectionVoiceInteractionCallback.Stub {
        private final Handler mHandler;
        // TODO: these need to be weak references.
        private final HotwordDetector.Callback mCallback;
        private final Executor mExecutor;

        BinderCallback(Handler handler, HotwordDetector.Callback callback) {
            this.mHandler = handler;
        BinderCallback(Executor executor, HotwordDetector.Callback callback) {
            this.mCallback = callback;
            this.mExecutor = executor;
        }

        /** TODO: onDetected */
@@ -220,33 +221,27 @@ abstract class AbstractDetector implements HotwordDetector {
                @Nullable HotwordDetectedResult hotwordDetectedResult,
                @Nullable AudioFormat audioFormat,
                @Nullable ParcelFileDescriptor audioStreamIgnored) {
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onDetected,
                    mCallback,
                    new AlwaysOnHotwordDetector.EventPayload.Builder()
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
                mCallback.onDetected(new AlwaysOnHotwordDetector.EventPayload.Builder()
                        .setCaptureAudioFormat(audioFormat)
                        .setHotwordDetectedResult(hotwordDetectedResult)
                            .build()));
                        .build());
            }));
        }

        /** Called when the detection fails due to an error. */
        @Override
        public void onError() {
            Slog.v(TAG, "BinderCallback#onError");
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onError,
                    mCallback));
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> mCallback.onError()));
        }

        @Override
        public void onRejected(@Nullable HotwordRejectedResult result) {
            if (result == null) {
                result = new HotwordRejectedResult.Builder().build();
            }
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onRejected,
                    mCallback,
                    result));
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
                mCallback.onRejected(
                        result != null ? result : new HotwordRejectedResult.Builder().build());
            }));
        }
    }
}
+46 −32
Original line number Diff line number Diff line
@@ -46,7 +46,9 @@ import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -71,6 +73,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * A class that lets a VoiceInteractionService implementation interact with
@@ -290,6 +293,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
    private IVoiceInteractionSoundTriggerSession mSoundTriggerSession;
    private final SoundTriggerListener mInternalCallback;
    private final Callback mExternalCallback;
    private final Executor mExternalExecutor;
    private final Handler mHandler;
    private final IBinder mBinder = new Binder();
    private final int mTargetSdkVersion;
@@ -761,17 +765,19 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
     *
     * @hide
     */
    public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
    public AlwaysOnHotwordDetector(String text, Locale locale, Executor executor, Callback callback,
            KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
            IVoiceInteractionManagerService modelManagementService, int targetSdkVersion,
            boolean supportSandboxedDetectionService) {
        super(modelManagementService, callback);
        super(modelManagementService, executor, callback);

        mHandler = new MyHandler();
        mHandler = new MyHandler(Looper.getMainLooper());
        mText = text;
        mLocale = locale;
        mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
        mExternalCallback = callback;
        mExternalExecutor = executor != null ? executor : new HandlerExecutor(
                new Handler(Looper.myLooper()));
        mInternalCallback = new SoundTriggerListener(mHandler);
        mModelManagementService = modelManagementService;
        mTargetSdkVersion = targetSdkVersion;
@@ -1673,6 +1679,10 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
    }

    class MyHandler extends Handler {
        MyHandler(@NonNull Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            synchronized (mLock) {
@@ -1681,13 +1691,15 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
                    return;
                }
            }

            switch (msg.what) {
            final Message message = Message.obtain(msg);
            Binder.withCleanCallingIdentity(() -> mExternalExecutor.execute(() -> {
                Slog.i(TAG, "handle message " + message.what);
                switch (message.what) {
                    case MSG_AVAILABILITY_CHANGED:
                    mExternalCallback.onAvailabilityChanged(msg.arg1);
                        mExternalCallback.onAvailabilityChanged(message.arg1);
                        break;
                    case MSG_HOTWORD_DETECTED:
                    mExternalCallback.onDetected((EventPayload) msg.obj);
                        mExternalCallback.onDetected((EventPayload) message.obj);
                        break;
                    case MSG_DETECTION_ERROR:
                        mExternalCallback.onError();
@@ -1699,17 +1711,19 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
                        mExternalCallback.onRecognitionResumed();
                        break;
                    case MSG_HOTWORD_REJECTED:
                    mExternalCallback.onRejected((HotwordRejectedResult) msg.obj);
                        mExternalCallback.onRejected((HotwordRejectedResult) message.obj);
                        break;
                    case MSG_HOTWORD_STATUS_REPORTED:
                    mExternalCallback.onHotwordDetectionServiceInitialized(msg.arg1);
                        mExternalCallback.onHotwordDetectionServiceInitialized(message.arg1);
                        break;
                    case MSG_PROCESS_RESTARTED:
                        mExternalCallback.onHotwordDetectionServiceRestarted();
                        break;
                    default:
                    super.handleMessage(msg);
                        super.handleMessage(message);
                }
                message.recycle();
            }));
        }
    }

+33 −39
Original line number Diff line number Diff line
@@ -18,13 +18,13 @@ package android.service.voice;

import static android.Manifest.permission.RECORD_AUDIO;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -37,6 +37,7 @@ import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionManagerService;

import java.io.PrintWriter;
import java.util.concurrent.Executor;

/**
 * Manages hotword detection not relying on a specific hardware.
@@ -53,24 +54,26 @@ class SoftwareHotwordDetector extends AbstractDetector {
    private final IVoiceInteractionManagerService mManagerService;
    private final HotwordDetector.Callback mCallback;
    private final AudioFormat mAudioFormat;
    private final Handler mHandler;
    private final Executor mExecutor;

    SoftwareHotwordDetector(
            IVoiceInteractionManagerService managerService,
            AudioFormat audioFormat,
            Executor executor,
            HotwordDetector.Callback callback) {
        super(managerService, callback);
        super(managerService, executor, callback);

        mManagerService = managerService;
        mAudioFormat = audioFormat;
        mCallback = callback;
        mHandler = new Handler(Looper.getMainLooper());
        mExecutor = executor != null ? executor : new HandlerExecutor(
                new Handler(Looper.getMainLooper()));
    }

    @Override
    void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
        initAndVerifyDetector(options, sharedMemory,
                new InitializationStateListener(mHandler, mCallback),
                new InitializationStateListener(mExecutor, mCallback),
                DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
    }

@@ -84,8 +87,8 @@ class SoftwareHotwordDetector extends AbstractDetector {
        maybeCloseExistingSession();

        try {
            mManagerService.startListeningFromMic(
                    mAudioFormat, new BinderCallback(mHandler, mCallback));
            mManagerService.startListeningFromMic(mAudioFormat,
                    new BinderCallback(mExecutor, mCallback));
        } catch (SecurityException e) {
            Slog.e(TAG, "startRecognition failed: " + e);
            return false;
@@ -140,13 +143,13 @@ class SoftwareHotwordDetector extends AbstractDetector {

    private static class BinderCallback
            extends IMicrophoneHotwordDetectionVoiceInteractionCallback.Stub {
        private final Handler mHandler;
        // TODO: this needs to be a weak reference.
        private final HotwordDetector.Callback mCallback;
        private final Executor mExecutor;

        BinderCallback(Handler handler, HotwordDetector.Callback callback) {
            this.mHandler = handler;
        BinderCallback(Executor executor, HotwordDetector.Callback callback) {
            this.mCallback = callback;
            this.mExecutor = executor;
        }

        /** Called when the detected result is valid. */
@@ -155,45 +158,39 @@ class SoftwareHotwordDetector extends AbstractDetector {
                @Nullable HotwordDetectedResult hotwordDetectedResult,
                @Nullable AudioFormat audioFormat,
                @Nullable ParcelFileDescriptor audioStream) {
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onDetected,
                    mCallback,
                    new AlwaysOnHotwordDetector.EventPayload.Builder()
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
                mCallback.onDetected(new AlwaysOnHotwordDetector.EventPayload.Builder()
                        .setCaptureAudioFormat(audioFormat)
                        .setAudioStream(audioStream)
                        .setHotwordDetectedResult(hotwordDetectedResult)
                            .build()));
                        .build());
            }));
        }

        /** Called when the detection fails due to an error. */
        @Override
        public void onError() {
            Slog.v(TAG, "BinderCallback#onError");
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onError,
                    mCallback));
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> mCallback.onError()));
        }

        @Override
        public void onRejected(@Nullable HotwordRejectedResult result) {
            if (result == null) {
                result = new HotwordRejectedResult.Builder().build();
            }
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onRejected,
                    mCallback,
                    result));
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
                mCallback.onRejected(
                        result != null ? result : new HotwordRejectedResult.Builder().build());
            }));
        }
    }

    private static class InitializationStateListener
            extends IHotwordRecognitionStatusCallback.Stub {
        private final Handler mHandler;
        private final HotwordDetector.Callback mCallback;
        private final Executor mExecutor;

        InitializationStateListener(Handler handler, HotwordDetector.Callback callback) {
            this.mHandler = handler;
        InitializationStateListener(Executor executor, HotwordDetector.Callback callback) {
            this.mCallback = callback;
            this.mExecutor = executor;
        }

        @Override
@@ -244,18 +241,15 @@ class SoftwareHotwordDetector extends AbstractDetector {
        @Override
        public void onStatusReported(int status) {
            Slog.v(TAG, "onStatusReported" + (DEBUG ? "(" + status + ")" : ""));
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onHotwordDetectionServiceInitialized,
                    mCallback,
                    status));
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
                    () -> mCallback.onHotwordDetectionServiceInitialized(status)));
        }

        @Override
        public void onProcessRestarted() throws RemoteException {
            Slog.v(TAG, "onProcessRestarted()");
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onHotwordDetectionServiceRestarted,
                    mCallback));
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
                    () -> mCallback.onHotwordDetectionServiceRestarted()));
        }
    }

+1 −1
Original line number Diff line number Diff line
@@ -223,7 +223,7 @@ public class VisualQueryDetector {
    private class VisualQueryDetectorInitializationDelegate extends AbstractDetector {

        VisualQueryDetectorInitializationDelegate() {
            super(mManagerService, null);
            super(mManagerService, mExecutor, /* callback= */ null);
        }

        @Override
Loading