Merge changes from topic "sthal-aidl"

* changes:
  Rearrange soundtrigger3 directory structure
  Update STHAL mock sysprop to integer
  Add sthal_cli_3
  Introduce soundtrigger HAL v3
This commit is contained in:
Ytai Ben-tsvi
2021-04-02 17:01:16 +00:00
committed by Android (Google) Code Review
13 changed files with 892 additions and 2 deletions

View File

@@ -527,6 +527,14 @@
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.soundtrigger3</name>
<version>1</version>
<interface>
<name>ISoundTriggerHw</name>
<instance>default</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.tetheroffload.config</name>
<version>1.0</version>

View File

@@ -61,7 +61,7 @@ public class SthalCli {
mService.registerAsService("mock");
System.out.println("Rebooting STHAL");
SystemProperties.set("debug.soundtrigger_middleware.use_mock_hal", "true");
SystemProperties.set("debug.soundtrigger_middleware.use_mock_hal", "2");
SystemProperties.set("sys.audio.restart.hal", "1");
while (processCommand()) ;
@@ -74,7 +74,7 @@ public class SthalCli {
private static void cleanup() {
System.out.println("Cleaning up.");
SystemProperties.set("debug.soundtrigger_middleware.use_mock_hal", "false");
SystemProperties.set("debug.soundtrigger_middleware.use_mock_hal", null);
HwBinder.setTrebleTestingOverride(false);
}

View File

@@ -0,0 +1,28 @@
aidl_interface {
name: "android.hardware.soundtrigger3",
vendor_available: true,
flags: ["-Werror", "-Weverything", ],
srcs: [
"android/hardware/soundtrigger3/ISoundTriggerHw.aidl",
"android/hardware/soundtrigger3/ISoundTriggerHwCallback.aidl",
"android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl",
],
stability: "vintf",
imports: [
"android.media.soundtrigger.types",
],
backend: {
cpp: {
// prefer NDK backend which can be used anywhere
enabled: false,
},
java: {
sdk_version: "module_current",
},
ndk: {
vndk: {
enabled: true,
},
},
},
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2021 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.soundtrigger3;
@VintfStability
interface ISoundTriggerHw {
android.media.soundtrigger.Properties getProperties();
void registerGlobalCallback(in android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback callback);
int loadSoundModel(in android.media.soundtrigger.SoundModel soundModel, in android.hardware.soundtrigger3.ISoundTriggerHwCallback callback);
int loadPhraseSoundModel(in android.media.soundtrigger.PhraseSoundModel soundModel, in android.hardware.soundtrigger3.ISoundTriggerHwCallback callback);
void unloadSoundModel(in int modelHandle);
void startRecognition(in int modelHandle, in int deviceHandle, in int ioHandle, in android.media.soundtrigger.RecognitionConfig config);
void stopRecognition(in int modelHandle);
void forceRecognitionEvent(in int modelHandle);
@nullable android.media.soundtrigger.ModelParameterRange queryParameter(in int modelHandle, in android.media.soundtrigger.ModelParameter modelParam);
int getParameter(in int modelHandle, in android.media.soundtrigger.ModelParameter modelParam);
void setParameter(in int modelHandle, in android.media.soundtrigger.ModelParameter modelParam, in int value);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2021 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.soundtrigger3;
@VintfStability
interface ISoundTriggerHwCallback {
void modelUnloaded(in int model);
void phraseRecognitionCallback(in int model, in android.media.soundtrigger.PhraseRecognitionEvent event);
void recognitionCallback(in int model, in android.media.soundtrigger.RecognitionEvent event);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2021 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.soundtrigger3;
@VintfStability
interface ISoundTriggerHwGlobalCallback {
void onResourcesAvailable();
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright 2021 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.soundtrigger3;
import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
import android.media.soundtrigger.PhraseSoundModel;
import android.media.soundtrigger.Properties;
import android.media.soundtrigger.RecognitionConfig;
import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger.ModelParameter;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.Properties;
import android.media.soundtrigger.RecognitionConfig;
/**
* SoundTrigger HAL interface. Used for hardware recognition of hotwords
* and other sounds.
*
* Basic usage:
* ============
* ISoundTriggerHw supports the ability to have one of more detection sessions running at a given
* time, and listening to acoustic events. The basic flow of setting up such a session is:
* - Load a model using loadSoundModel() or loadPhraseSoundModel(). The provided model object would
* indicate the (implementation-specific) detection algorithm (engine) to use, as well as any
* parameters applicable for this agorithm. Upon success, those methods would return a handle
* which will be used to reference this model in subsequent calls.
* - Once the model had been successfully loaded, detection can begin by calling startRecognition().
* - Recognition will continue running in the background until one of the following events occurs:
* - stopRecognition() has been called on this model.
* - A detection has occurred.
* - Detection was aborted, typically for resource constraints, for example, when a higher-
* priority use-case has been initiated.
* - In the latter two cases, a recognition event will be sent via a the callback interface that was
* registered by the client upon loading. In either case, after any of these events occur, the
* detection becomes inactive and no more recognition callbacks are allowed.
* - The same model maybe started again at a later time, and this process may repeat as many times
* as needed.
* - Finally, an inactive model that is no longer needed may be unloaded via unloadModel().
*
* Important notes about the threading model:
* ==========================================
* Both this interface and the corresponding callback interface use a synchronous calling
* convention. This model comes with some advantages, but also with some risks of deadlocks if the
* implementation does not handle this correctly. Please consider the following:
* - After stopRecognition() returns, no more recognition events for that model may be sent. This
* implies that any queues holding such events must be flushed before the call returns and that
* may imply that callback from the HAL to the client are done while stopRecognition() is blocked.
* This is OK, and supported by the framework.
* - Similarly, the same relationship applies between unloadModel() and subsequent callbacks to
* modelUnloaded().
* - Other than these two cases, calls into the HAL *MAY NOT* block on callbacks from the HAL, or
* else deadlock conditions may result, which may be handled by rebooting of the HAL process and
* cause service outages.
*
* Due to the asynchronous nature of recognition events and preemptive model unloading, the HAL must
* correctly handle requests that would have been valid before an event has been delivered, but
* became moot as result of the event. Namely:
* - stopRecognition() may be called on a model that has already delivered an event and became
* inactive as a result. The HAL must return a successful return code in this case.
* - Furthermore, if a model is preemptively unloaded after it triggers (typically, this would
* happen when it is first aborted and immediately preemptively unloaded), stopRecognition() may
* be called on it. The HAL must return successfully in this case.
* - startRecognition() may be called on a model that has been preemptively unloaded. In this case,
* the HAL must signal a ServiceSpecificException(RESOURCE_CONTENTION) to indicate that the
* operation is temporarily unsuccessful.
* - unloadSoundModel() may be called on a model that has been preemptively unloaded. The HAL must
* return a successful return code in this case. This also implies that model handles should
* generally not be reused until explicitly unloaded. To avoid the rare possibility of running out
* of handles, the framework may call unloadModel() on models that have been preemptively unloaded
* by the HAL.
*
* Important notes about resource constraints and concurrency
* =========================================================
* Up until this version, the framework would enforce concurrency constraints expressed by the
* Properties presented by the soundtrigger instance. These include constraints on the maximum
* amount of models that can be loaded at the same time and on running recognition while capturing
* from the microphone.
* This version changes the approach for how these constraints are modeled, both offering the HAL
* implementation more flexibility and simplifying the framework's job in enforcing these
* limitations. Note that there is no change for how the framework behaves with earlier versions,
* everything described below only applies to this version and onward.
* The way this is achieved is as following:
* - The framework will no longer enforce constraints on concurrent loading of models, as expressed
* in the Properties.maxSoundModels field (this property is merely a hint at this point and may be
* deprecated in the future), or any other implicit constraints.
* - The framework will no longer enforce constraints on concurrency of audio recording and
* soundtrigger operation, as expressed in the Properties.concurrentCapture field (this property
* is merely a hint at this point and may be deprecated in the future).
* - The HAL implementation is free to reject loading of any model at any time by having the
* respective load*() method signal a ServiceSpecificException(RESOURCE_CONTENTION).
* - The HAL implementation is free to reject starting of any model at any time by having the
* respective start*() method signal a ServiceSpecificException(RESOURCE_CONTENTION).
* - The HAL implementation is free to preemptively stop a previously started model at its own
* discretion (for example, if a higher priority use-case which cannot coexist with detection
* has been requested). The HAL must notify the framework of the preemption by sending a
* recognition event with an `ABORTED` status. The implementation must NOT attempt to restart the
* recognition automatically when conditions change.
* - The HAL implementation is free to preemptively unload a previously loaded model at its own
* discretion (for example, if a higher-priority model is being loaded and the two cannot
* coexist). When doing so, it must first abort the detection if active (as per above) and then
* notify the framework of the unload using the modelUnloaded() callback.
* - When conditions change, such that a model that couldn't previously load or start or that had
* previously been preemptively stopped or unloaded, the HAL must notify the framework via the
* newly added onResourcesAvailable() callback. This callback is not a guarantee that any
* operation would now succeed, but merely a hint that retrying something that had previously
* failed, now MAY succeed. Until this callback is invoked, the client may assume that any
* operation that had previously failed or aborted would still fail if retried, so the
* implementation should not forget to deliver it.
* There are no guarantees regarding how the framework may respond to this event and the order in
* which it may choose to reload/restart its models. Typically, as result of this event the
* framework will make a single attempt per model to bring this model to its desired state
* (loaded, started).
*/
@VintfStability
interface ISoundTriggerHw {
/**
* Retrieve implementation properties.
*
* @return A Properties structure containing implementation description and capabilities.
*/
Properties getProperties();
/**
* This will get called at most once per every attachment to the service.
*
* All events not tied to a specific model should go through this callback.
*
* @param callback An interface to receive global event callbacks.
*/
void registerGlobalCallback(in ISoundTriggerHwGlobalCallback callback);
/**
* Load a sound model. Once loaded, recognition of this model can be started and stopped.
*
* @param soundModel A SoundModel structure describing the sound model to load.
* @param callback The callback interface on which the recognitionCallback()
* method will be called upon completion and modelUnloaded() upon preemptive unload.
* @return A unique handle assigned by the HAL for use by the client when controlling
* activity for this sound model.
* @throws ServiceSpecificException(RESOURCE_CONTENTION) if the model cannot be loaded due
* to resource constraints. This is typically a temporary condition and the client may
* retry after the onResourcesAvailable() global callback is invoked.
*/
int loadSoundModel(in SoundModel soundModel, in ISoundTriggerHwCallback callback);
/**
* Load a key phrase sound model. Once loaded, recognition of this model can be started and
* stopped.
*
* @param soundModel A PhraseSoundModel structure describing the sound model to load.
* @param callback The callback interface on which the phraseRecognitionCallback() method will
* be called upon completion and modelUnloaded() upon preempted unload.
* @return A unique handle assigned by the HAL for use by the framework when controlling
* activity for this sound model.
* @throws ServiceSpecificException(RESOURCE_CONTENTION) if the model cannot be loaded due
* to resource constraints. This is typically a temporary condition and the client may
* retry after the onResourcesAvailable() global callback is invoked.
*/
int loadPhraseSoundModel(in PhraseSoundModel soundModel, in ISoundTriggerHwCallback callback);
/**
* Unload a sound model. A sound model may be unloaded to free up resources and make room for a
* new one to overcome implementation limitations.
* This call is idempotent, to avoid any race conditions.
*
* @param modelHandle the handle of the sound model to unload.
*/
void unloadSoundModel(in int modelHandle);
/**
* Start recognition on a given model.
* This must be called on a model that is in the stopped state.
* The state of this model becomes active and will remain so until explicitly stopped, or a
* recognition event had been delivered to the client.
*
* @param modelHandle the handle of the sound model to use for recognition
* @param deviceHandle The handle of the audio device to be used for recognition, as declared by
* the audio subsystem.
* @param ioHandle A handle assigned by the framework, which will later be used to retrieve
* an audio stream associated with this recognition session.
* @param config A RecognitionConfig structure containing attributes of the recognition to
* perform.
* @throws ServiceSpecificException(RESOURCE_CONTENTION) if the model cannot be started due
* to resource constraints. This is typically a temporary condition and the client may
* retry after the onResourcesAvailable() global callback is invoked.
*/
void startRecognition(in int modelHandle, in int deviceHandle,
in int ioHandle, in RecognitionConfig config);
/**
* Stop recognition on a given model.
* This call is idempotent, to avoid any race conditions.
*
* @param modelHandle The handle of the sound model to use for recognition
*/
void stopRecognition(in int modelHandle);
/**
* Request a recognition event to be generated.
* The model must be in the started state and will remain started after the event is sent.
* The model state is returned asynchronously as a RecognitionEvent via the callback that was
* registered upon loading. That event must have a RecognitionStatus.FORCED status.
*
* @param modelHandle The handle of the sound model whose state is being
* queried.
*/
void forceRecognitionEvent(in int modelHandle);
/**
* Get supported parameter attributes with respect to the provided model handle.
* Model parameters are used to query/control model-specific detection behavior during a
* detection session.
* Along with determining the valid range, this API is also used to determine if a given
* parameter ID is supported at all by the modelHandle for use with getParameter() and
* setParameter() APIs.
*
* @param modelHandle The sound model handle indicating which model to query.
* @param modelParam Parameter to set which will be validated against the ModelParameter type.
* @return This structure indicates supported attributes of the parameter for the given model
* handle. If the parameter is not supported, null is returned.
*/
@nullable ModelParameterRange queryParameter(in int modelHandle, in ModelParameter modelParam);
/**
* Get a model specific parameter.
* If the value has not been set, a default value is returned. See ModelParameter for parameter
* default values.
* The caller must check if the handle supports the parameter via the queryParameter API prior
* to calling this method.
*
* @param modelHandle The sound model associated with given modelParam
* @param modelParam Parameter to set which will be validated against the ModelParameter type.
* Not putting ModelParameter type directly in the definition and validating internally
* allows for forward compatibility.
* @return Value set to the requested parameter.
*/
int getParameter(in int modelHandle, in ModelParameter modelParam);
/**
* Set a model specific parameter with the given value.
* This parameter will keep its value for the duration the model is loaded regardless of
* starting and stopping recognition. Once the model is unloaded, the value will be lost.
* The caller must check if the handle supports the parameter via the queryParameter API prior
* to calling this method.
*
* @param modelHandle The sound model handle indicating which model to modify parameters
* @param modelParam Parameter to set which will be validated against the ModelParameter type.
* Not putting ModelParameter type directly in the definition and validating internally
* allows for forward compatibility.
* @param value The value to set for the given model parameter.
*/
void setParameter(in int modelHandle, in ModelParameter modelParam, in int value);
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2021 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.soundtrigger3;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.RecognitionEvent;
/**
* SoundTrigger HAL per-model Callback interface.
*/
@VintfStability
interface ISoundTriggerHwCallback {
/**
* Callback method called by the HAL when a model has been unloaded at the HAL implementation's
* discretion.
* This event may only be delivered when the model state is 'stopped'.
* This event is NOT sent as part of an unload sequence initiated by the client.
*
* @param model The model handle.
*/
void modelUnloaded(in int model);
/**
* Callback method called by the HAL when the sound recognition triggers for a key phrase sound
* model.
* This event may only be delivered when the model state is 'started'.
* Unless the status of the event is RecognitionStatus.FORCED, this event indicates that the
* state of this model has become 'stopped'.
*
* @param event A RecognitionEvent structure containing detailed results of the recognition
* triggered
*/
void phraseRecognitionCallback(in int model, in PhraseRecognitionEvent event);
/**
* Callback method called by the HAL when the sound recognition triggers.
* This event may only be delivered when the model state is 'started'.
* Unless the status of the event is RecognitionStatus.FORCED, this event indicates that the
* state of this model has become 'stopped'.
*
* @param event A RecognitionEvent structure containing detailed results of the recognition
* triggered
*/
void recognitionCallback(in int model, in RecognitionEvent event);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2021 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.soundtrigger3;
/**
* SoundTrigger HAL callback interface for events not associated with a particular model.
*/
@VintfStability
interface ISoundTriggerHwGlobalCallback {
/**
* Callback method called by the HAL whenever internal conditions have been made available, such
* that a call that would previously have failed with an -EBUSY status may now succeed.
* There is no guarantee that any call would succeed following this event. It is merely a hint
* to the client that it may retry.
* Conversely, any call that have failed previously with a
* ServiceSpecificException(RESOURCE_CONTENTION) is guaranteed to fail again if retried, until
* this callback is delivered.
*/
void onResourcesAvailable();
}

View File

@@ -0,0 +1,8 @@
java_binary {
name: "sthal_cli_3",
wrapper: "sthal_cli_3",
srcs: ["java/**/*.java"],
static_libs: [
"android.hardware.soundtrigger3-V1-java",
],
}

View File

@@ -0,0 +1 @@
include /media/java/android/media/soundtrigger_middleware/OWNERS

View File

@@ -0,0 +1,351 @@
/*
* Copyright 2021 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.soundtrigger3.cli;
import android.annotation.NonNull;
import android.hardware.soundtrigger3.ISoundTriggerHw;
import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
import android.media.audio.common.AudioConfig;
import android.media.soundtrigger.ConfidenceLevel;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.PhraseRecognitionExtra;
import android.media.soundtrigger.PhraseSoundModel;
import android.media.soundtrigger.Properties;
import android.media.soundtrigger.RecognitionConfig;
import android.media.soundtrigger.RecognitionEvent;
import android.media.soundtrigger.RecognitionMode;
import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger.SoundModelType;
import android.os.HwBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* This is a quick-and-dirty sound trigger HAL console mock.
*
* It would only work on userdebug builds.
*
* When this app is started, it will initially:
* - Register a ISoundTriggerHw HAL with an instance name "mock".
* - Set a sysprop that tells SoundTriggerMiddlewareService to try to connect to the mock instance
* rather than the default one.
* - Reboot the real (default) HAL.
*
* In response to that, SoundTriggerMiddlewareService is going to connect to the mock HAL and resume
* normal operation.
*
* Our mock HAL will print to stdout every call it receives as well as expose a basic set of
* operations for sending event callbacks to the client. This allows us to simulate the frameworks
* behavior in response to different HAL behaviors.
*/
public class SthalCli {
private static SoundTriggerImpl mService;
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
try {
printUsage();
System.out.println("Registering mock STHAL");
mService = new SoundTriggerImpl();
// This allows us to register the service, even if it is not declared in the manifest.
mService.forceDowngradeToSystemStability();
ServiceManager.addService(ISoundTriggerHw.class.getCanonicalName() + "/mock", mService);
System.out.println("Rebooting STHAL");
SystemProperties.set("debug.soundtrigger_middleware.use_mock_hal", "3");
SystemProperties.set("sys.audio.restart.hal", "1");
while (processCommand()) ;
} catch (Exception e) {
e.printStackTrace();
} finally {
cleanup();
}
}
private static void cleanup() {
System.out.println("Cleaning up.");
SystemProperties.set("debug.soundtrigger_middleware.use_mock_hal", null);
HwBinder.setTrebleTestingOverride(false);
}
private static boolean processCommand() {
String line = scanner.nextLine();
String[] tokens = line.split("\\s+");
if (tokens.length < 1) {
return false;
}
switch (tokens[0]) {
case "q":
return false;
case "a":
mService.sendOnResourcesAvailable();
return true;
case "u":
mService.sendModelUnloaded(Integer.parseInt(tokens[1]));
return true;
case "r":
mService.sendRecognitionEvent(Integer.parseInt(tokens[1]),
Integer.parseInt(tokens[2]));
return true;
case "p":
mService.sendPhraseRecognitionEvent(Integer.parseInt(tokens[1]),
Integer.parseInt(tokens[2]));
return true;
case "d":
mService.dumpModels();
return true;
default:
printUsage();
return true;
}
}
private static void printUsage() {
System.out.print(
"Sound Trigger HAL v3 mock\n"
+ "Available commands:\n"
+ "h - help\n"
+ "q - quit\n"
+ "a - send onResourcesAvailable event\n"
+ "u <model> - send modelUnloaded event\n"
+ "r <model> <status> - send recognitionEvent\n"
+ "p <model> <status> - send phraseRecognitionEvent\n"
+ "d - dump models\n");
}
private static class SoundTriggerImpl extends ISoundTriggerHw.Stub {
static class Model {
final ISoundTriggerHwCallback callback;
final SoundModel model;
final PhraseSoundModel phraseModel;
public RecognitionConfig config = null;
Model(ISoundTriggerHwCallback callback, SoundModel model) {
this.callback = callback;
this.model = model;
this.phraseModel = null;
}
Model(ISoundTriggerHwCallback callback, PhraseSoundModel model) {
this.callback = callback;
this.model = null;
this.phraseModel = model;
}
}
private ISoundTriggerHwGlobalCallback mGlobalCallback;
private final ConcurrentMap<Integer, Model> mLoadedModels = new ConcurrentHashMap<>();
private int mHandleCounter = 1;
public void dumpModels() {
mLoadedModels.forEach((handle, model) -> {
System.out.println("+++ Model " + handle);
System.out.println(" config = " + model.config);
RecognitionConfig recognitionConfig = model.config;
if (recognitionConfig != null) {
System.out.println(" ACTIVE recognitionConfig = " + recognitionConfig);
} else {
System.out.println(" INACTIVE");
}
});
}
public void sendOnResourcesAvailable() {
if (mGlobalCallback != null) {
try {
mGlobalCallback.onResourcesAvailable();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void sendRecognitionEvent(int modelHandle, int status) {
Model model = mLoadedModels.get(modelHandle);
if (model != null && model.config != null) {
RecognitionEvent event = new RecognitionEvent();
event.type = SoundModelType.GENERIC;
event.status = status;
event.captureAvailable = true;
event.audioConfig.channelMask = 16;
event.audioConfig.format = 1;
event.audioConfig.sampleRateHz = 16000;
try {
model.callback.recognitionCallback(modelHandle, event);
} catch (RemoteException e) {
e.printStackTrace();
}
model.config = null;
}
}
public void sendPhraseRecognitionEvent(int modelHandle, int status) {
Model model = mLoadedModels.get(modelHandle);
if (model != null && model.config != null) {
PhraseRecognitionEvent event = new PhraseRecognitionEvent();
event.common = new RecognitionEvent();
event.common.type = SoundModelType.KEYPHRASE;
event.common.status = status;
event.common.captureAvailable = true;
event.common.audioConfig = new AudioConfig();
event.common.audioConfig.channelMask = 16;
event.common.audioConfig.format = 1;
event.common.audioConfig.sampleRateHz = 16000;
if (model.phraseModel.phrases.length > 0) {
PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
extra.id = model.phraseModel.phrases[0].id;
extra.confidenceLevel = 100;
extra.recognitionModes = model.phraseModel.phrases[0].recognitionModes;
extra.levels = new ConfidenceLevel[0];
event.phraseExtras = new PhraseRecognitionExtra[]{extra};
} else {
event.phraseExtras = new PhraseRecognitionExtra[0];
}
try {
model.callback.phraseRecognitionCallback(modelHandle, event);
} catch (RemoteException e) {
e.printStackTrace();
}
model.config = null;
}
}
public void sendModelUnloaded(int modelHandle) {
Model model = mLoadedModels.remove(modelHandle);
if (model != null) {
try {
model.callback.modelUnloaded(modelHandle);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void registerGlobalCallback(ISoundTriggerHwGlobalCallback callback) {
System.out.println("registerGlobalCallback()");
mGlobalCallback = callback;
}
@Override
public int loadSoundModel(SoundModel soundModel, ISoundTriggerHwCallback callback) {
int handle = mHandleCounter++;
System.out.printf("loadSoundModel(soundModel=%s) -> %d%n", soundModel, handle);
mLoadedModels.put(handle, new Model(callback, soundModel));
return handle;
}
@Override
public int loadPhraseSoundModel(PhraseSoundModel soundModel,
ISoundTriggerHwCallback callback) {
int handle = mHandleCounter++;
System.out.printf("loadPhraseSoundModel(soundModel=%s) -> %d%n", soundModel, handle);
mLoadedModels.put(handle, new Model(callback, soundModel));
return handle;
}
@Override
public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
RecognitionConfig config) {
System.out.printf("startRecognition(modelHandle=%d, deviceHandle=%d, ioHandle=%d)%n",
modelHandle, deviceHandle, ioHandle);
Model model = mLoadedModels.get(modelHandle);
if (model != null) {
model.config = config;
}
}
@Override
public Properties getProperties() {
System.out.println("getProperties()");
Properties properties = new Properties();
properties.implementor = "Android";
properties.description = "Mock STHAL";
properties.maxSoundModels = 2;
properties.maxKeyPhrases = 1;
properties.recognitionModes =
RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
return properties;
}
@Override
public ModelParameterRange queryParameter(int modelHandle, int modelParam) {
System.out.printf("queryParameter(modelHandle=%d, modelParam=%d)%n", modelHandle,
modelParam);
return null;
}
@Override
public void forceRecognitionEvent(int modelHandle) {
System.out.printf("getModelState(modelHandle=%d)%n", modelHandle);
}
@Override
public void unloadSoundModel(int modelHandle) {
System.out.printf("unloadSoundModel(modelHandle=%d)%n", modelHandle);
}
@Override
public void stopRecognition(int modelHandle) {
System.out.printf("stopRecognition(modelHandle=%d)%n", modelHandle);
Model model = mLoadedModels.get(modelHandle);
if (model != null) {
model.config = null;
}
}
@Override
public int handleShellCommand(@NonNull ParcelFileDescriptor in,
@NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
@NonNull String[] args) {
if (args.length > 0) {
switch (args[0]) {
case "reboot":
System.out.println("Received a reboot request. Exiting.");
cleanup();
System.exit(1);
}
}
return 0;
}
@Override
public void setParameter(int modelHandle, int modelParam, int value) {
throw new IllegalArgumentException();
}
@Override
public int getParameter(int modelHandle, int modelParam) {
throw new IllegalArgumentException();
}
}
}

View File

@@ -0,0 +1,6 @@
#!/system/bin/sh
# Script to start "sthal_cli_3" on the device
#
base=/system
export CLASSPATH=$base/framework/sthal_cli_3.jar
exec app_process $base/bin android.hardware.soundtrigger3.cli.SthalCli "$@"