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

Commit 5a162ba7 authored by Atneya Nair's avatar Atneya Nair Committed by Android (Google) Code Review
Browse files

Merge changes from topic "stmockhal_cts" into udc-dev

* changes:
  Add VIMService model enrollment override
  ST HAL Instrumentation client side
  Add TestAPI for VIService to create AOHD for test
parents d301a7c8 b32371dd
Loading
Loading
Loading
Loading
+78 −0
Original line number Diff line number Diff line
@@ -1651,6 +1651,11 @@ package android.hardware.soundtrigger {
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.KeyphraseMetadata> CREATOR;
  }

  public class SoundTrigger {
    field public static final int MODEL_PARAM_INVALID = -1; // 0xffffffff
    field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
  }

  public static final class SoundTrigger.KeyphraseRecognitionExtra implements android.os.Parcelable {
    ctor public SoundTrigger.KeyphraseRecognitionExtra(int, int, int);
  }
@@ -1663,6 +1668,19 @@ package android.hardware.soundtrigger {
    ctor public SoundTrigger.ModuleProperties(int, @NonNull String, @NonNull String, @NonNull String, int, @NonNull String, int, int, int, int, boolean, int, boolean, int, boolean, int);
  }

  public static final class SoundTrigger.RecognitionConfig implements android.os.Parcelable {
    ctor public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[], int);
    ctor public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[]);
    method public int describeContents();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.RecognitionConfig> CREATOR;
    field public final boolean allowMultipleTriggers;
    field public final int audioCapabilities;
    field public final boolean captureRequested;
    field @NonNull public final byte[] data;
    field @NonNull public final android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[] keyphrases;
  }

  public static class SoundTrigger.RecognitionEvent {
    ctor public SoundTrigger.RecognitionEvent(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], long);
  }
@@ -2000,6 +2018,57 @@ package android.media.metrics {

}

package android.media.soundtrigger {

  public final class SoundTriggerInstrumentation {
    method public void setResourceContention(boolean);
    method public void triggerOnResourcesAvailable();
    method public void triggerRestart();
  }

  public static interface SoundTriggerInstrumentation.GlobalCallback {
    method public default void onClientAttached();
    method public default void onClientDetached();
    method public default void onFrameworkDetached();
    method public void onModelLoaded(@NonNull android.media.soundtrigger.SoundTriggerInstrumentation.ModelSession);
    method public default void onPreempted();
    method public default void onRestarted();
  }

  public static interface SoundTriggerInstrumentation.ModelCallback {
    method public default void onModelUnloaded();
    method public default void onParamSet(int, int);
    method public void onRecognitionStarted(@NonNull android.media.soundtrigger.SoundTriggerInstrumentation.RecognitionSession);
  }

  public class SoundTriggerInstrumentation.ModelSession {
    method public void clearModelCallback();
    method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.Keyphrase> getPhrases();
    method @NonNull public android.media.soundtrigger.SoundTriggerManager.Model getSoundModel();
    method public boolean isKeyphrase();
    method public void setModelCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.soundtrigger.SoundTriggerInstrumentation.ModelCallback);
    method public void triggerUnloadModel();
  }

  public static interface SoundTriggerInstrumentation.RecognitionCallback {
    method public void onRecognitionStopped();
  }

  public class SoundTriggerInstrumentation.RecognitionSession {
    method public void clearRecognitionCallback();
    method public int getAudioSession();
    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig getRecognitionConfig();
    method public void setRecognitionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.soundtrigger.SoundTriggerInstrumentation.RecognitionCallback);
    method public void triggerAbortRecognition();
    method public void triggerRecognitionEvent(@NonNull byte[], @Nullable java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
  }

  public final class SoundTriggerManager {
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public static android.media.soundtrigger.SoundTriggerInstrumentation attachInstrumentation(@NonNull java.util.concurrent.Executor, @NonNull android.media.soundtrigger.SoundTriggerInstrumentation.GlobalCallback);
  }

}

