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

Commit 4348003a authored by Ahaan Ugale's avatar Ahaan Ugale Committed by Android (Google) Code Review
Browse files

Merge "Create basic behavior of HotwordDetectionService"

parents a9afbdd1 205ee1ef
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ package android {
    field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
    field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
    field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
    field public static final String BIND_HOTWORD_DETECTION_SERVICE = "android.permission.BIND_HOTWORD_DETECTION_SERVICE";
    field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
    field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
    field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE";
@@ -10211,6 +10212,7 @@ package android.service.voice {
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
    field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
    field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
    field public static final int HOTWORD_DETECTION_FALSE_ALERT = 0; // 0x0
    field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
    field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
    field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
@@ -10233,6 +10235,7 @@ package android.service.voice {
    method public abstract void onError();
    method public abstract void onRecognitionPaused();
    method public abstract void onRecognitionResumed();
    method public void onRejected(int);
  }
  public static class AlwaysOnHotwordDetector.EventPayload {
@@ -10245,9 +10248,24 @@ package android.service.voice {
    method public int getStart();
  }
  public abstract class HotwordDetectionService extends android.app.Service {
    ctor public HotwordDetectionService();
    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
    method public void onDetectFromDspSource(int, @NonNull android.service.voice.HotwordDetectionService.DspHotwordDetectionCallback);
    field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
  }
  public static final class HotwordDetectionService.DspHotwordDetectionCallback {
    method public void onDetected();
    method public void onRejected();
  }
  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.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
    method public final int setHotwordDetectionConfig(@Nullable android.os.Bundle);
    field public static final int HOTWORD_CONFIG_FAILURE = 1; // 0x1
    field public static final int HOTWORD_CONFIG_SUCCESS = 0; // 0x0
  }
}
+35 −2
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
import android.hardware.soundtrigger.KeyphraseMetadata;
import android.hardware.soundtrigger.SoundTrigger;
@@ -48,6 +47,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;

import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSoundTriggerSession;

@@ -236,6 +236,18 @@ public class AlwaysOnHotwordDetector {
    })
    public @interface ModelParams {}

    /**
     * Indicates that the given audio data is a false alert for {@link VoiceInteractionService}.
     */
    public static final int HOTWORD_DETECTION_FALSE_ALERT = 0;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "HOTWORD_DETECTION_" }, value = {
            HOTWORD_DETECTION_FALSE_ALERT,
    })
    public @interface HotwordDetectionResult {}

    /**
     * Controls the sensitivity threshold adjustment factor for a given model.
     * Negative value corresponds to less sensitive model (high threshold) and
@@ -256,6 +268,7 @@ public class AlwaysOnHotwordDetector {
    private static final int MSG_DETECTION_ERROR = 3;
    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 final String mText;
    private final Locale mLocale;
@@ -460,6 +473,13 @@ public class AlwaysOnHotwordDetector {
         * except showing an indication on their UI if they have to.
         */
        public abstract void onRecognitionResumed();

        /**
         * Called when the validated result is invalid.
         *
         * @param reason The reason why the validated result is invalid.
         */
        public void onRejected(@HotwordDetectionResult int reason) {}
    }

    /**
@@ -966,7 +986,7 @@ public class AlwaysOnHotwordDetector {
    }

    /** @hide */
    static final class SoundTriggerListener extends IRecognitionStatusCallback.Stub {
    static final class SoundTriggerListener extends IHotwordRecognitionStatusCallback.Stub {
        private final Handler mHandler;

        public SoundTriggerListener(Handler handler) {
@@ -990,6 +1010,16 @@ public class AlwaysOnHotwordDetector {
            Slog.w(TAG, "Generic sound trigger event detected at AOHD: " + event);
        }

        @Override
        public void onRejected(int reason) {
            if (DBG) {
                Slog.d(TAG, "onRejected(" + reason + ")");
            } else {
                Slog.i(TAG, "onRejected");
            }
            Message.obtain(mHandler, MSG_HOTWORD_REJECTED, reason).sendToTarget();
        }

        @Override
        public void onError(int status) {
            Slog.i(TAG, "onError: " + status);
@@ -1035,6 +1065,9 @@ public class AlwaysOnHotwordDetector {
                case MSG_DETECTION_RESUME:
                    mExternalCallback.onRecognitionResumed();
                    break;
                case MSG_HOTWORD_REJECTED:
                    mExternalCallback.onRejected(msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
+140 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.service.voice;

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

import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;

/**
 * Implemented by an application that wants to offer detection for hotword. The system will
 * start the service after calling {@link VoiceInteractionService#setHotwordDetectionConfig}.
 *
 * @hide
 */
@SystemApi
public abstract class HotwordDetectionService extends Service {
    private static final String TAG = "HotwordDetectionService";
    // TODO (b/177502877): Set the Debug flag to false before shipping.
    private static final boolean DBG = true;

    /**
     * The {@link Intent} that must be declared as handled by the service.
     * To be supported, the service must also require the
     * {@link android.Manifest.permission#BIND_HOTWORD_DETECTION_SERVICE} permission so
     * that other applications can not abuse it.
     */
    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
    public static final String SERVICE_INTERFACE =
            "android.service.voice.HotwordDetectionService";

    private Handler mHandler;

    private final IHotwordDetectionService mInterface = new IHotwordDetectionService.Stub() {
        @Override
        public void detectFromDspSource(int sessionId, IDspHotwordDetectionCallback callback)
                throws RemoteException {
            if (DBG) {
                Log.d(TAG, "#detectFromDspSource");
            }
            mHandler.sendMessage(obtainMessage(HotwordDetectionService::onDetectFromDspSource,
                    HotwordDetectionService.this,
                    sessionId, new DspHotwordDetectionCallback(callback)));
        }
    };

    @CallSuper
    @Override
    public void onCreate() {
        super.onCreate();
        mHandler = Handler.createAsync(Looper.getMainLooper());
    }

    @Override
    @Nullable
    public final IBinder onBind(@NonNull Intent intent) {
        if (SERVICE_INTERFACE.equals(intent.getAction())) {
            return mInterface.asBinder();
        }
        Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": "
                + intent);
        return null;
    }

    /**
     * Detect the audio data generated from Dsp.
     *
     * @param sessionId The session to use when attempting to capture more audio from the DSP
     *                  hardware.
     * @param callback Use {@link HotwordDetectionService#DspHotwordDetectionCallback} to return
     * the detected result.
     *
     * @hide
     */
    @SystemApi
    public void onDetectFromDspSource(int sessionId,
            @NonNull DspHotwordDetectionCallback callback) {
    }

    /**
     * Callback for returning the detected result.
     *
     * @hide
     */
    @SystemApi
    public static final class DspHotwordDetectionCallback {
        // TODO: need to make sure we don't store remote references, but not a high priority.
        private final IDspHotwordDetectionCallback mRemoteCallback;

        private DspHotwordDetectionCallback(IDspHotwordDetectionCallback remoteCallback) {
            mRemoteCallback = remoteCallback;
        }

        /**
         * Called when the detected result is valid.
         */
        public void onDetected() {
            try {
                mRemoteCallback.onDetected();
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

        /**
         * Called when the detected result is invalid.
         */
        public void onRejected() {
            try {
                mRemoteCallback.onRejected();
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }
}
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.service.voice;

/**
 * Callback for returning the detected result from the HotwordDetectionService.
 *
 * @hide
 */
oneway interface IDspHotwordDetectionCallback {
    /**
     * Called when the detected result is valid.
     */
    void onDetected();

    /**
     * Called when the detected result is invalid.
     */
    void onRejected();
}
+28 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.service.voice;

import android.service.voice.IDspHotwordDetectionCallback;

/**
 * Provide the interface to communicate with hotword detection service.
 *
 * @hide
 */
oneway interface IHotwordDetectionService {
    void detectFromDspSource(int sessionId, in IDspHotwordDetectionCallback callback);
}
Loading