summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Atneya Nair <atneya@google.com> 2023-02-13 09:45:15 -0800
committer Atneya Nair <atneya@google.com> 2023-02-15 16:35:42 -0800
commit333b72b7b5ba4f6179b1d3fa50d5406dcb9af777 (patch)
treeca79e15c439cbf6201ed6c9b0ef0d2c0fd85d1fd
parent9e31351327f707a9c13be5736988d99a2179c529 (diff)
Add VIService ST module selection TestApis
Allow VIService to enumerate available underlying ST modules for test purposes. Allow VIService to select a specific STModule (via ModuleProperties) when creating an AOHD. If no STModule is passed, AOHD will attach to the first available module -- retaining existing behavior. Test: Manual verification of assistant recognition flow Bug: 269358382 Change-Id: Id18240ba8db2c096b5703d068917fc022f0cd61e
-rw-r--r--core/api/test-current.txt6
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java18
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java91
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl9
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java24
5 files changed, 126 insertions, 22 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d5ced049e9cd..a0835e3f5aea 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2800,6 +2800,12 @@ package android.service.voice {
ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
}
+ public class VoiceInteractionService extends android.app.Service {
+ 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();
+ }
+
public static class VoiceInteractionSession.ActivityId {
method @NonNull public android.os.IBinder getAssistToken();
method public int getTaskId();
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index bcc3f58a3bbf..48b7a5915658 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -784,8 +784,13 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mSupportSandboxedDetectionService = supportSandboxedDetectionService;
}
+ // Do nothing. This method should not be abstract.
+ // TODO (b/269355519) un-subclass AOHD.
@Override
- void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
+ void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {}
+
+ void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
+ @Nullable SoundTrigger.ModuleProperties moduleProperties) {
if (mSupportSandboxedDetectionService) {
initAndVerifyDetector(options, sharedMemory, mInternalCallback,
DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
@@ -793,9 +798,18 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
try {
Identity identity = new Identity();
identity.packageName = ActivityThread.currentOpPackageName();
+ if (moduleProperties == null) {
+ List<SoundTrigger.ModuleProperties> modulePropList =
+ mModelManagementService.listModuleProperties(identity);
+ if (modulePropList.size() > 0) {
+ moduleProperties = modulePropList.get(0);
+ }
+ // (@atneya) intentionally let a null moduleProperties through until
+ // all CTS tests are fixed
+ }
mSoundTriggerSession =
mModelManagementService.createSoundTriggerSessionAsOriginator(
- identity, mBinder);
+ identity, mBinder, moduleProperties);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 12853cbaf6e1..a684e41a2a95 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -24,6 +24,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.app.Service;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
@@ -33,7 +35,9 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
+import android.hardware.soundtrigger.SoundTrigger;
import android.media.voice.KeyphraseModelManager;
+import android.media.permission.Identity;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -386,6 +390,22 @@ public class VoiceInteractionService extends Service {
}
/**
+ * List available ST modules to attach to for test purposes.
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public final List<SoundTrigger.ModuleProperties> listModuleProperties() {
+ Identity identity = new Identity();
+ identity.packageName = ActivityThread.currentOpPackageName();
+ try {
+ return mSystemService.listModuleProperties(identity);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
* This instance must be retained and used by the client.
* Calling this a second time invalidates the previously created hotword detector
@@ -422,7 +442,8 @@ public class VoiceInteractionService extends Service {
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ false, /* options= */ null,
- /* sharedMemory= */ null, /* executor= */ null, callback);
+ /* sharedMemory= */ null, /* moduleProperties */ null,
+ /* executor= */ null, callback);
}
/**
@@ -461,10 +482,37 @@ public class VoiceInteractionService extends Service {
Objects.requireNonNull(callback);
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ false, /* options= */ null,
- /* sharedMemory= */ 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.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
+ @NonNull
+ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(
+ @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
+ @NonNull SoundTrigger.ModuleProperties moduleProperties,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AlwaysOnHotwordDetector.Callback callback) {
+
+ Objects.requireNonNull(keyphrase);
+ Objects.requireNonNull(locale);
+ Objects.requireNonNull(moduleProperties);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+ /* supportHotwordDetectionService= */ false, /* options= */ null,
+ /* sharedMemory= */ null, moduleProperties, executor, callback);
+ }
+
+
+ /**
* Create an {@link AlwaysOnHotwordDetector} and trigger a {@link HotwordDetectionService}
* service, then it will also pass the read-only data to hotword detection service.
*
@@ -515,7 +563,7 @@ public class VoiceInteractionService extends Service {
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ true, options, sharedMemory,
- /* executor= */ null, callback);
+ /* modulProperties */ null, /* executor= */ null, callback);
}
/**
@@ -566,16 +614,46 @@ public class VoiceInteractionService extends Service {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
- /* supportHotwordDetectionService= */ true, options, sharedMemory, executor,
- callback);
+ /* supportHotwordDetectionService= */ true, options, sharedMemory,
+ /* 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.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
+ @NonNull
+ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorForTest(
+ @NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
+ @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
+ @NonNull SoundTrigger.ModuleProperties moduleProperties,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AlwaysOnHotwordDetector.Callback callback) {
+
+ Objects.requireNonNull(keyphrase);
+ Objects.requireNonNull(locale);
+ Objects.requireNonNull(moduleProperties);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+ /* supportHotwordDetectionService= */ true, options, sharedMemory,
+ moduleProperties, executor, callback);
+ }
+
+
+
private AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorInternal(
@SuppressLint("MissingNullability") String keyphrase, // TODO: nullability properly
@SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
boolean supportHotwordDetectionService,
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory,
+ @Nullable SoundTrigger.ModuleProperties moduleProperties,
@Nullable @CallbackExecutor Executor executor,
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
@@ -609,7 +687,8 @@ public class VoiceInteractionService extends Service {
try {
dspDetector.registerOnDestroyListener(this::onHotwordDetectorDestroyed);
- dspDetector.initialize(options, sharedMemory);
+ // If moduleProperties is null, the default STModule is used.
+ dspDetector.initialize(options, sharedMemory, moduleProperties);
} catch (Exception e) {
mActiveDetectors.remove(dspDetector);
dspDetector.destroy();
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 619b4208048c..5eb97862c79f 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -243,7 +243,14 @@ interface IVoiceInteractionManagerService {
*/
IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
in Identity originatorIdentity,
- IBinder client);
+ IBinder client,
+ in SoundTrigger.ModuleProperties moduleProperties);
+
+ /**
+ * Lists properties of SoundTrigger modules that can be attached to by
+ * @{link createSoundTriggerSessionAsOriginator}.
+ */
+ List<SoundTrigger.ModuleProperties> listModuleProperties(in Identity originatorIdentity);
/**
* Set configuration and pass read-only data to hotword detection service.
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a08eaca25713..d4374a9c2654 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -377,7 +377,8 @@ public class VoiceInteractionManagerService extends SystemService {
@Override
public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
- @NonNull Identity originatorIdentity, IBinder client) {
+ @NonNull Identity originatorIdentity, IBinder client,
+ @NonNull ModuleProperties moduleProperties) {
Objects.requireNonNull(originatorIdentity);
boolean forHotwordDetectionService;
synchronized (VoiceInteractionManagerServiceStub.this) {
@@ -396,7 +397,7 @@ public class VoiceInteractionManagerService extends SystemService {
originatorIdentity.uid = Binder.getCallingUid();
originatorIdentity.pid = Binder.getCallingPid();
session = new SoundTriggerSessionPermissionsDecorator(
- createSoundTriggerSessionForSelfIdentity(client),
+ createSoundTriggerSessionForSelfIdentity(client, moduleProperties),
mContext,
originatorIdentity);
} else {
@@ -406,14 +407,14 @@ public class VoiceInteractionManagerService extends SystemService {
try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
originatorIdentity)) {
session = new SoundTriggerSession(mSoundTriggerInternal.attach(client,
- getDefaultModuleProperties(originatorIdentity)));
+ moduleProperties));
}
}
return new SoundTriggerSessionBinderProxy(session);
}
private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity(
- IBinder client) {
+ IBinder client, ModuleProperties moduleProperties) {
Identity identity = new Identity();
identity.uid = Process.myUid();
identity.pid = Process.myPid();
@@ -421,20 +422,17 @@ public class VoiceInteractionManagerService extends SystemService {
return Binder.withCleanCallingIdentity(() -> {
try (SafeCloseable ignored = IdentityContext.create(identity)) {
return new SoundTriggerSession(
- mSoundTriggerInternal.attach(client,
- getDefaultModuleProperties(identity)));
+ mSoundTriggerInternal.attach(client, moduleProperties));
}
});
}
- private ModuleProperties getDefaultModuleProperties(Identity originatorIdentity) {
- List<ModuleProperties> modulePropList = mSoundTriggerInternal
- .listModuleProperties(originatorIdentity);
- if (modulePropList.isEmpty()) {
- return null;
- } else {
- return modulePropList.get(0);
+ @Override
+ public List<ModuleProperties> listModuleProperties(Identity originatorIdentity) {
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
}
+ return mSoundTriggerInternal.listModuleProperties(originatorIdentity);
}
// TODO: VI Make sure the caller is the current user or profile