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

Commit f771e6c9 authored by Nicholas Ambur's avatar Nicholas Ambur
Browse files

async enrollment support AlwaysOnHotwordDetector

Added ability for AlwaysOnHotwordDetector to support async enrollment
performed outside of support detected through KeyphraseEnrollmentInfo.

Bug: 147159435
Test: tested enrollment and availability is updated when enrolling
outside of KeyphraseEnrollmentInfo

Change-Id: Ia5d71e90c062ac100d4c6df760acf0d41920853e
parent ef84fc48
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -42845,7 +42845,7 @@ package android.service.voice {
    field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
    field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
    field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
    field public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
    field @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
  }
  public abstract static class AlwaysOnHotwordDetector.Callback {
+19 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2014 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.hardware.soundtrigger;

parcelable KeyphraseMetadata;
+147 −11
Original line number Diff line number Diff line
@@ -16,8 +16,13 @@

package android.hardware.soundtrigger;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcelable;
import android.util.ArraySet;

import com.android.internal.util.DataClass;

import java.util.Locale;

/**
@@ -25,37 +30,168 @@ import java.util.Locale;
 *
 * @hide
 */
public class KeyphraseMetadata {
@DataClass(
        genEqualsHashCode = true,
        genToString = true,
        genConstructor = false,
        genHiddenConstDefs = true)
public final class KeyphraseMetadata implements Parcelable {
    public final int id;
    @NonNull
    public final String keyphrase;
    @NonNull
    public final ArraySet<Locale> supportedLocales;
    public final int recognitionModeFlags;

    public KeyphraseMetadata(int id, String keyphrase, ArraySet<Locale> supportedLocales,
            int recognitionModeFlags) {
    public KeyphraseMetadata(int id, @NonNull String keyphrase,
            @NonNull ArraySet<Locale> supportedLocales, int recognitionModeFlags) {
        this.id = id;
        this.keyphrase = keyphrase;
        this.supportedLocales = supportedLocales;
        this.recognitionModeFlags = recognitionModeFlags;
    }

    @Override
    public String toString() {
        return "id=" + id + ", keyphrase=" + keyphrase + ", supported-locales=" + supportedLocales
                + ", recognition-modes=" + recognitionModeFlags;
    }

    /**
     * @return Indicates if we support the given phrase.
     */
    public boolean supportsPhrase(String phrase) {
    public boolean supportsPhrase(@Nullable String phrase) {
        return keyphrase.isEmpty() || keyphrase.equalsIgnoreCase(phrase);
    }

    /**
     * @return Indicates if we support the given locale.
     */
    public boolean supportsLocale(Locale locale) {
    public boolean supportsLocale(@Nullable Locale locale) {
        return supportedLocales.isEmpty() || supportedLocales.contains(locale);
    }




    // Code below generated by codegen v1.0.14.
    //
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    //
    // To regenerate run:
    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
    //
    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
    //   Settings > Editor > Code Style > Formatter Control
    //@formatter:off


    @Override
    @DataClass.Generated.Member
    public String toString() {
        // You can override field toString logic by defining methods like:
        // String fieldNameToString() { ... }

        return "KeyphraseMetadata { " +
                "id = " + id + ", " +
                "keyphrase = " + keyphrase + ", " +
                "supportedLocales = " + supportedLocales + ", " +
                "recognitionModeFlags = " + recognitionModeFlags +
        " }";
    }

    @Override
    @DataClass.Generated.Member
    public boolean equals(@Nullable Object o) {
        // You can override field equality logic by defining either of the methods like:
        // boolean fieldNameEquals(KeyphraseMetadata other) { ... }
        // boolean fieldNameEquals(FieldType otherValue) { ... }

        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        @SuppressWarnings("unchecked")
        KeyphraseMetadata that = (KeyphraseMetadata) o;
        //noinspection PointlessBooleanExpression
        return true
                && id == that.id
                && java.util.Objects.equals(keyphrase, that.keyphrase)
                && java.util.Objects.equals(supportedLocales, that.supportedLocales)
                && recognitionModeFlags == that.recognitionModeFlags;
    }

    @Override
    @DataClass.Generated.Member
    public int hashCode() {
        // You can override field hashCode logic by defining methods like:
        // int fieldNameHashCode() { ... }

        int _hash = 1;
        _hash = 31 * _hash + id;
        _hash = 31 * _hash + java.util.Objects.hashCode(keyphrase);
        _hash = 31 * _hash + java.util.Objects.hashCode(supportedLocales);
        _hash = 31 * _hash + recognitionModeFlags;
        return _hash;
    }

    @Override
    @DataClass.Generated.Member
    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
        // You can override field parcelling by defining methods like:
        // void parcelFieldName(Parcel dest, int flags) { ... }

        dest.writeInt(id);
        dest.writeString(keyphrase);
        dest.writeArraySet(supportedLocales);
        dest.writeInt(recognitionModeFlags);
    }

    @Override
    @DataClass.Generated.Member
    public int describeContents() { return 0; }

    /** @hide */
    @SuppressWarnings({"unchecked", "RedundantCast"})
    @DataClass.Generated.Member
    /* package-private */ KeyphraseMetadata(@NonNull android.os.Parcel in) {
        // You can override field unparcelling by defining methods like:
        // static FieldType unparcelFieldName(Parcel in) { ... }

        int _id = in.readInt();
        String _keyphrase = in.readString();
        ArraySet<Locale> _supportedLocales = (ArraySet) in.readArraySet(null);
        int _recognitionModeFlags = in.readInt();

        this.id = _id;
        this.keyphrase = _keyphrase;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, keyphrase);
        this.supportedLocales = _supportedLocales;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, supportedLocales);
        this.recognitionModeFlags = _recognitionModeFlags;

        // onConstructed(); // You can define this method to get a callback
    }

    @DataClass.Generated.Member
    public static final @NonNull Parcelable.Creator<KeyphraseMetadata> CREATOR
            = new Parcelable.Creator<KeyphraseMetadata>() {
        @Override
        public KeyphraseMetadata[] newArray(int size) {
            return new KeyphraseMetadata[size];
        }

        @Override
        public KeyphraseMetadata createFromParcel(@NonNull android.os.Parcel in) {
            return new KeyphraseMetadata(in);
        }
    };

    @DataClass.Generated(
            time = 1579290593964L,
            codegenVersion = "1.0.14",
            sourceFile = "frameworks/base/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java",
            inputSignatures = "public final  int id\npublic final @android.annotation.NonNull java.lang.String keyphrase\npublic final @android.annotation.NonNull android.util.ArraySet<java.util.Locale> supportedLocales\npublic final  int recognitionModeFlags\npublic  boolean supportsPhrase(java.lang.String)\npublic  boolean supportsLocale(java.util.Locale)\nclass KeyphraseMetadata extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genConstructor=false, genHiddenConstDefs=true)")
    @Deprecated
    private void __metadata() {}


    //@formatter:on
    // End of generated code

}
+27 −32
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.media.AudioFormat;
@@ -67,7 +66,12 @@ public class AlwaysOnHotwordDetector {
    /**
     * Indicates that recognition for the given keyphrase is not supported.
     * No further interaction should be performed with the detector that returns this availability.
     *
     * @deprecated This is no longer a valid state. Enrollment can occur outside of
     * {@link KeyphraseEnrollmentInfo} through another privileged application. We can no longer
     * determine ahead of time if the keyphrase and locale are unsupported by the system.
     */
    @Deprecated
    public static final int STATE_KEYPHRASE_UNSUPPORTED = -1;
    /**
     * Indicates that the given keyphrase is not enrolled.
@@ -220,7 +224,8 @@ public class AlwaysOnHotwordDetector {
     * The metadata of the Keyphrase, derived from the enrollment application.
     * This may be null if this keyphrase isn't supported by the enrollment application.
     */
    private final KeyphraseMetadata mKeyphraseMetadata;
    @Nullable
    private KeyphraseMetadata mKeyphraseMetadata;
    private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
    private final IVoiceInteractionService mVoiceInteractionService;
    private final IVoiceInteractionManagerService mModelManagementService;
@@ -420,7 +425,6 @@ public class AlwaysOnHotwordDetector {
        mText = text;
        mLocale = locale;
        mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
        mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale);
        mExternalCallback = callback;
        mHandler = new MyHandler();
        mInternalCallback = new SoundTriggerListener(mHandler);
@@ -456,8 +460,7 @@ public class AlwaysOnHotwordDetector {
        }

        // This method only makes sense if we can actually support a recognition.
        if (mAvailability != STATE_KEYPHRASE_ENROLLED
                && mAvailability != STATE_KEYPHRASE_UNENROLLED) {
        if (mAvailability != STATE_KEYPHRASE_ENROLLED || mKeyphraseMetadata == null) {
            throw new UnsupportedOperationException(
                    "Getting supported recognition modes for the keyphrase is not supported");
        }
@@ -733,8 +736,7 @@ public class AlwaysOnHotwordDetector {
    void onSoundModelsChanged() {
        synchronized (mLock) {
            if (mAvailability == STATE_INVALID
                    || mAvailability == STATE_HARDWARE_UNAVAILABLE
                    || mAvailability == STATE_KEYPHRASE_UNSUPPORTED) {
                    || mAvailability == STATE_HARDWARE_UNAVAILABLE) {
                Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config");
                return;
            }
@@ -744,7 +746,9 @@ public class AlwaysOnHotwordDetector {
            // or was deleted.
            // The availability change callback should ensure that the client starts recognition
            // again if needed.
            if (mAvailability == STATE_KEYPHRASE_ENROLLED) {
                stopRecognitionLocked();
            }

            // Execute a refresh availability task - which should then notify of a change.
            new RefreshAvailabiltyTask().execute();
@@ -927,20 +931,17 @@ public class AlwaysOnHotwordDetector {
        @Override
        public Void doInBackground(Void... params) {
            int availability = internalGetInitialAvailability();
            boolean enrolled = false;
            // Fetch the sound model if the availability is one of the supported ones.
            if (availability == STATE_NOT_READY
                    || availability == STATE_KEYPHRASE_UNENROLLED
                    || availability == STATE_KEYPHRASE_ENROLLED) {
                enrolled = internalGetIsEnrolled(mKeyphraseMetadata.id, mLocale);
                if (!enrolled) {
                    availability = STATE_KEYPHRASE_UNENROLLED;
                } else {

            synchronized (mLock) {
                if (availability == STATE_NOT_READY) {
                    internalUpdateEnrolledKeyphraseMetadata();
                    if (mKeyphraseMetadata != null) {
                        availability = STATE_KEYPHRASE_ENROLLED;
                    } else {
                        availability = STATE_KEYPHRASE_UNENROLLED;
                    }
                }

            synchronized (mLock) {
                if (DBG) {
                    Slog.d(TAG, "Hotword availability changed from " + mAvailability
                            + " -> " + availability);
@@ -969,28 +970,22 @@ public class AlwaysOnHotwordDetector {
            } catch (RemoteException e) {
                Slog.w(TAG, "RemoteException in getDspProperties!", e);
            }

            // No DSP available
            if (dspModuleProperties == null) {
                return STATE_HARDWARE_UNAVAILABLE;
            }
            // No enrollment application supports this keyphrase/locale
            if (mKeyphraseMetadata == null) {
                return STATE_KEYPHRASE_UNSUPPORTED;
            }

            return STATE_NOT_READY;
        }

        /**
         * @return The corresponding {@link KeyphraseSoundModel} or null if none is found.
         */
        private boolean internalGetIsEnrolled(int keyphraseId, Locale locale) {
        private void internalUpdateEnrolledKeyphraseMetadata() {
            try {
                return mModelManagementService.isEnrolledForKeyphrase(
                        mVoiceInteractionService, keyphraseId, locale.toLanguageTag());
                mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata(
                        mVoiceInteractionService, mText, mLocale.toLanguageTag());
            } catch (RemoteException e) {
                Slog.w(TAG, "RemoteException in listRegisteredKeyphraseSoundModels!", e);
                Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e);
            }
            return false;
        }
    }

+15 −2
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseMetadata;
import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
@@ -72,8 +73,8 @@ interface IVoiceInteractionManagerService {
     */
    SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service);
    /**
     * Indicates if there's a keyphrase sound model available for the given keyphrase ID.
     * This performs the check for the current user.
     * Indicates if there's a keyphrase sound model available for the given keyphrase ID and the
     * user ID of the caller.
     *
     * @param service The current VoiceInteractionService.
     * @param keyphraseId The unique identifier for the keyphrase.
@@ -81,6 +82,18 @@ interface IVoiceInteractionManagerService {
     */
    boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
            String bcp47Locale);
    /**
     * Generates KeyphraseMetadata for an enrolled sound model based on keyphrase string, locale,
     * and the user ID of the caller.
     *
     * @param service The current VoiceInteractionService
     * @param keyphrase Keyphrase text associated with the enrolled model
     * @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
     * @return The metadata for the enrolled voice model bassed on the passed in parameters. Null if
     *         no matching voice model exists.
     */
    KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
            String keyphrase, String bcp47Locale);
    /**
     * Starts a recognition for the given keyphrase.
     */
Loading