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

Commit a434963f authored by Sandeep Siddhartha's avatar Sandeep Siddhartha Committed by Android (Google) Code Review
Browse files

Merge "Fix the Locale story in the hotword API" into lmp-dev

parents 79dbce86 dcf3068f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -27464,7 +27464,7 @@ package android.service.voice {
  public class VoiceInteractionService extends android.app.Service {
    ctor public VoiceInteractionService();
    method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(java.lang.String, java.lang.String, android.service.voice.AlwaysOnHotwordDetector.Callback);
    method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(java.lang.String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
    method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
    method public android.os.IBinder onBind(android.content.Intent);
    method public void onReady();
+66 −30
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.service.voice.AlwaysOnHotwordDetector;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Slog;
import android.util.Xml;
@@ -35,6 +37,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
 * Enrollment information about the different available keyphrases.
@@ -151,33 +154,8 @@ public class KeyphraseEnrollmentInfo {

            TypedArray array = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.VoiceEnrollmentApplication);
            int searchKeyphraseId = array.getInt(
                    com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId,
                    -1);
            if (searchKeyphraseId != -1) {
                String searchKeyphrase = array.getString(com.android.internal.R.styleable
                        .VoiceEnrollmentApplication_searchKeyphrase);
                if (searchKeyphrase == null) {
                    searchKeyphrase = "";
                }
                String searchKeyphraseSupportedLocales =
                        array.getString(com.android.internal.R.styleable
                                .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
                String[] supportedLocales = new String[0];
                // Get all the supported locales from the comma-delimted string.
                if (searchKeyphraseSupportedLocales != null
                        && !searchKeyphraseSupportedLocales.isEmpty()) {
                    supportedLocales = searchKeyphraseSupportedLocales.split(",");
                }
                int recognitionModes = array.getInt(com.android.internal.R.styleable
                        .VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, 0);
                mKeyphrases = new KeyphraseMetadata[1];
                mKeyphrases[0] = new KeyphraseMetadata(
                        searchKeyphraseId, searchKeyphrase, supportedLocales, recognitionModes);
            } else {
                mParseError = "searchKeyphraseId not specified in meta-data";
                return;
            }
            initializeKeyphrasesFromTypedArray(array);
            array.recycle();
        } catch (XmlPullParserException e) {
            mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
            Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
@@ -195,6 +173,65 @@ public class KeyphraseEnrollmentInfo {
        }
    }

    private void initializeKeyphrasesFromTypedArray(TypedArray array) {
        // Get the keyphrase ID.
        int searchKeyphraseId = array.getInt(
                com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, -1);
        if (searchKeyphraseId <= 0) {
            mParseError = "No valid searchKeyphraseId specified in meta-data";
            Slog.w(TAG, mParseError);
            return;
        }

        // Get the keyphrase text.
        String searchKeyphrase = array.getString(
                com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphrase);
        if (searchKeyphrase == null) {
            mParseError = "No valid searchKeyphrase specified in meta-data";
            Slog.w(TAG, mParseError);
            return;
        }

        // Get the supported locales.
        String searchKeyphraseSupportedLocales = array.getString(
                com.android.internal.R.styleable
                        .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
        if (searchKeyphraseSupportedLocales == null) {
            mParseError = "No valid searchKeyphraseSupportedLocales specified in meta-data";
            Slog.w(TAG, mParseError);
            return;
        }
        ArraySet<Locale> locales = new ArraySet<>();
        // Try adding locales if the locale string is non-empty.
        if (!TextUtils.isEmpty(searchKeyphraseSupportedLocales)) {
            try {
                String[] supportedLocalesDelimited = searchKeyphraseSupportedLocales.split(",");
                for (int i = 0; i < supportedLocalesDelimited.length; i++) {
                    locales.add(Locale.forLanguageTag(supportedLocalesDelimited[i]));
                }
            } catch (Exception ex) {
                // We catch a generic exception here because we don't want the system service
                // to be affected by a malformed metadata because invalid locales were specified
                // by the system application.
                mParseError = "Error reading searchKeyphraseSupportedLocales from meta-data";
                Slog.w(TAG, mParseError, ex);
                return;
            }
        }

        // Get the supported recognition modes.
        int recognitionModes = array.getInt(com.android.internal.R.styleable
                .VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, -1);
        if (recognitionModes < 0) {
            mParseError = "No valid searchKeyphraseRecognitionFlags specified in meta-data";
            Slog.w(TAG, mParseError);
            return;
        }
        mKeyphrases = new KeyphraseMetadata[1];
        mKeyphrases[0] = new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales,
                recognitionModes);
    }

    public String getParseError() {
        return mParseError;
    }
@@ -217,11 +254,10 @@ public class KeyphraseEnrollmentInfo {
     *        or {@link AlwaysOnHotwordDetector#MANAGE_ACTION_UN_ENROLL}
     * @param keyphrase The keyphrase that the user needs to be enrolled to.
     * @param locale The locale for which the enrollment needs to be performed.
     *        This is a Java locale, for example "en_US".
     * @return An {@link Intent} to manage the keyphrase. This can be null if managing the
     *         given keyphrase/locale combination isn't possible.
     */
    public Intent getManageKeyphraseIntent(int action, String keyphrase, String locale) {
    public Intent getManageKeyphraseIntent(int action, String keyphrase, Locale locale) {
        if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) {
            Slog.w(TAG, "No enrollment application exists");
            return null;
@@ -248,7 +284,7 @@ public class KeyphraseEnrollmentInfo {
     * @return The metadata, if the enrollment client supports the given keyphrase
     *         and locale, null otherwise.
     */
    public KeyphraseMetadata getKeyphraseMetadata(String keyphrase, String locale) {
    public KeyphraseMetadata getKeyphraseMetadata(String keyphrase, Locale locale) {
        if (mKeyphrases == null || mKeyphrases.length == 0) {
            Slog.w(TAG, "Enrollment application doesn't support keyphrases");
            return null;
+6 −7
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package android.hardware.soundtrigger;

import android.util.ArraySet;

import java.util.Locale;

/**
 * A Voice Keyphrase metadata read from the enrollment application.
 *
@@ -26,17 +28,14 @@ import android.util.ArraySet;
public class KeyphraseMetadata {
    public final int id;
    public final String keyphrase;
    public final ArraySet<String> supportedLocales;
    public final ArraySet<Locale> supportedLocales;
    public final int recognitionModeFlags;

    public KeyphraseMetadata(int id, String keyphrase, String[] supportedLocales,
    public KeyphraseMetadata(int id, String keyphrase, ArraySet<Locale> supportedLocales,
            int recognitionModeFlags) {
        this.id = id;
        this.keyphrase = keyphrase;
        this.supportedLocales = new ArraySet<String>(supportedLocales.length);
        for (String locale : supportedLocales) {
            this.supportedLocales.add(locale);
        }
        this.supportedLocales = supportedLocales;
        this.recognitionModeFlags = recognitionModeFlags;
    }

@@ -56,7 +55,7 @@ public class KeyphraseMetadata {
    /**
     * @return Indicates if we support the given locale.
     */
    public boolean supportsLocale(String locale) {
    public boolean supportsLocale(Locale locale) {
        return supportedLocales.isEmpty() || supportedLocales.contains(locale);
    }
}
+10 −5
Original line number Diff line number Diff line
@@ -39,10 +39,10 @@ import android.util.Slog;

import com.android.internal.app.IVoiceInteractionManagerService;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;

/**
 * A class that lets a VoiceInteractionService implementation interact with
@@ -167,7 +167,7 @@ public class AlwaysOnHotwordDetector {
    private static final int MSG_DETECTION_RESUME = 5;

    private final String mText;
    private final String mLocale;
    private final Locale mLocale;
    /**
     * The metadata of the Keyphrase, derived from the enrollment application.
     * This may be null if this keyphrase isn't supported by the enrollment application.
@@ -317,7 +317,7 @@ public class AlwaysOnHotwordDetector {
     *
     * @hide
     */
    public AlwaysOnHotwordDetector(String text, String locale, Callback callback,
    public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
            KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
            IVoiceInteractionService voiceInteractionService,
            IVoiceInteractionManagerService modelManagementService) {
@@ -491,8 +491,6 @@ public class AlwaysOnHotwordDetector {
     */
    void onSoundModelsChanged() {
        synchronized (mLock) {
            // FIXME: This should stop the recognition if it was using an enrolled sound model
            // that's no longer available.
            if (mAvailability == STATE_INVALID
                    || mAvailability == STATE_HARDWARE_UNAVAILABLE
                    || mAvailability == STATE_KEYPHRASE_UNSUPPORTED) {
@@ -500,6 +498,13 @@ public class AlwaysOnHotwordDetector {
                return;
            }

            // Stop the recognition before proceeding.
            // This is done because we want to stop the recognition on an older model if it changed
            // or was deleted.
            // The availability change callback should ensure that the client starts recognition
            // again if needed.
            stopRecognitionLocked();

            // Execute a refresh availability task - which should then notify of a change.
            new RefreshAvailabiltyTask().execute();
        }
+14 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.internal.app.IVoiceInteractionManagerService;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Locale;


/**
@@ -163,7 +164,7 @@ public class VoiceInteractionService extends Service {
     * Called during service initialization to tell you when the system is ready
     * to receive interaction from it. You should generally do initialization here
     * rather than in {@link #onCreate()}. Methods such as {@link #startSession(Bundle)} and
     * {@link #createAlwaysOnHotwordDetector(String, String, android.service.voice.AlwaysOnHotwordDetector.Callback)}
     * {@link #createAlwaysOnHotwordDetector(String, Locale, android.service.voice.AlwaysOnHotwordDetector.Callback)}
     * will not be operational until this point.
     */
    public void onReady() {
@@ -199,6 +200,17 @@ public class VoiceInteractionService extends Service {
        }
    }

    /**
     * FIXME: Remove once the prebuilts are updated.
     *
     * @hide
     */
    @Deprecated
    public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
            String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) {
        return createAlwaysOnHotwordDetector(keyphrase, new Locale(locale), callback);
    }

    /**
     * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
     * This instance must be retained and used by the client.
@@ -207,12 +219,11 @@ public class VoiceInteractionService extends Service {
     *
     * @param keyphrase The keyphrase that's being used, for example "Hello Android".
     * @param locale The locale for which the enrollment needs to be performed.
     *        This is a Java locale, for example "en_US".
     * @param callback The callback to notify of detection events.
     * @return An always-on hotword detector for the given keyphrase and locale.
     */
    public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
            String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) {
            String keyphrase, Locale locale, AlwaysOnHotwordDetector.Callback callback) {
        if (mSystemService == null) {
            throw new IllegalStateException("Not available until onReady() is called");
        }
Loading