package android.media.tv {

  public final class TvInputManager {
@@ -2024,6 +2093,14 @@ package android.media.tv.tuner {

}

package android.media.voice {

  public final class KeyphraseModelManager {
    method @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public void setModelDatabaseForTestEnabled(boolean);
  }

}

package android.net {

  public class NetworkPolicyManager {
@@ -2944,6 +3021,7 @@ package android.service.voice {
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(@NonNull String, @NonNull java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.AlwaysOnHotwordDetector.Callback);
    method @NonNull public final java.util.List<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> listModuleProperties();
    method public final void setTestModuleForAlwaysOnHotwordDetectorEnabled(boolean);
  }

  public static class VoiceInteractionSession.ActivityId {
+31 −5
Original line number Diff line number Diff line
@@ -1051,6 +1051,29 @@ public class SoundTrigger {
            return "ModelParamRange [start=" + mStart + ", end=" + mEnd + "]";
        }
    }
    /**
     * SoundTrigger model parameter types.
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "MODEL_PARAM" }, value = {
            MODEL_PARAM_INVALID,
            MODEL_PARAM_THRESHOLD_FACTOR
    })
    public @interface ModelParamTypes {}

    /**
     * See {@link ModelParams.INVALID}
     * @hide
     */
    @TestApi
    public static final int MODEL_PARAM_INVALID = ModelParams.INVALID;
    /**
     * See {@link ModelParams.THRESHOLD_FACTOR}
     * @hide
     */
    @TestApi
    public static final int MODEL_PARAM_THRESHOLD_FACTOR = ModelParams.THRESHOLD_FACTOR;

    /**
     * Modes for key phrase recognition
@@ -1450,7 +1473,8 @@ public class SoundTrigger {
     *
     *  @hide
     */
    public static class RecognitionConfig implements Parcelable {
    @TestApi
    public static final class RecognitionConfig implements Parcelable {
        /** True if the DSP should capture the trigger sound and make it available for further
         * capture. */
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1464,6 +1488,7 @@ public class SoundTrigger {
         * options for each keyphrase. */
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        @NonNull
        @SuppressLint("ArrayReturn")
        public final KeyphraseRecognitionExtra keyphrases[];
        /** Opaque data for use by system applications who know about voice engine internals,
         * typically during enrollment. */
@@ -1479,8 +1504,8 @@ public class SoundTrigger {
        public final int audioCapabilities;

        public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
                int audioCapabilities) {
                @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases,
                @Nullable byte[] data, int audioCapabilities) {
            this.captureRequested = captureRequested;
            this.allowMultipleTriggers = allowMultipleTriggers;
            this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
@@ -1490,7 +1515,8 @@ public class SoundTrigger {

        @UnsupportedAppUsage
        public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
                @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases,
                @Nullable byte[] data) {
            this(captureRequested, allowMultipleTriggers, keyphrases, data, 0);
        }

@@ -1517,7 +1543,7 @@ public class SoundTrigger {
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
        public void writeToParcel(@NonNull Parcel dest, int flags) {
            dest.writeByte((byte) (captureRequested ? 1 : 0));
            dest.writeByte((byte) (allowMultipleTriggers ? 1 : 0));
            dest.writeTypedArray(keyphrases, flags);
+50 −4
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
@@ -200,6 +201,9 @@ public class VoiceInteractionService extends Service {

    private final Set<HotwordDetector> mActiveDetectors = new ArraySet<>();

    // True if any of the createAOHD methods should use the test ST module.
    @GuardedBy("mLock")
    private boolean mTestModuleForAlwaysOnHotwordDetectorEnabled = false;

    private void onDetectorRemoteException(@NonNull IBinder token, int detectorType) {
        Log.d(TAG, "onDetectorRemoteException for " + HotwordDetector.detectorTypeToString(
@@ -512,14 +516,14 @@ public class VoiceInteractionService extends Service {
        Objects.requireNonNull(callback);
        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
                /* supportHotwordDetectionService= */ false, /* options= */ null,
                /* sharedMemory= */ null, /* moduleProperties */ null, executor, callback);
                /* sharedMemory= */ null, /* moduleProperties= */ null, executor, callback);
    }

    /**
     * Same as {@link createAlwaysOnHotwordDetector(String, Locale, Executor,
     * AlwaysOnHotwordDetector.Callback)}, but allow explicit selection of the underlying ST
     * module to attach to.
     * Use {@link listModuleProperties} to get available modules to attach to.
     * Use {@link #listModuleProperties()} to get available modules to attach to.
     * @hide
     */
    @TestApi
@@ -645,14 +649,14 @@ public class VoiceInteractionService extends Service {
        Objects.requireNonNull(callback);
        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
                /* supportHotwordDetectionService= */ true, options, sharedMemory,
                /* moduleProperties */ null, executor, callback);
                /* moduleProperties= */ null, executor, callback);
    }

    /**
     * Same as {@link createAlwaysOnHotwordDetector(String, Locale,
     * PersistableBundle, SharedMemory, Executor, AlwaysOnHotwordDetector.Callback)},
     * but allow explicit selection of the underlying ST module to attach to.
     * Use {@link listModuleProperties} to get available modules to attach to.
     * Use {@link #listModuleProperties()} to get available modules to attach to.
     * @hide
     */
    @TestApi
@@ -717,6 +721,10 @@ public class VoiceInteractionService extends Service {

            try {
                dspDetector.registerOnDestroyListener(this::onHotwordDetectorDestroyed);
                // Check if we are currently overridden, and should use the test module.
                if (mTestModuleForAlwaysOnHotwordDetectorEnabled) {
                    moduleProperties = getTestModuleProperties();
                }
                // If moduleProperties is null, the default STModule is used.
                dspDetector.initialize(options, sharedMemory, moduleProperties);
            } catch (Exception e) {
@@ -990,6 +998,44 @@ public class VoiceInteractionService extends Service {
        return mKeyphraseEnrollmentInfo;
    }


    /**
     * Configure {@link createAlwaysOnHotwordDetector(String, Locale,
     * SoundTrigger.ModuleProperties, Executor, AlwaysOnHotwordDetector.Callback)}
     * and similar overloads to utilize the test SoundTrigger module instead of the
     * actual DSP module.
     * @param isEnabled - {@code true} if subsequently created {@link AlwaysOnHotwordDetector}
     * objects should attach to a test module. {@code false} if subsequently created
     * {@link AlwaysOnHotwordDetector} should attach to the actual DSP module.
     * @hide
     */
    @TestApi
    public final void setTestModuleForAlwaysOnHotwordDetectorEnabled(boolean isEnabled) {
        synchronized (mLock) {
            mTestModuleForAlwaysOnHotwordDetectorEnabled = isEnabled;
        }
    }

    /**
     * Get the {@link SoundTrigger.ModuleProperties} representing the fake
     * STHAL to attach to via {@link createAlwaysOnHotwordDetector(String, Locale,
     * SoundTrigger.ModuleProperties, Executor, AlwaysOnHotwordDetector.Callback)} and
     * similar overloads for test purposes.
     * @return ModuleProperties to use for test purposes.
     */
    private final @NonNull SoundTrigger.ModuleProperties getTestModuleProperties() {
        var moduleProps = listModuleProperties()
                .stream()
                .filter((SoundTrigger.ModuleProperties prop)
                        -> prop.getSupportedModelArch().equals(SoundTrigger.FAKE_HAL_ARCH))
                .findFirst()
                .orElse(null);
        if (moduleProps == null) {
            throw new IllegalStateException("Fake ST HAL should always be available");
        }
        return moduleProps;
    }

    /**
     * Checks if a given keyphrase and locale are supported to create an
     * {@link AlwaysOnHotwordDetector}.
+6 −1
Original line number Diff line number Diff line
@@ -16,8 +16,9 @@

package com.android.internal.app;

import android.media.permission.Identity;
import android.hardware.soundtrigger.SoundTrigger;
import android.media.permission.Identity;
import android.media.soundtrigger_middleware.ISoundTriggerInjection;
import com.android.internal.app.ISoundTriggerSession;

/**
@@ -74,4 +75,8 @@ interface ISoundTriggerService {
     */
    List<SoundTrigger.ModuleProperties> listModuleProperties(in Identity originatorIdentity);

    /**
     * Attach an HAL injection interface.
     */
     void attachInjection(ISoundTriggerInjection injection);
}
+16 −0
Original line number Diff line number Diff line
@@ -96,6 +96,21 @@ interface IVoiceInteractionManagerService {
     * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
     */
    int deleteKeyphraseSoundModel(int keyphraseId, in String bcp47Locale);

    /**
     * Override the persistent enrolled model database with an in-memory
     * fake for testing purposes.
     *
     * @param enabled - {@code true} to enable the test database. {@code false} to enable
     * the real, persistent database.
     * @param token - IBinder used to register a death listener to clean-up the override
     * if tests do not clean up gracefully.
     */
    @EnforcePermission("MANAGE_VOICE_KEYPHRASES")
    @JavaPassthrough(annotation= "@android.annotation.RequiresPermission(" +
            "android.Manifest.permission.MANAGE_VOICE_KEYPHRASES)")
    void setModelDatabaseForTestEnabled(boolean enabled, IBinder token);

    /**
     * Indicates if there's a keyphrase sound model available for the given keyphrase ID and the
     * user ID of the caller.
@@ -106,6 +121,7 @@ interface IVoiceInteractionManagerService {
     * @param bcp47Locale The BCP47 language tag  for the keyphrase's locale.
     */
    boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale);

    /**
     * Generates KeyphraseMetadata for an enrolled sound model based on keyphrase string, locale,
     * and the user ID of the caller.
Loading