Rearrange soundtrigger3 directory structure

Test: m
Change-Id: I35f041769c3835fa6d63bcb0d4bffac3c8a1aa46
diff --git a/soundtrigger/aidl/Android.bp b/soundtrigger/aidl/Android.bp
new file mode 100644
index 0000000..6c084da
--- /dev/null
+++ b/soundtrigger/aidl/Android.bp
@@ -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,
+            },
+        },
+    },
+}
diff --git a/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHw.aidl b/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHw.aidl
new file mode 100644
index 0000000..bbfe7d9
--- /dev/null
+++ b/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHw.aidl
@@ -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);
+}
diff --git a/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHwCallback.aidl b/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHwCallback.aidl
new file mode 100644
index 0000000..152dfed
--- /dev/null
+++ b/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHwCallback.aidl
@@ -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);
+}
diff --git a/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl b/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl
new file mode 100644
index 0000000..6dfee9f
--- /dev/null
+++ b/soundtrigger/aidl/aidl_api/android.hardware.soundtrigger3/current/android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl
@@ -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();
+}
diff --git a/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl
new file mode 100644
index 0000000..2a3fc64
--- /dev/null
+++ b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHw.aidl
@@ -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);
+}
diff --git a/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHwCallback.aidl b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHwCallback.aidl
new file mode 100644
index 0000000..049ca65
--- /dev/null
+++ b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHwCallback.aidl
@@ -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);
+}
diff --git a/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl
new file mode 100644
index 0000000..d6d8630
--- /dev/null
+++ b/soundtrigger/aidl/android/hardware/soundtrigger3/ISoundTriggerHwGlobalCallback.aidl
@@ -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();
+}
diff --git a/soundtrigger/aidl/cli/Android.bp b/soundtrigger/aidl/cli/Android.bp
new file mode 100644
index 0000000..880903a
--- /dev/null
+++ b/soundtrigger/aidl/cli/Android.bp
@@ -0,0 +1,8 @@
+java_binary {
+    name: "sthal_cli_3",
+    wrapper: "sthal_cli_3",
+    srcs: ["java/**/*.java"],
+    static_libs: [
+        "android.hardware.soundtrigger3-V1-java",
+    ],
+}
diff --git a/soundtrigger/aidl/cli/OWNERS b/soundtrigger/aidl/cli/OWNERS
new file mode 100644
index 0000000..e21b66e
--- /dev/null
+++ b/soundtrigger/aidl/cli/OWNERS
@@ -0,0 +1 @@
+include /media/java/android/media/soundtrigger_middleware/OWNERS
diff --git a/soundtrigger/aidl/cli/java/android/hardware/soundtrigger3/cli/SthalCli.java b/soundtrigger/aidl/cli/java/android/hardware/soundtrigger3/cli/SthalCli.java
new file mode 100644
index 0000000..d3e1aa7
--- /dev/null
+++ b/soundtrigger/aidl/cli/java/android/hardware/soundtrigger3/cli/SthalCli.java
@@ -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();
+        }
+    }
+}
diff --git a/soundtrigger/aidl/cli/sthal_cli_3 b/soundtrigger/aidl/cli/sthal_cli_3
new file mode 100644
index 0000000..f157c50
--- /dev/null
+++ b/soundtrigger/aidl/cli/sthal_cli_3
@@ -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 "$@"