[automerger skipped] Merge changes from topic "upstream-audio-aidl-hal" am: 2468ff5608 am: 933e626ce7 am: 06bd2b0cff am: 6881a2e9ce am: eb46983781 -s ours
am skip reason: Merged-In I269d61c8c441b3db0832f5450f5e5a84a5989a99 with SHA-1 446519a370 is already in history
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2190590
Change-Id: I406aff5556677589283efd0051b6d66fcae0c2c6
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/audio/README.md b/audio/README.md
index 1938ad4..3f40d72 100644
--- a/audio/README.md
+++ b/audio/README.md
@@ -2,10 +2,29 @@
Directory structure of the audio HAL related code.
-Run `common/all-versions/copyHAL.sh` to create a new version of the audio HAL
-based on an existing one.
+## Directory Structure for AIDL audio HAL
-## Directory Structure
+The AIDL version is located inside `aidl` directory. The tree below explains
+the role of each subdirectory:
+
+* `aidl_api` — snapshots of the API created each Android release. Every
+ release, the current version of the API becomes "frozen" and gets assigned
+ the next version number. If the API needs further modifications, they are
+ made on the "current" version. After making modifications, run
+ `m <package name>-update-api` to update the snapshot of the "current"
+ version.
+* `android/hardware/audio/common` — data structures and interfaces shared
+ between various HALs: BT HAL, core and effects audio HALs.
+* `android/hardware/audio/core` — data structures and interfaces of the
+ core audio HAL.
+* `default` — the default, reference implementation of the audio HAL service.
+* `vts` — VTS tests for the AIDL HAL.
+
+## Directory Structure for HIDL audio HAL
+
+Run `common/all-versions/copyHAL.sh` to create a new version of the HIDL audio
+HAL based on an existing one. Note that this isn't possible since Android T
+release. Android U and above uses AIDL audio HAL.
* `2.0` — version 2.0 of the core HIDL API. Note that `.hal` files
can not be moved into the `core` directory because that would change
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 4927e83..440422e 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -75,17 +75,24 @@
"android/hardware/audio/core/IModule.aidl",
"android/hardware/audio/core/IStreamIn.aidl",
"android/hardware/audio/core/IStreamOut.aidl",
+ "android/hardware/audio/core/MmapBufferDescriptor.aidl",
+ "android/hardware/audio/core/ModuleDebug.aidl",
+ "android/hardware/audio/core/StreamDescriptor.aidl",
],
imports: [
"android.hardware.audio.common-V1",
+ "android.hardware.common-V2",
+ "android.hardware.common.fmq-V1",
"android.media.audio.common.types-V1",
],
stability: "vintf",
backend: {
+ // The C++ backend is disabled transitively due to use of FMQ.
+ cpp: {
+ enabled: false,
+ },
java: {
- platform_apis: true,
+ sdk_version: "module_current",
},
},
- versions: [
- ],
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl
index 1cef4cd..078b5ea 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioPatch.aidl
@@ -37,4 +37,6 @@
int id;
int[] sourcePortConfigIds;
int[] sinkPortConfigIds;
+ int minimumStreamBufferSizeFrames;
+ int[] latenciesMs;
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
index 33e8290..a8bbb15 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
@@ -34,15 +34,42 @@
package android.hardware.audio.core;
@VintfStability
interface IModule {
+ void setModuleDebug(in android.hardware.audio.core.ModuleDebug debug);
+ android.media.audio.common.AudioPort connectExternalDevice(in android.media.audio.common.AudioPort templateIdAndAdditionalData);
+ void disconnectExternalDevice(int portId);
android.hardware.audio.core.AudioPatch[] getAudioPatches();
android.media.audio.common.AudioPort getAudioPort(int portId);
android.media.audio.common.AudioPortConfig[] getAudioPortConfigs();
android.media.audio.common.AudioPort[] getAudioPorts();
android.hardware.audio.core.AudioRoute[] getAudioRoutes();
- android.hardware.audio.core.IStreamIn openInputStream(int portConfigId, in android.hardware.audio.common.SinkMetadata sinkMetadata);
- android.hardware.audio.core.IStreamOut openOutputStream(int portConfigId, in android.hardware.audio.common.SourceMetadata sourceMetadata, in @nullable android.media.audio.common.AudioOffloadInfo offloadInfo);
+ android.hardware.audio.core.AudioRoute[] getAudioRoutesForAudioPort(int portId);
+ android.hardware.audio.core.IModule.OpenInputStreamReturn openInputStream(in android.hardware.audio.core.IModule.OpenInputStreamArguments args);
+ android.hardware.audio.core.IModule.OpenOutputStreamReturn openOutputStream(in android.hardware.audio.core.IModule.OpenOutputStreamArguments args);
android.hardware.audio.core.AudioPatch setAudioPatch(in android.hardware.audio.core.AudioPatch requested);
boolean setAudioPortConfig(in android.media.audio.common.AudioPortConfig requested, out android.media.audio.common.AudioPortConfig suggested);
void resetAudioPatch(int patchId);
void resetAudioPortConfig(int portConfigId);
+ @VintfStability
+ parcelable OpenInputStreamArguments {
+ int portConfigId;
+ android.hardware.audio.common.SinkMetadata sinkMetadata;
+ long bufferSizeFrames;
+ }
+ @VintfStability
+ parcelable OpenInputStreamReturn {
+ android.hardware.audio.core.IStreamIn stream;
+ android.hardware.audio.core.StreamDescriptor desc;
+ }
+ @VintfStability
+ parcelable OpenOutputStreamArguments {
+ int portConfigId;
+ android.hardware.audio.common.SourceMetadata sourceMetadata;
+ @nullable android.media.audio.common.AudioOffloadInfo offloadInfo;
+ long bufferSizeFrames;
+ }
+ @VintfStability
+ parcelable OpenOutputStreamReturn {
+ android.hardware.audio.core.IStreamOut stream;
+ android.hardware.audio.core.StreamDescriptor desc;
+ }
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MmapBufferDescriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MmapBufferDescriptor.aidl
new file mode 100644
index 0000000..6ea1c69
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/MmapBufferDescriptor.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable MmapBufferDescriptor {
+ android.hardware.common.Ashmem sharedMemory;
+ long burstSizeFrames;
+ int flags;
+ const int FLAG_INDEX_APPLICATION_SHAREABLE = 0;
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
new file mode 100644
index 0000000..80ee185
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ModuleDebug {
+ boolean simulateDeviceConnections;
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
new file mode 100644
index 0000000..db1ac22
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable StreamDescriptor {
+ android.hardware.common.fmq.MQDescriptor<android.hardware.audio.core.StreamDescriptor.Command,android.hardware.common.fmq.SynchronizedReadWrite> command;
+ android.hardware.common.fmq.MQDescriptor<android.hardware.audio.core.StreamDescriptor.Reply,android.hardware.common.fmq.SynchronizedReadWrite> reply;
+ int frameSizeBytes;
+ long bufferSizeFrames;
+ android.hardware.audio.core.StreamDescriptor.AudioBuffer audio;
+ const int COMMAND_BURST = 1;
+ @FixedSize @VintfStability
+ parcelable Position {
+ long frames;
+ long timeNs;
+ }
+ @FixedSize @VintfStability
+ parcelable Command {
+ int code;
+ int fmqByteCount;
+ }
+ @FixedSize @VintfStability
+ parcelable Reply {
+ int status;
+ int fmqByteCount;
+ android.hardware.audio.core.StreamDescriptor.Position observable;
+ android.hardware.audio.core.StreamDescriptor.Position hardware;
+ int latencyMs;
+ }
+ @VintfStability
+ union AudioBuffer {
+ android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> fmq;
+ android.hardware.audio.core.MmapBufferDescriptor mmap;
+ }
+}
diff --git a/audio/aidl/android/hardware/audio/core/AudioPatch.aidl b/audio/aidl/android/hardware/audio/core/AudioPatch.aidl
index 48ca214..005d4c0 100644
--- a/audio/aidl/android/hardware/audio/core/AudioPatch.aidl
+++ b/audio/aidl/android/hardware/audio/core/AudioPatch.aidl
@@ -37,4 +37,18 @@
* unique.
*/
int[] sinkPortConfigIds;
+ /**
+ * The minimum buffer size, in frames, which streams must use for
+ * this connection configuration. This field is filled out by the
+ * HAL module on creation of the patch and must be a positive number.
+ */
+ int minimumStreamBufferSizeFrames;
+ /**
+ * Latencies, in milliseconds, associated with each sink port config from
+ * the 'sinkPortConfigIds' field. This field is filled out by the HAL module
+ * on creation or updating of the patch and must be a positive number. This
+ * is a nominal value. The current value of latency is provided via
+ * 'StreamDescriptor' command exchange on each audio I/O operation.
+ */
+ int[] latenciesMs;
}
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index d47ea3c..735f87f 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -22,6 +22,8 @@
import android.hardware.audio.core.AudioRoute;
import android.hardware.audio.core.IStreamIn;
import android.hardware.audio.core.IStreamOut;
+import android.hardware.audio.core.ModuleDebug;
+import android.hardware.audio.core.StreamDescriptor;
import android.media.audio.common.AudioOffloadInfo;
import android.media.audio.common.AudioPort;
import android.media.audio.common.AudioPortConfig;
@@ -44,6 +46,116 @@
@VintfStability
interface IModule {
/**
+ * Sets debugging configuration for the HAL module. This method is only
+ * called during xTS testing and is intended for validating the aspects of
+ * the HAL module behavior that would otherwise require human intervention.
+ *
+ * The HAL module must throw an error if there is an attempt to change
+ * the debug behavior for the aspect which is currently in use.
+ *
+ * @param debug The debug options.
+ * @throws EX_ILLEGAL_STATE If the flag(s) being changed affect functionality
+ * which is currently in use.
+ */
+ void setModuleDebug(in ModuleDebug debug);
+
+ /**
+ * Set a device port of an external device into connected state.
+ *
+ * This method is used to inform the HAL module that an external device has
+ * been connected to a device port selected using the 'id' field of the
+ * input AudioPort parameter. This device port must have dynamic profiles
+ * (an empty list of profiles). This port is further referenced to as "port
+ * template" because it acts as a template for creating a new instance of a
+ * "connected" device port which gets returned from this method.
+ *
+ * The input AudioPort parameter may contain any additional data obtained by
+ * the system side from other subsystems. The nature of data depends on the
+ * type of the connection. For example, for point-to-multipoint external
+ * device connections, the input parameter may contain the address of the
+ * connected external device. Another example is EDID information for HDMI
+ * connections (ExtraAudioDescriptor), which can be provided by the HDMI-CEC
+ * HAL module.
+ *
+ * It is the responsibility of the HAL module to query audio profiles
+ * supported by the connected device and return them as part of the returned
+ * AudioPort instance. In the case when the HAL is unable to query the
+ * external device, an error must be thrown.
+ *
+ * Thus, the returned audio port instance is the result of combining the
+ * following information:
+ * - a unique port ID generated by the HAL module;
+ * - static information from the port template;
+ * - list of audio profiles supported by the connected device;
+ * - additional data from the input AudioPort parameter.
+ *
+ * The HAL module must also update the list of audio routes to include the
+ * ID of the instantiated connected device port. Normally, the connected
+ * port allows the same routing as the port template.
+ *
+ * Also see notes on 'ModuleDebug.simulateDeviceConnections'.
+ *
+ * The following protocol is used by HAL module client for handling
+ * connection of an external device:
+ * 1. Obtain the list of device ports and their IDs via 'getAudioPorts'
+ * method. Select the appropriate port template using
+ * AudioDeviceDescription ('ext.device' field of AudioPort).
+ * 2. Combine the ID of the port template with any additional data and call
+ * 'connectExternalDevice'. The HAL module returns a new instance of
+ * AudioPort created using the rules explained above. Both
+ * 'getAudioPort' and 'getAudioPorts' methods will be returning the same
+ * information for this port until disconnection.
+ * 3. Configure the connected port with one of supported profiles using
+ * 'setAudioPortConfig'.
+ * 4. Query the list of AudioRoutes for the new AudioPort using
+ * 'getAudioRoutesForAudioPort' or 'getAudioRoutes' methods.
+ *
+ * External devices are distinguished by the connection type and device
+ * address. Calling this method multiple times to inform about connection of
+ * the same external device without disconnecting it first is an error.
+ *
+ * The HAL module must perform validation of the input parameter and throw
+ * an error if it is lacking required information, for example, when no
+ * device address is specified for a point-to-multipoint external device
+ * connection.
+ *
+ * Handling of a disconnect is done in a reverse order:
+ * 1. Reset port configuration using the 'resetAudioPortConfig' method.
+ * 2. Release the connected device port by calling the 'disconnectExternalDevice'
+ * method. This also removes the audio routes associated with this
+ * device port.
+ *
+ * @return New instance of an audio port for the connected external device.
+ * @param templateIdAndAdditionalData Specifies port template ID and any
+ * additional data.
+ * @throws EX_ILLEGAL_ARGUMENT In the following cases:
+ * - If the template port can not be found by the ID.
+ * - If the template is not a device port, or
+ * it does not have dynamic profiles.
+ * - If the input parameter is lacking required
+ * information.
+ * @throws EX_ILLEGAL_STATE In the following cases:
+ * - If the HAL module is unable to query audio profiles.
+ * - If the external device has already been connected.
+ */
+ AudioPort connectExternalDevice(in AudioPort templateIdAndAdditionalData);
+
+ /**
+ * Set a device port of a an external device into disconnected state.
+ *
+ * This method is used to inform the HAL module that an external device has
+ * been disconnected. The 'portId' must be of a connected device port
+ * instance previously instantiated using the 'connectExternalDevice'
+ * method.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT In the following cases:
+ * - If the port can not be found by the ID.
+ * - If this is not a connected device port.
+ * @throws EX_ILLEGAL_STATE If the port has active configurations.
+ */
+ void disconnectExternalDevice(int portId);
+
+ /**
* Return all audio patches of this module.
*
* Returns a list of audio patches, that is, established connections between
@@ -57,12 +169,10 @@
* Return the current state of the audio port.
*
* Using the port ID provided on input, returns the current state of the
- * audio port. For device port representing a connection to some external
- * device, e.g. over HDMI or USB, currently supported audio profiles and
- * extra audio descriptors may change.
- *
- * For all other audio ports it must be the same configuration as returned
- * for this port ID by 'getAudioPorts'.
+ * audio port. The values of the AudioPort structure must be the same as
+ * currently returned by the 'getAudioPorts' method. The 'getAudioPort'
+ * method is provided to reduce overhead in the case when the client needs
+ * to check the state of one port only.
*
* @return The current state of an audio port.
* @param portId The ID of the audio port.
@@ -87,55 +197,99 @@
AudioPortConfig[] getAudioPortConfigs();
/**
- * Return all audio ports provided by this module.
+ * Return the current state of all audio ports provided by this module.
*
- * Returns a list of all mix ports and device ports provided by this
- * module. Each returned port must have a unique ID within this module
- * ('AudioPort.id' field). The returned list must not change during
- * the lifetime of the IModule instance. For audio ports with dynamic
- * profiles (changing depending on external devices being connected
- * to the system) an empty list of profiles must be returned. The list
- * of currently supported audio profiles is obtained from 'getAudioPort'
- * method.
+ * Returns a list of all mix ports and device ports provided by this HAL
+ * module, reflecting their current states. Each returned port must have a
+ * unique ID within this module ('AudioPort.id' field). The list also
+ * includes "connected" ports created using 'connectExternalDevice' method.
*
* @return The list of audio ports.
*/
AudioPort[] getAudioPorts();
/**
- * Return all audio routes of this module.
+ * Return all current audio routes of this module.
*
- * Returns a list of audio routes, that is, allowed connections between
- * audio ports. The returned list must not change during the lifetime of the
- * IModule instance.
+ * Returns the current list of audio routes, that is, allowed connections
+ * between audio ports. The list can change when new device audio ports
+ * get created as a result of connecting or disconnecting of external
+ * devices.
*
* @return The list of audio routes.
*/
AudioRoute[] getAudioRoutes();
/**
+ * Return audio routes related to the specified audio port.
+ *
+ * Returns the list of audio routes that include the specified port ID
+ * as a source or as a sink. The returned list is a subset of the result
+ * returned by the 'getAudioRoutes' method, filtered by the port ID.
+ * An empty list can be returned, indicating that the audio port can not
+ * be used for creating audio patches.
+ *
+ * @return The list of audio routes.
+ * @param portId The ID of the audio port.
+ * @throws EX_ILLEGAL_ARGUMENT If the port can not be found by the ID.
+ */
+ AudioRoute[] getAudioRoutesForAudioPort(int portId);
+
+ /**
* Open an input stream using an existing audio mix port configuration.
*
* The audio port configuration ID must be obtained by calling
* 'setAudioPortConfig' method. Existence of an audio patch involving this
* port configuration is not required for successful opening of a stream.
*
+ * The requested buffer size is expressed in frames, thus the actual size
+ * in bytes depends on the audio port configuration. Also, the HAL module
+ * may end up providing a larger buffer, thus the requested size is treated
+ * as the minimum size that the client needs. The minimum buffer size
+ * suggested by the HAL is in the 'AudioPatch.minimumStreamBufferSizeFrames'
+ * field, returned as a result of calling the 'setAudioPatch' method.
+ *
* Only one stream is allowed per audio port configuration. HAL module can
* also set a limit on how many output streams can be opened for a particular
* mix port by using its 'AudioPortMixExt.maxOpenStreamCount' field.
*
- * @return An opened input stream.
- * @param portConfigId The ID of the audio mix port config.
- * @param sinkMetadata Description of the audio that will be recorded.
+ * Note that although it's not prohibited to open a stream on a mix port
+ * configuration which is not connected (using a patch) to any device port,
+ * and set up a patch afterwards, this sequence of calls is not recommended,
+ * because setting up of a patch might fail due to an insufficient stream
+ * buffer size. Another consequence of having a stream on an unconnected mix
+ * port is that capture positions can not be determined because there is no
+ * "external observer," thus read operations done via StreamDescriptor will
+ * be completing with an error, although data (zero filled) will still be
+ * provided.
+ *
+ * @return An opened input stream and the associated descriptor.
+ * @param args The pack of arguments, see 'OpenInputStreamArguments' parcelable.
* @throws EX_ILLEGAL_ARGUMENT In the following cases:
* - If the port config can not be found by the ID.
* - If the port config is not of an input mix port.
+ * - If a buffer of the requested size can not be provided.
* @throws EX_ILLEGAL_STATE In the following cases:
* - If the port config already has a stream opened on it.
* - If the limit on the open stream count for the port has
* been reached.
+ * - If the HAL module failed to initialize the stream.
*/
- IStreamIn openInputStream(int portConfigId, in SinkMetadata sinkMetadata);
+ @VintfStability
+ parcelable OpenInputStreamArguments {
+ /** The ID of the audio mix port config. */
+ int portConfigId;
+ /** Description of the audio that will be recorded. */
+ SinkMetadata sinkMetadata;
+ /** Requested audio I/O buffer minimum size, in frames. */
+ long bufferSizeFrames;
+ }
+ @VintfStability
+ parcelable OpenInputStreamReturn {
+ IStreamIn stream;
+ StreamDescriptor desc;
+ }
+ OpenInputStreamReturn openInputStream(in OpenInputStreamArguments args);
/**
* Open an output stream using an existing audio mix port configuration.
@@ -148,28 +302,62 @@
* the framework must provide additional information about the encoded
* audio stream in 'offloadInfo' argument.
*
+ * The requested buffer size is expressed in frames, thus the actual size
+ * in bytes depends on the audio port configuration. Also, the HAL module
+ * may end up providing a larger buffer, thus the requested size is treated
+ * as the minimum size that the client needs. The minimum buffer size
+ * suggested by the HAL is in the 'AudioPatch.minimumStreamBufferSizeFrames'
+ * field, returned as a result of calling the 'setAudioPatch' method.
+ *
* Only one stream is allowed per audio port configuration. HAL module can
* also set a limit on how many output streams can be opened for a particular
* mix port by using its 'AudioPortMixExt.maxOpenStreamCount' field.
* Only one stream can be opened on the audio port with 'PRIMARY' output
* flag. This rule can not be overridden with 'maxOpenStreamCount' field.
*
- * @return An opened output stream.
- * @param portConfigId The ID of the audio mix port config.
- * @param sourceMetadata Description of the audio that will be played.
- * @param offloadInfo Additional information for offloaded playback.
+ * Note that although it's not prohibited to open a stream on a mix port
+ * configuration which is not connected (using a patch) to any device port,
+ * and set up a patch afterwards, this sequence of calls is not recommended,
+ * because setting up of a patch might fail due to an insufficient stream
+ * buffer size. Another consequence of having a stream on an unconnected mix
+ * port is that presentation positions can not be determined because there
+ * is no "external observer," thus write operations done via
+ * StreamDescriptor will be completing with an error, although the data
+ * will still be accepted and immediately discarded.
+ *
+ * @return An opened output stream and the associated descriptor.
+ * @param args The pack of arguments, see 'OpenOutputStreamArguments' parcelable.
* @throws EX_ILLEGAL_ARGUMENT In the following cases:
* - If the port config can not be found by the ID.
* - If the port config is not of an output mix port.
+ * - If the offload info is not provided for an offload
+ * port configuration.
+ * - If a buffer of the requested size can not be provided.
* @throws EX_ILLEGAL_STATE In the following cases:
* - If the port config already has a stream opened on it.
* - If the limit on the open stream count for the port has
* been reached.
* - If another opened stream already exists for the 'PRIMARY'
* output port.
+ * - If the HAL module failed to initialize the stream.
*/
- IStreamOut openOutputStream(int portConfigId, in SourceMetadata sourceMetadata,
- in @nullable AudioOffloadInfo offloadInfo);
+ @VintfStability
+ parcelable OpenOutputStreamArguments {
+ /** The ID of the audio mix port config. */
+ int portConfigId;
+ /** Description of the audio that will be played. */
+ SourceMetadata sourceMetadata;
+ /** Additional information used for offloaded playback only. */
+ @nullable AudioOffloadInfo offloadInfo;
+ /** Requested audio I/O buffer minimum size, in frames. */
+ long bufferSizeFrames;
+ }
+ @VintfStability
+ parcelable OpenOutputStreamReturn {
+ IStreamOut stream;
+ StreamDescriptor desc;
+ }
+ OpenOutputStreamReturn openOutputStream(in OpenOutputStreamArguments args);
/**
* Set an audio patch.
@@ -177,19 +365,27 @@
* This method creates new or updates an existing audio patch. If the
* requested audio patch does not have a specified id, then a new patch is
* created and an ID is allocated for it by the HAL module. Otherwise an
- * attempt to update an existing patch is made. It is recommended that
- * updating of an existing audio patch should be performed by the HAL module
- * in a way that does not interrupt active audio streams involving audio
- * port configurations of the patch. If the HAL module is unable to avoid
- * interruption when updating a certain patch, it is permitted to allocate a
- * new patch ID for the result. The returned audio patch contains all the
- * information about the new or updated audio patch.
+ * attempt to update an existing patch is made.
+ *
+ * The operation of updating an existing audio patch must not change
+ * playback state of audio streams opened on the audio port configurations
+ * of the patch. That is, the HAL module must still be able to consume or
+ * to provide data from / to streams continuously during the patch
+ * switching. Natural intermittent audible loss of some audio frames due to
+ * switching between device ports which does not affect stream playback is
+ * allowed. If the HAL module is unable to avoid playback or recording
+ * state change when updating a certain patch, it must return an error. In
+ * that case, the client must take care of changing port configurations,
+ * patches, and recreating streams in a way which provides an acceptable
+ * user experience.
*
* Audio port configurations specified in the patch must be obtained by
* calling 'setAudioPortConfig' method. There must be an audio route which
* allows connection between the audio ports whose configurations are used.
- * An audio patch may be created before or after an audio steam is created
- * for this configuration.
+ *
+ * When updating an existing audio patch, nominal latency values may change
+ * and must be provided by the HAL module in the returned 'AudioPatch'
+ * structure.
*
* @return Resulting audio patch.
* @param requested Requested audio patch.
@@ -201,6 +397,9 @@
* @throws EX_ILLEGAL_STATE In the following cases:
* - If application of the patch can only use a route with an
* exclusive use the sink port, and it is already patched.
+ * - If updating an existing patch will cause interruption
+ * of audio, or requires re-opening of streams due to
+ * change of minimum audio I/O buffer size.
* @throws EX_UNSUPPORTED_OPERATION If the patch can not be established because
* the HAL module does not support this otherwise valid
* patch configuration. For example, if it's a patch
@@ -230,6 +429,10 @@
* parameter. The framework can then set the suggested configuration on a
* subsequent retry call to this method.
*
+ * Device ports with dynamic audio profiles (an empty list of profiles)
+ * can not be used with this method. The list of profiles must be filled in
+ * as a result of calling 'connectExternalDevice' method.
+ *
* @return Whether the requested configuration has been applied.
* @param requested Requested audio port configuration.
* @param suggested Same as requested configuration, if it was applied.
@@ -241,7 +444,7 @@
* - If the port can not be found by the port ID.
* - If it is not possible to generate a suggested port
* configuration, for example, if the port only has dynamic
- * profiles and they are currently empty.
+ * profiles.
*/
boolean setAudioPortConfig(in AudioPortConfig requested, out AudioPortConfig suggested);
diff --git a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
index b770449..0c3e3d1 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamIn.aidl
@@ -27,8 +27,12 @@
* Close the stream.
*
* Releases any resources allocated for this stream on the HAL module side.
- * The stream can not be operated after it has been closed. Methods of this
- * interface throw EX_ILLEGAL_STATE in for a closed stream.
+ * This includes the fast message queues and shared memories returned via
+ * the StreamDescriptor. Thus, the stream can not be operated anymore after
+ * it has been closed. The client needs to release the audio data I/O
+ * objects after the call to this method returns.
+ *
+ * Methods of this interface throw EX_ILLEGAL_STATE for a closed stream.
*
* @throws EX_ILLEGAL_STATE If the stream has already been closed.
*/
diff --git a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
index 60212fc..9fdb37d 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
@@ -27,8 +27,12 @@
* Close the stream.
*
* Releases any resources allocated for this stream on the HAL module side.
- * The stream can not be operated after it has been closed. Methods of this
- * interface throw EX_ILLEGAL_STATE in for a closed stream.
+ * This includes the fast message queues and shared memories returned via
+ * the StreamDescriptor. Thus, the stream can not be operated anymore after
+ * it has been closed. The client needs to release the audio data I/O
+ * objects after the call to this method returns.
+ *
+ * Methods of this interface throw EX_ILLEGAL_STATE for a closed stream.
*
* @throws EX_ILLEGAL_STATE If the stream has already been closed.
*/
diff --git a/audio/aidl/android/hardware/audio/core/MmapBufferDescriptor.aidl b/audio/aidl/android/hardware/audio/core/MmapBufferDescriptor.aidl
new file mode 100644
index 0000000..108bcbe
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/MmapBufferDescriptor.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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.audio.core;
+
+import android.hardware.common.Ashmem;
+
+/**
+ * MMap buffer descriptor is used by streams opened in MMap No IRQ mode.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable MmapBufferDescriptor {
+ /**
+ * MMap memory buffer.
+ */
+ Ashmem sharedMemory;
+ /**
+ * Transfer size granularity in frames.
+ */
+ long burstSizeFrames;
+ /**
+ * Attributes describing the buffer. Bitmask indexed by FLAG_INDEX_*
+ * constants.
+ */
+ int flags;
+
+ /**
+ * Whether the buffer can be securely shared to untrusted applications
+ * through the AAudio exclusive mode.
+ *
+ * Only set this flag if applications are restricted from accessing the
+ * memory surrounding the audio data buffer by a kernel mechanism.
+ * See Linux kernel's dma-buf
+ * (https://www.kernel.org/doc/html/v4.16/driver-api/dma-buf.html).
+ */
+ const int FLAG_INDEX_APPLICATION_SHAREABLE = 0;
+}
diff --git a/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
new file mode 100644
index 0000000..858a9bd
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.audio.core;
+
+/**
+ * This structure contains flags used for enabling various debugging aspects
+ * in a HAL module. By default, all debugging aspects are turned off. They
+ * can be enabled during xTS tests for functionality that, for example, would
+ * otherwise require human intervention (e.g. connection of external devices).
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable ModuleDebug {
+ /**
+ * When set to 'true', HAL module must simulate connection of external
+ * devices. An external device becomes 'connected' after a call to
+ * IModule.connectExternalDevice, simulation of connection requires:
+ * - provision of at least one non-dynamic device port profile on
+ * connection (as if it was retrieved from a connected device);
+ * - simulating successful application of port configurations for reported
+ * profiles.
+ */
+ boolean simulateDeviceConnections;
+}
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
new file mode 100644
index 0000000..2b1ed8c
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 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.audio.core;
+
+import android.hardware.audio.core.MmapBufferDescriptor;
+import android.hardware.common.fmq.MQDescriptor;
+import android.hardware.common.fmq.SynchronizedReadWrite;
+
+/**
+ * Stream descriptor contains fast message queues and buffers used for sending
+ * and receiving audio data. The descriptor complements IStream* interfaces by
+ * providing communication channels that serve as an alternative to Binder
+ * transactions.
+ *
+ * Handling of audio data and commands must be done by the HAL module on a
+ * dedicated thread with high priority, for all modes, including MMap No
+ * IRQ. The HAL module is responsible for creating this thread and setting its
+ * priority. The HAL module is also responsible for serializing access to the
+ * internal components of the stream while serving commands invoked via the
+ * stream's AIDL interface and commands invoked via the command queue of the
+ * descriptor.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable StreamDescriptor {
+ /**
+ * Position binds together a position within the stream and time.
+ *
+ * The timestamp must use "monotonic" clock.
+ *
+ * The frame count must advance between consecutive I/O operations, and stop
+ * advancing when the stream was put into the 'standby' mode. On exiting the
+ * 'standby' mode, the frame count must not reset, but continue counting.
+ */
+ @VintfStability
+ @FixedSize
+ parcelable Position {
+ /** Frame count. */
+ long frames;
+ /** Timestamp in nanoseconds. */
+ long timeNs;
+ }
+
+ /**
+ * The command used for audio I/O, see 'AudioBuffer'. For MMap No IRQ mode
+ * this command only provides updated positions and latency because actual
+ * audio I/O is done via the 'AudioBuffer.mmap' shared buffer.
+ */
+ const int COMMAND_BURST = 1;
+
+ /**
+ * Used for sending commands to the HAL module. The client writes into
+ * the queue, the HAL module reads. The queue can only contain a single
+ * command.
+ */
+ @VintfStability
+ @FixedSize
+ parcelable Command {
+ /**
+ * One of COMMAND_* codes.
+ */
+ int code;
+ /**
+ * For output streams: the amount of bytes that the client requests the
+ * HAL module to read from the 'audio.fmq' queue.
+ * For input streams: the amount of bytes requested by the client to
+ * read from the hardware into the 'audio.fmq' queue.
+ *
+ * In both cases it is allowed for this field to contain any
+ * non-negative number. The value 0 can be used if the client only needs
+ * to retrieve current positions and latency. Any sufficiently big value
+ * which exceeds the size of the queue's area which is currently
+ * available for reading or writing by the HAL module must be trimmed by
+ * the HAL module to the available size. Note that the HAL module is
+ * allowed to consume or provide less data than requested, and it must
+ * return the amount of actually read or written data via the
+ * 'Reply.fmqByteCount' field. Thus, only attempts to pass a negative
+ * number must be constituted as a client's error.
+ */
+ int fmqByteCount;
+ }
+ MQDescriptor<Command, SynchronizedReadWrite> command;
+
+ /**
+ * Used for providing replies to commands. The HAL module writes into
+ * the queue, the client reads. The queue can only contain a single reply,
+ * corresponding to the last command sent by the client.
+ */
+ @VintfStability
+ @FixedSize
+ parcelable Reply {
+ /**
+ * One of Binder STATUS_* statuses:
+ * - STATUS_OK: the command has completed successfully;
+ * - STATUS_BAD_VALUE: invalid value in the 'Command' structure;
+ * - STATUS_INVALID_OPERATION: the mix port is not connected
+ * to any producer or consumer, thus
+ * positions can not be reported;
+ * - STATUS_NOT_ENOUGH_DATA: a read or write error has
+ * occurred for the 'audio.fmq' queue;
+ *
+ */
+ int status;
+ /**
+ * For output streams: the amount of bytes actually consumed by the HAL
+ * module from the 'audio.fmq' queue.
+ * For input streams: the amount of bytes actually provided by the HAL
+ * in the 'audio.fmq' queue.
+ *
+ * The returned value must not exceed the value passed in the
+ * 'fmqByteCount' field of the corresponding command or be negative.
+ */
+ int fmqByteCount;
+ /**
+ * For output streams: the moment when the specified stream position
+ * was presented to an external observer (i.e. presentation position).
+ * For input streams: the moment when data at the specified stream position
+ * was acquired (i.e. capture position).
+ */
+ Position observable;
+ /**
+ * Used only for MMap streams to provide the hardware read / write
+ * position for audio data in the shared memory buffer 'audio.mmap'.
+ */
+ Position hardware;
+ /**
+ * Current latency reported by the hardware.
+ */
+ int latencyMs;
+ }
+ MQDescriptor<Reply, SynchronizedReadWrite> reply;
+
+ /**
+ * The size of one frame of audio data in bytes. For PCM formats this is
+ * usually equal to the size of a sample multiplied by the number of
+ * channels used. For encoded bitstreams encapsulated into PCM the sample
+ * size of the underlying PCM stream is used. For encoded bitstreams that
+ * are passed without encapsulation, the frame size is usually 1 byte.
+ */
+ int frameSizeBytes;
+ /**
+ * Total buffer size in frames. This applies both to the size of the 'audio.fmq'
+ * queue and to the size of the shared memory buffer for MMap No IRQ streams.
+ * Note that this size may end up being slightly larger than the size requested
+ * in a call to 'IModule.openInputStream' or 'openOutputStream' due to memory
+ * alignment requirements.
+ */
+ long bufferSizeFrames;
+
+ /**
+ * Used for sending or receiving audio data to/from the stream. In the case
+ * of MMap No IRQ streams this structure only contains the information about
+ * the shared memory buffer. Audio data is sent via the shared buffer
+ * directly.
+ */
+ @VintfStability
+ union AudioBuffer {
+ /**
+ * The fast message queue used for all modes except MMap No IRQ. Both
+ * reads and writes into this queue are non-blocking because access to
+ * this queue is synchronized via the 'command' and 'reply' queues as
+ * described below. The queue nevertheless uses 'SynchronizedReadWrite'
+ * because there is only one reader, and the reading position must be
+ * shared.
+ *
+ * For output streams the following sequence of operations is used:
+ * 1. The client writes audio data into the 'audio.fmq' queue.
+ * 2. The client writes the 'BURST' command into the 'command' queue,
+ * and hangs on waiting on a read from the 'reply' queue.
+ * 3. The high priority thread in the HAL module wakes up due to 2.
+ * 4. The HAL module reads the command and audio data.
+ * 5. The HAL module writes the command status and current positions
+ * into 'reply' queue, and hangs on waiting on a read from
+ * the 'command' queue.
+ * 6. The client wakes up due to 5. and reads the reply.
+ *
+ * For input streams the following sequence of operations is used:
+ * 1. The client writes the 'BURST' command into the 'command' queue,
+ * and hangs on waiting on a read from the 'reply' queue.
+ * 2. The high priority thread in the HAL module wakes up due to 1.
+ * 3. The HAL module writes audio data into the 'audio.fmq' queue.
+ * 4. The HAL module writes the command status and current positions
+ * into 'reply' queue, and hangs on waiting on a read from
+ * the 'command' queue.
+ * 5. The client wakes up due to 4.
+ * 6. The client reads the reply and audio data.
+ */
+ MQDescriptor<byte, SynchronizedReadWrite> fmq;
+ /**
+ * MMap buffers are shared directly with the DSP, which operates
+ * independently from the CPU. Writes and reads into these buffers
+ * are not synchronized with 'command' and 'reply' queues. However,
+ * the client still uses the 'BURST' command for obtaining current
+ * positions from the HAL module.
+ */
+ MmapBufferDescriptor mmap;
+ }
+ AudioBuffer audio;
+}
diff --git a/audio/aidl/common/Android.bp b/audio/aidl/common/Android.bp
new file mode 100644
index 0000000..a3f7f0b
--- /dev/null
+++ b/audio/aidl/common/Android.bp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library {
+ name: "libaudioaidlcommon",
+ host_supported: true,
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ header_libs: [
+ "libbase_headers",
+ "libsystem_headers",
+ ],
+ export_header_lib_headers: [
+ "libbase_headers",
+ "libsystem_headers",
+ ],
+ srcs: [
+ "StreamWorker.cpp",
+ ],
+}
+
+cc_test {
+ name: "libaudioaidlcommon_test",
+ host_supported: true,
+ vendor_available: true,
+ static_libs: [
+ "android.media.audio.common.types-V1-ndk",
+ "libaudioaidlcommon",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ ],
+ srcs: [
+ "tests/streamworker_tests.cpp",
+ "tests/utils_tests.cpp",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/audio/aidl/common/StreamWorker.cpp b/audio/aidl/common/StreamWorker.cpp
new file mode 100644
index 0000000..9bca760
--- /dev/null
+++ b/audio/aidl/common/StreamWorker.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include "include/StreamWorker.h"
+
+namespace android::hardware::audio::common::internal {
+
+bool ThreadController::start(const std::string& name, int priority) {
+ mThreadName = name;
+ mThreadPriority = priority;
+ mWorker = std::thread(&ThreadController::workerThread, this);
+ std::unique_lock<std::mutex> lock(mWorkerLock);
+ android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+ mWorkerCv.wait(lock, [&]() {
+ android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+ return mWorkerState == WorkerState::RUNNING || !mError.empty();
+ });
+ mWorkerStateChangeRequest = false;
+ return mWorkerState == WorkerState::RUNNING;
+}
+
+void ThreadController::stop() {
+ {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ if (mWorkerState != WorkerState::STOPPED) {
+ mWorkerState = WorkerState::STOPPED;
+ mWorkerStateChangeRequest = true;
+ }
+ }
+ if (mWorker.joinable()) {
+ mWorker.join();
+ }
+}
+
+bool ThreadController::waitForAtLeastOneCycle() {
+ WorkerState newState;
+ switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED, &newState);
+ if (newState != WorkerState::PAUSED) return false;
+ switchWorkerStateSync(newState, WorkerState::RESUME_REQUESTED, &newState);
+ return newState == WorkerState::RUNNING;
+}
+
+void ThreadController::switchWorkerStateSync(WorkerState oldState, WorkerState newState,
+ WorkerState* finalState) {
+ std::unique_lock<std::mutex> lock(mWorkerLock);
+ android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+ if (mWorkerState != oldState) {
+ if (finalState) *finalState = mWorkerState;
+ return;
+ }
+ mWorkerState = newState;
+ mWorkerStateChangeRequest = true;
+ mWorkerCv.wait(lock, [&]() {
+ android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+ return mWorkerState != newState;
+ });
+ if (finalState) *finalState = mWorkerState;
+}
+
+void ThreadController::workerThread() {
+ using Status = StreamLogic::Status;
+
+ std::string error = mLogic->init();
+ if (error.empty() && !mThreadName.empty()) {
+ std::string compliantName(mThreadName.substr(0, 15));
+ if (int errCode = pthread_setname_np(pthread_self(), compliantName.c_str()); errCode != 0) {
+ error.append("Failed to set thread name: ").append(strerror(errCode));
+ }
+ }
+ if (error.empty() && mThreadPriority != ANDROID_PRIORITY_DEFAULT) {
+ if (int result = setpriority(PRIO_PROCESS, 0, mThreadPriority); result != 0) {
+ int errCode = errno;
+ error.append("Failed to set thread priority: ").append(strerror(errCode));
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ mWorkerState = error.empty() ? WorkerState::RUNNING : WorkerState::STOPPED;
+ mError = error;
+ }
+ mWorkerCv.notify_one();
+ if (!error.empty()) return;
+
+ for (WorkerState state = WorkerState::RUNNING; state != WorkerState::STOPPED;) {
+ bool needToNotify = false;
+ if (Status status = state != WorkerState::PAUSED ? mLogic->cycle()
+ : (sched_yield(), Status::CONTINUE);
+ status == Status::CONTINUE) {
+ {
+ // See https://developer.android.com/training/articles/smp#nonracing
+ android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
+ if (!mWorkerStateChangeRequest.load(std::memory_order_relaxed)) continue;
+ }
+ //
+ // Pause and resume are synchronous. One worker cycle must complete
+ // before the worker indicates a state change. This is how 'mWorkerState' and
+ // 'state' interact:
+ //
+ // mWorkerState == RUNNING
+ // client sets mWorkerState := PAUSE_REQUESTED
+ // last workerCycle gets executed, state := mWorkerState := PAUSED by us
+ // (or the workers enters the 'error' state if workerCycle fails)
+ // client gets notified about state change in any case
+ // thread is doing a busy wait while 'state == PAUSED'
+ // client sets mWorkerState := RESUME_REQUESTED
+ // state := mWorkerState (RESUME_REQUESTED)
+ // mWorkerState := RUNNING, but we don't notify the client yet
+ // first workerCycle gets executed, the code below triggers a client notification
+ // (or if workerCycle fails, worker enters 'error' state and also notifies)
+ // state := mWorkerState (RUNNING)
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ if (state == WorkerState::RESUME_REQUESTED) {
+ needToNotify = true;
+ }
+ state = mWorkerState;
+ if (mWorkerState == WorkerState::PAUSE_REQUESTED) {
+ state = mWorkerState = WorkerState::PAUSED;
+ needToNotify = true;
+ } else if (mWorkerState == WorkerState::RESUME_REQUESTED) {
+ mWorkerState = WorkerState::RUNNING;
+ }
+ } else {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ if (state == WorkerState::RESUME_REQUESTED ||
+ mWorkerState == WorkerState::PAUSE_REQUESTED) {
+ needToNotify = true;
+ }
+ state = mWorkerState = WorkerState::STOPPED;
+ if (status == Status::ABORT) {
+ mError = "Received ABORT from the logic cycle";
+ }
+ }
+ if (needToNotify) {
+ {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ mWorkerStateChangeRequest = false;
+ }
+ mWorkerCv.notify_one();
+ }
+ }
+}
+
+} // namespace android::hardware::audio::common::internal
diff --git a/audio/aidl/common/TEST_MAPPING b/audio/aidl/common/TEST_MAPPING
new file mode 100644
index 0000000..9dcf44e
--- /dev/null
+++ b/audio/aidl/common/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libaudioaidlcommon_test"
+ }
+ ]
+}
diff --git a/audio/aidl/common/include/StreamWorker.h b/audio/aidl/common/include/StreamWorker.h
new file mode 100644
index 0000000..6260eca
--- /dev/null
+++ b/audio/aidl/common/include/StreamWorker.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+#include <system/thread_defs.h>
+
+namespace android::hardware::audio::common {
+
+class StreamLogic;
+
+namespace internal {
+
+class ThreadController {
+ enum class WorkerState { STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED };
+
+ public:
+ explicit ThreadController(StreamLogic* logic) : mLogic(logic) {}
+ ~ThreadController() { stop(); }
+
+ bool start(const std::string& name, int priority);
+ void pause() { switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED); }
+ void resume() { switchWorkerStateSync(WorkerState::PAUSED, WorkerState::RESUME_REQUESTED); }
+ bool hasError() {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ return !mError.empty();
+ }
+ std::string getError() {
+ std::lock_guard<std::mutex> lock(mWorkerLock);
+ return mError;
+ }
+ void stop();
+ bool waitForAtLeastOneCycle();
+
+ // Only used by unit tests.
+ void lockUnlockMutex(bool lock) NO_THREAD_SAFETY_ANALYSIS {
+ lock ? mWorkerLock.lock() : mWorkerLock.unlock();
+ }
+ std::thread::native_handle_type getThreadNativeHandle() { return mWorker.native_handle(); }
+
+ private:
+ void switchWorkerStateSync(WorkerState oldState, WorkerState newState,
+ WorkerState* finalState = nullptr);
+ void workerThread();
+
+ StreamLogic* const mLogic;
+ std::string mThreadName;
+ int mThreadPriority = ANDROID_PRIORITY_DEFAULT;
+ std::thread mWorker;
+ std::mutex mWorkerLock;
+ std::condition_variable mWorkerCv;
+ WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::STOPPED;
+ std::string mError GUARDED_BY(mWorkerLock);
+ // The atomic lock-free variable is used to prevent priority inversions
+ // that can occur when a high priority worker tries to acquire the lock
+ // which has been taken by a lower priority control thread which in its turn
+ // got preempted. To prevent a PI under normal operating conditions, that is,
+ // when there are no errors or state changes, the worker does not attempt
+ // taking `mWorkerLock` unless `mWorkerStateChangeRequest` is set.
+ // To make sure that updates to `mWorkerState` and `mWorkerStateChangeRequest`
+ // are serialized, they are always made under a lock.
+ static_assert(std::atomic<bool>::is_always_lock_free);
+ std::atomic<bool> mWorkerStateChangeRequest GUARDED_BY(mWorkerLock) = false;
+};
+
+} // namespace internal
+
+class StreamLogic {
+ public:
+ friend class internal::ThreadController;
+
+ virtual ~StreamLogic() = default;
+
+ protected:
+ enum class Status { ABORT, CONTINUE, EXIT };
+
+ /* Called once at the beginning of the thread loop. Must return
+ * an empty string to enter the thread loop, otherwise the thread loop
+ * exits and the worker switches into the 'error' state, setting
+ * the error to the returned value.
+ */
+ virtual std::string init() = 0;
+
+ /* Called for each thread loop unless the thread is in 'paused' state.
+ * Must return 'CONTINUE' to continue running, otherwise the thread loop
+ * exits. If the result from worker cycle is 'ABORT' then the worker switches
+ * into the 'error' state with a generic error message. It is recommended that
+ * the subclass reports any problems via logging facilities. Returning the 'EXIT'
+ * status is equivalent to calling 'stop()' method. This is just a way of
+ * of stopping the worker by its own initiative.
+ */
+ virtual Status cycle() = 0;
+};
+
+template <class LogicImpl>
+class StreamWorker : public LogicImpl {
+ public:
+ template <class... Args>
+ explicit StreamWorker(Args&&... args) : LogicImpl(std::forward<Args>(args)...), mThread(this) {}
+
+ // Methods of LogicImpl are available via inheritance.
+ // Forwarded methods of ThreadController follow.
+
+ // Note that 'priority' here is what is known as the 'nice number' in *nix systems.
+ // The nice number is used with the default scheduler. For threads that
+ // need to use a specialized scheduler (e.g. SCHED_FIFO) and set the priority within it,
+ // it is recommended to implement an appropriate configuration sequence within
+ // 'LogicImpl' or 'StreamLogic::init'.
+ bool start(const std::string& name = "", int priority = ANDROID_PRIORITY_DEFAULT) {
+ return mThread.start(name, priority);
+ }
+ void pause() { mThread.pause(); }
+ void resume() { mThread.resume(); }
+ bool hasError() { return mThread.hasError(); }
+ std::string getError() { return mThread.getError(); }
+ void stop() { return mThread.stop(); }
+ bool waitForAtLeastOneCycle() { return mThread.waitForAtLeastOneCycle(); }
+
+ // Only used by unit tests.
+ void testLockUnlockMutex(bool lock) { mThread.lockUnlockMutex(lock); }
+ std::thread::native_handle_type testGetThreadNativeHandle() {
+ return mThread.getThreadNativeHandle();
+ }
+
+ private:
+ // The ThreadController gets destroyed before LogicImpl.
+ // After the controller has been destroyed, it is guaranteed that
+ // the thread was joined, thus the 'cycle' method of LogicImpl
+ // will not be called anymore, and it is safe to destroy LogicImpl.
+ internal::ThreadController mThread;
+};
+
+} // namespace android::hardware::audio::common
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
new file mode 100644
index 0000000..74549d4
--- /dev/null
+++ b/audio/aidl/common/include/Utils.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <aidl/android/media/audio/common/PcmType.h>
+
+namespace android::hardware::audio::common {
+
+constexpr size_t getPcmSampleSizeInBytes(::aidl::android::media::audio::common::PcmType pcm) {
+ using ::aidl::android::media::audio::common::PcmType;
+ switch (pcm) {
+ case PcmType::UINT_8_BIT:
+ return 1;
+ case PcmType::INT_16_BIT:
+ return 2;
+ case PcmType::INT_32_BIT:
+ return 4;
+ case PcmType::FIXED_Q_8_24:
+ return 4;
+ case PcmType::FLOAT_32_BIT:
+ return 4;
+ case PcmType::INT_24_BIT:
+ return 3;
+ }
+ return 0;
+}
+
+constexpr size_t getChannelCount(
+ const ::aidl::android::media::audio::common::AudioChannelLayout& layout) {
+ using Tag = ::aidl::android::media::audio::common::AudioChannelLayout::Tag;
+ switch (layout.getTag()) {
+ case Tag::none:
+ return 0;
+ case Tag::invalid:
+ return 0;
+ case Tag::indexMask:
+ return __builtin_popcount(layout.get<Tag::indexMask>());
+ case Tag::layoutMask:
+ return __builtin_popcount(layout.get<Tag::layoutMask>());
+ case Tag::voiceMask:
+ return __builtin_popcount(layout.get<Tag::voiceMask>());
+ }
+ return 0;
+}
+
+constexpr size_t getFrameSizeInBytes(
+ const ::aidl::android::media::audio::common::AudioFormatDescription& format,
+ const ::aidl::android::media::audio::common::AudioChannelLayout& layout) {
+ if (format == ::aidl::android::media::audio::common::AudioFormatDescription{}) {
+ // Unspecified format.
+ return 0;
+ }
+ using ::aidl::android::media::audio::common::AudioFormatType;
+ if (format.type == AudioFormatType::PCM) {
+ return getPcmSampleSizeInBytes(format.pcm) * getChannelCount(layout);
+ } else if (format.type == AudioFormatType::NON_PCM) {
+ // For non-PCM formats always use the underlying PCM size. The default value for
+ // PCM is "UINT_8_BIT", thus non-encapsulated streams have the frame size of 1.
+ return getPcmSampleSizeInBytes(format.pcm);
+ }
+ // Something unexpected.
+ return 0;
+}
+
+} // namespace android::hardware::audio::common
diff --git a/audio/aidl/common/tests/streamworker_tests.cpp b/audio/aidl/common/tests/streamworker_tests.cpp
new file mode 100644
index 0000000..e3e484d
--- /dev/null
+++ b/audio/aidl/common/tests/streamworker_tests.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <atomic>
+
+#include <StreamWorker.h>
+
+#include <gtest/gtest.h>
+#define LOG_TAG "StreamWorker_Test"
+#include <log/log.h>
+
+using android::hardware::audio::common::StreamLogic;
+using android::hardware::audio::common::StreamWorker;
+
+class TestWorkerLogic : public StreamLogic {
+ public:
+ struct Stream {
+ void setErrorStatus() { status = Status::ABORT; }
+ void setStopStatus() { status = Status::EXIT; }
+ std::atomic<Status> status = Status::CONTINUE;
+ };
+
+ // Use nullptr to test error reporting from the worker thread.
+ explicit TestWorkerLogic(Stream* stream) : mStream(stream) {}
+
+ size_t getWorkerCycles() const { return mWorkerCycles; }
+ int getPriority() const { return mPriority; }
+ bool hasWorkerCycleCalled() const { return mWorkerCycles != 0; }
+ bool hasNoWorkerCycleCalled(useconds_t usec) {
+ const size_t cyclesBefore = mWorkerCycles;
+ usleep(usec);
+ return mWorkerCycles == cyclesBefore;
+ }
+
+ protected:
+ // StreamLogic implementation
+ std::string init() override { return mStream != nullptr ? "" : "Expected error"; }
+ Status cycle() override {
+ mPriority = getpriority(PRIO_PROCESS, 0);
+ do {
+ mWorkerCycles++;
+ } while (mWorkerCycles == 0);
+ return mStream->status;
+ }
+
+ private:
+ Stream* const mStream;
+ std::atomic<size_t> mWorkerCycles = 0;
+ std::atomic<int> mPriority = ANDROID_PRIORITY_DEFAULT;
+};
+using TestWorker = StreamWorker<TestWorkerLogic>;
+
+// The parameter specifies whether an extra call to 'stop' is made at the end.
+class StreamWorkerInvalidTest : public testing::TestWithParam<bool> {
+ public:
+ StreamWorkerInvalidTest() : StreamWorkerInvalidTest(nullptr) {}
+ void TearDown() override {
+ if (GetParam()) {
+ worker.stop();
+ }
+ }
+
+ protected:
+ StreamWorkerInvalidTest(TestWorker::Stream* stream)
+ : testing::TestWithParam<bool>(), worker(stream) {}
+ TestWorker worker;
+};
+
+TEST_P(StreamWorkerInvalidTest, Uninitialized) {
+ EXPECT_FALSE(worker.hasWorkerCycleCalled());
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedPauseIgnored) {
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedResumeIgnored) {
+ EXPECT_FALSE(worker.hasError());
+ worker.resume();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, Start) {
+ EXPECT_FALSE(worker.start());
+ EXPECT_FALSE(worker.hasWorkerCycleCalled());
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, PauseIgnored) {
+ EXPECT_FALSE(worker.start());
+ EXPECT_TRUE(worker.hasError());
+ worker.pause();
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, ResumeIgnored) {
+ EXPECT_FALSE(worker.start());
+ EXPECT_TRUE(worker.hasError());
+ worker.resume();
+ EXPECT_TRUE(worker.hasError());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorkerInvalid, StreamWorkerInvalidTest, testing::Bool());
+
+class StreamWorkerTest : public StreamWorkerInvalidTest {
+ public:
+ StreamWorkerTest() : StreamWorkerInvalidTest(&stream) {}
+
+ protected:
+ TestWorker::Stream stream;
+};
+
+static constexpr unsigned kWorkerIdleCheckTime = 50 * 1000;
+
+TEST_P(StreamWorkerTest, Uninitialized) {
+ EXPECT_FALSE(worker.hasWorkerCycleCalled());
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, Start) {
+ ASSERT_TRUE(worker.start());
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, StartStop) {
+ ASSERT_TRUE(worker.start());
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_FALSE(worker.hasError());
+ worker.stop();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerExit) {
+ ASSERT_TRUE(worker.start());
+ stream.setStopStatus();
+ worker.waitForAtLeastOneCycle();
+ EXPECT_FALSE(worker.hasError());
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, WorkerError) {
+ ASSERT_TRUE(worker.start());
+ stream.setErrorStatus();
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, StopAfterError) {
+ ASSERT_TRUE(worker.start());
+ stream.setErrorStatus();
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+ worker.stop();
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, PauseResume) {
+ ASSERT_TRUE(worker.start());
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+ EXPECT_FALSE(worker.hasError());
+ const size_t workerCyclesBefore = worker.getWorkerCycles();
+ worker.resume();
+ // 'resume' is synchronous and returns after the worker has looped at least once.
+ EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, StopPaused) {
+ ASSERT_TRUE(worker.start());
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ worker.stop();
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, PauseAfterErrorIgnored) {
+ ASSERT_TRUE(worker.start());
+ stream.setErrorStatus();
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ worker.pause();
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, ResumeAfterErrorIgnored) {
+ ASSERT_TRUE(worker.start());
+ stream.setErrorStatus();
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ worker.resume();
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+ EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerErrorOnResume) {
+ ASSERT_TRUE(worker.start());
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_FALSE(worker.hasError());
+ worker.pause();
+ EXPECT_FALSE(worker.hasError());
+ stream.setErrorStatus();
+ EXPECT_FALSE(worker.hasError());
+ worker.resume();
+ worker.waitForAtLeastOneCycle();
+ EXPECT_TRUE(worker.hasError());
+ EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycle) {
+ ASSERT_TRUE(worker.start());
+ const size_t workerCyclesBefore = worker.getWorkerCycles();
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycleError) {
+ ASSERT_TRUE(worker.start());
+ stream.setErrorStatus();
+ EXPECT_FALSE(worker.waitForAtLeastOneCycle());
+}
+
+TEST_P(StreamWorkerTest, MutexDoesNotBlockWorker) {
+ ASSERT_TRUE(worker.start());
+ const size_t workerCyclesBefore = worker.getWorkerCycles();
+ worker.testLockUnlockMutex(true);
+ while (worker.getWorkerCycles() == workerCyclesBefore) {
+ usleep(kWorkerIdleCheckTime);
+ }
+ worker.testLockUnlockMutex(false);
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, ThreadName) {
+ const std::string workerName = "TestWorker";
+ ASSERT_TRUE(worker.start(workerName)) << worker.getError();
+ char nameBuf[128];
+ ASSERT_EQ(0, pthread_getname_np(worker.testGetThreadNativeHandle(), nameBuf, sizeof(nameBuf)));
+ EXPECT_EQ(workerName, nameBuf);
+}
+
+TEST_P(StreamWorkerTest, ThreadPriority) {
+ const int priority = ANDROID_PRIORITY_LOWEST;
+ ASSERT_TRUE(worker.start("", priority)) << worker.getError();
+ EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+ EXPECT_EQ(priority, worker.getPriority());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorker, StreamWorkerTest, testing::Bool());
diff --git a/audio/aidl/common/tests/utils_tests.cpp b/audio/aidl/common/tests/utils_tests.cpp
new file mode 100644
index 0000000..d7f1a5d
--- /dev/null
+++ b/audio/aidl/common/tests/utils_tests.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <Utils.h>
+
+#include <gtest/gtest.h>
+#define LOG_TAG "Utils_Test"
+#include <log/log.h>
+
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::PcmType;
+using android::hardware::audio::common::getChannelCount;
+using android::hardware::audio::common::getFrameSizeInBytes;
+using android::hardware::audio::common::getPcmSampleSizeInBytes;
+
+TEST(UtilsTest, ChannelCountOddCases) {
+ using Tag = AudioChannelLayout::Tag;
+ EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout{}));
+ EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::invalid>(0)));
+ EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::invalid>(-1)));
+}
+
+TEST(UtilsTest, ChannelCountForIndexMask) {
+ using Tag = AudioChannelLayout::Tag;
+ EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::indexMask>(0)));
+#define VERIFY_INDEX_MASK(N) \
+ { \
+ const auto l = \
+ AudioChannelLayout::make<Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##N); \
+ EXPECT_EQ(N##UL, getChannelCount(l)) << l.toString(); \
+ }
+ VERIFY_INDEX_MASK(1);
+ VERIFY_INDEX_MASK(2);
+ VERIFY_INDEX_MASK(3);
+ VERIFY_INDEX_MASK(4);
+ VERIFY_INDEX_MASK(5);
+ VERIFY_INDEX_MASK(6);
+ VERIFY_INDEX_MASK(7);
+ VERIFY_INDEX_MASK(8);
+ VERIFY_INDEX_MASK(9);
+ VERIFY_INDEX_MASK(10);
+ VERIFY_INDEX_MASK(11);
+ VERIFY_INDEX_MASK(12);
+ VERIFY_INDEX_MASK(13);
+ VERIFY_INDEX_MASK(14);
+ VERIFY_INDEX_MASK(15);
+ VERIFY_INDEX_MASK(16);
+ VERIFY_INDEX_MASK(17);
+ VERIFY_INDEX_MASK(18);
+ VERIFY_INDEX_MASK(19);
+ VERIFY_INDEX_MASK(20);
+ VERIFY_INDEX_MASK(21);
+ VERIFY_INDEX_MASK(22);
+ VERIFY_INDEX_MASK(23);
+ VERIFY_INDEX_MASK(24);
+#undef VERIFY_INDEX_MASK
+}
+
+TEST(UtilsTest, ChannelCountForLayoutMask) {
+ using Tag = AudioChannelLayout::Tag;
+ const std::vector<std::pair<size_t, int32_t>> kTestLayouts = {
+ std::make_pair(0UL, 0),
+ std::make_pair(1UL, AudioChannelLayout::LAYOUT_MONO),
+ std::make_pair(2UL, AudioChannelLayout::LAYOUT_STEREO),
+ std::make_pair(6UL, AudioChannelLayout::LAYOUT_5POINT1),
+ std::make_pair(8UL, AudioChannelLayout::LAYOUT_7POINT1),
+ std::make_pair(16UL, AudioChannelLayout::LAYOUT_9POINT1POINT6),
+ std::make_pair(13UL, AudioChannelLayout::LAYOUT_13POINT_360RA),
+ std::make_pair(24UL, AudioChannelLayout::LAYOUT_22POINT2),
+ std::make_pair(3UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_A),
+ std::make_pair(4UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_AB)};
+ for (const auto& [expected_count, layout] : kTestLayouts) {
+ const auto l = AudioChannelLayout::make<Tag::layoutMask>(layout);
+ EXPECT_EQ(expected_count, getChannelCount(l)) << l.toString();
+ }
+}
+
+TEST(UtilsTest, ChannelCountForVoiceMask) {
+ using Tag = AudioChannelLayout::Tag;
+ // clang-format off
+ const std::vector<std::pair<size_t, int32_t>> kTestLayouts = {
+ std::make_pair(0UL, 0),
+ std::make_pair(1UL, AudioChannelLayout::VOICE_UPLINK_MONO),
+ std::make_pair(1UL, AudioChannelLayout::VOICE_DNLINK_MONO),
+ std::make_pair(2UL, AudioChannelLayout::VOICE_CALL_MONO)};
+ // clang-format on
+ for (const auto& [expected_count, layout] : kTestLayouts) {
+ const auto l = AudioChannelLayout::make<Tag::voiceMask>(layout);
+ EXPECT_EQ(expected_count, getChannelCount(l)) << l.toString();
+ }
+}
+
+namespace {
+
+AudioChannelLayout make_AudioChannelLayout_Mono() {
+ return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::LAYOUT_MONO);
+}
+
+AudioChannelLayout make_AudioChannelLayout_Stereo() {
+ return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::LAYOUT_STEREO);
+}
+
+AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
+ AudioFormatDescription result;
+ result.type = type;
+ return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
+ auto result = make_AudioFormatDescription(AudioFormatType::PCM);
+ result.pcm = pcm;
+ return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(const std::string& encoding) {
+ AudioFormatDescription result;
+ result.encoding = encoding;
+ return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType transport, const std::string& encoding) {
+ auto result = make_AudioFormatDescription(encoding);
+ result.pcm = transport;
+ return result;
+}
+
+} // namespace
+
+TEST(UtilsTest, FrameSize) {
+ EXPECT_EQ(0UL, getFrameSizeInBytes(AudioFormatDescription{}, AudioChannelLayout{}));
+ EXPECT_EQ(sizeof(int16_t), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT),
+ make_AudioChannelLayout_Mono()));
+ EXPECT_EQ(2 * sizeof(int16_t),
+ getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT),
+ make_AudioChannelLayout_Stereo()));
+ EXPECT_EQ(sizeof(int32_t), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_32_BIT),
+ make_AudioChannelLayout_Mono()));
+ EXPECT_EQ(2 * sizeof(int32_t),
+ getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_32_BIT),
+ make_AudioChannelLayout_Stereo()));
+ EXPECT_EQ(sizeof(float), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::FLOAT_32_BIT),
+ make_AudioChannelLayout_Mono()));
+ EXPECT_EQ(2 * sizeof(float),
+ getFrameSizeInBytes(make_AudioFormatDescription(PcmType::FLOAT_32_BIT),
+ make_AudioChannelLayout_Stereo()));
+ EXPECT_EQ(sizeof(uint8_t),
+ getFrameSizeInBytes(make_AudioFormatDescription("bitstream"), AudioChannelLayout{}));
+ EXPECT_EQ(sizeof(int16_t),
+ getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT, "encapsulated"),
+ AudioChannelLayout{}));
+}
+
+TEST(UtilsTest, PcmSampleSize) {
+ EXPECT_EQ(1UL, getPcmSampleSizeInBytes(PcmType{}));
+ EXPECT_EQ(sizeof(uint8_t), getPcmSampleSizeInBytes(PcmType::UINT_8_BIT));
+ EXPECT_EQ(sizeof(int16_t), getPcmSampleSizeInBytes(PcmType::INT_16_BIT));
+ EXPECT_EQ(sizeof(int32_t), getPcmSampleSizeInBytes(PcmType::INT_32_BIT));
+ EXPECT_EQ(sizeof(int32_t), getPcmSampleSizeInBytes(PcmType::FIXED_Q_8_24));
+ EXPECT_EQ(sizeof(float), getPcmSampleSizeInBytes(PcmType::FLOAT_32_BIT));
+ EXPECT_EQ(3UL, getPcmSampleSizeInBytes(PcmType::INT_24_BIT));
+ EXPECT_EQ(0UL, getPcmSampleSizeInBytes(PcmType(-1)));
+ using PcmTypeUnderlyingType = std::underlying_type_t<PcmType>;
+ EXPECT_EQ(0UL,
+ getPcmSampleSizeInBytes(PcmType(std::numeric_limits<PcmTypeUnderlyingType>::min())));
+ EXPECT_EQ(0UL,
+ getPcmSampleSizeInBytes(PcmType(std::numeric_limits<PcmTypeUnderlyingType>::max())));
+}
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 0a6fe60..03f8c64 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -7,14 +7,27 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
-cc_library_static {
- name: "libaudioserviceexampleimpl",
+cc_defaults {
+ name: "aidlaudioservice_defaults",
vendor: true,
shared_libs: [
+ "libaudioaidlcommon",
"libbase",
"libbinder_ndk",
+ "libcutils",
+ "libfmq",
+ "libstagefright_foundation",
+ "libutils",
+ "android.media.audio.common.types-V1-ndk",
"android.hardware.audio.core-V1-ndk",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
],
+}
+
+cc_library_static {
+ name: "libaudioserviceexampleimpl",
+ defaults: ["aidlaudioservice_defaults"],
export_include_dirs: ["include"],
srcs: [
"Config.cpp",
@@ -32,12 +45,7 @@
relative_install_path: "hw",
init_rc: ["android.hardware.audio.service-aidl.example.rc"],
vintf_fragments: ["android.hardware.audio.service-aidl.xml"],
- vendor: true,
- shared_libs: [
- "libbase",
- "libbinder_ndk",
- "android.hardware.audio.core-V1-ndk",
- ],
+ defaults: ["aidlaudioservice_defaults"],
static_libs: [
"libaudioserviceexampleimpl",
],
diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp
index 1104caa..f5d679b 100644
--- a/audio/aidl/default/Configuration.cpp
+++ b/audio/aidl/default/Configuration.cpp
@@ -16,14 +16,16 @@
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceType.h>
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <aidl/android/media/audio/common/AudioFormatType.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
+#include <media/stagefright/foundation/MediaDefs.h>
-#include "aidl/android/media/audio/common/AudioFormatDescription.h"
#include "core-impl/Configuration.h"
using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
@@ -41,22 +43,38 @@
namespace aidl::android::hardware::audio::core::internal {
+static void fillProfile(AudioProfile* profile, const std::vector<int32_t>& channelLayouts,
+ const std::vector<int32_t>& sampleRates) {
+ for (auto layout : channelLayouts) {
+ profile->channelMasks.push_back(
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout));
+ }
+ profile->sampleRates.insert(profile->sampleRates.end(), sampleRates.begin(), sampleRates.end());
+}
+
static AudioProfile createProfile(PcmType pcmType, const std::vector<int32_t>& channelLayouts,
const std::vector<int32_t>& sampleRates) {
AudioProfile profile;
profile.format.type = AudioFormatType::PCM;
profile.format.pcm = pcmType;
- for (auto layout : channelLayouts) {
- profile.channelMasks.push_back(
- AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout));
- }
- profile.sampleRates.insert(profile.sampleRates.end(), sampleRates.begin(), sampleRates.end());
+ fillProfile(&profile, channelLayouts, sampleRates);
return profile;
}
-static AudioPortExt createDeviceExt(AudioDeviceType devType, int32_t flags) {
+static AudioProfile createProfile(const std::string& encodingType,
+ const std::vector<int32_t>& channelLayouts,
+ const std::vector<int32_t>& sampleRates) {
+ AudioProfile profile;
+ profile.format.encoding = encodingType;
+ fillProfile(&profile, channelLayouts, sampleRates);
+ return profile;
+}
+
+static AudioPortExt createDeviceExt(AudioDeviceType devType, int32_t flags,
+ std::string connection = "") {
AudioPortDeviceExt deviceExt;
deviceExt.device.type.type = devType;
+ deviceExt.device.type.connection = std::move(connection);
deviceExt.flags = flags;
return AudioPortExt::make<AudioPortExt::Tag::device>(deviceExt);
}
@@ -102,8 +120,66 @@
return route;
}
+// Configuration:
+//
+// Device ports:
+// * "Null", OUT_SPEAKER, default
+// - no profiles specified
+// * "Loopback Out", OUT_SUBMIX
+// - profile PCM 24-bit; STEREO; 48000
+// * "USB Out", OUT_DEVICE, CONNECTION_USB
+// - no profiles specified
+// * "Zero", IN_MICROPHONE, default
+// - no profiles specified
+// * "Loopback In", IN_SUBMIX
+// - profile PCM 24-bit; STEREO; 48000
+// * "USB In", IN_DEVICE, CONNECTION_USB
+// - no profiles specified
+//
+// Mix ports:
+// * "primary output", PRIMARY, 1 max open, 1 max active stream
+// - profile PCM 16-bit; MONO, STEREO; 44100, 48000
+// - profile PCM 24-bit; MONO, STEREO; 44100, 48000
+// * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream
+// - profile MP3; MONO, STEREO; 44100, 48000
+// * "loopback output", stream count unlimited
+// - profile PCM 24-bit; STEREO; 48000
+// * "primary input", 2 max open, 2 max active streams
+// - profile PCM 16-bit; MONO, STEREO, FRONT_BACK;
+// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
+// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+// * "loopback input", stream count unlimited
+// - profile PCM 24-bit; STEREO; 48000
+//
+// Routes:
+// "primary out", "compressed offload" -> "Null"
+// "primary out", "compressed offload" -> "USB Out"
+// "loopback out" -> "Loopback Out"
+// "Zero", "USB In" -> "primary input"
+// "Loopback In" -> "loopback input"
+//
+// Initial port configs:
+// * "Null" device port: PCM 24-bit; STEREO; 48000
+// * "Zero" device port: PCM 24-bit; MONO; 48000
+//
+// Profiles for device port connected state:
+// * USB Out":
+// - profile PCM 16-bit; MONO, STEREO; 44100, 48000
+// - profile PCM 24-bit; MONO, STEREO; 44100, 48000
+// * USB In":
+// - profile PCM 16-bit; MONO, STEREO; 44100, 48000
+// - profile PCM 24-bit; MONO, STEREO; 44100, 48000
+//
Configuration& getNullPrimaryConfiguration() {
static Configuration configuration = []() {
+ const std::vector<AudioProfile> standardPcmAudioProfiles = {
+ createProfile(PcmType::INT_16_BIT,
+ {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+ {44100, 48000}),
+ createProfile(PcmType::INT_24_BIT,
+ {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+ {44100, 48000})};
Configuration c;
AudioPort nullOutDevice =
@@ -111,27 +187,31 @@
createDeviceExt(AudioDeviceType::OUT_SPEAKER,
1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
c.ports.push_back(nullOutDevice);
-
- AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output",
- 1 << static_cast<int32_t>(AudioOutputFlags::PRIMARY),
- false, createPortMixExt(1, 1));
- primaryOutMix.profiles.push_back(
- createProfile(PcmType::INT_16_BIT,
- {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
- {44100, 48000}));
- primaryOutMix.profiles.push_back(
- createProfile(PcmType::INT_24_BIT,
- {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
- {44100, 48000}));
- c.ports.push_back(primaryOutMix);
-
- c.routes.push_back(createRoute({primaryOutMix.id}, nullOutDevice.id));
-
c.initialConfigs.push_back(
createPortConfig(nullOutDevice.id, nullOutDevice.id, PcmType::INT_24_BIT,
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)));
+ AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output",
+ 1 << static_cast<int32_t>(AudioOutputFlags::PRIMARY),
+ false, createPortMixExt(1, 1));
+ primaryOutMix.profiles.insert(primaryOutMix.profiles.begin(),
+ standardPcmAudioProfiles.begin(),
+ standardPcmAudioProfiles.end());
+ c.ports.push_back(primaryOutMix);
+
+ AudioPort compressedOffloadOutMix =
+ createPort(c.nextPortId++, "compressed offload",
+ 1 << static_cast<int32_t>(AudioOutputFlags::DIRECT) |
+ 1 << static_cast<int32_t>(AudioOutputFlags::COMPRESS_OFFLOAD) |
+ 1 << static_cast<int32_t>(AudioOutputFlags::NON_BLOCKING),
+ false, createPortMixExt(1, 1));
+ compressedOffloadOutMix.profiles.push_back(
+ createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG,
+ {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
+ {44100, 48000}));
+ c.ports.push_back(compressedOffloadOutMix);
+
AudioPort loopOutDevice = createPort(c.nextPortId++, "Loopback Out", 0, false,
createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0));
loopOutDevice.profiles.push_back(
@@ -144,13 +224,22 @@
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
c.ports.push_back(loopOutMix);
- c.routes.push_back(createRoute({loopOutMix.id}, loopOutDevice.id));
+ AudioPort usbOutDevice =
+ createPort(c.nextPortId++, "USB Out", 0, false,
+ createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
+ AudioDeviceDescription::CONNECTION_USB));
+ c.ports.push_back(usbOutDevice);
+ c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
AudioPort zeroInDevice =
createPort(c.nextPortId++, "Zero", 0, true,
createDeviceExt(AudioDeviceType::IN_MICROPHONE,
1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
c.ports.push_back(zeroInDevice);
+ c.initialConfigs.push_back(
+ createPortConfig(zeroInDevice.id, zeroInDevice.id, PcmType::INT_24_BIT,
+ AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
+ createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
AudioPort primaryInMix =
createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(2, 2));
@@ -166,13 +255,6 @@
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
c.ports.push_back(primaryInMix);
- c.routes.push_back(createRoute({zeroInDevice.id}, primaryInMix.id));
-
- c.initialConfigs.push_back(
- createPortConfig(zeroInDevice.id, zeroInDevice.id, PcmType::INT_24_BIT,
- AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
- createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
-
AudioPort loopInDevice = createPort(c.nextPortId++, "Loopback In", 0, true,
createDeviceExt(AudioDeviceType::IN_SUBMIX, 0));
loopInDevice.profiles.push_back(
@@ -185,6 +267,18 @@
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
c.ports.push_back(loopInMix);
+ AudioPort usbInDevice = createPort(c.nextPortId++, "USB In", 0, true,
+ createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
+ AudioDeviceDescription::CONNECTION_USB));
+ c.ports.push_back(usbInDevice);
+ c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
+
+ c.routes.push_back(
+ createRoute({primaryOutMix.id, compressedOffloadOutMix.id}, nullOutDevice.id));
+ c.routes.push_back(
+ createRoute({primaryOutMix.id, compressedOffloadOutMix.id}, usbOutDevice.id));
+ c.routes.push_back(createRoute({loopOutMix.id}, loopOutDevice.id));
+ c.routes.push_back(createRoute({zeroInDevice.id, usbInDevice.id}, primaryInMix.id));
c.routes.push_back(createRoute({loopInDevice.id}, loopInMix.id));
c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index e0a68a5..af033d0 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -18,9 +18,10 @@
#include <set>
#define LOG_TAG "AHAL_Module"
-#define LOG_NDEBUG 0
#include <android-base/logging.h>
+#include <Utils.h>
+#include <aidl/android/media/audio/common/AudioInputFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include "core-impl/Module.h"
@@ -28,7 +29,10 @@
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioInputFlags;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioOutputFlags;
@@ -37,6 +41,8 @@
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::AudioProfile;
using aidl::android::media::audio::common::Int;
+using aidl::android::media::audio::common::PcmType;
+using android::hardware::audio::common::getFrameSizeInBytes;
namespace aidl::android::hardware::audio::core {
@@ -81,34 +87,101 @@
}
return false;
}
+
} // namespace
void Module::cleanUpPatch(int32_t patchId) {
erase_all_values(mPatches, std::set<int32_t>{patchId});
}
-void Module::cleanUpPatches(int32_t portConfigId) {
- auto& patches = getConfig().patches;
- if (patches.size() == 0) return;
- auto range = mPatches.equal_range(portConfigId);
- for (auto it = range.first; it != range.second; ++it) {
- auto patchIt = findById<AudioPatch>(patches, it->second);
- if (patchIt != patches.end()) {
- erase_if(patchIt->sourcePortConfigIds,
- [portConfigId](auto e) { return e == portConfigId; });
- erase_if(patchIt->sinkPortConfigIds,
- [portConfigId](auto e) { return e == portConfigId; });
- }
+ndk::ScopedAStatus Module::createStreamContext(int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+ StreamContext* out_context) {
+ if (in_bufferSizeFrames <= 0) {
+ LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- std::set<int32_t> erasedPatches;
- for (size_t i = patches.size() - 1; i != 0; --i) {
- const auto& patch = patches[i];
- if (patch.sourcePortConfigIds.empty() || patch.sinkPortConfigIds.empty()) {
- erasedPatches.insert(patch.id);
- patches.erase(patches.begin() + i);
- }
+ if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) {
+ LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
+ << ", must be at least " << kMinimumStreamBufferSizeFrames;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- erase_all_values(mPatches, erasedPatches);
+ auto& configs = getConfig().portConfigs;
+ auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
+ // Since this is a private method, it is assumed that
+ // validity of the portConfigId has already been checked.
+ const size_t frameSize =
+ getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
+ if (frameSize == 0) {
+ LOG(ERROR) << __func__ << ": could not calculate frame size for port config "
+ << portConfigIt->toString();
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ LOG(DEBUG) << __func__ << ": frame size " << frameSize << " bytes";
+ if (frameSize > kMaximumStreamBufferSizeBytes / in_bufferSizeFrames) {
+ LOG(ERROR) << __func__ << ": buffer size " << in_bufferSizeFrames
+ << " frames is too large, maximum size is "
+ << kMaximumStreamBufferSizeBytes / frameSize;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ const auto& flags = portConfigIt->flags.value();
+ if ((flags.getTag() == AudioIoFlags::Tag::input &&
+ (flags.get<AudioIoFlags::Tag::input>() &
+ 1 << static_cast<int32_t>(AudioInputFlags::MMAP_NOIRQ)) == 0) ||
+ (flags.getTag() == AudioIoFlags::Tag::output &&
+ (flags.get<AudioIoFlags::Tag::output>() &
+ 1 << static_cast<int32_t>(AudioOutputFlags::MMAP_NOIRQ)) == 0)) {
+ StreamContext temp(
+ std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
+ std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
+ frameSize,
+ std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames));
+ if (temp.isValid()) {
+ *out_context = std::move(temp);
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ } else {
+ // TODO: Implement simulation of MMAP buffer allocation
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::findPortIdForNewStream(int32_t in_portConfigId, AudioPort** port) {
+ auto& configs = getConfig().portConfigs;
+ auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
+ if (portConfigIt == configs.end()) {
+ LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ const int32_t portId = portConfigIt->portId;
+ // In our implementation, configs of mix ports always have unique IDs.
+ CHECK(portId != in_portConfigId);
+ auto& ports = getConfig().ports;
+ auto portIt = findById<AudioPort>(ports, portId);
+ if (portIt == ports.end()) {
+ LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
+ << in_portConfigId << " not found";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (mStreams.count(in_portConfigId) != 0) {
+ LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+ << " already has a stream opened on it";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (portIt->ext.getTag() != AudioPortExt::Tag::mix) {
+ LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+ << " does not correspond to a mix port";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ const int32_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
+ if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
+ LOG(ERROR) << __func__ << ": port id " << portId
+ << " has already reached maximum allowed opened stream count: "
+ << maxOpenStreamCount;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ *port = &(*portIt);
+ return ndk::ScopedAStatus::ok();
}
internal::Configuration& Module::getConfig() {
@@ -135,6 +208,176 @@
do_insert(patch.sinkPortConfigIds);
}
+void Module::updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch) {
+ // Streams from the old patch need to be disconnected, streams from the new
+ // patch need to be connected. If the stream belongs to both patches, no need
+ // to update it.
+ std::set<int32_t> idsToDisconnect, idsToConnect;
+ idsToDisconnect.insert(oldPatch.sourcePortConfigIds.begin(),
+ oldPatch.sourcePortConfigIds.end());
+ idsToDisconnect.insert(oldPatch.sinkPortConfigIds.begin(), oldPatch.sinkPortConfigIds.end());
+ idsToConnect.insert(newPatch.sourcePortConfigIds.begin(), newPatch.sourcePortConfigIds.end());
+ idsToConnect.insert(newPatch.sinkPortConfigIds.begin(), newPatch.sinkPortConfigIds.end());
+ std::for_each(idsToDisconnect.begin(), idsToDisconnect.end(), [&](const auto& portConfigId) {
+ if (idsToConnect.count(portConfigId) == 0) {
+ mStreams.setStreamIsConnected(portConfigId, false);
+ }
+ });
+ std::for_each(idsToConnect.begin(), idsToConnect.end(), [&](const auto& portConfigId) {
+ if (idsToDisconnect.count(portConfigId) == 0) {
+ mStreams.setStreamIsConnected(portConfigId, true);
+ }
+ });
+}
+
+ndk::ScopedAStatus Module::setModuleDebug(
+ const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) {
+ LOG(DEBUG) << __func__ << ": old flags:" << mDebug.toString()
+ << ", new flags: " << in_debug.toString();
+ if (mDebug.simulateDeviceConnections != in_debug.simulateDeviceConnections &&
+ !mConnectedDevicePorts.empty()) {
+ LOG(ERROR) << __func__ << ": attempting to change device connections simulation "
+ << "while having external devices connected";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ mDebug = in_debug;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdAndAdditionalData,
+ AudioPort* _aidl_return) {
+ const int32_t templateId = in_templateIdAndAdditionalData.id;
+ auto& ports = getConfig().ports;
+ AudioPort connectedPort;
+ { // Scope the template port so that we don't accidentally modify it.
+ auto templateIt = findById<AudioPort>(ports, templateId);
+ if (templateIt == ports.end()) {
+ LOG(ERROR) << __func__ << ": port id " << templateId << " not found";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (templateIt->ext.getTag() != AudioPortExt::Tag::device) {
+ LOG(ERROR) << __func__ << ": port id " << templateId << " is not a device port";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (!templateIt->profiles.empty()) {
+ LOG(ERROR) << __func__ << ": port id " << templateId
+ << " does not have dynamic profiles";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ auto& templateDevicePort = templateIt->ext.get<AudioPortExt::Tag::device>();
+ if (templateDevicePort.device.type.connection.empty()) {
+ LOG(ERROR) << __func__ << ": port id " << templateId << " is permanently attached";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ // Postpone id allocation until we ensure that there are no client errors.
+ connectedPort = *templateIt;
+ connectedPort.extraAudioDescriptors = in_templateIdAndAdditionalData.extraAudioDescriptors;
+ const auto& inputDevicePort =
+ in_templateIdAndAdditionalData.ext.get<AudioPortExt::Tag::device>();
+ auto& connectedDevicePort = connectedPort.ext.get<AudioPortExt::Tag::device>();
+ connectedDevicePort.device.address = inputDevicePort.device.address;
+ LOG(DEBUG) << __func__ << ": device port " << connectedPort.id << " device set to "
+ << connectedDevicePort.device.toString();
+ // Check if there is already a connected port with for the same external device.
+ for (auto connectedPortId : mConnectedDevicePorts) {
+ auto connectedPortIt = findById<AudioPort>(ports, connectedPortId);
+ if (connectedPortIt->ext.get<AudioPortExt::Tag::device>().device ==
+ connectedDevicePort.device) {
+ LOG(ERROR) << __func__ << ": device " << connectedDevicePort.device.toString()
+ << " is already connected at the device port id " << connectedPortId;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ }
+ }
+
+ if (!mDebug.simulateDeviceConnections) {
+ // In a real HAL here we would attempt querying the profiles from the device.
+ LOG(ERROR) << __func__ << ": failed to query supported device profiles";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ connectedPort.id = ++getConfig().nextPortId;
+ mConnectedDevicePorts.insert(connectedPort.id);
+ LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
+ << "connected port ID " << connectedPort.id;
+ auto& connectedProfiles = getConfig().connectedProfiles;
+ if (auto connectedProfilesIt = connectedProfiles.find(templateId);
+ connectedProfilesIt != connectedProfiles.end()) {
+ connectedPort.profiles = connectedProfilesIt->second;
+ }
+ ports.push_back(connectedPort);
+ *_aidl_return = std::move(connectedPort);
+
+ std::vector<AudioRoute> newRoutes;
+ auto& routes = getConfig().routes;
+ for (auto& r : routes) {
+ if (r.sinkPortId == templateId) {
+ AudioRoute newRoute;
+ newRoute.sourcePortIds = r.sourcePortIds;
+ newRoute.sinkPortId = connectedPort.id;
+ newRoute.isExclusive = r.isExclusive;
+ newRoutes.push_back(std::move(newRoute));
+ } else {
+ auto& srcs = r.sourcePortIds;
+ if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) {
+ srcs.push_back(connectedPort.id);
+ }
+ }
+ }
+ routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
+ auto& ports = getConfig().ports;
+ auto portIt = findById<AudioPort>(ports, in_portId);
+ if (portIt == ports.end()) {
+ LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (portIt->ext.getTag() != AudioPortExt::Tag::device) {
+ LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a device port";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (mConnectedDevicePorts.count(in_portId) == 0) {
+ LOG(ERROR) << __func__ << ": port id " << in_portId << " is not a connected device port";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ auto& configs = getConfig().portConfigs;
+ auto& initials = getConfig().initialConfigs;
+ auto configIt = std::find_if(configs.begin(), configs.end(), [&](const auto& config) {
+ if (config.portId == in_portId) {
+ // Check if the configuration was provided by the client.
+ const auto& initialIt = findById<AudioPortConfig>(initials, config.id);
+ return initialIt == initials.end() || config != *initialIt;
+ }
+ return false;
+ });
+ if (configIt != configs.end()) {
+ LOG(ERROR) << __func__ << ": port id " << in_portId << " has a non-default config with id "
+ << configIt->id;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ ports.erase(portIt);
+ mConnectedDevicePorts.erase(in_portId);
+ LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released";
+
+ auto& routes = getConfig().routes;
+ for (auto routesIt = routes.begin(); routesIt != routes.end();) {
+ if (routesIt->sinkPortId == in_portId) {
+ routesIt = routes.erase(routesIt);
+ } else {
+ // Note: the list of sourcePortIds can't become empty because there must
+ // be the id of the template port in the route.
+ erase_if(routesIt->sourcePortIds, [in_portId](auto src) { return src == in_portId; });
+ ++routesIt;
+ }
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus Module::getAudioPatches(std::vector<AudioPatch>* _aidl_return) {
*_aidl_return = getConfig().patches;
LOG(DEBUG) << __func__ << ": returning " << _aidl_return->size() << " patches";
@@ -171,94 +414,100 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Module::openInputStream(int32_t in_portConfigId,
- const SinkMetadata& in_sinkMetadata,
- std::shared_ptr<IStreamIn>* _aidl_return) {
- auto& configs = getConfig().portConfigs;
- auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
- if (portConfigIt == configs.end()) {
- LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
- }
- const int32_t portId = portConfigIt->portId;
- // In our implementation, configs of mix ports always have unique IDs.
- CHECK(portId != in_portConfigId);
+ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId,
+ std::vector<AudioRoute>* _aidl_return) {
auto& ports = getConfig().ports;
- auto portIt = findById<AudioPort>(ports, portId);
- if (portIt == ports.end()) {
- LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
- << in_portConfigId << " not found";
+ if (auto portIt = findById<AudioPort>(ports, in_portId); portIt == ports.end()) {
+ LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- if (portIt->flags.getTag() != AudioIoFlags::Tag::input ||
- portIt->ext.getTag() != AudioPortExt::Tag::mix) {
- LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
- << " does not correspond to an input mix port";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
- }
- if (mStreams.count(in_portConfigId) != 0) {
- LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
- << " already has a stream opened on it";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- const int32_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
- if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
- LOG(ERROR) << __func__ << ": port id " << portId
- << " has already reached maximum allowed opened stream count: "
- << maxOpenStreamCount;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- auto stream = ndk::SharedRefBase::make<StreamIn>(in_sinkMetadata);
- mStreams.insert(portId, in_portConfigId, StreamWrapper(stream));
- *_aidl_return = std::move(stream);
+ auto& routes = getConfig().routes;
+ std::copy_if(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
+ [&](const auto& r) {
+ const auto& srcs = r.sourcePortIds;
+ return r.sinkPortId == in_portId ||
+ std::find(srcs.begin(), srcs.end(), in_portId) != srcs.end();
+ });
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Module::openOutputStream(int32_t in_portConfigId,
- const SourceMetadata& in_sourceMetadata,
- const std::optional<AudioOffloadInfo>& in_offloadInfo,
- std::shared_ptr<IStreamOut>* _aidl_return) {
- auto& configs = getConfig().portConfigs;
- auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
- if (portConfigIt == configs.end()) {
- LOG(ERROR) << __func__ << ": existing port config id " << in_portConfigId << " not found";
+ndk::ScopedAStatus Module::openInputStream(const OpenInputStreamArguments& in_args,
+ OpenInputStreamReturn* _aidl_return) {
+ LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", buffer size "
+ << in_args.bufferSizeFrames << " frames";
+ AudioPort* port = nullptr;
+ if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
+ return status;
+ }
+ if (port->flags.getTag() != AudioIoFlags::Tag::input) {
+ LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
+ << " does not correspond to an input mix port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- const int32_t portId = portConfigIt->portId;
- // In our implementation, configs of mix ports always have unique IDs.
- CHECK(portId != in_portConfigId);
- auto& ports = getConfig().ports;
- auto portIt = findById<AudioPort>(ports, portId);
- if (portIt == ports.end()) {
- LOG(ERROR) << __func__ << ": port id " << portId << " used by port config id "
- << in_portConfigId << " not found";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ StreamContext context;
+ if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+ !status.isOk()) {
+ return status;
}
- if (portIt->flags.getTag() != AudioIoFlags::Tag::output ||
- portIt->ext.getTag() != AudioPortExt::Tag::mix) {
- LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
+ context.fillDescriptor(&_aidl_return->desc);
+ auto stream = ndk::SharedRefBase::make<StreamIn>(in_args.sinkMetadata, std::move(context));
+ if (auto status = stream->init(); !status.isOk()) {
+ return status;
+ }
+ StreamWrapper streamWrapper(stream);
+ auto patchIt = mPatches.find(in_args.portConfigId);
+ if (patchIt != mPatches.end()) {
+ streamWrapper.setStreamIsConnected(true);
+ }
+ mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
+ _aidl_return->stream = std::move(stream);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::openOutputStream(const OpenOutputStreamArguments& in_args,
+ OpenOutputStreamReturn* _aidl_return) {
+ LOG(DEBUG) << __func__ << ": port config id " << in_args.portConfigId << ", has offload info? "
+ << (in_args.offloadInfo.has_value()) << ", buffer size " << in_args.bufferSizeFrames
+ << " frames";
+ AudioPort* port = nullptr;
+ if (auto status = findPortIdForNewStream(in_args.portConfigId, &port); !status.isOk()) {
+ return status;
+ }
+ if (port->flags.getTag() != AudioIoFlags::Tag::output) {
+ LOG(ERROR) << __func__ << ": port config id " << in_args.portConfigId
<< " does not correspond to an output mix port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- if (mStreams.count(in_portConfigId) != 0) {
- LOG(ERROR) << __func__ << ": port config id " << in_portConfigId
- << " already has a stream opened on it";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ if ((port->flags.get<AudioIoFlags::Tag::output>() &
+ 1 << static_cast<int32_t>(AudioOutputFlags::COMPRESS_OFFLOAD)) != 0 &&
+ !in_args.offloadInfo.has_value()) {
+ LOG(ERROR) << __func__ << ": port id " << port->id
+ << " has COMPRESS_OFFLOAD flag set, requires offload info";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- const int32_t maxOpenStreamCount = portIt->ext.get<AudioPortExt::Tag::mix>().maxOpenStreamCount;
- if (maxOpenStreamCount != 0 && mStreams.count(portId) >= maxOpenStreamCount) {
- LOG(ERROR) << __func__ << ": port id " << portId
- << " has already reached maximum allowed opened stream count: "
- << maxOpenStreamCount;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ StreamContext context;
+ if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+ !status.isOk()) {
+ return status;
}
- auto stream = ndk::SharedRefBase::make<StreamOut>(in_sourceMetadata, in_offloadInfo);
- mStreams.insert(portId, in_portConfigId, StreamWrapper(stream));
- *_aidl_return = std::move(stream);
+ context.fillDescriptor(&_aidl_return->desc);
+ auto stream = ndk::SharedRefBase::make<StreamOut>(in_args.sourceMetadata, std::move(context),
+ in_args.offloadInfo);
+ if (auto status = stream->init(); !status.isOk()) {
+ return status;
+ }
+ StreamWrapper streamWrapper(stream);
+ auto patchIt = mPatches.find(in_args.portConfigId);
+ if (patchIt != mPatches.end()) {
+ streamWrapper.setStreamIsConnected(true);
+ }
+ mStreams.insert(port->id, in_args.portConfigId, std::move(streamWrapper));
+ _aidl_return->stream = std::move(stream);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPatch* _aidl_return) {
+ LOG(DEBUG) << __func__ << ": requested patch " << in_requested.toString();
if (in_requested.sourcePortConfigIds.empty()) {
LOG(ERROR) << __func__ << ": requested patch has empty sources list";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -338,15 +587,24 @@
}
}
*_aidl_return = in_requested;
+ _aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames;
+ _aidl_return->latenciesMs.clear();
+ _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
+ _aidl_return->sinkPortConfigIds.size(), kLatencyMs);
+ AudioPatch oldPatch{};
if (existing == patches.end()) {
_aidl_return->id = getConfig().nextPatchId++;
patches.push_back(*_aidl_return);
existing = patches.begin() + (patches.size() - 1);
} else {
+ oldPatch = *existing;
*existing = *_aidl_return;
}
registerPatch(*existing);
- LOG(DEBUG) << __func__ << ": created or updated patch id " << _aidl_return->id;
+ updateStreamsConnectedState(oldPatch, *_aidl_return);
+
+ LOG(DEBUG) << __func__ << ": " << (oldPatch.id == 0 ? "created" : "updated") << " patch "
+ << _aidl_return->toString();
return ndk::ScopedAStatus::ok();
}
@@ -481,6 +739,7 @@
auto patchIt = findById<AudioPatch>(patches, in_patchId);
if (patchIt != patches.end()) {
cleanUpPatch(patchIt->id);
+ updateStreamsConnectedState(*patchIt, AudioPatch{});
patches.erase(patchIt);
LOG(DEBUG) << __func__ << ": erased patch " << in_patchId;
return ndk::ScopedAStatus::ok();
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index e16b2c6..312df72 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -15,9 +15,10 @@
*/
#define LOG_TAG "AHAL_Stream"
-#define LOG_NDEBUG 0
#include <android-base/logging.h>
+#include <utils/SystemClock.h>
+#include "core-impl/Module.h"
#include "core-impl/Stream.h"
using aidl::android::hardware::audio::common::SinkMetadata;
@@ -26,11 +27,199 @@
namespace aidl::android::hardware::audio::core {
-StreamIn::StreamIn(const SinkMetadata& sinkMetadata) : mMetadata(sinkMetadata) {}
+void StreamContext::fillDescriptor(StreamDescriptor* desc) {
+ if (mCommandMQ) {
+ desc->command = mCommandMQ->dupeDesc();
+ }
+ if (mReplyMQ) {
+ desc->reply = mReplyMQ->dupeDesc();
+ }
+ if (mDataMQ) {
+ desc->frameSizeBytes = mFrameSize;
+ desc->bufferSizeFrames =
+ mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / mFrameSize;
+ desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
+ }
+}
-ndk::ScopedAStatus StreamIn::close() {
+bool StreamContext::isValid() const {
+ if (mCommandMQ && !mCommandMQ->isValid()) {
+ LOG(ERROR) << "command FMQ is invalid";
+ return false;
+ }
+ if (mReplyMQ && !mReplyMQ->isValid()) {
+ LOG(ERROR) << "reply FMQ is invalid";
+ return false;
+ }
+ if (mFrameSize == 0) {
+ LOG(ERROR) << "frame size is not set";
+ return false;
+ }
+ if (mDataMQ && !mDataMQ->isValid()) {
+ LOG(ERROR) << "data FMQ is invalid";
+ return false;
+ }
+ return true;
+}
+
+void StreamContext::reset() {
+ mCommandMQ.reset();
+ mReplyMQ.reset();
+ mDataMQ.reset();
+}
+
+std::string StreamWorkerCommonLogic::init() {
+ if (mCommandMQ == nullptr) return "Command MQ is null";
+ if (mReplyMQ == nullptr) return "Reply MQ is null";
+ if (mDataMQ == nullptr) return "Data MQ is null";
+ if (sizeof(decltype(mDataBuffer)::element_type) != mDataMQ->getQuantumSize()) {
+ return "Unexpected Data MQ quantum size: " + std::to_string(mDataMQ->getQuantumSize());
+ }
+ mDataBufferSize = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize();
+ mDataBuffer.reset(new (std::nothrow) int8_t[mDataBufferSize]);
+ if (mDataBuffer == nullptr) {
+ return "Failed to allocate data buffer for element count " +
+ std::to_string(mDataMQ->getQuantumCount()) +
+ ", size in bytes: " + std::to_string(mDataBufferSize);
+ }
+ return "";
+}
+
+const std::string StreamInWorkerLogic::kThreadName = "reader";
+
+StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
+ StreamDescriptor::Command command{};
+ if (!mCommandMQ->readBlocking(&command, 1)) {
+ LOG(ERROR) << __func__ << ": reading of command from MQ failed";
+ return Status::ABORT;
+ }
+ StreamDescriptor::Reply reply{};
+ if (command.code == StreamContext::COMMAND_EXIT &&
+ command.fmqByteCount == mInternalCommandCookie) {
+ LOG(DEBUG) << __func__ << ": received EXIT command";
+ // This is an internal command, no need to reply.
+ return Status::EXIT;
+ } else if (command.code == StreamDescriptor::COMMAND_BURST && command.fmqByteCount >= 0) {
+ LOG(DEBUG) << __func__ << ": received BURST read command for " << command.fmqByteCount
+ << " bytes";
+ usleep(3000); // Simulate a blocking call into the driver.
+ const size_t byteCount = std::min({static_cast<size_t>(command.fmqByteCount),
+ mDataMQ->availableToWrite(), mDataBufferSize});
+ const bool isConnected = mIsConnected;
+ // Simulate reading of data, or provide zeroes if the stream is not connected.
+ for (size_t i = 0; i < byteCount; ++i) {
+ using buffer_type = decltype(mDataBuffer)::element_type;
+ constexpr int kBufferValueRange = std::numeric_limits<buffer_type>::max() -
+ std::numeric_limits<buffer_type>::min() + 1;
+ mDataBuffer[i] = isConnected ? (std::rand() % kBufferValueRange) +
+ std::numeric_limits<buffer_type>::min()
+ : 0;
+ }
+ bool success = byteCount > 0 ? mDataMQ->write(&mDataBuffer[0], byteCount) : true;
+ if (success) {
+ LOG(DEBUG) << __func__ << ": writing of " << byteCount << " bytes into data MQ"
+ << " succeeded; connected? " << isConnected;
+ // Frames are provided and counted regardless of connection status.
+ reply.fmqByteCount = byteCount;
+ mFrameCount += byteCount / mFrameSize;
+ if (isConnected) {
+ reply.status = STATUS_OK;
+ reply.observable.frames = mFrameCount;
+ reply.observable.timeNs = ::android::elapsedRealtimeNano();
+ } else {
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ } else {
+ LOG(WARNING) << __func__ << ": writing of " << byteCount
+ << " bytes of data to MQ failed";
+ reply.status = STATUS_NOT_ENOUGH_DATA;
+ }
+ reply.latencyMs = Module::kLatencyMs;
+ } else {
+ LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
+ << ") or count: " << command.fmqByteCount;
+ reply.status = STATUS_BAD_VALUE;
+ }
+ LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
+ if (!mReplyMQ->writeBlocking(&reply, 1)) {
+ LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+ return Status::ABORT;
+ }
+ return Status::CONTINUE;
+}
+
+const std::string StreamOutWorkerLogic::kThreadName = "writer";
+
+StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
+ StreamDescriptor::Command command{};
+ if (!mCommandMQ->readBlocking(&command, 1)) {
+ LOG(ERROR) << __func__ << ": reading of command from MQ failed";
+ return Status::ABORT;
+ }
+ StreamDescriptor::Reply reply{};
+ if (command.code == StreamContext::COMMAND_EXIT &&
+ command.fmqByteCount == mInternalCommandCookie) {
+ LOG(DEBUG) << __func__ << ": received EXIT command";
+ // This is an internal command, no need to reply.
+ return Status::EXIT;
+ } else if (command.code == StreamDescriptor::COMMAND_BURST && command.fmqByteCount >= 0) {
+ LOG(DEBUG) << __func__ << ": received BURST write command for " << command.fmqByteCount
+ << " bytes";
+ const size_t byteCount = std::min({static_cast<size_t>(command.fmqByteCount),
+ mDataMQ->availableToRead(), mDataBufferSize});
+ bool success = byteCount > 0 ? mDataMQ->read(&mDataBuffer[0], byteCount) : true;
+ if (success) {
+ const bool isConnected = mIsConnected;
+ LOG(DEBUG) << __func__ << ": reading of " << byteCount << " bytes from data MQ"
+ << " succeeded; connected? " << isConnected;
+ // Frames are consumed and counted regardless of connection status.
+ reply.fmqByteCount = byteCount;
+ mFrameCount += byteCount / mFrameSize;
+ if (isConnected) {
+ reply.status = STATUS_OK;
+ reply.observable.frames = mFrameCount;
+ reply.observable.timeNs = ::android::elapsedRealtimeNano();
+ } else {
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ usleep(3000); // Simulate a blocking call into the driver.
+ } else {
+ LOG(WARNING) << __func__ << ": reading of " << byteCount
+ << " bytes of data from MQ failed";
+ reply.status = STATUS_NOT_ENOUGH_DATA;
+ }
+ reply.latencyMs = Module::kLatencyMs;
+ } else {
+ LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
+ << ") or count: " << command.fmqByteCount;
+ reply.status = STATUS_BAD_VALUE;
+ }
+ LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
+ if (!mReplyMQ->writeBlocking(&reply, 1)) {
+ LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
+ return Status::ABORT;
+ }
+ return Status::CONTINUE;
+}
+
+template <class Metadata, class StreamWorker>
+StreamCommon<Metadata, StreamWorker>::~StreamCommon() {
+ if (!mIsClosed) {
+ LOG(ERROR) << __func__ << ": stream was not closed prior to destruction, resource leak";
+ stopWorker();
+ // The worker and the context should clean up by themselves via destructors.
+ }
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::close() {
LOG(DEBUG) << __func__;
if (!mIsClosed) {
+ stopWorker();
+ LOG(DEBUG) << __func__ << ": joining the worker thread...";
+ mWorker.stop();
+ LOG(DEBUG) << __func__ << ": worker thread joined";
+ mContext.reset();
mIsClosed = true;
return ndk::ScopedAStatus::ok();
} else {
@@ -39,38 +228,44 @@
}
}
-ndk::ScopedAStatus StreamIn::updateMetadata(const SinkMetadata& in_sinkMetadata) {
+template <class Metadata, class StreamWorker>
+void StreamCommon<Metadata, StreamWorker>::stopWorker() {
+ if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
+ LOG(DEBUG) << __func__ << ": asking the worker to stop...";
+ StreamDescriptor::Command cmd;
+ cmd.code = StreamContext::COMMAND_EXIT;
+ cmd.fmqByteCount = mContext.getInternalCommandCookie();
+ // FIXME: This can block in the case when the client wrote a command
+ // while the stream worker's cycle is not running. Need to revisit
+ // when implementing standby and pause/resume.
+ if (!commandMQ->writeBlocking(&cmd, 1)) {
+ LOG(ERROR) << __func__ << ": failed to write exit command to the MQ";
+ }
+ LOG(DEBUG) << __func__ << ": done";
+ }
+}
+
+template <class Metadata, class StreamWorker>
+ndk::ScopedAStatus StreamCommon<Metadata, StreamWorker>::updateMetadata(const Metadata& metadata) {
LOG(DEBUG) << __func__;
if (!mIsClosed) {
- mMetadata = in_sinkMetadata;
+ mMetadata = metadata;
return ndk::ScopedAStatus::ok();
}
LOG(ERROR) << __func__ << ": stream was closed";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
-StreamOut::StreamOut(const SourceMetadata& sourceMetadata,
+StreamIn::StreamIn(const SinkMetadata& sinkMetadata, StreamContext context)
+ : StreamCommon<SinkMetadata, StreamInWorker>(sinkMetadata, std::move(context)) {
+ LOG(DEBUG) << __func__;
+}
+
+StreamOut::StreamOut(const SourceMetadata& sourceMetadata, StreamContext context,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : mMetadata(sourceMetadata), mOffloadInfo(offloadInfo) {}
-
-ndk::ScopedAStatus StreamOut::close() {
+ : StreamCommon<SourceMetadata, StreamOutWorker>(sourceMetadata, std::move(context)),
+ mOffloadInfo(offloadInfo) {
LOG(DEBUG) << __func__;
- if (!mIsClosed) {
- mIsClosed = true;
- return ndk::ScopedAStatus::ok();
- }
- LOG(ERROR) << __func__ << ": stream was already closed";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
-}
-
-ndk::ScopedAStatus StreamOut::updateMetadata(const SourceMetadata& in_sourceMetadata) {
- LOG(DEBUG) << __func__;
- if (!mIsClosed) {
- mMetadata = in_sourceMetadata;
- return ndk::ScopedAStatus::ok();
- }
- LOG(ERROR) << __func__ << ": stream was closed";
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Configuration.h b/audio/aidl/default/include/core-impl/Configuration.h
index 17e342d..d5cd30b 100644
--- a/audio/aidl/default/include/core-impl/Configuration.h
+++ b/audio/aidl/default/include/core-impl/Configuration.h
@@ -16,6 +16,7 @@
#pragma once
+#include <map>
#include <vector>
#include <aidl/android/hardware/audio/core/AudioPatch.h>
@@ -29,6 +30,9 @@
std::vector<::aidl::android::media::audio::common::AudioPort> ports;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> portConfigs;
std::vector<::aidl::android::media::audio::common::AudioPortConfig> initialConfigs;
+ // Port id -> List of profiles to use when the device port state is set to 'connected'.
+ std::map<int32_t, std::vector<::aidl::android::media::audio::common::AudioProfile>>
+ connectedProfiles;
std::vector<AudioRoute> routes;
std::vector<AudioPatch> patches;
int32_t nextPortId = 1;
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 359626c..61516b2 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -18,6 +18,7 @@
#include <map>
#include <memory>
+#include <set>
#include <aidl/android/hardware/audio/core/BnModule.h>
@@ -27,6 +28,17 @@
namespace aidl::android::hardware::audio::core {
class Module : public BnModule {
+ public:
+ // This value is used for all AudioPatches and reported by all streams.
+ static constexpr int32_t kLatencyMs = 10;
+
+ private:
+ ndk::ScopedAStatus setModuleDebug(
+ const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) override;
+ ndk::ScopedAStatus connectExternalDevice(
+ const ::aidl::android::media::audio::common::AudioPort& in_templateIdAndAdditionalData,
+ ::aidl::android::media::audio::common::AudioPort* _aidl_return) override;
+ ndk::ScopedAStatus disconnectExternalDevice(int32_t in_portId) override;
ndk::ScopedAStatus getAudioPatches(std::vector<AudioPatch>* _aidl_return) override;
ndk::ScopedAStatus getAudioPort(
int32_t in_portId,
@@ -37,16 +49,19 @@
ndk::ScopedAStatus getAudioPorts(
std::vector<::aidl::android::media::audio::common::AudioPort>* _aidl_return) override;
ndk::ScopedAStatus getAudioRoutes(std::vector<AudioRoute>* _aidl_return) override;
+ ndk::ScopedAStatus getAudioRoutesForAudioPort(
+ int32_t in_portId,
+ std::vector<::aidl::android::hardware::audio::core::AudioRoute>* _aidl_return) override;
ndk::ScopedAStatus openInputStream(
- int32_t in_portConfigId,
- const ::aidl::android::hardware::audio::common::SinkMetadata& in_sinkMetadata,
- std::shared_ptr<IStreamIn>* _aidl_return) override;
+ const ::aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments&
+ in_args,
+ ::aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn* _aidl_return)
+ override;
ndk::ScopedAStatus openOutputStream(
- int32_t in_portConfigId,
- const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata,
- const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
- in_offloadInfo,
- std::shared_ptr<IStreamOut>* _aidl_return) override;
+ const ::aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments&
+ in_args,
+ ::aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn* _aidl_return)
+ override;
ndk::ScopedAStatus setAudioPatch(const AudioPatch& in_requested,
AudioPatch* _aidl_return) override;
ndk::ScopedAStatus setAudioPortConfig(
@@ -56,13 +71,25 @@
ndk::ScopedAStatus resetAudioPatch(int32_t in_patchId) override;
ndk::ScopedAStatus resetAudioPortConfig(int32_t in_portConfigId) override;
- private:
void cleanUpPatch(int32_t patchId);
- void cleanUpPatches(int32_t portConfigId);
+ ndk::ScopedAStatus createStreamContext(
+ int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+ ::aidl::android::hardware::audio::core::StreamContext* out_context);
+ ndk::ScopedAStatus findPortIdForNewStream(
+ int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
internal::Configuration& getConfig();
void registerPatch(const AudioPatch& patch);
+ void updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch);
+
+ // This value is used for all AudioPatches.
+ static constexpr int32_t kMinimumStreamBufferSizeFrames = 16;
+ // The maximum stream buffer size is 1 GiB = 2 ** 30 bytes;
+ static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;
std::unique_ptr<internal::Configuration> mConfig;
+ ModuleDebug mDebug;
+ // ids of ports created at runtime via 'connectExternalDevice'.
+ std::set<int32_t> mConnectedDevicePorts;
Streams mStreams;
// Maps port ids and port config ids to patch ids.
// Multimap because both ports and configs can be used by multiple patches.
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 87104dd..816cdb1 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -16,50 +16,203 @@
#pragma once
+#include <atomic>
+#include <cstdlib>
#include <map>
+#include <memory>
#include <optional>
#include <variant>
+#include <StreamWorker.h>
#include <aidl/android/hardware/audio/common/SinkMetadata.h>
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
#include <aidl/android/hardware/audio/core/BnStreamIn.h>
#include <aidl/android/hardware/audio/core/BnStreamOut.h>
+#include <aidl/android/hardware/audio/core/StreamDescriptor.h>
#include <aidl/android/media/audio/common/AudioOffloadInfo.h>
+#include <fmq/AidlMessageQueue.h>
+#include <system/thread_defs.h>
#include "core-impl/utils.h"
namespace aidl::android::hardware::audio::core {
-class StreamIn : public BnStreamIn {
- ndk::ScopedAStatus close() override;
- ndk::ScopedAStatus updateMetadata(
- const ::aidl::android::hardware::audio::common::SinkMetadata& in_sinkMetadata) override;
-
+// This class is similar to StreamDescriptor, but unlike
+// the descriptor, it actually owns the objects implementing
+// data exchange: FMQs etc, whereas StreamDescriptor only
+// contains their descriptors.
+class StreamContext {
public:
- explicit StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata);
- bool isClosed() const { return mIsClosed; }
+ typedef ::android::AidlMessageQueue<
+ StreamDescriptor::Command,
+ ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+ CommandMQ;
+ typedef ::android::AidlMessageQueue<
+ StreamDescriptor::Reply, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+ ReplyMQ;
+ typedef ::android::AidlMessageQueue<
+ int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+ DataMQ;
+
+ // Ensure that this value is not used by any of StreamDescriptor.COMMAND_*
+ static constexpr int COMMAND_EXIT = -1;
+
+ StreamContext() = default;
+ StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
+ size_t frameSize, std::unique_ptr<DataMQ> dataMQ)
+ : mCommandMQ(std::move(commandMQ)),
+ mInternalCommandCookie(std::rand()),
+ mReplyMQ(std::move(replyMQ)),
+ mFrameSize(frameSize),
+ mDataMQ(std::move(dataMQ)) {}
+ StreamContext(StreamContext&& other)
+ : mCommandMQ(std::move(other.mCommandMQ)),
+ mInternalCommandCookie(other.mInternalCommandCookie),
+ mReplyMQ(std::move(other.mReplyMQ)),
+ mFrameSize(other.mFrameSize),
+ mDataMQ(std::move(other.mDataMQ)) {}
+ StreamContext& operator=(StreamContext&& other) {
+ mCommandMQ = std::move(other.mCommandMQ);
+ mInternalCommandCookie = other.mInternalCommandCookie;
+ mReplyMQ = std::move(other.mReplyMQ);
+ mFrameSize = other.mFrameSize;
+ mDataMQ = std::move(other.mDataMQ);
+ return *this;
+ }
+
+ void fillDescriptor(StreamDescriptor* desc);
+ CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
+ DataMQ* getDataMQ() const { return mDataMQ.get(); }
+ size_t getFrameSize() const { return mFrameSize; }
+ int getInternalCommandCookie() const { return mInternalCommandCookie; }
+ ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+ bool isValid() const;
+ void reset();
private:
- ::aidl::android::hardware::audio::common::SinkMetadata mMetadata;
- bool mIsClosed = false;
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ int mInternalCommandCookie; // The value used to confirm that the command was posted internally
+ std::unique_ptr<ReplyMQ> mReplyMQ;
+ size_t mFrameSize;
+ std::unique_ptr<DataMQ> mDataMQ;
};
-class StreamOut : public BnStreamOut {
- ndk::ScopedAStatus close() override;
+class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
+ public:
+ void setIsConnected(bool connected) { mIsConnected = connected; }
+
+ protected:
+ explicit StreamWorkerCommonLogic(const StreamContext& context)
+ : mInternalCommandCookie(context.getInternalCommandCookie()),
+ mFrameSize(context.getFrameSize()),
+ mCommandMQ(context.getCommandMQ()),
+ mReplyMQ(context.getReplyMQ()),
+ mDataMQ(context.getDataMQ()) {}
+ std::string init() override;
+
+ // Used both by the main and worker threads.
+ std::atomic<bool> mIsConnected = false;
+ // All fields are used on the worker thread only.
+ const int mInternalCommandCookie;
+ const size_t mFrameSize;
+ StreamContext::CommandMQ* mCommandMQ;
+ StreamContext::ReplyMQ* mReplyMQ;
+ StreamContext::DataMQ* mDataMQ;
+ // We use an array and the "size" field instead of a vector to be able to detect
+ // memory allocation issues.
+ std::unique_ptr<int8_t[]> mDataBuffer;
+ size_t mDataBufferSize;
+ long mFrameCount = 0;
+};
+
+class StreamInWorkerLogic : public StreamWorkerCommonLogic {
+ public:
+ static const std::string kThreadName;
+ explicit StreamInWorkerLogic(const StreamContext& context) : StreamWorkerCommonLogic(context) {}
+
+ protected:
+ Status cycle() override;
+};
+using StreamInWorker = ::android::hardware::audio::common::StreamWorker<StreamInWorkerLogic>;
+
+class StreamOutWorkerLogic : public StreamWorkerCommonLogic {
+ public:
+ static const std::string kThreadName;
+ explicit StreamOutWorkerLogic(const StreamContext& context)
+ : StreamWorkerCommonLogic(context) {}
+
+ protected:
+ Status cycle() override;
+};
+using StreamOutWorker = ::android::hardware::audio::common::StreamWorker<StreamOutWorkerLogic>;
+
+template <class Metadata, class StreamWorker>
+class StreamCommon {
+ public:
+ ndk::ScopedAStatus close();
+ ndk::ScopedAStatus init() {
+ return mWorker.start(StreamWorker::kThreadName, ANDROID_PRIORITY_AUDIO)
+ ? ndk::ScopedAStatus::ok()
+ : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ bool isClosed() const { return mIsClosed; }
+ void setIsConnected(bool connected) { mWorker.setIsConnected(connected); }
+ ndk::ScopedAStatus updateMetadata(const Metadata& metadata);
+
+ protected:
+ StreamCommon(const Metadata& metadata, StreamContext context)
+ : mMetadata(metadata), mContext(std::move(context)), mWorker(mContext) {}
+ ~StreamCommon();
+ void stopWorker();
+
+ Metadata mMetadata;
+ StreamContext mContext;
+ StreamWorker mWorker;
+ // This variable is checked in the destructor which can be called on an arbitrary Binder thread,
+ // thus we need to ensure that any changes made by other threads are sequentially consistent.
+ std::atomic<bool> mIsClosed = false;
+};
+
+class StreamIn
+ : public StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata, StreamInWorker>,
+ public BnStreamIn {
+ ndk::ScopedAStatus close() override {
+ return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
+ StreamInWorker>::close();
+ }
+ ndk::ScopedAStatus updateMetadata(const ::aidl::android::hardware::audio::common::SinkMetadata&
+ in_sinkMetadata) override {
+ return StreamCommon<::aidl::android::hardware::audio::common::SinkMetadata,
+ StreamInWorker>::updateMetadata(in_sinkMetadata);
+ }
+
+ public:
+ StreamIn(const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
+ StreamContext context);
+};
+
+class StreamOut : public StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
+ StreamOutWorker>,
+ public BnStreamOut {
+ ndk::ScopedAStatus close() override {
+ return StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
+ StreamOutWorker>::close();
+ }
ndk::ScopedAStatus updateMetadata(
const ::aidl::android::hardware::audio::common::SourceMetadata& in_sourceMetadata)
- override;
+ override {
+ return StreamCommon<::aidl::android::hardware::audio::common::SourceMetadata,
+ StreamOutWorker>::updateMetadata(in_sourceMetadata);
+ }
public:
StreamOut(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+ StreamContext context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
- bool isClosed() const { return mIsClosed; }
private:
- ::aidl::android::hardware::audio::common::SourceMetadata mMetadata;
std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
- bool mIsClosed = false;
};
class StreamWrapper {
@@ -74,6 +227,15 @@
},
mStream);
}
+ void setStreamIsConnected(bool connected) {
+ std::visit(
+ [&](auto&& ws) -> bool {
+ auto s = ws.lock();
+ if (s) s->setIsConnected(connected);
+ return !!s;
+ },
+ mStream);
+ }
private:
std::variant<std::weak_ptr<StreamIn>, std::weak_ptr<StreamOut>> mStream;
@@ -93,6 +255,11 @@
mStreams.insert(std::pair{portConfigId, sw});
mStreams.insert(std::pair{portId, sw});
}
+ void setStreamIsConnected(int32_t portConfigId, bool connected) {
+ if (auto it = mStreams.find(portConfigId); it != mStreams.end()) {
+ it->second.setStreamIsConnected(connected);
+ }
+ }
private:
// Maps port ids and port config ids to streams. Multimap because a port
diff --git a/audio/aidl/default/include/core-impl/utils.h b/audio/aidl/default/include/core-impl/utils.h
index 7101012..9d06f08 100644
--- a/audio/aidl/default/include/core-impl/utils.h
+++ b/audio/aidl/default/include/core-impl/utils.h
@@ -38,11 +38,11 @@
return oldSize - c.size();
}
-// Erase all the elements in the map that satisfy the provided predicate.
+// Erase all the elements in the container that satisfy the provided predicate.
template <typename C, typename P>
auto erase_if(C& c, P pred) {
auto oldSize = c.size();
- for (auto it = c.begin(), last = c.end(); it != last;) {
+ for (auto it = c.begin(); it != c.end();) {
if (pred(*it)) {
it = c.erase(it);
} else {
diff --git a/audio/aidl/default/main.cpp b/audio/aidl/default/main.cpp
index 0de6047..15874a0 100644
--- a/audio/aidl/default/main.cpp
+++ b/audio/aidl/default/main.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <cstdlib>
+#include <ctime>
+
#include "core-impl/Config.h"
#include "core-impl/Module.h"
@@ -25,20 +28,25 @@
using aidl::android::hardware::audio::core::Module;
int main() {
+ // Random values are used in the implementation.
+ std::srand(std::time(nullptr));
+
+ // This is a debug implementation, always enable debug logging.
+ android::base::SetMinimumLogSeverity(::android::base::DEBUG);
ABinderProcess_setThreadPoolMaxThreadCount(16);
- // make the default config service
+ // Make the default config service
auto config = ndk::SharedRefBase::make<Config>();
const std::string configName = std::string() + Config::descriptor + "/default";
binder_status_t status =
AServiceManager_addService(config->asBinder().get(), configName.c_str());
- CHECK(status == STATUS_OK);
+ CHECK_EQ(STATUS_OK, status);
- // make the default module
+ // Make the default module
auto moduleDefault = ndk::SharedRefBase::make<Module>();
const std::string moduleDefaultName = std::string() + Module::descriptor + "/default";
status = AServiceManager_addService(moduleDefault->asBinder().get(), moduleDefaultName.c_str());
- CHECK(status == STATUS_OK);
+ CHECK_EQ(STATUS_OK, status);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index c160d1f..7b35133 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -13,18 +13,29 @@
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcutils",
+ "libfmq",
+ ],
+ static_libs: [
+ "android.hardware.audio.common-V1-ndk",
+ "android.hardware.audio.core-V1-ndk",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ "android.media.audio.common.types-V1-ndk",
+ "libaudioaidlcommon",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ ],
srcs: [
"ModuleConfig.cpp",
"VtsHalAudioCoreTargetTest.cpp",
],
- shared_libs: [
- "libbinder",
- ],
- static_libs: [
- "android.hardware.audio.common-V1-cpp",
- "android.hardware.audio.core-V1-cpp",
- "android.media.audio.common.types-V1-cpp",
- ],
test_suites: [
"general-tests",
"vts",
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index 3faa39a..e36ab4a 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -15,25 +15,50 @@
*/
#include <algorithm>
+#include <chrono>
-#include <android/media/audio/common/AudioIoFlags.h>
-#include <android/media/audio/common/AudioOutputFlags.h>
+#include <aidl/android/media/audio/common/AudioIoFlags.h>
+#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include "ModuleConfig.h"
using namespace android;
+using namespace std::chrono_literals;
-using android::hardware::audio::core::IModule;
-using android::media::audio::common::AudioChannelLayout;
-using android::media::audio::common::AudioFormatDescription;
-using android::media::audio::common::AudioFormatType;
-using android::media::audio::common::AudioIoFlags;
-using android::media::audio::common::AudioOutputFlags;
-using android::media::audio::common::AudioPort;
-using android::media::audio::common::AudioPortConfig;
-using android::media::audio::common::AudioPortExt;
-using android::media::audio::common::AudioProfile;
-using android::media::audio::common::Int;
+using aidl::android::hardware::audio::core::IModule;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioEncapsulationMode;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioIoFlags;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::AudioOutputFlags;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::AudioPortExt;
+using aidl::android::media::audio::common::AudioProfile;
+using aidl::android::media::audio::common::AudioUsage;
+using aidl::android::media::audio::common::Int;
+
+// static
+std::optional<AudioOffloadInfo> ModuleConfig::generateOffloadInfoIfNeeded(
+ const AudioPortConfig& portConfig) {
+ if (portConfig.flags.has_value() &&
+ portConfig.flags.value().getTag() == AudioIoFlags::Tag::output &&
+ (portConfig.flags.value().get<AudioIoFlags::Tag::output>() &
+ 1 << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD)) != 0) {
+ AudioOffloadInfo offloadInfo;
+ offloadInfo.base.sampleRate = portConfig.sampleRate.value().value;
+ offloadInfo.base.channelMask = portConfig.channelMask.value();
+ offloadInfo.base.format = portConfig.format.value();
+ offloadInfo.bitRatePerSecond = 256; // Arbitrary value.
+ offloadInfo.durationUs = std::chrono::microseconds(1min).count(); // Arbitrary value.
+ offloadInfo.usage = AudioUsage::MEDIA;
+ offloadInfo.encapsulationMode = AudioEncapsulationMode::NONE;
+ return offloadInfo;
+ }
+ return {};
+}
template <typename T>
auto findById(const std::vector<T>& v, int32_t id) {
@@ -46,14 +71,16 @@
for (const auto& port : mPorts) {
if (port.ext.getTag() != AudioPortExt::Tag::device) continue;
const auto& devicePort = port.ext.get<AudioPortExt::Tag::device>();
- const bool isInput = port.flags.getTag() == AudioIoFlags::Tag::input;
if (devicePort.device.type.connection.empty()) {
+ const bool isInput = port.flags.getTag() == AudioIoFlags::Tag::input;
// Permanently attached device.
if (isInput) {
mAttachedSourceDevicePorts.insert(port.id);
} else {
mAttachedSinkDevicePorts.insert(port.id);
}
+ } else if (port.profiles.empty()) {
+ mExternalDevicePorts.insert(port.id);
}
}
if (!mStatus.isOk()) return;
@@ -62,6 +89,22 @@
mStatus = module->getAudioPortConfigs(&mInitialConfigs);
}
+std::vector<AudioPort> ModuleConfig::getAttachedDevicePorts() const {
+ std::vector<AudioPort> result;
+ std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
+ return mAttachedSinkDevicePorts.count(port.id) != 0 ||
+ mAttachedSourceDevicePorts.count(port.id) != 0;
+ });
+ return result;
+}
+
+std::vector<AudioPort> ModuleConfig::getExternalDevicePorts() const {
+ std::vector<AudioPort> result;
+ std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result),
+ [&](const auto& port) { return mExternalDevicePorts.count(port.id) != 0; });
+ return result;
+}
+
std::vector<AudioPort> ModuleConfig::getInputMixPorts() const {
std::vector<AudioPort> result;
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) {
@@ -80,6 +123,15 @@
return result;
}
+std::vector<AudioPort> ModuleConfig::getAttachedDevicesPortsForMixPort(
+ bool isInput, const AudioPortConfig& mixPortConfig) const {
+ const auto mixPortIt = findById<AudioPort>(mPorts, mixPortConfig.portId);
+ if (mixPortIt != mPorts.end()) {
+ return getAttachedDevicesPortsForMixPort(isInput, *mixPortIt);
+ }
+ return {};
+}
+
std::vector<AudioPort> ModuleConfig::getAttachedSinkDevicesPortsForMixPort(
const AudioPort& mixPort) const {
std::vector<AudioPort> result;
@@ -229,10 +281,27 @@
return result;
}
-static std::vector<AudioPortConfig> combineAudioConfigs(const AudioPort& port,
- const AudioProfile& profile) {
- std::vector<AudioPortConfig> configs;
- configs.reserve(profile.channelMasks.size() * profile.sampleRates.size());
+std::string ModuleConfig::toString() const {
+ std::string result;
+ result.append("Ports: ");
+ result.append(android::internal::ToString(mPorts));
+ result.append("\nInitial configs: ");
+ result.append(android::internal::ToString(mInitialConfigs));
+ result.append("\nAttached sink device ports: ");
+ result.append(android::internal::ToString(mAttachedSinkDevicePorts));
+ result.append("\nAttached source device ports: ");
+ result.append(android::internal::ToString(mAttachedSourceDevicePorts));
+ result.append("\nExternal device ports: ");
+ result.append(android::internal::ToString(mExternalDevicePorts));
+ result.append("\nRoutes: ");
+ result.append(android::internal::ToString(mRoutes));
+ return result;
+}
+
+static size_t combineAudioConfigs(const AudioPort& port, const AudioProfile& profile,
+ std::vector<AudioPortConfig>* result) {
+ const size_t newConfigCount = profile.channelMasks.size() * profile.sampleRates.size();
+ result->reserve(result->capacity() + newConfigCount);
for (auto channelMask : profile.channelMasks) {
for (auto sampleRate : profile.sampleRates) {
AudioPortConfig config{};
@@ -242,66 +311,32 @@
config.sampleRate = sr;
config.channelMask = channelMask;
config.format = profile.format;
+ config.flags = port.flags;
config.ext = port.ext;
- configs.push_back(config);
+ result->push_back(std::move(config));
}
}
- return configs;
+ return newConfigCount;
}
-std::vector<AudioPortConfig> ModuleConfig::generateInputAudioMixPortConfigs(
- const std::vector<AudioPort>& ports, bool singleProfile) const {
+static bool isDynamicProfile(const AudioProfile& profile) {
+ return (profile.format.type == AudioFormatType::DEFAULT && profile.format.encoding.empty()) ||
+ profile.sampleRates.empty() || profile.channelMasks.empty();
+}
+
+std::vector<AudioPortConfig> ModuleConfig::generateAudioMixPortConfigs(
+ const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
std::vector<AudioPortConfig> result;
for (const auto& mixPort : ports) {
- if (getAttachedSourceDevicesPortsForMixPort(mixPort).empty()) {
- continue; // no attached devices
+ if (getAttachedDevicesPortsForMixPort(isInput, mixPort).empty()) {
+ continue;
}
for (const auto& profile : mixPort.profiles) {
- if (profile.format.type == AudioFormatType::DEFAULT || profile.sampleRates.empty() ||
- profile.channelMasks.empty()) {
- continue; // dynamic profile
- }
- auto configs = combineAudioConfigs(mixPort, profile);
- for (auto& config : configs) {
- config.flags = mixPort.flags;
- result.push_back(config);
- if (singleProfile) return result;
- }
- }
- }
- return result;
-}
-
-static std::tuple<AudioIoFlags, bool> generateOutFlags(const AudioPort& mixPort) {
- static const AudioIoFlags offloadFlags = AudioIoFlags::make<AudioIoFlags::Tag::output>(
- (1 << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD)) |
- (1 << static_cast<int>(AudioOutputFlags::DIRECT)));
- const bool isOffload = (mixPort.flags.get<AudioIoFlags::Tag::output>() &
- (1 << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD))) != 0;
- return {isOffload ? offloadFlags : mixPort.flags, isOffload};
-}
-
-std::vector<AudioPortConfig> ModuleConfig::generateOutputAudioMixPortConfigs(
- const std::vector<AudioPort>& ports, bool singleProfile) const {
- std::vector<AudioPortConfig> result;
- for (const auto& mixPort : ports) {
- if (getAttachedSinkDevicesPortsForMixPort(mixPort).empty()) {
- continue; // no attached devices
- }
- auto [flags, isOffload] = generateOutFlags(mixPort);
- (void)isOffload;
- for (const auto& profile : mixPort.profiles) {
- if (profile.format.type == AudioFormatType::DEFAULT) continue;
- auto configs = combineAudioConfigs(mixPort, profile);
- for (auto& config : configs) {
- // Some combinations of flags declared in the config file require special
- // treatment.
- // if (isOffload) {
- // config.offloadInfo.info(generateOffloadInfo(config.base));
- // }
- config.flags = flags;
- result.push_back(config);
- if (singleProfile) return result;
+ if (isDynamicProfile(profile)) continue;
+ combineAudioConfigs(mixPort, profile, &result);
+ if (singleProfile && !result.empty()) {
+ result.resize(1);
+ return result;
}
}
}
@@ -314,15 +349,21 @@
for (const auto& devicePort : ports) {
const size_t resultSizeBefore = result.size();
for (const auto& profile : devicePort.profiles) {
- auto configs = combineAudioConfigs(devicePort, profile);
- result.insert(result.end(), configs.begin(), configs.end());
- if (singleProfile && !result.empty()) return result;
+ combineAudioConfigs(devicePort, profile, &result);
+ if (singleProfile && !result.empty()) {
+ result.resize(1);
+ return result;
+ }
}
if (resultSizeBefore == result.size()) {
- AudioPortConfig empty;
- empty.portId = devicePort.id;
- empty.ext = devicePort.ext;
- result.push_back(empty);
+ std::copy_if(mInitialConfigs.begin(), mInitialConfigs.end(), std::back_inserter(result),
+ [&](const auto& config) { return config.portId == devicePort.id; });
+ if (resultSizeBefore == result.size()) {
+ AudioPortConfig empty;
+ empty.portId = devicePort.id;
+ empty.ext = devicePort.ext;
+ result.push_back(empty);
+ }
}
if (singleProfile) return result;
}
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index 2e86b97..552f971 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -21,111 +21,122 @@
#include <utility>
#include <vector>
-#include <android/hardware/audio/core/AudioRoute.h>
-#include <android/hardware/audio/core/IModule.h>
-#include <android/media/audio/common/AudioPort.h>
-#include <binder/Status.h>
+#include <aidl/android/hardware/audio/core/AudioRoute.h>
+#include <aidl/android/hardware/audio/core/IModule.h>
+#include <aidl/android/media/audio/common/AudioOffloadInfo.h>
+#include <aidl/android/media/audio/common/AudioPort.h>
class ModuleConfig {
public:
- using SrcSinkPair = std::pair<android::media::audio::common::AudioPortConfig,
- android::media::audio::common::AudioPortConfig>;
+ using SrcSinkPair = std::pair<aidl::android::media::audio::common::AudioPortConfig,
+ aidl::android::media::audio::common::AudioPortConfig>;
using SrcSinkGroup =
- std::pair<android::hardware::audio::core::AudioRoute, std::vector<SrcSinkPair>>;
+ std::pair<aidl::android::hardware::audio::core::AudioRoute, std::vector<SrcSinkPair>>;
- explicit ModuleConfig(android::hardware::audio::core::IModule* module);
- android::binder::Status getStatus() const { return mStatus; }
- std::string getError() const { return mStatus.toString8().c_str(); }
+ static std::optional<aidl::android::media::audio::common::AudioOffloadInfo>
+ generateOffloadInfoIfNeeded(
+ const aidl::android::media::audio::common::AudioPortConfig& portConfig);
- std::vector<android::media::audio::common::AudioPort> getInputMixPorts() const;
- std::vector<android::media::audio::common::AudioPort> getOutputMixPorts() const;
- std::vector<android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
+ explicit ModuleConfig(aidl::android::hardware::audio::core::IModule* module);
+ const ndk::ScopedAStatus& getStatus() const { return mStatus; }
+ std::string getError() const { return mStatus.getMessage(); }
+
+ std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicePorts() const;
+ std::vector<aidl::android::media::audio::common::AudioPort> getExternalDevicePorts() const;
+ std::vector<aidl::android::media::audio::common::AudioPort> getInputMixPorts() const;
+ std::vector<aidl::android::media::audio::common::AudioPort> getOutputMixPorts() const;
+ std::vector<aidl::android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
return isInput ? getInputMixPorts() : getOutputMixPorts();
}
- std::vector<android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
- bool isInput, const android::media::audio::common::AudioPort& mixPort) const {
+ std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
+ bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const {
return isInput ? getAttachedSourceDevicesPortsForMixPort(mixPort)
: getAttachedSinkDevicesPortsForMixPort(mixPort);
}
- std::vector<android::media::audio::common::AudioPort> getAttachedSinkDevicesPortsForMixPort(
- const android::media::audio::common::AudioPort& mixPort) const;
- std::vector<android::media::audio::common::AudioPort> getAttachedSourceDevicesPortsForMixPort(
- const android::media::audio::common::AudioPort& mixPort) const;
- std::optional<android::media::audio::common::AudioPort> getSourceMixPortForAttachedDevice()
- const;
+ std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
+ bool isInput,
+ const aidl::android::media::audio::common::AudioPortConfig& mixPortConfig) const;
+ std::vector<aidl::android::media::audio::common::AudioPort>
+ getAttachedSinkDevicesPortsForMixPort(
+ const aidl::android::media::audio::common::AudioPort& mixPort) const;
+ std::vector<aidl::android::media::audio::common::AudioPort>
+ getAttachedSourceDevicesPortsForMixPort(
+ const aidl::android::media::audio::common::AudioPort& mixPort) const;
+ std::optional<aidl::android::media::audio::common::AudioPort>
+ getSourceMixPortForAttachedDevice() const;
std::optional<SrcSinkPair> getNonRoutableSrcSinkPair(bool isInput) const;
std::optional<SrcSinkPair> getRoutableSrcSinkPair(bool isInput) const;
std::vector<SrcSinkGroup> getRoutableSrcSinkGroups(bool isInput) const;
- std::vector<android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts() const {
- auto inputs = generateInputAudioMixPortConfigs(getInputMixPorts(), false);
- auto outputs = generateOutputAudioMixPortConfigs(getOutputMixPorts(), false);
+ std::vector<aidl::android::media::audio::common::AudioPortConfig>
+ getPortConfigsForAttachedDevicePorts() const {
+ return generateAudioDevicePortConfigs(getAttachedDevicePorts(), false);
+ }
+ std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts()
+ const {
+ auto inputs = generateAudioMixPortConfigs(getInputMixPorts(), true, false);
+ auto outputs = generateAudioMixPortConfigs(getOutputMixPorts(), false, false);
inputs.insert(inputs.end(), outputs.begin(), outputs.end());
return inputs;
}
- std::vector<android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
+ std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
bool isInput) const {
- return isInput ? generateInputAudioMixPortConfigs(getInputMixPorts(), false)
- : generateOutputAudioMixPortConfigs(getOutputMixPorts(), false);
+ return generateAudioMixPortConfigs(getMixPorts(isInput), isInput, false);
}
- std::vector<android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
- bool isInput, const android::media::audio::common::AudioPort& port) const {
- return isInput ? generateInputAudioMixPortConfigs({port}, false)
- : generateOutputAudioMixPortConfigs({port}, false);
+ std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts(
+ bool isInput, const aidl::android::media::audio::common::AudioPort& port) const {
+ return generateAudioMixPortConfigs({port}, isInput, false);
}
- std::optional<android::media::audio::common::AudioPortConfig> getSingleConfigForMixPort(
+ std::optional<aidl::android::media::audio::common::AudioPortConfig> getSingleConfigForMixPort(
bool isInput) const {
- const auto config = isInput ? generateInputAudioMixPortConfigs(getInputMixPorts(), true)
- : generateOutputAudioMixPortConfigs(getOutputMixPorts(), true);
- // TODO: Avoid returning configs for offload since they require an extra
- // argument to openOutputStream.
+ const auto config = generateAudioMixPortConfigs(getMixPorts(isInput), isInput, true);
if (!config.empty()) {
return *config.begin();
- } else {
- return {};
}
+ return {};
}
- std::optional<android::media::audio::common::AudioPortConfig> getSingleConfigForMixPort(
- bool isInput, const android::media::audio::common::AudioPort& port) const {
- const auto config = isInput ? generateInputAudioMixPortConfigs({port}, true)
- : generateOutputAudioMixPortConfigs({port}, true);
+ std::optional<aidl::android::media::audio::common::AudioPortConfig> getSingleConfigForMixPort(
+ bool isInput, const aidl::android::media::audio::common::AudioPort& port) const {
+ const auto config = generateAudioMixPortConfigs({port}, isInput, true);
if (!config.empty()) {
return *config.begin();
- } else {
- return {};
}
+ return {};
}
- android::media::audio::common::AudioPortConfig getSingleConfigForDevicePort(
- const android::media::audio::common::AudioPort& port) const {
- for (const auto& config : mInitialConfigs) {
- if (config.portId == port.id) return config;
- }
+ std::vector<aidl::android::media::audio::common::AudioPortConfig> getPortConfigsForDevicePort(
+ const aidl::android::media::audio::common::AudioPort& port) const {
+ return generateAudioDevicePortConfigs({port}, false);
+ }
+ aidl::android::media::audio::common::AudioPortConfig getSingleConfigForDevicePort(
+ const aidl::android::media::audio::common::AudioPort& port) const {
const auto config = generateAudioDevicePortConfigs({port}, true);
return *config.begin();
}
+ std::string toString() const;
+
private:
- std::vector<android::media::audio::common::AudioPortConfig> generateInputAudioMixPortConfigs(
- const std::vector<android::media::audio::common::AudioPort>& ports,
- bool singleProfile) const;
- std::vector<android::media::audio::common::AudioPortConfig> generateOutputAudioMixPortConfigs(
- const std::vector<android::media::audio::common::AudioPort>& ports,
+ std::vector<aidl::android::media::audio::common::AudioPortConfig> generateAudioMixPortConfigs(
+ const std::vector<aidl::android::media::audio::common::AudioPort>& ports, bool isInput,
bool singleProfile) const;
// Unlike MixPorts, the generator for DevicePorts always returns a non-empty
// vector for a non-empty input port list. If there are no profiles in the
- // port, a vector with an empty config is returned.
- std::vector<android::media::audio::common::AudioPortConfig> generateAudioDevicePortConfigs(
- const std::vector<android::media::audio::common::AudioPort>& ports,
+ // port, its initial configs are looked up, if there are none,
+ // then an empty config is used, assuming further negotiation via setAudioPortConfig.
+ std::vector<aidl::android::media::audio::common::AudioPortConfig>
+ generateAudioDevicePortConfigs(
+ const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
bool singleProfile) const;
- android::binder::Status mStatus = android::binder::Status::ok();
- std::vector<android::media::audio::common::AudioPort> mPorts;
- std::vector<android::media::audio::common::AudioPortConfig> mInitialConfigs;
+ ndk::ScopedAStatus mStatus = ndk::ScopedAStatus::ok();
+ std::vector<aidl::android::media::audio::common::AudioPort> mPorts;
+ std::vector<aidl::android::media::audio::common::AudioPortConfig> mInitialConfigs;
std::set<int32_t> mAttachedSinkDevicePorts;
std::set<int32_t> mAttachedSourceDevicePorts;
- std::vector<android::hardware::audio::core::AudioRoute> mRoutes;
+ std::set<int32_t> mExternalDevicePorts;
+ std::vector<aidl::android::hardware::audio::core::AudioRoute> mRoutes;
};
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index cadeb0c..290f5f0 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -16,46 +16,68 @@
#include <algorithm>
#include <condition_variable>
+#include <limits>
#include <memory>
#include <mutex>
#include <optional>
#include <set>
#include <string>
+#include <vector>
+#define LOG_TAG "VtsHalAudioCore"
+#include <android-base/logging.h>
+
+#include <StreamWorker.h>
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
+#include <aidl/android/hardware/audio/core/IConfig.h>
+#include <aidl/android/hardware/audio/core/IModule.h>
+#include <aidl/android/media/audio/common/AudioIoFlags.h>
+#include <aidl/android/media/audio/common/AudioOutputFlags.h>
+#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
-#include <android/hardware/audio/core/IConfig.h>
-#include <android/hardware/audio/core/IModule.h>
-#include <android/media/audio/common/AudioIoFlags.h>
-#include <android/media/audio/common/AudioOutputFlags.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <fmq/AidlMessageQueue.h>
#include "ModuleConfig.h"
using namespace android;
-using android::binder::Status;
-using android::hardware::audio::common::PlaybackTrackMetadata;
-using android::hardware::audio::common::RecordTrackMetadata;
-using android::hardware::audio::common::SinkMetadata;
-using android::hardware::audio::common::SourceMetadata;
-using android::hardware::audio::core::AudioPatch;
-using android::hardware::audio::core::AudioRoute;
-using android::hardware::audio::core::IModule;
-using android::hardware::audio::core::IStreamIn;
-using android::hardware::audio::core::IStreamOut;
-using android::media::audio::common::AudioContentType;
-using android::media::audio::common::AudioDevice;
-using android::media::audio::common::AudioDeviceType;
-using android::media::audio::common::AudioIoFlags;
-using android::media::audio::common::AudioOutputFlags;
-using android::media::audio::common::AudioPort;
-using android::media::audio::common::AudioPortConfig;
-using android::media::audio::common::AudioPortDeviceExt;
-using android::media::audio::common::AudioPortExt;
-using android::media::audio::common::AudioSource;
-using android::media::audio::common::AudioUsage;
+using aidl::android::hardware::audio::common::PlaybackTrackMetadata;
+using aidl::android::hardware::audio::common::RecordTrackMetadata;
+using aidl::android::hardware::audio::common::SinkMetadata;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::audio::core::AudioPatch;
+using aidl::android::hardware::audio::core::AudioRoute;
+using aidl::android::hardware::audio::core::IModule;
+using aidl::android::hardware::audio::core::IStreamIn;
+using aidl::android::hardware::audio::core::IStreamOut;
+using aidl::android::hardware::audio::core::ModuleDebug;
+using aidl::android::hardware::audio::core::StreamDescriptor;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using aidl::android::media::audio::common::AudioContentType;
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioDeviceAddress;
+using aidl::android::media::audio::common::AudioDeviceType;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioIoFlags;
+using aidl::android::media::audio::common::AudioOutputFlags;
+using aidl::android::media::audio::common::AudioPort;
+using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::AudioPortDeviceExt;
+using aidl::android::media::audio::common::AudioPortExt;
+using aidl::android::media::audio::common::AudioSource;
+using aidl::android::media::audio::common::AudioUsage;
+using android::hardware::audio::common::StreamLogic;
+using android::hardware::audio::common::StreamWorker;
+using ndk::ScopedAStatus;
+
+namespace ndk {
+std::ostream& operator<<(std::ostream& str, const ScopedAStatus& status) {
+ str << status.getDescription();
+ return str;
+}
+} // namespace ndk
template <typename T>
auto findById(std::vector<T>& v, int32_t id) {
@@ -63,7 +85,7 @@
}
template <typename C>
-std::vector<int32_t> getNonExistentIds(const C& allIds) {
+std::vector<int32_t> GetNonExistentIds(const C& allIds) {
if (allIds.empty()) {
return std::vector<int32_t>{-1, 0, 1};
}
@@ -73,16 +95,29 @@
return nonExistentIds;
}
-struct AidlDeathRecipient : IBinder::DeathRecipient {
+AudioDeviceAddress GenerateUniqueDeviceAddress() {
+ static int nextId = 1;
+ // TODO: Use connection-specific ID.
+ return AudioDeviceAddress::make<AudioDeviceAddress::Tag::id>(std::to_string(++nextId));
+}
+
+struct AidlDeathRecipient {
+ const ndk::SpAIBinder binder;
+ const ndk::ScopedAIBinder_DeathRecipient recipient;
std::mutex mutex;
std::condition_variable condition;
bool fired = false;
- wp<IBinder> who;
- void binderDied(const wp<IBinder>& who) override {
+ explicit AidlDeathRecipient(const ndk::SpAIBinder& binder)
+ : binder(binder), recipient(AIBinder_DeathRecipient_new(&binderDiedCallbackAidl)) {}
+
+ binder_status_t linkToDeath() {
+ return AIBinder_linkToDeath(binder.get(), recipient.get(), this);
+ }
+
+ void binderDied() {
std::unique_lock<std::mutex> lock(mutex);
fired = true;
- this->who = who;
condition.notify_one();
};
@@ -91,50 +126,168 @@
condition.wait_for(lock, std::chrono::milliseconds(timeoutMs), [this]() { return fired; });
return fired;
}
+
+ static void binderDiedCallbackAidl(void* cookie) {
+ AidlDeathRecipient* self = static_cast<AidlDeathRecipient*>(cookie);
+ self->binderDied();
+ }
};
-template <typename T>
-struct IsInput {
- constexpr operator bool() const;
+// All 'With*' classes are move-only because they are associated with some
+// resource or state of a HAL module.
+class WithDebugFlags {
+ public:
+ static WithDebugFlags createNested(const WithDebugFlags& parent) {
+ return WithDebugFlags(parent.mFlags);
+ }
+
+ WithDebugFlags() {}
+ explicit WithDebugFlags(const ModuleDebug& initial) : mInitial(initial), mFlags(initial) {}
+ WithDebugFlags(const WithDebugFlags&) = delete;
+ WithDebugFlags& operator=(const WithDebugFlags&) = delete;
+ ~WithDebugFlags() {
+ if (mModule != nullptr) {
+ ScopedAStatus status = mModule->setModuleDebug(mInitial);
+ EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ }
+ }
+ void SetUp(IModule* module) {
+ ScopedAStatus status = module->setModuleDebug(mFlags);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ }
+ ModuleDebug& flags() { return mFlags; }
+
+ private:
+ ModuleDebug mInitial;
+ ModuleDebug mFlags;
+ IModule* mModule = nullptr;
};
-template <>
-constexpr IsInput<IStreamIn>::operator bool() const {
- return true;
-}
-template <>
-constexpr IsInput<IStreamOut>::operator bool() const {
- return false;
-}
+// For consistency, WithAudioPortConfig can start both with a non-existent
+// port config, and with an existing one. Existence is determined by the
+// id of the provided config. If it's not 0, then WithAudioPortConfig is
+// essentially a no-op wrapper.
+class WithAudioPortConfig {
+ public:
+ WithAudioPortConfig() {}
+ explicit WithAudioPortConfig(const AudioPortConfig& config) : mInitialConfig(config) {}
+ WithAudioPortConfig(const WithAudioPortConfig&) = delete;
+ WithAudioPortConfig& operator=(const WithAudioPortConfig&) = delete;
+ ~WithAudioPortConfig() {
+ if (mModule != nullptr) {
+ ScopedAStatus status = mModule->resetAudioPortConfig(getId());
+ EXPECT_EQ(EX_NONE, status.getExceptionCode())
+ << status << "; port config id " << getId();
+ }
+ }
+ void SetUp(IModule* module) {
+ ASSERT_NE(AudioPortExt::Tag::unspecified, mInitialConfig.ext.getTag())
+ << "config: " << mInitialConfig.toString();
+ // Negotiation is allowed for device ports because the HAL module is
+ // allowed to provide an empty profiles list for attached devices.
+ ASSERT_NO_FATAL_FAILURE(
+ SetUpImpl(module, mInitialConfig.ext.getTag() == AudioPortExt::Tag::device));
+ }
+ int32_t getId() const { return mConfig.id; }
+ const AudioPortConfig& get() const { return mConfig; }
+
+ private:
+ void SetUpImpl(IModule* module, bool negotiate) {
+ if (mInitialConfig.id == 0) {
+ AudioPortConfig suggested;
+ bool applied = false;
+ ScopedAStatus status = module->setAudioPortConfig(mInitialConfig, &suggested, &applied);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode())
+ << status << "; Config: " << mInitialConfig.toString();
+ if (!applied && negotiate) {
+ mInitialConfig = suggested;
+ ASSERT_NO_FATAL_FAILURE(SetUpImpl(module, false))
+ << " while applying suggested config: " << suggested.toString();
+ } else {
+ ASSERT_TRUE(applied) << "Suggested: " << suggested.toString();
+ mConfig = suggested;
+ mModule = module;
+ }
+ } else {
+ mConfig = mInitialConfig;
+ }
+ }
+
+ AudioPortConfig mInitialConfig;
+ IModule* mModule = nullptr;
+ AudioPortConfig mConfig;
+};
class AudioCoreModule : public testing::TestWithParam<std::string> {
public:
- void SetUp() override { ASSERT_NO_FATAL_FAILURE(ConnectToService()); }
+ // The default buffer size is used mostly for negative tests.
+ static constexpr int kDefaultBufferSizeFrames = 256;
+
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(ConnectToService());
+ debug.flags().simulateDeviceConnections = true;
+ ASSERT_NO_FATAL_FAILURE(debug.SetUp(module.get()));
+ }
+
+ void TearDown() override {
+ if (module != nullptr) {
+ ScopedAStatus status = module->setModuleDebug(ModuleDebug{});
+ EXPECT_EQ(EX_NONE, status.getExceptionCode())
+ << status << " returned when resetting debug flags";
+ }
+ }
void ConnectToService() {
- module = android::waitForDeclaredService<IModule>(String16(GetParam().c_str()));
+ module = IModule::fromBinder(
+ ndk::SpAIBinder(AServiceManager_getService(GetParam().c_str())));
ASSERT_NE(module, nullptr);
}
void RestartService() {
ASSERT_NE(module, nullptr);
moduleConfig.reset();
- deathHandler = sp<AidlDeathRecipient>::make();
- ASSERT_EQ(NO_ERROR, IModule::asBinder(module)->linkToDeath(deathHandler));
+ deathHandler.reset(new AidlDeathRecipient(module->asBinder()));
+ ASSERT_EQ(STATUS_OK, deathHandler->linkToDeath());
ASSERT_TRUE(base::SetProperty("sys.audio.restart.hal", "1"));
EXPECT_TRUE(deathHandler->waitForFired(3000));
- deathHandler = nullptr;
+ deathHandler.reset();
ASSERT_NO_FATAL_FAILURE(ConnectToService());
}
+ void ApplyEveryConfig(const std::vector<AudioPortConfig>& configs) {
+ for (const auto& config : configs) {
+ ASSERT_NE(0, config.portId);
+ WithAudioPortConfig portConfig(config);
+ ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get())); // calls setAudioPortConfig
+ EXPECT_EQ(config.portId, portConfig.get().portId);
+ std::vector<AudioPortConfig> retrievedPortConfigs;
+ ScopedAStatus status = module->getAudioPortConfigs(&retrievedPortConfigs);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ const int32_t portConfigId = portConfig.getId();
+ auto configIt = std::find_if(
+ retrievedPortConfigs.begin(), retrievedPortConfigs.end(),
+ [&portConfigId](const auto& retrConf) { return retrConf.id == portConfigId; });
+ EXPECT_NE(configIt, retrievedPortConfigs.end())
+ << "Port config id returned by setAudioPortConfig: " << portConfigId
+ << " is not found in the list returned by getAudioPortConfigs";
+ if (configIt != retrievedPortConfigs.end()) {
+ EXPECT_EQ(portConfig.get(), *configIt)
+ << "Applied port config returned by setAudioPortConfig: "
+ << portConfig.get().toString()
+ << " is not the same as retrieved via getAudioPortConfigs: "
+ << configIt->toString();
+ }
+ }
+ }
+
template <typename Entity>
void GetAllEntityIds(std::set<int32_t>* entityIds,
- Status (IModule::*getter)(std::vector<Entity>*),
+ ScopedAStatus (IModule::*getter)(std::vector<Entity>*),
const std::string& errorMessage) {
std::vector<Entity> entities;
{
- Status status = (module.get()->*getter)(&entities);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = (module.get()->*getter)(&entities);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
std::transform(entities.begin(), entities.end(),
std::inserter(*entityIds, entityIds->begin()),
@@ -163,67 +316,261 @@
void SetUpModuleConfig() {
if (moduleConfig == nullptr) {
moduleConfig = std::make_unique<ModuleConfig>(module.get());
- ASSERT_EQ(Status::EX_NONE, moduleConfig->getStatus().exceptionCode())
+ ASSERT_EQ(EX_NONE, moduleConfig->getStatus().getExceptionCode())
<< "ModuleConfig init error: " << moduleConfig->getError();
}
}
- sp<IModule> module;
- sp<AidlDeathRecipient> deathHandler;
+ std::shared_ptr<IModule> module;
+ std::unique_ptr<AidlDeathRecipient> deathHandler;
std::unique_ptr<ModuleConfig> moduleConfig;
+ WithDebugFlags debug;
};
-// For consistency, WithAudioPortConfig can start both with a non-existent
-// port config, and with an existing one. Existence is determined by the
-// id of the provided config. If it's not 0, then WithAudioPortConfig is
-// essentially a no-op wrapper.
-class WithAudioPortConfig {
+class WithDevicePortConnectedState {
public:
- WithAudioPortConfig() {}
- explicit WithAudioPortConfig(const AudioPortConfig& config) : mInitialConfig(config) {}
- ~WithAudioPortConfig() {
+ explicit WithDevicePortConnectedState(const AudioPort& idAndData) : mIdAndData(idAndData) {}
+ WithDevicePortConnectedState(const AudioPort& id, const AudioDeviceAddress& address)
+ : mIdAndData(setAudioPortAddress(id, address)) {}
+ WithDevicePortConnectedState(const WithDevicePortConnectedState&) = delete;
+ WithDevicePortConnectedState& operator=(const WithDevicePortConnectedState&) = delete;
+ ~WithDevicePortConnectedState() {
if (mModule != nullptr) {
- Status status = mModule->resetAudioPortConfig(getId());
- EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
- << status << "; port config id " << getId();
+ ScopedAStatus status = mModule->disconnectExternalDevice(getId());
+ EXPECT_EQ(EX_NONE, status.getExceptionCode())
+ << status << " returned when disconnecting device port ID " << getId();
}
}
void SetUp(IModule* module) {
- ASSERT_NE(AudioPortExt::Tag::unspecified, mInitialConfig.ext.getTag())
- << "config: " << mInitialConfig.toString();
- // Negotiation is allowed for device ports because the HAL module is
- // allowed to provide an empty profiles list for attached devices.
- ASSERT_NO_FATAL_FAILURE(
- SetUpImpl(module, mInitialConfig.ext.getTag() == AudioPortExt::Tag::device));
+ ScopedAStatus status = module->connectExternalDevice(mIdAndData, &mConnectedPort);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode())
+ << status << " returned when connecting device port ID & data "
+ << mIdAndData.toString();
+ ASSERT_NE(mIdAndData.id, getId())
+ << "ID of the connected port must not be the same as the ID of the template port";
+ mModule = module;
}
- int32_t getId() const { return mConfig.id; }
- const AudioPortConfig& get() const { return mConfig; }
+ int32_t getId() const { return mConnectedPort.id; }
+ const AudioPort& get() { return mConnectedPort; }
private:
- void SetUpImpl(IModule* module, bool negotiate) {
- if (mInitialConfig.id == 0) {
- AudioPortConfig suggested;
- bool applied = false;
- Status status = module->setAudioPortConfig(mInitialConfig, &suggested, &applied);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode())
- << status << "; Config: " << mInitialConfig.toString();
- if (!applied && negotiate) {
- mInitialConfig = suggested;
- ASSERT_NO_FATAL_FAILURE(SetUpImpl(module, false))
- << " while applying suggested config: " << suggested.toString();
- } else {
- ASSERT_TRUE(applied) << "Suggested: " << suggested.toString();
- mConfig = suggested;
- mModule = module;
- }
- } else {
- mConfig = mInitialConfig;
- }
+ static AudioPort setAudioPortAddress(const AudioPort& id, const AudioDeviceAddress& address) {
+ AudioPort result = id;
+ result.ext.get<AudioPortExt::Tag::device>().device.address = address;
+ return result;
}
- AudioPortConfig mInitialConfig;
+ const AudioPort mIdAndData;
IModule* mModule = nullptr;
- AudioPortConfig mConfig;
+ AudioPort mConnectedPort;
+};
+
+class StreamContext {
+ public:
+ typedef AidlMessageQueue<StreamDescriptor::Command, SynchronizedReadWrite> CommandMQ;
+ typedef AidlMessageQueue<StreamDescriptor::Reply, SynchronizedReadWrite> ReplyMQ;
+ typedef AidlMessageQueue<int8_t, SynchronizedReadWrite> DataMQ;
+
+ explicit StreamContext(const StreamDescriptor& descriptor)
+ : mFrameSizeBytes(descriptor.frameSizeBytes),
+ mCommandMQ(new CommandMQ(descriptor.command)),
+ mReplyMQ(new ReplyMQ(descriptor.reply)),
+ mBufferSizeFrames(descriptor.bufferSizeFrames),
+ mDataMQ(maybeCreateDataMQ(descriptor)) {}
+ void checkIsValid() const {
+ EXPECT_NE(0UL, mFrameSizeBytes);
+ ASSERT_NE(nullptr, mCommandMQ);
+ EXPECT_TRUE(mCommandMQ->isValid());
+ ASSERT_NE(nullptr, mReplyMQ);
+ EXPECT_TRUE(mReplyMQ->isValid());
+ if (mDataMQ != nullptr) {
+ EXPECT_TRUE(mDataMQ->isValid());
+ EXPECT_GE(mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize(),
+ mFrameSizeBytes * mBufferSizeFrames)
+ << "Data MQ actual buffer size is "
+ "less than the buffer size as specified by the descriptor";
+ }
+ }
+ size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; }
+ size_t getBufferSizeFrames() const { return mBufferSizeFrames; }
+ CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
+ DataMQ* getDataMQ() const { return mDataMQ.get(); }
+ ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+
+ private:
+ static std::unique_ptr<DataMQ> maybeCreateDataMQ(const StreamDescriptor& descriptor) {
+ using Tag = StreamDescriptor::AudioBuffer::Tag;
+ if (descriptor.audio.getTag() == Tag::fmq) {
+ return std::make_unique<DataMQ>(descriptor.audio.get<Tag::fmq>());
+ }
+ return nullptr;
+ }
+
+ const size_t mFrameSizeBytes;
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ std::unique_ptr<ReplyMQ> mReplyMQ;
+ const size_t mBufferSizeFrames;
+ std::unique_ptr<DataMQ> mDataMQ;
+};
+
+class StreamCommonLogic : public StreamLogic {
+ public:
+ StreamDescriptor::Position getLastObservablePosition() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mLastReply.observable;
+ }
+
+ protected:
+ explicit StreamCommonLogic(const StreamContext& context)
+ : mCommandMQ(context.getCommandMQ()),
+ mReplyMQ(context.getReplyMQ()),
+ mDataMQ(context.getDataMQ()),
+ mData(context.getBufferSizeBytes()) {}
+ StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
+ StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
+
+ std::string init() override { return ""; }
+
+ StreamContext::CommandMQ* mCommandMQ;
+ StreamContext::ReplyMQ* mReplyMQ;
+ StreamContext::DataMQ* mDataMQ;
+ std::vector<int8_t> mData;
+ std::mutex mLock;
+ StreamDescriptor::Reply mLastReply GUARDED_BY(mLock);
+};
+
+class StreamReaderLogic : public StreamCommonLogic {
+ public:
+ explicit StreamReaderLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+
+ protected:
+ Status cycle() override {
+ StreamDescriptor::Command command{};
+ command.code = StreamDescriptor::COMMAND_BURST;
+ command.fmqByteCount = mData.size();
+ if (!mCommandMQ->writeBlocking(&command, 1)) {
+ LOG(ERROR) << __func__ << ": writing of command into MQ failed";
+ return Status::ABORT;
+ }
+ StreamDescriptor::Reply reply{};
+ if (!mReplyMQ->readBlocking(&reply, 1)) {
+ LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
+ return Status::ABORT;
+ }
+ if (reply.status != STATUS_OK) {
+ LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
+ return Status::ABORT;
+ }
+ if (reply.fmqByteCount < 0 || reply.fmqByteCount > command.fmqByteCount) {
+ LOG(ERROR) << __func__
+ << ": received invalid byte count in the reply: " << reply.fmqByteCount;
+ return Status::ABORT;
+ }
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mLastReply = reply;
+ }
+ const size_t readCount = std::min({mDataMQ->availableToRead(),
+ static_cast<size_t>(reply.fmqByteCount), mData.size()});
+ if (readCount == 0 || mDataMQ->read(mData.data(), readCount)) {
+ return Status::CONTINUE;
+ }
+ LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
+ return Status::ABORT;
+ }
+};
+using StreamReader = StreamWorker<StreamReaderLogic>;
+
+class StreamWriterLogic : public StreamCommonLogic {
+ public:
+ explicit StreamWriterLogic(const StreamContext& context) : StreamCommonLogic(context) {}
+
+ protected:
+ Status cycle() override {
+ if (!mDataMQ->write(mData.data(), mData.size())) {
+ LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
+ return Status::ABORT;
+ }
+ StreamDescriptor::Command command{};
+ command.code = StreamDescriptor::COMMAND_BURST;
+ command.fmqByteCount = mData.size();
+ if (!mCommandMQ->writeBlocking(&command, 1)) {
+ LOG(ERROR) << __func__ << ": writing of command into MQ failed";
+ return Status::ABORT;
+ }
+ StreamDescriptor::Reply reply{};
+ if (!mReplyMQ->readBlocking(&reply, 1)) {
+ LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
+ return Status::ABORT;
+ }
+ if (reply.status != STATUS_OK) {
+ LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
+ return Status::ABORT;
+ }
+ if (reply.fmqByteCount < 0 || reply.fmqByteCount > command.fmqByteCount) {
+ LOG(ERROR) << __func__
+ << ": received invalid byte count in the reply: " << reply.fmqByteCount;
+ return Status::ABORT;
+ }
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mLastReply = reply;
+ }
+ return Status::CONTINUE;
+ }
+};
+using StreamWriter = StreamWorker<StreamWriterLogic>;
+
+template <typename T>
+struct IOTraits {
+ static constexpr bool is_input = std::is_same_v<T, IStreamIn>;
+ using Worker = std::conditional_t<is_input, StreamReader, StreamWriter>;
+};
+
+// A dedicated version to test replies to invalid commands.
+class StreamInvalidCommandLogic : public StreamCommonLogic {
+ public:
+ StreamInvalidCommandLogic(const StreamContext& context,
+ const std::vector<StreamDescriptor::Command>& commands)
+ : StreamCommonLogic(context), mCommands(commands) {}
+
+ std::vector<std::string> getUnexpectedStatuses() {
+ std::lock_guard<std::mutex> lock(mLock);
+ return mUnexpectedStatuses;
+ }
+
+ protected:
+ Status cycle() override {
+ // Send all commands in one cycle to simplify testing.
+ // Extra logging helps to sort out issues with unexpected HAL behavior.
+ for (const auto& command : mCommands) {
+ LOG(INFO) << __func__ << ": writing command " << command.toString() << " into MQ...";
+ if (!getCommandMQ()->writeBlocking(&command, 1)) {
+ LOG(ERROR) << __func__ << ": writing of command into MQ failed";
+ return Status::ABORT;
+ }
+ StreamDescriptor::Reply reply{};
+ LOG(INFO) << __func__ << ": reading reply for command " << command.toString() << "...";
+ if (!getReplyMQ()->readBlocking(&reply, 1)) {
+ LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
+ return Status::ABORT;
+ }
+ LOG(INFO) << __func__ << ": received status " << statusToString(reply.status)
+ << " for command " << command.toString();
+ if (reply.status != STATUS_BAD_VALUE) {
+ std::string s = command.toString();
+ s.append(", ").append(statusToString(reply.status));
+ std::lock_guard<std::mutex> lock(mLock);
+ mUnexpectedStatuses.push_back(std::move(s));
+ }
+ };
+ return Status::EXIT;
+ }
+
+ private:
+ const std::vector<StreamDescriptor::Command> mCommands;
+ std::mutex mLock;
+ std::vector<std::string> mUnexpectedStatuses GUARDED_BY(mLock);
};
template <typename Stream>
@@ -231,45 +578,74 @@
public:
WithStream() {}
explicit WithStream(const AudioPortConfig& portConfig) : mPortConfig(portConfig) {}
+ WithStream(const WithStream&) = delete;
+ WithStream& operator=(const WithStream&) = delete;
~WithStream() {
if (mStream != nullptr) {
- Status status = mStream->close();
- EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
+ mContext.reset();
+ ScopedAStatus status = mStream->close();
+ EXPECT_EQ(EX_NONE, status.getExceptionCode())
<< status << "; port config id " << getPortId();
}
}
void SetUpPortConfig(IModule* module) { ASSERT_NO_FATAL_FAILURE(mPortConfig.SetUp(module)); }
- Status SetUpNoChecks(IModule* module) { return SetUpNoChecks(module, mPortConfig.get()); }
- Status SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig);
- void SetUp(IModule* module) {
+ ScopedAStatus SetUpNoChecks(IModule* module, long bufferSizeFrames) {
+ return SetUpNoChecks(module, mPortConfig.get(), bufferSizeFrames);
+ }
+ ScopedAStatus SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig,
+ long bufferSizeFrames);
+ void SetUp(IModule* module, long bufferSizeFrames) {
ASSERT_NO_FATAL_FAILURE(SetUpPortConfig(module));
- Status status = SetUpNoChecks(module);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode())
+ ScopedAStatus status = SetUpNoChecks(module, bufferSizeFrames);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode())
<< status << "; port config id " << getPortId();
ASSERT_NE(nullptr, mStream) << "; port config id " << getPortId();
+ EXPECT_GE(mDescriptor.bufferSizeFrames, bufferSizeFrames)
+ << "actual buffer size must be no less than requested";
+ mContext.emplace(mDescriptor);
+ ASSERT_NO_FATAL_FAILURE(mContext.value().checkIsValid());
}
Stream* get() const { return mStream.get(); }
+ const StreamContext* getContext() const { return mContext ? &(mContext.value()) : nullptr; }
+ std::shared_ptr<Stream> getSharedPointer() const { return mStream; }
const AudioPortConfig& getPortConfig() const { return mPortConfig.get(); }
int32_t getPortId() const { return mPortConfig.getId(); }
private:
WithAudioPortConfig mPortConfig;
- sp<Stream> mStream;
+ std::shared_ptr<Stream> mStream;
+ StreamDescriptor mDescriptor;
+ std::optional<StreamContext> mContext;
};
-template <>
-Status WithStream<IStreamIn>::SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig) {
+SinkMetadata GenerateSinkMetadata(const AudioPortConfig& portConfig) {
RecordTrackMetadata trackMeta;
trackMeta.source = AudioSource::MIC;
trackMeta.gain = 1.0;
trackMeta.channelMask = portConfig.channelMask.value();
SinkMetadata metadata;
metadata.tracks.push_back(trackMeta);
- return module->openInputStream(portConfig.id, metadata, &mStream);
+ return metadata;
}
template <>
-Status WithStream<IStreamOut>::SetUpNoChecks(IModule* module, const AudioPortConfig& portConfig) {
+ScopedAStatus WithStream<IStreamIn>::SetUpNoChecks(IModule* module,
+ const AudioPortConfig& portConfig,
+ long bufferSizeFrames) {
+ aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
+ args.portConfigId = portConfig.id;
+ args.sinkMetadata = GenerateSinkMetadata(portConfig);
+ args.bufferSizeFrames = bufferSizeFrames;
+ aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
+ ScopedAStatus status = module->openInputStream(args, &ret);
+ if (status.isOk()) {
+ mStream = std::move(ret.stream);
+ mDescriptor = std::move(ret.desc);
+ }
+ return status;
+}
+
+SourceMetadata GenerateSourceMetadata(const AudioPortConfig& portConfig) {
PlaybackTrackMetadata trackMeta;
trackMeta.usage = AudioUsage::MEDIA;
trackMeta.contentType = AudioContentType::MUSIC;
@@ -277,7 +653,25 @@
trackMeta.channelMask = portConfig.channelMask.value();
SourceMetadata metadata;
metadata.tracks.push_back(trackMeta);
- return module->openOutputStream(portConfig.id, metadata, {}, &mStream);
+ return metadata;
+}
+
+template <>
+ScopedAStatus WithStream<IStreamOut>::SetUpNoChecks(IModule* module,
+ const AudioPortConfig& portConfig,
+ long bufferSizeFrames) {
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+ args.portConfigId = portConfig.id;
+ args.sourceMetadata = GenerateSourceMetadata(portConfig);
+ args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig);
+ args.bufferSizeFrames = bufferSizeFrames;
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+ ScopedAStatus status = module->openOutputStream(args, &ret);
+ if (status.isOk()) {
+ mStream = std::move(ret.stream);
+ mDescriptor = std::move(ret.desc);
+ }
+ return status;
}
class WithAudioPatch {
@@ -285,18 +679,23 @@
WithAudioPatch() {}
WithAudioPatch(const AudioPortConfig& srcPortConfig, const AudioPortConfig& sinkPortConfig)
: mSrcPortConfig(srcPortConfig), mSinkPortConfig(sinkPortConfig) {}
+ WithAudioPatch(bool sinkIsCfg1, const AudioPortConfig& portConfig1,
+ const AudioPortConfig& portConfig2)
+ : mSrcPortConfig(sinkIsCfg1 ? portConfig2 : portConfig1),
+ mSinkPortConfig(sinkIsCfg1 ? portConfig1 : portConfig2) {}
+ WithAudioPatch(const WithAudioPatch&) = delete;
+ WithAudioPatch& operator=(const WithAudioPatch&) = delete;
~WithAudioPatch() {
if (mModule != nullptr && mPatch.id != 0) {
- Status status = mModule->resetAudioPatch(mPatch.id);
- EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
- << status << "; patch id " << getId();
+ ScopedAStatus status = mModule->resetAudioPatch(mPatch.id);
+ EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status << "; patch id " << getId();
}
}
void SetUpPortConfigs(IModule* module) {
ASSERT_NO_FATAL_FAILURE(mSrcPortConfig.SetUp(module));
ASSERT_NO_FATAL_FAILURE(mSinkPortConfig.SetUp(module));
}
- Status SetUpNoChecks(IModule* module) {
+ ScopedAStatus SetUpNoChecks(IModule* module) {
mModule = module;
mPatch.sourcePortConfigIds = std::vector<int32_t>{mSrcPortConfig.getId()};
mPatch.sinkPortConfigIds = std::vector<int32_t>{mSinkPortConfig.getId()};
@@ -304,13 +703,22 @@
}
void SetUp(IModule* module) {
ASSERT_NO_FATAL_FAILURE(SetUpPortConfigs(module));
- Status status = SetUpNoChecks(module);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode())
+ ScopedAStatus status = SetUpNoChecks(module);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode())
<< status << "; source port config id " << mSrcPortConfig.getId()
<< "; sink port config id " << mSinkPortConfig.getId();
+ EXPECT_GT(mPatch.minimumStreamBufferSizeFrames, 0) << "patch id " << getId();
+ for (auto latencyMs : mPatch.latenciesMs) {
+ EXPECT_GT(latencyMs, 0) << "patch id " << getId();
+ }
}
int32_t getId() const { return mPatch.id; }
const AudioPatch& get() const { return mPatch; }
+ const AudioPortConfig& getSinkPortConfig() const { return mSinkPortConfig.get(); }
+ const AudioPortConfig& getSrcPortConfig() const { return mSrcPortConfig.get(); }
+ const AudioPortConfig& getPortConfig(bool getSink) const {
+ return getSink ? getSinkPortConfig() : getSrcPortConfig();
+ }
private:
WithAudioPortConfig mSrcPortConfig;
@@ -332,37 +740,37 @@
ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
}
-TEST_P(AudioCoreModule, GetAudioPortsIsStatic) {
+TEST_P(AudioCoreModule, GetAudioPortsIsStable) {
std::vector<AudioPort> ports1;
{
- Status status = module->getAudioPorts(&ports1);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPorts(&ports1);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
std::vector<AudioPort> ports2;
{
- Status status = module->getAudioPorts(&ports2);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPorts(&ports2);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
ASSERT_EQ(ports1.size(), ports2.size())
- << "Sizes of audio port arrays do not match across calls to getAudioPorts";
+ << "Sizes of audio port arrays do not match across consequent calls to getAudioPorts";
std::sort(ports1.begin(), ports1.end());
std::sort(ports2.begin(), ports2.end());
EXPECT_EQ(ports1, ports2);
}
-TEST_P(AudioCoreModule, GetAudioRoutesIsStatic) {
+TEST_P(AudioCoreModule, GetAudioRoutesIsStable) {
std::vector<AudioRoute> routes1;
{
- Status status = module->getAudioRoutes(&routes1);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioRoutes(&routes1);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
std::vector<AudioRoute> routes2;
{
- Status status = module->getAudioRoutes(&routes2);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioRoutes(&routes2);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
ASSERT_EQ(routes1.size(), routes2.size())
- << "Sizes of audio route arrays do not match across calls to getAudioRoutes";
+ << "Sizes of audio route arrays do not match across consequent calls to getAudioRoutes";
std::sort(routes1.begin(), routes1.end());
std::sort(routes2.begin(), routes2.end());
EXPECT_EQ(routes1, routes2);
@@ -371,12 +779,12 @@
TEST_P(AudioCoreModule, GetAudioRoutesAreValid) {
std::vector<AudioRoute> routes;
{
- Status status = module->getAudioRoutes(&routes);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioRoutes(&routes);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
for (const auto& route : routes) {
std::set<int32_t> sources(route.sourcePortIds.begin(), route.sourcePortIds.end());
- EXPECT_NE(0, sources.size())
+ EXPECT_NE(0UL, sources.size())
<< "empty audio port sinks in the audio route: " << route.toString();
EXPECT_EQ(sources.size(), route.sourcePortIds.size())
<< "IDs of audio port sinks are not unique in the audio route: "
@@ -389,23 +797,49 @@
ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
std::vector<AudioRoute> routes;
{
- Status status = module->getAudioRoutes(&routes);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioRoutes(&routes);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
for (const auto& route : routes) {
- EXPECT_EQ(1, portIds.count(route.sinkPortId))
+ EXPECT_EQ(1UL, portIds.count(route.sinkPortId))
<< route.sinkPortId << " sink port id is unknown";
for (const auto& source : route.sourcePortIds) {
- EXPECT_EQ(1, portIds.count(source)) << source << " source port id is unknown";
+ EXPECT_EQ(1UL, portIds.count(source)) << source << " source port id is unknown";
}
}
}
+TEST_P(AudioCoreModule, GetAudioRoutesForAudioPort) {
+ std::set<int32_t> portIds;
+ ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
+ if (portIds.empty()) {
+ GTEST_SKIP() << "No ports in the module.";
+ }
+ for (const auto portId : portIds) {
+ std::vector<AudioRoute> routes;
+ ScopedAStatus status = module->getAudioRoutesForAudioPort(portId, &routes);
+ EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ for (const auto& r : routes) {
+ if (r.sinkPortId != portId) {
+ const auto& srcs = r.sourcePortIds;
+ EXPECT_TRUE(std::find(srcs.begin(), srcs.end(), portId) != srcs.end())
+ << " port ID " << portId << " does not used by the route " << r.toString();
+ }
+ }
+ }
+ for (const auto portId : GetNonExistentIds(portIds)) {
+ std::vector<AudioRoute> routes;
+ ScopedAStatus status = module->getAudioRoutesForAudioPort(portId, &routes);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned for port ID " << portId;
+ }
+}
+
TEST_P(AudioCoreModule, CheckDevicePorts) {
std::vector<AudioPort> ports;
{
- Status status = module->getAudioPorts(&ports);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPorts(&ports);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
std::optional<int32_t> defaultOutput, defaultInput;
std::set<AudioDevice> inputs, outputs;
@@ -432,7 +866,7 @@
<< "At least two output device ports are declared as default: "
<< defaultOutput.value() << " and " << port.id;
defaultOutput = port.id;
- EXPECT_EQ(0, outputs.count(devicePort.device))
+ EXPECT_EQ(0UL, outputs.count(devicePort.device))
<< "Non-unique output device: " << devicePort.device.toString();
outputs.insert(devicePort.device);
} else if (port.flags.getTag() == AudioIoFlags::Tag::input) {
@@ -440,7 +874,7 @@
<< "At least two input device ports are declared as default: "
<< defaultInput.value() << " and " << port.id;
defaultInput = port.id;
- EXPECT_EQ(0, inputs.count(devicePort.device))
+ EXPECT_EQ(0UL, inputs.count(devicePort.device))
<< "Non-unique input device: " << devicePort.device.toString();
inputs.insert(devicePort.device);
} else {
@@ -453,8 +887,8 @@
TEST_P(AudioCoreModule, CheckMixPorts) {
std::vector<AudioPort> ports;
{
- Status status = module->getAudioPorts(&ports);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPorts(&ports);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
std::optional<int32_t> primaryMixPort;
constexpr int primaryOutputFlag = 1 << static_cast<int>(AudioOutputFlags::PRIMARY);
@@ -482,35 +916,96 @@
}
for (const auto portId : portIds) {
AudioPort port;
- Status status = module->getAudioPort(portId, &port);
- EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPort(portId, &port);
+ EXPECT_EQ(EX_NONE, status.getExceptionCode()) << status;
EXPECT_EQ(portId, port.id);
}
- for (const auto portId : getNonExistentIds(portIds)) {
+ for (const auto portId : GetNonExistentIds(portIds)) {
AudioPort port;
- Status status = module->getAudioPort(portId, &port);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ ScopedAStatus status = module->getAudioPort(portId, &port);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " returned for port ID " << portId;
}
}
+TEST_P(AudioCoreModule, SetUpModuleConfig) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ // Send the module config to logcat to facilitate failures investigation.
+ LOG(INFO) << "SetUpModuleConfig: " << moduleConfig->toString();
+}
+
+// Verify that HAL module reports for a connected device port at least one non-dynamic profile,
+// that is, a profile with actual supported configuration.
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, GetAudioPortWithExternalDevices) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ AudioPort portWithData = port;
+ portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+ GenerateUniqueDeviceAddress();
+ WithDevicePortConnectedState portConnected(portWithData);
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ const int32_t connectedPortId = portConnected.getId();
+ ASSERT_NE(portWithData.id, connectedPortId);
+ ASSERT_EQ(portWithData.ext.getTag(), portConnected.get().ext.getTag());
+ EXPECT_EQ(portWithData.ext.get<AudioPortExt::Tag::device>().device,
+ portConnected.get().ext.get<AudioPortExt::Tag::device>().device);
+ // Verify that 'getAudioPort' and 'getAudioPorts' return the same connected port.
+ AudioPort connectedPort;
+ ScopedAStatus status = module->getAudioPort(connectedPortId, &connectedPort);
+ EXPECT_EQ(EX_NONE, status.getExceptionCode())
+ << status << " returned for getAudioPort port ID " << connectedPortId;
+ EXPECT_EQ(portConnected.get(), connectedPort);
+ const auto& portProfiles = connectedPort.profiles;
+ EXPECT_NE(0UL, portProfiles.size())
+ << "Connected port has no profiles: " << connectedPort.toString();
+ const auto dynamicProfileIt =
+ std::find_if(portProfiles.begin(), portProfiles.end(), [](const auto& profile) {
+ return profile.format.type == AudioFormatType::DEFAULT;
+ });
+ EXPECT_EQ(portProfiles.end(), dynamicProfileIt) << "Connected port contains dynamic "
+ << "profiles: " << connectedPort.toString();
+
+ std::vector<AudioPort> allPorts;
+ {
+ ScopedAStatus status = module->getAudioPorts(&allPorts);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ }
+ const auto allPortsIt = findById(allPorts, connectedPortId);
+ EXPECT_NE(allPorts.end(), allPortsIt);
+ if (allPortsIt != allPorts.end()) {
+ EXPECT_EQ(portConnected.get(), *allPortsIt);
+ }
+ }
+}
+
TEST_P(AudioCoreModule, OpenStreamInvalidPortConfigId) {
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
{
- sp<IStreamIn> stream;
- Status status = module->openInputStream(portConfigId, {}, &stream);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
+ args.portConfigId = portConfigId;
+ args.bufferSizeFrames = kDefaultBufferSizeFrames;
+ aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
+ ScopedAStatus status = module->openInputStream(args, &ret);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " openInputStream returned for port config ID " << portConfigId;
- EXPECT_EQ(nullptr, stream);
+ EXPECT_EQ(nullptr, ret.stream);
}
{
- sp<IStreamOut> stream;
- Status status = module->openOutputStream(portConfigId, {}, {}, &stream);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+ args.portConfigId = portConfigId;
+ args.bufferSizeFrames = kDefaultBufferSizeFrames;
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+ ScopedAStatus status = module->openOutputStream(args, &ret);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " openOutputStream returned for port config ID " << portConfigId;
- EXPECT_EQ(nullptr, stream);
+ EXPECT_EQ(nullptr, ret.stream);
}
}
}
@@ -525,11 +1020,11 @@
ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
std::vector<AudioPortConfig> portConfigs;
{
- Status status = module->getAudioPortConfigs(&portConfigs);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPortConfigs(&portConfigs);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
for (const auto& config : portConfigs) {
- EXPECT_EQ(1, portIds.count(config.portId))
+ EXPECT_EQ(1UL, portIds.count(config.portId))
<< config.portId << " port id is unknown, config id " << config.id;
}
}
@@ -537,9 +1032,9 @@
TEST_P(AudioCoreModule, ResetAudioPortConfigInvalidId) {
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
- Status status = module->resetAudioPortConfig(portConfigId);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
+ ScopedAStatus status = module->resetAudioPortConfig(portConfigId);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " returned for port config ID " << portConfigId;
}
}
@@ -549,19 +1044,19 @@
TEST_P(AudioCoreModule, ResetAudioPortConfigToInitialValue) {
std::vector<AudioPortConfig> portConfigsBefore;
{
- Status status = module->getAudioPortConfigs(&portConfigsBefore);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPortConfigs(&portConfigsBefore);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
// TODO: Change port configs according to port profiles.
for (const auto& c : portConfigsBefore) {
- Status status = module->resetAudioPortConfig(c.id);
- EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
+ ScopedAStatus status = module->resetAudioPortConfig(c.id);
+ EXPECT_EQ(EX_NONE, status.getExceptionCode())
<< status << " returned for port config ID " << c.id;
}
std::vector<AudioPortConfig> portConfigsAfter;
{
- Status status = module->getAudioPortConfigs(&portConfigsAfter);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ScopedAStatus status = module->getAudioPortConfigs(&portConfigsAfter);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
}
for (const auto& c : portConfigsBefore) {
auto afterIt = findById<AudioPortConfig>(portConfigsAfter, c.id);
@@ -584,8 +1079,8 @@
portConfig.portId = srcMixPort.value().id;
{
bool applied = true;
- Status status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode())
+ ScopedAStatus status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode())
<< status << "; Config: " << portConfig.toString();
EXPECT_FALSE(applied);
}
@@ -608,44 +1103,40 @@
EXPECT_EQ(suggestedConfig.flags.value(), appliedConfig.flags.value());
}
+TEST_P(AudioCoreModule, SetAllAttachedDevicePortConfigs) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ ASSERT_NO_FATAL_FAILURE(ApplyEveryConfig(moduleConfig->getPortConfigsForAttachedDevicePorts()));
+}
+
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, SetAllExternalDevicePortConfigs) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ ASSERT_NO_FATAL_FAILURE(
+ ApplyEveryConfig(moduleConfig->getPortConfigsForDevicePort(portConnected.get())));
+ }
+}
+
TEST_P(AudioCoreModule, SetAllStaticAudioPortConfigs) {
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
- const auto allPortConfigs = moduleConfig->getPortConfigsForMixPorts();
- for (const auto& config : allPortConfigs) {
- ASSERT_NE(0, config.portId);
- WithAudioPortConfig portConfig(config);
- ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
- EXPECT_EQ(config.portId, portConfig.get().portId);
- std::vector<AudioPortConfig> retrievedPortConfigs;
- {
- Status status = module->getAudioPortConfigs(&retrievedPortConfigs);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
- }
- const int32_t portConfigId = portConfig.getId();
- auto configIt = std::find_if(
- retrievedPortConfigs.begin(), retrievedPortConfigs.end(),
- [&portConfigId](const auto& retrConf) { return retrConf.id == portConfigId; });
- EXPECT_NE(configIt, retrievedPortConfigs.end())
- << "Port config id returned by setAudioPortConfig: " << portConfigId
- << " is not found in the list returned by getPortConfigsForMixPorts";
- if (configIt != retrievedPortConfigs.end()) {
- EXPECT_EQ(portConfig.get(), *configIt)
- << "Port config returned by getPortConfigsForMixPorts: " << configIt->toString()
- << " is not the same as returned by setAudioPortConfig: "
- << portConfig.get().toString();
- }
- }
+ ASSERT_NO_FATAL_FAILURE(ApplyEveryConfig(moduleConfig->getPortConfigsForMixPorts()));
}
TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortId) {
std::set<int32_t> portIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
- for (const auto portId : getNonExistentIds(portIds)) {
+ for (const auto portId : GetNonExistentIds(portIds)) {
AudioPortConfig portConfig, suggestedConfig;
bool applied;
portConfig.portId = portId;
- Status status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ ScopedAStatus status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " returned for port ID " << portId;
EXPECT_FALSE(suggestedConfig.format.has_value());
EXPECT_FALSE(suggestedConfig.channelMask.has_value());
@@ -656,12 +1147,12 @@
TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortConfigId) {
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
AudioPortConfig portConfig, suggestedConfig;
bool applied;
portConfig.id = portConfigId;
- Status status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ ScopedAStatus status = module->setAudioPortConfig(portConfig, &suggestedConfig, &applied);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " returned for port config ID " << portConfigId;
EXPECT_FALSE(suggestedConfig.format.has_value());
EXPECT_FALSE(suggestedConfig.channelMask.has_value());
@@ -669,6 +1160,203 @@
}
}
+TEST_P(AudioCoreModule, TryConnectMissingDevice) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ AudioPort ignored;
+ WithDebugFlags doNotSimulateConnections = WithDebugFlags::createNested(debug);
+ doNotSimulateConnections.flags().simulateDeviceConnections = false;
+ ASSERT_NO_FATAL_FAILURE(doNotSimulateConnections.SetUp(module.get()));
+ for (const auto& port : ports) {
+ AudioPort portWithData = port;
+ portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+ GenerateUniqueDeviceAddress();
+ ScopedAStatus status = module->connectExternalDevice(portWithData, &ignored);
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
+ << status << " returned for static port " << portWithData.toString();
+ }
+}
+
+TEST_P(AudioCoreModule, TryChangingConnectionSimulationMidway) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ WithDevicePortConnectedState portConnected(*ports.begin(), GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ ModuleDebug midwayDebugChange = debug.flags();
+ midwayDebugChange.simulateDeviceConnections = false;
+ ScopedAStatus status = module->setModuleDebug(midwayDebugChange);
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
+ << status << " returned when trying to disable connections simulation "
+ << "while having a connected device";
+}
+
+TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceInvalidPorts) {
+ AudioPort ignored;
+ std::set<int32_t> portIds;
+ ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
+ for (const auto portId : GetNonExistentIds(portIds)) {
+ AudioPort invalidPort;
+ invalidPort.id = portId;
+ ScopedAStatus status = module->connectExternalDevice(invalidPort, &ignored);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned for port ID " << portId << " when setting CONNECTED state";
+ status = module->disconnectExternalDevice(portId);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned for port ID " << portId
+ << " when setting DISCONNECTED state";
+ }
+
+ std::vector<AudioPort> ports;
+ {
+ ScopedAStatus status = module->getAudioPorts(&ports);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ }
+ for (const auto& port : ports) {
+ if (port.ext.getTag() != AudioPortExt::Tag::device) {
+ ScopedAStatus status = module->connectExternalDevice(port, &ignored);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned for non-device port ID " << port.id
+ << " when setting CONNECTED state";
+ status = module->disconnectExternalDevice(port.id);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned for non-device port ID " << port.id
+ << " when setting DISCONNECTED state";
+ } else {
+ const auto& devicePort = port.ext.get<AudioPortExt::Tag::device>();
+ if (devicePort.device.type.connection.empty()) {
+ ScopedAStatus status = module->connectExternalDevice(port, &ignored);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned for permanently attached device port ID " << port.id
+ << " when setting CONNECTED state";
+ status = module->disconnectExternalDevice(port.id);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned for permanently attached device port ID " << port.id
+ << " when setting DISCONNECTED state";
+ }
+ }
+ }
+}
+
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceTwice) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ AudioPort ignored;
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ ScopedAStatus status = module->disconnectExternalDevice(port.id);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned when disconnecting already disconnected device port ID "
+ << port.id;
+ AudioPort portWithData = port;
+ portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+ GenerateUniqueDeviceAddress();
+ WithDevicePortConnectedState portConnected(portWithData);
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ status = module->connectExternalDevice(portConnected.get(), &ignored);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned when trying to connect a connected device port "
+ << portConnected.get().toString();
+ status = module->connectExternalDevice(portWithData, &ignored);
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
+ << status << " returned when connecting again the external device "
+ << portWithData.ext.get<AudioPortExt::Tag::device>().device.toString();
+ if (status.getExceptionCode() == EX_NONE) {
+ ADD_FAILURE() << "Returned connected port " << ignored.toString() << " for template "
+ << portWithData.toString();
+ }
+ }
+}
+
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, DisconnectExternalDeviceNonResetPortConfig) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ const auto portConfig = moduleConfig->getSingleConfigForDevicePort(portConnected.get());
+ {
+ WithAudioPortConfig config(portConfig);
+ // Note: if SetUp fails, check the status of 'GetAudioPortWithExternalDevices' test.
+ // Our test assumes that 'getAudioPort' returns at least one profile, and it
+ // is not a dynamic profile.
+ ASSERT_NO_FATAL_FAILURE(config.SetUp(module.get()));
+ ScopedAStatus status = module->disconnectExternalDevice(portConnected.getId());
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
+ << status << " returned when trying to disconnect device port ID " << port.id
+ << " with active configuration " << config.getId();
+ }
+ }
+}
+
+TEST_P(AudioCoreModule, ExternalDevicePortRoutes) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ std::vector<AudioRoute> routesBefore;
+ {
+ ScopedAStatus status = module->getAudioRoutes(&routesBefore);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ }
+
+ int32_t connectedPortId;
+ {
+ WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ connectedPortId = portConnected.getId();
+ std::vector<AudioRoute> connectedPortRoutes;
+ {
+ ScopedAStatus status =
+ module->getAudioRoutesForAudioPort(connectedPortId, &connectedPortRoutes);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode())
+ << status << " returned when retrieving routes for connected port id "
+ << connectedPortId;
+ }
+ // There must be routes for the port to be useful.
+ if (connectedPortRoutes.empty()) {
+ std::vector<AudioRoute> allRoutes;
+ ScopedAStatus status = module->getAudioRoutes(&allRoutes);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ ADD_FAILURE() << " no routes returned for the connected port "
+ << portConnected.get().toString()
+ << "; all routes: " << android::internal::ToString(allRoutes);
+ }
+ }
+ std::vector<AudioRoute> ignored;
+ ScopedAStatus status = module->getAudioRoutesForAudioPort(connectedPortId, &ignored);
+ ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " returned when retrieving routes for released connected port id "
+ << connectedPortId;
+
+ std::vector<AudioRoute> routesAfter;
+ {
+ ScopedAStatus status = module->getAudioRoutes(&routesAfter);
+ ASSERT_EQ(EX_NONE, status.getExceptionCode()) << status;
+ }
+ ASSERT_EQ(routesBefore.size(), routesAfter.size())
+ << "Sizes of audio route arrays do not match after creating and "
+ << "releasing a connected port";
+ std::sort(routesBefore.begin(), routesBefore.end());
+ std::sort(routesAfter.begin(), routesAfter.end());
+ EXPECT_EQ(routesBefore, routesAfter);
+ }
+}
+
template <typename Stream>
class AudioStream : public AudioCoreModule {
public:
@@ -680,31 +1368,66 @@
}
void CloseTwice() {
- const auto portConfig = moduleConfig->getSingleConfigForMixPort(IsInput<Stream>());
+ const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
if (!portConfig.has_value()) {
GTEST_SKIP() << "No mix port for attached devices";
}
- sp<Stream> heldStream;
+ std::shared_ptr<Stream> heldStream;
{
WithStream<Stream> stream(portConfig.value());
- ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
- heldStream = stream.get();
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ heldStream = stream.getSharedPointer();
}
- Status status = heldStream->close();
- EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ ScopedAStatus status = heldStream->close();
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
<< status << " when closing the stream twice";
}
void OpenAllConfigs() {
- const auto allPortConfigs = moduleConfig->getPortConfigsForMixPorts(IsInput<Stream>());
+ const auto allPortConfigs =
+ moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
for (const auto& portConfig : allPortConfigs) {
WithStream<Stream> stream(portConfig);
- ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
}
}
+ void OpenInvalidBufferSize() {
+ const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
+ if (!portConfig.has_value()) {
+ GTEST_SKIP() << "No mix port for attached devices";
+ }
+ WithStream<Stream> stream(portConfig.value());
+ ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
+ // The buffer size of 1 frame should be impractically small, and thus
+ // less than any minimum buffer size suggested by any HAL.
+ for (long bufferSize : std::array<long, 4>{-1, 0, 1, std::numeric_limits<long>::max()}) {
+ ScopedAStatus status = stream.SetUpNoChecks(module.get(), bufferSize);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " open" << direction(true) << "Stream returned for " << bufferSize
+ << " buffer size";
+ EXPECT_EQ(nullptr, stream.get());
+ }
+ }
+
+ void OpenInvalidDirection() {
+ // Important! The direction of the port config must be reversed.
+ const auto portConfig =
+ moduleConfig->getSingleConfigForMixPort(!IOTraits<Stream>::is_input);
+ if (!portConfig.has_value()) {
+ GTEST_SKIP() << "No mix port for attached devices";
+ }
+ WithStream<Stream> stream(portConfig.value());
+ ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
+ ScopedAStatus status = stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status << " open" << direction(true) << "Stream returned for port config ID "
+ << stream.getPortId();
+ EXPECT_EQ(nullptr, stream.get());
+ }
+
void OpenOverMaxCount() {
- constexpr bool isInput = IsInput<Stream>();
+ constexpr bool isInput = IOTraits<Stream>::is_input;
auto ports = moduleConfig->getMixPorts(isInput);
bool hasSingleRun = false;
for (const auto& port : ports) {
@@ -725,15 +1448,15 @@
streamWraps[i].emplace(portConfigs[i]);
WithStream<Stream>& stream = streamWraps[i].value();
if (i < maxStreamCount) {
- ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
} else {
ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
- Status status = stream.SetUpNoChecks(module.get());
- EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ ScopedAStatus status =
+ stream.SetUpNoChecks(module.get(), kDefaultBufferSizeFrames);
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
<< status << " open" << direction(true)
- << "Stream"
- " returned for port config ID "
- << stream.getPortId() << ", maxOpenStreamCount is " << maxStreamCount;
+ << "Stream returned for port config ID " << stream.getPortId()
+ << ", maxOpenStreamCount is " << maxStreamCount;
}
}
}
@@ -743,50 +1466,146 @@
}
}
- void OpenInvalidDirection() {
- // Important! The direction of the port config must be reversed.
- const auto portConfig = moduleConfig->getSingleConfigForMixPort(!IsInput<Stream>());
- if (!portConfig.has_value()) {
- GTEST_SKIP() << "No mix port for attached devices";
- }
- WithStream<Stream> stream(portConfig.value());
- ASSERT_NO_FATAL_FAILURE(stream.SetUpPortConfig(module.get()));
- Status status = stream.SetUpNoChecks(module.get());
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
- << status << " open" << direction(true) << "Stream returned for port config ID "
- << stream.getPortId();
- EXPECT_EQ(nullptr, stream.get());
- }
-
void OpenTwiceSamePortConfig() {
- const auto portConfig = moduleConfig->getSingleConfigForMixPort(IsInput<Stream>());
+ const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
if (!portConfig.has_value()) {
GTEST_SKIP() << "No mix port for attached devices";
}
EXPECT_NO_FATAL_FAILURE(OpenTwiceSamePortConfigImpl(portConfig.value()));
}
+ void ReadOrWrite(bool useImpl2, bool testObservablePosition) {
+ const auto allPortConfigs =
+ moduleConfig->getPortConfigsForMixPorts(IOTraits<Stream>::is_input);
+ if (allPortConfigs.empty()) {
+ GTEST_SKIP() << "No mix ports have attached devices";
+ }
+ for (const auto& portConfig : allPortConfigs) {
+ EXPECT_NO_FATAL_FAILURE(ReadOrWriteImpl(portConfig, useImpl2, testObservablePosition))
+ << portConfig.toString();
+ }
+ }
+
void ResetPortConfigWithOpenStream() {
- const auto portConfig = moduleConfig->getSingleConfigForMixPort(IsInput<Stream>());
+ const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
if (!portConfig.has_value()) {
GTEST_SKIP() << "No mix port for attached devices";
}
WithStream<Stream> stream(portConfig.value());
- ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get()));
- Status status = module->resetAudioPortConfig(stream.getPortId());
- EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ ScopedAStatus status = module->resetAudioPortConfig(stream.getPortId());
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
<< status << " returned for port config ID " << stream.getPortId();
}
+ void SendInvalidCommand() {
+ const auto portConfig = moduleConfig->getSingleConfigForMixPort(IOTraits<Stream>::is_input);
+ if (!portConfig.has_value()) {
+ GTEST_SKIP() << "No mix port for attached devices";
+ }
+ EXPECT_NO_FATAL_FAILURE(SendInvalidCommandImpl(portConfig.value()));
+ }
+
void OpenTwiceSamePortConfigImpl(const AudioPortConfig& portConfig) {
WithStream<Stream> stream1(portConfig);
- ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get()));
+ ASSERT_NO_FATAL_FAILURE(stream1.SetUp(module.get(), kDefaultBufferSizeFrames));
WithStream<Stream> stream2;
- Status status = stream2.SetUpNoChecks(module.get(), stream1.getPortConfig());
- EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ ScopedAStatus status = stream2.SetUpNoChecks(module.get(), stream1.getPortConfig(),
+ kDefaultBufferSizeFrames);
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
<< status << " when opening " << direction(false)
<< " stream twice for the same port config ID " << stream1.getPortId();
}
+
+ template <class Worker>
+ void WaitForObservablePositionAdvance(Worker& worker) {
+ static constexpr int kWriteDurationUs = 50 * 1000;
+ static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
+ int64_t framesInitial;
+ framesInitial = worker.getLastObservablePosition().frames;
+ ASSERT_FALSE(worker.hasError());
+ bool timedOut = false;
+ int64_t frames = framesInitial;
+ for (android::base::Timer elapsed;
+ frames <= framesInitial && !worker.hasError() &&
+ !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+ usleep(kWriteDurationUs);
+ frames = worker.getLastObservablePosition().frames;
+ }
+ EXPECT_FALSE(timedOut);
+ EXPECT_FALSE(worker.hasError()) << worker.getError();
+ EXPECT_GT(frames, framesInitial);
+ }
+
+ void ReadOrWriteImpl(const AudioPortConfig& portConfig, bool useImpl2,
+ bool testObservablePosition) {
+ if (!useImpl2) {
+ ASSERT_NO_FATAL_FAILURE(ReadOrWriteImpl1(portConfig, testObservablePosition));
+ } else {
+ ASSERT_NO_FATAL_FAILURE(ReadOrWriteImpl2(portConfig, testObservablePosition));
+ }
+ }
+
+ // Set up a patch first, then open a stream.
+ void ReadOrWriteImpl1(const AudioPortConfig& portConfig, bool testObservablePosition) {
+ auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
+ IOTraits<Stream>::is_input, portConfig);
+ ASSERT_FALSE(devicePorts.empty());
+ auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
+ WithAudioPatch patch(IOTraits<Stream>::is_input, portConfig, devicePortConfig);
+ ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+
+ WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ typename IOTraits<Stream>::Worker worker(*stream.getContext());
+
+ ASSERT_TRUE(worker.start());
+ ASSERT_TRUE(worker.waitForAtLeastOneCycle());
+ if (testObservablePosition) {
+ ASSERT_NO_FATAL_FAILURE(WaitForObservablePositionAdvance(worker));
+ }
+ }
+
+ // Open a stream, then set up a patch for it.
+ void ReadOrWriteImpl2(const AudioPortConfig& portConfig, bool testObservablePosition) {
+ WithStream<Stream> stream(portConfig);
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ typename IOTraits<Stream>::Worker worker(*stream.getContext());
+
+ auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
+ IOTraits<Stream>::is_input, portConfig);
+ ASSERT_FALSE(devicePorts.empty());
+ auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]);
+ WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
+ ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+
+ ASSERT_TRUE(worker.start());
+ ASSERT_TRUE(worker.waitForAtLeastOneCycle());
+ if (testObservablePosition) {
+ ASSERT_NO_FATAL_FAILURE(WaitForObservablePositionAdvance(worker));
+ }
+ }
+
+ void SendInvalidCommandImpl(const AudioPortConfig& portConfig) {
+ std::vector<StreamDescriptor::Command> commands(6);
+ commands[0].code = -1;
+ commands[1].code = StreamDescriptor::COMMAND_BURST - 1;
+ commands[2].code = std::numeric_limits<int32_t>::min();
+ commands[3].code = std::numeric_limits<int32_t>::max();
+ commands[4].code = StreamDescriptor::COMMAND_BURST;
+ commands[4].fmqByteCount = -1;
+ commands[5].code = StreamDescriptor::COMMAND_BURST;
+ commands[5].fmqByteCount = std::numeric_limits<int32_t>::min();
+ WithStream<Stream> stream(portConfig);
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ StreamWorker<StreamInvalidCommandLogic> writer(*stream.getContext(), commands);
+ ASSERT_TRUE(writer.start());
+ writer.waitForAtLeastOneCycle();
+ auto unexpectedStatuses = writer.getUnexpectedStatuses();
+ EXPECT_EQ(0UL, unexpectedStatuses.size())
+ << "Pairs of (command, actual status): "
+ << android::internal::ToString(unexpectedStatuses);
+ }
};
using AudioStreamIn = AudioStream<IStreamIn>;
using AudioStreamOut = AudioStream<IStreamOut>;
@@ -803,20 +1622,33 @@
#define TEST_IO_STREAM(method_name) \
TEST_P(AudioStreamIn, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } \
TEST_P(AudioStreamOut, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); }
+#define TEST_IO_STREAM_2(method_name, arg1, arg2) \
+ TEST_P(AudioStreamIn, method_name##_##arg1##_##arg2) { \
+ ASSERT_NO_FATAL_FAILURE(method_name(arg1, arg2)); \
+ } \
+ TEST_P(AudioStreamOut, method_name##_##arg1##_##arg2) { \
+ ASSERT_NO_FATAL_FAILURE(method_name(arg1, arg2)); \
+ }
TEST_IO_STREAM(CloseTwice);
TEST_IO_STREAM(OpenAllConfigs);
+TEST_IO_STREAM(OpenInvalidBufferSize);
TEST_IO_STREAM(OpenInvalidDirection);
TEST_IO_STREAM(OpenOverMaxCount);
TEST_IO_STREAM(OpenTwiceSamePortConfig);
+TEST_IO_STREAM_2(ReadOrWrite, false, false);
+TEST_IO_STREAM_2(ReadOrWrite, true, false);
+TEST_IO_STREAM_2(ReadOrWrite, false, true);
+TEST_IO_STREAM_2(ReadOrWrite, true, true);
TEST_IO_STREAM(ResetPortConfigWithOpenStream);
+TEST_IO_STREAM(SendInvalidCommand);
TEST_P(AudioStreamOut, OpenTwicePrimary) {
const auto mixPorts = moduleConfig->getMixPorts(false);
auto primaryPortIt = std::find_if(mixPorts.begin(), mixPorts.end(), [](const AudioPort& port) {
constexpr int primaryOutputFlag = 1 << static_cast<int>(AudioOutputFlags::PRIMARY);
return port.flags.getTag() == AudioIoFlags::Tag::output &&
- ((port.flags.get<AudioIoFlags::Tag::output>() & primaryOutputFlag) != 0);
+ (port.flags.get<AudioIoFlags::Tag::output>() & primaryOutputFlag) != 0;
});
if (primaryPortIt == mixPorts.end()) {
GTEST_SKIP() << "No primary mix port";
@@ -829,6 +1661,35 @@
EXPECT_NO_FATAL_FAILURE(OpenTwiceSamePortConfigImpl(portConfig.value()));
}
+TEST_P(AudioStreamOut, RequireOffloadInfo) {
+ const auto mixPorts = moduleConfig->getMixPorts(false);
+ auto offloadPortIt = std::find_if(mixPorts.begin(), mixPorts.end(), [&](const AudioPort& port) {
+ constexpr int compressOffloadFlag = 1
+ << static_cast<int>(AudioOutputFlags::COMPRESS_OFFLOAD);
+ return port.flags.getTag() == AudioIoFlags::Tag::output &&
+ (port.flags.get<AudioIoFlags::Tag::output>() & compressOffloadFlag) != 0 &&
+ !moduleConfig->getAttachedSinkDevicesPortsForMixPort(port).empty();
+ });
+ if (offloadPortIt == mixPorts.end()) {
+ GTEST_SKIP()
+ << "No mix port for compressed offload that could be routed to attached devices";
+ }
+ const auto portConfig = moduleConfig->getSingleConfigForMixPort(false, *offloadPortIt);
+ ASSERT_TRUE(portConfig.has_value())
+ << "No profiles specified for the compressed offload mix port";
+ StreamDescriptor descriptor;
+ std::shared_ptr<IStreamOut> ignored;
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+ args.portConfigId = portConfig.value().id;
+ args.sourceMetadata = GenerateSourceMetadata(portConfig.value());
+ args.bufferSizeFrames = kDefaultBufferSizeFrames;
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+ ScopedAStatus status = module->openOutputStream(args, &ret);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
+ << status
+ << " returned when no offload info is provided for a compressed offload mix port";
+}
+
// Tests specific to audio patches. The fixure class is named 'AudioModulePatch'
// to avoid clashing with 'AudioPatch' class.
class AudioModulePatch : public AudioCoreModule {
@@ -847,8 +1708,8 @@
AudioPatch patch;
patch.sourcePortConfigIds = sources;
patch.sinkPortConfigIds = sinks;
- Status status = module->setAudioPatch(patch, &patch);
- ASSERT_EQ(expectedException, status.exceptionCode())
+ ScopedAStatus status = module->setAudioPatch(patch, &patch);
+ ASSERT_EQ(expectedException, status.getExceptionCode())
<< status << ": patch source ids: " << android::internal::ToString(sources)
<< "; sink ids: " << android::internal::ToString(sinks);
}
@@ -867,8 +1728,8 @@
patch.get().sinkPortConfigIds.begin(),
patch.get().sinkPortConfigIds.end());
for (const auto portConfigId : sourceAndSinkPortConfigIds) {
- Status status = module->resetAudioPortConfig(portConfigId);
- EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ ScopedAStatus status = module->resetAudioPortConfig(portConfigId);
+ EXPECT_EQ(EX_ILLEGAL_STATE, status.getExceptionCode())
<< status << " returned for port config ID " << portConfigId;
}
}
@@ -887,22 +1748,22 @@
ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
}
EXPECT_NO_FATAL_FAILURE(
- SetInvalidPatchHelper(Status::EX_ILLEGAL_ARGUMENT, {}, {sinkPortConfig.getId()}));
+ SetInvalidPatchHelper(EX_ILLEGAL_ARGUMENT, {}, {sinkPortConfig.getId()}));
EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(
- Status::EX_ILLEGAL_ARGUMENT, {srcPortConfig.getId(), srcPortConfig.getId()},
+ EX_ILLEGAL_ARGUMENT, {srcPortConfig.getId(), srcPortConfig.getId()},
{sinkPortConfig.getId()}));
EXPECT_NO_FATAL_FAILURE(
- SetInvalidPatchHelper(Status::EX_ILLEGAL_ARGUMENT, {srcPortConfig.getId()}, {}));
+ SetInvalidPatchHelper(EX_ILLEGAL_ARGUMENT, {srcPortConfig.getId()}, {}));
EXPECT_NO_FATAL_FAILURE(
- SetInvalidPatchHelper(Status::EX_ILLEGAL_ARGUMENT, {srcPortConfig.getId()},
+ SetInvalidPatchHelper(EX_ILLEGAL_ARGUMENT, {srcPortConfig.getId()},
{sinkPortConfig.getId(), sinkPortConfig.getId()}));
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
- EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(
- Status::EX_ILLEGAL_ARGUMENT, {portConfigId}, {sinkPortConfig.getId()}));
- EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(Status::EX_ILLEGAL_ARGUMENT,
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
+ EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(EX_ILLEGAL_ARGUMENT, {portConfigId},
+ {sinkPortConfig.getId()}));
+ EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(EX_ILLEGAL_ARGUMENT,
{srcPortConfig.getId()}, {portConfigId}));
}
}
@@ -914,8 +1775,8 @@
}
WithAudioPatch patch(srcSinkPair.value().first, srcSinkPair.value().second);
ASSERT_NO_FATAL_FAILURE(patch.SetUpPortConfigs(module.get()));
- Status status = patch.SetUpNoChecks(module.get());
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ ScopedAStatus status = patch.SetUpNoChecks(module.get());
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << ": when setting up a patch from "
<< srcSinkPair.value().first.toString() << " to "
<< srcSinkPair.value().second.toString() << " that does not have a route";
@@ -928,11 +1789,12 @@
}
for (const auto& srcSinkGroup : srcSinkGroups) {
const auto& route = srcSinkGroup.first;
- std::vector<WithAudioPatch> patches;
+ std::vector<std::unique_ptr<WithAudioPatch>> patches;
for (const auto& srcSink : srcSinkGroup.second) {
if (!route.isExclusive) {
- patches.emplace_back(srcSink.first, srcSink.second);
- EXPECT_NO_FATAL_FAILURE(patches[patches.size() - 1].SetUp(module.get()));
+ patches.push_back(
+ std::make_unique<WithAudioPatch>(srcSink.first, srcSink.second));
+ EXPECT_NO_FATAL_FAILURE(patches[patches.size() - 1]->SetUp(module.get()));
} else {
WithAudioPatch patch(srcSink.first, srcSink.second);
EXPECT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
@@ -969,11 +1831,12 @@
// Then use the same patch setting, except for having an invalid ID.
std::set<int32_t> patchIds;
ASSERT_NO_FATAL_FAILURE(GetAllPatchIds(&patchIds));
- for (const auto patchId : getNonExistentIds(patchIds)) {
+ for (const auto patchId : GetNonExistentIds(patchIds)) {
AudioPatch patchWithNonExistendId = patch.get();
patchWithNonExistendId.id = patchId;
- Status status = module->setAudioPatch(patchWithNonExistendId, &patchWithNonExistendId);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ ScopedAStatus status =
+ module->setAudioPatch(patchWithNonExistendId, &patchWithNonExistendId);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " returned for patch ID " << patchId;
}
}
@@ -995,9 +1858,9 @@
TEST_P(AudioModulePatch, ResetInvalidPatchId) {
std::set<int32_t> patchIds;
ASSERT_NO_FATAL_FAILURE(GetAllPatchIds(&patchIds));
- for (const auto patchId : getNonExistentIds(patchIds)) {
- Status status = module->resetAudioPatch(patchId);
- EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ for (const auto patchId : GetNonExistentIds(patchIds)) {
+ ScopedAStatus status = module->resetAudioPatch(patchId);
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode())
<< status << " returned for patch ID " << patchId;
}
}
@@ -1019,9 +1882,26 @@
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioModulePatch);
+class TestExecutionTracer : public ::testing::EmptyTestEventListener {
+ public:
+ void OnTestStart(const ::testing::TestInfo& test_info) override {
+ TraceTestState("Started", test_info);
+ }
+
+ void OnTestEnd(const ::testing::TestInfo& test_info) override {
+ TraceTestState("Completed", test_info);
+ }
+
+ private:
+ static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
+ LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
+ }
+};
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- ProcessState::self()->setThreadPoolMaxThreadCount(1);
- ProcessState::self()->startThreadPool();
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 505c54c..4b76a0b 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -710,11 +710,9 @@
}
void releasePatchIfNeeded() {
- if (getDevice()) {
- if (areAudioPatchesSupported() && mHasPatch) {
- EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
- mHasPatch = false;
- }
+ if (getDevice() && areAudioPatchesSupported() && mHasPatch) {
+ EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
+ mHasPatch = false;
}
}
diff --git a/automotive/can/1.0/default/libnetdevice/can.cpp b/automotive/can/1.0/default/libnetdevice/can.cpp
index 083f4f0..2a0545a 100644
--- a/automotive/can/1.0/default/libnetdevice/can.cpp
+++ b/automotive/can/1.0/default/libnetdevice/can.cpp
@@ -80,7 +80,7 @@
{
auto linkinfo = req.addNested(IFLA_LINKINFO);
- req.add(IFLA_INFO_KIND, "can");
+ req.addBuffer(IFLA_INFO_KIND, "can");
{
auto infodata = req.addNested(IFLA_INFO_DATA);
/* For CAN FD, it would require to add IFLA_CAN_DATA_BITTIMING
diff --git a/automotive/can/1.0/default/libnetdevice/ifreqs.cpp b/automotive/can/1.0/default/libnetdevice/ifreqs.cpp
index 8df6434..8471173 100644
--- a/automotive/can/1.0/default/libnetdevice/ifreqs.cpp
+++ b/automotive/can/1.0/default/libnetdevice/ifreqs.cpp
@@ -47,7 +47,7 @@
return params;
}
-bool send(unsigned long request, struct ifreq& ifr) {
+int trySend(unsigned long request, struct ifreq& ifr) {
const auto sp = getSocketParams(socketDomain);
base::unique_fd sock(socket(sp.domain, sp.type, sp.protocol));
if (!sock.ok()) {
@@ -55,7 +55,12 @@
return false;
}
- if (ioctl(sock.get(), request, &ifr) < 0) {
+ if (ioctl(sock.get(), request, &ifr) < 0) return errno;
+ return 0;
+}
+
+bool send(unsigned long request, struct ifreq& ifr) {
+ if (trySend(request, ifr) != 0) {
PLOG(ERROR) << "ioctl(" << std::hex << request << std::dec << ") failed";
return false;
}
diff --git a/automotive/can/1.0/default/libnetdevice/ifreqs.h b/automotive/can/1.0/default/libnetdevice/ifreqs.h
index 74e5877..d8d6fe0 100644
--- a/automotive/can/1.0/default/libnetdevice/ifreqs.h
+++ b/automotive/can/1.0/default/libnetdevice/ifreqs.h
@@ -28,6 +28,15 @@
extern std::atomic_int socketDomain;
/**
+ * Tries to send ioctl interface request.
+ *
+ * \param request Request type (such as SIOCGIFFLAGS)
+ * \param ifr Request data (both input and output)
+ * \return error code of the call (0 for success)
+ */
+int trySend(unsigned long request, struct ifreq& ifr);
+
+/**
* Sends ioctl interface request.
*
* \param request Request type (such as SIOCGIFFLAGS)
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
index 70cb688..657f9b2 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -68,20 +68,32 @@
PRESENT_AND_UP,
/**
+ * Interface is up and with IPv4 address configured.
+ */
+ PRESENT_AND_IPV4,
+
+ /**
* Interface is down or not present (disconnected) at all.
*/
DOWN_OR_GONE,
};
+enum class Quantifier {
+ ALL_OF,
+ ANY_OF,
+};
+
/**
* Listens for interface changes until anticipated condition takes place.
*
* \param ifnames List of interfaces to watch for.
* \param cnd Awaited condition.
- * \param allOf true if all interfaces need to satisfy the condition, false if only one satistying
+ * \param quant Whether all interfaces need to satisfy the condition or just one satistying
* interface should stop the wait.
+ * \return name of one interface that satisfied the condition
*/
-void waitFor(std::set<std::string> ifnames, WaitCondition cnd, bool allOf = true);
+std::optional<std::string> waitFor(std::set<std::string> ifnames, WaitCondition cnd,
+ Quantifier quant = Quantifier::ALL_OF);
/**
* Brings network interface up.
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
index 4c5b309..aad07de 100644
--- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -60,7 +60,7 @@
{
auto linkinfo = req.addNested(IFLA_LINKINFO);
- req.add(IFLA_INFO_KIND, type);
+ req.addBuffer(IFLA_INFO_KIND, type);
}
nl::Socket sock(NETLINK_ROUTE);
@@ -100,23 +100,36 @@
return ifr.ifr_flags & IFF_UP;
}
+static bool hasIpv4(std::string ifname) {
+ auto ifr = ifreqs::fromName(ifname);
+ switch (const auto status = ifreqs::trySend(SIOCGIFADDR, ifr)) {
+ case 0:
+ return true;
+ case EADDRNOTAVAIL:
+ case ENODEV:
+ return false;
+ default:
+ PLOG(WARNING) << "Failed checking IPv4 address";
+ return false;
+ }
+}
+
struct WaitState {
bool present;
bool up;
+ bool hasIpv4Addr;
bool satisfied(WaitCondition cnd) const {
switch (cnd) {
case WaitCondition::PRESENT:
- if (present) return true;
- break;
+ return present;
case WaitCondition::PRESENT_AND_UP:
- if (present && up) return true;
- break;
+ return present && up;
+ case WaitCondition::PRESENT_AND_IPV4:
+ return present && up && hasIpv4Addr;
case WaitCondition::DOWN_OR_GONE:
- if (!present || !up) return true;
- break;
+ return !present || !up;
}
- return false;
}
};
@@ -126,11 +139,22 @@
return "become present";
case WaitCondition::PRESENT_AND_UP:
return "come up";
+ case WaitCondition::PRESENT_AND_IPV4:
+ return "get IPv4 address";
case WaitCondition::DOWN_OR_GONE:
return "go down";
}
}
+static std::string toString(Quantifier quant) {
+ switch (quant) {
+ case Quantifier::ALL_OF:
+ return "all of";
+ case Quantifier::ANY_OF:
+ return "any of";
+ }
+}
+
static std::string toString(const std::set<std::string>& ifnames) {
std::stringstream ss;
std::copy(ifnames.begin(), ifnames.end(), std::ostream_iterator<std::string>(ss, ","));
@@ -139,50 +163,73 @@
return str;
}
-void waitFor(std::set<std::string> ifnames, WaitCondition cnd, bool allOf) {
- nl::Socket sock(NETLINK_ROUTE, 0, RTMGRP_LINK);
+std::optional<std::string> waitFor(std::set<std::string> ifnames, WaitCondition cnd,
+ Quantifier quant) {
+ nl::Socket sock(NETLINK_ROUTE, 0, RTMGRP_LINK | RTMGRP_IPV4_IFADDR);
using StatesMap = std::map<std::string, WaitState>;
StatesMap states = {};
for (const auto ifname : ifnames) {
const auto present = exists(ifname);
const auto up = present && isUp(ifname).value_or(false);
- states[ifname] = {present, up};
+ const auto hasIpv4Addr = present && hasIpv4(ifname);
+ states[ifname] = {present, up, hasIpv4Addr};
}
const auto mapConditionChecker = [cnd](const StatesMap::iterator::value_type& it) {
return it.second.satisfied(cnd);
};
- const auto isFullySatisfied = [&states, allOf, mapConditionChecker]() {
- if (allOf) {
- return std::all_of(states.begin(), states.end(), mapConditionChecker);
- } else {
- return std::any_of(states.begin(), states.end(), mapConditionChecker);
+ const auto isFullySatisfied = [&states, quant,
+ mapConditionChecker]() -> std::optional<std::string> {
+ if (quant == Quantifier::ALL_OF) {
+ if (!std::all_of(states.begin(), states.end(), mapConditionChecker)) return {};
+ return states.begin()->first;
+ } else { // Quantifier::ANY_OF
+ const auto it = std::find_if(states.begin(), states.end(), mapConditionChecker);
+ if (it == states.end()) return {};
+ return it->first;
}
};
- if (isFullySatisfied()) return;
+ if (const auto iface = isFullySatisfied()) return iface;
- LOG(DEBUG) << "Waiting for " << (allOf ? "" : "any of ") << toString(ifnames) << " to "
+ LOG(DEBUG) << "Waiting for " << toString(quant) << " " << toString(ifnames) << " to "
<< toString(cnd);
for (const auto rawMsg : sock) {
- const auto msg = nl::Message<ifinfomsg>::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK});
- if (!msg.has_value()) continue;
+ if (const auto msg = nl::Message<ifinfomsg>::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK});
+ msg.has_value()) {
+ // Interface added / removed
+ const auto ifname = msg->attributes.get<std::string>(IFLA_IFNAME);
+ if (ifnames.count(ifname) == 0) continue;
- const auto ifname = msg->attributes.get<std::string>(IFLA_IFNAME);
- if (ifnames.count(ifname) == 0) continue;
+ auto& state = states[ifname];
+ state.present = (msg->header.nlmsg_type != RTM_DELLINK);
+ state.up = state.present && (msg->data.ifi_flags & IFF_UP) != 0;
+ if (!state.present) state.hasIpv4Addr = false;
- const bool present = (msg->header.nlmsg_type != RTM_DELLINK);
- const bool up = present && (msg->data.ifi_flags & IFF_UP) != 0;
- states[ifname] = {present, up};
+ } else if (const auto msg =
+ nl::Message<ifaddrmsg>::parse(rawMsg, {RTM_NEWADDR, RTM_DELADDR});
+ msg.has_value()) {
+ // Address added / removed
+ const auto ifname = msg->attributes.get<std::string>(IFLA_IFNAME);
+ if (ifnames.count(ifname) == 0) continue;
- if (isFullySatisfied()) {
- LOG(DEBUG) << "Finished waiting for " << (allOf ? "" : "some of ") << toString(ifnames)
+ if (msg->header.nlmsg_type == RTM_NEWADDR) {
+ states[ifname].hasIpv4Addr = true;
+ } else {
+ // instead of tracking which one got deleted, let's just ask
+ states[ifname].hasIpv4Addr = hasIpv4(ifname);
+ }
+ }
+
+ if (const auto iface = isFullySatisfied()) {
+ LOG(DEBUG) << "Finished waiting for " << toString(quant) << " " << toString(ifnames)
<< " to " << toString(cnd);
- return;
+ return iface;
}
}
LOG(FATAL) << "Can't read Netlink socket";
+ return {};
}
} // namespace android::netdevice
diff --git a/automotive/can/1.0/default/libnetdevice/vlan.cpp b/automotive/can/1.0/default/libnetdevice/vlan.cpp
index ee02f7b..35b21b8 100644
--- a/automotive/can/1.0/default/libnetdevice/vlan.cpp
+++ b/automotive/can/1.0/default/libnetdevice/vlan.cpp
@@ -40,7 +40,7 @@
{
auto linkinfo = req.addNested(IFLA_LINKINFO);
- req.add(IFLA_INFO_KIND, "vlan");
+ req.addBuffer(IFLA_INFO_KIND, "vlan");
{
auto linkinfo = req.addNested(IFLA_INFO_DATA);
diff --git a/automotive/can/1.0/default/libnl++/Android.bp b/automotive/can/1.0/default/libnl++/Android.bp
index 2ebd1b4..01c1e55 100644
--- a/automotive/can/1.0/default/libnl++/Android.bp
+++ b/automotive/can/1.0/default/libnl++/Android.bp
@@ -37,8 +37,10 @@
"protocols/generic/Unknown.cpp",
"protocols/generic/families/Mac80211hwsim.cpp",
"protocols/generic/families/Nl80211.cpp",
+ "protocols/route/Addr.cpp",
"protocols/route/Link.cpp",
"protocols/route/Route.cpp",
+ "protocols/route/attributes.cpp",
"protocols/route/structs.cpp",
"protocols/MessageDefinition.cpp",
"protocols/NetlinkProtocol.cpp",
diff --git a/automotive/can/1.0/default/libnl++/MessageFactory.cpp b/automotive/can/1.0/default/libnl++/MessageFactory.cpp
index 6f35897..eff068e 100644
--- a/automotive/can/1.0/default/libnl++/MessageFactory.cpp
+++ b/automotive/can/1.0/default/libnl++/MessageFactory.cpp
@@ -27,8 +27,8 @@
nlattr* MessageFactoryBase::add(nlmsghdr* msg, size_t maxLen, nlattrtype_t type, const void* data,
size_t dataLen) {
- const auto totalAttrLen = impl::space<nlattr>(dataLen);
- const auto newLen = impl::align(msg->nlmsg_len) + totalAttrLen;
+ const auto totalAttrLen = impl::length<nlattr>(dataLen);
+ const auto newLen = impl::align(msg->nlmsg_len) + impl::align(totalAttrLen);
if (newLen > maxLen) {
LOG(ERROR) << "Can't add attribute of size " << dataLen //
<< " - exceeded maxLen: " << newLen << " > " << maxLen;
diff --git a/automotive/can/1.0/default/libnl++/Socket.cpp b/automotive/can/1.0/default/libnl++/Socket.cpp
index cc1d839..221063d 100644
--- a/automotive/can/1.0/default/libnl++/Socket.cpp
+++ b/automotive/can/1.0/default/libnl++/Socket.cpp
@@ -47,6 +47,17 @@
}
}
+void Socket::clearPollErr() {
+ sockaddr_nl sa = {};
+ socklen_t saLen = sizeof(sa);
+ const auto bytesReceived = recvfrom(mFd.get(), mReceiveBuffer.data(), mReceiveBuffer.size(), 0,
+ reinterpret_cast<sockaddr*>(&sa), &saLen);
+ if (errno != EINVAL) {
+ PLOG(WARNING) << "clearPollError() caught unexpected error: ";
+ }
+ CHECK_LE(bytesReceived, 0) << "clearPollError() didn't find an error!";
+}
+
bool Socket::send(const Buffer<nlmsghdr>& msg, const sockaddr_nl& sa) {
if constexpr (kSuperVerbose) {
LOG(VERBOSE) << (mFailed ? "(not) " : "") << "sending to " << sa.nl_pid << ": "
@@ -110,6 +121,13 @@
if constexpr (kSuperVerbose) {
LOG(VERBOSE) << "received from " << sa.nl_pid << ": " << toString(msg, mProtocol);
}
+ long headerByteTotal = 0;
+ for (const auto hdr : msg) {
+ headerByteTotal += hdr->nlmsg_len;
+ }
+ if (bytesReceived != headerByteTotal) {
+ LOG(ERROR) << "received " << bytesReceived << " bytes, header claims " << headerByteTotal;
+ }
return {msg, sa};
}
@@ -159,6 +177,7 @@
}
pollfd Socket::preparePoll(short events) {
+ CHECK(mFd.get() > 0) << "Netlink socket fd is invalid!";
return {mFd.get(), events, 0};
}
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h b/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h
index c3d72c5..a5a425e 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/MessageFactory.h
@@ -106,18 +106,25 @@
*/
template <class A>
void add(nlattrtype_t type, const A& attr) {
- add(type, &attr, sizeof(attr));
+ addInternal(type, &attr, sizeof(attr));
}
+ // It will always send the last null character, otherwise use addBuffer
+ // variant instead
template <>
void add(nlattrtype_t type, const std::string& s) {
- add(type, s.c_str(), s.size() + 1);
+ addInternal(type, s.c_str(), s.size() + 1);
+ }
+
+ void addBuffer(nlattrtype_t type, const std::string_view& s) {
+ addInternal(type, s.data(), s.size());
}
/** Guard class to frame nested attributes. \see addNested(nlattrtype_t). */
class [[nodiscard]] NestedGuard {
public:
- NestedGuard(MessageFactory & req, nlattrtype_t type) : mReq(req), mAttr(req.add(type)) {}
+ NestedGuard(MessageFactory& req, nlattrtype_t type)
+ : mReq(req), mAttr(req.addInternal(type)) {}
~NestedGuard() { closeNested(&mReq.mMessage.header, mAttr); }
private:
@@ -138,7 +145,7 @@
* MessageFactory<ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST);
* {
* auto linkinfo = req.addNested(IFLA_LINKINFO);
- * req.add(IFLA_INFO_KIND, "can");
+ * req.addBuffer(IFLA_INFO_KIND, "can");
* {
* auto infodata = req.addNested(IFLA_INFO_DATA);
* req.add(IFLA_CAN_BITTIMING, bitTimingStruct);
@@ -154,7 +161,7 @@
Message mMessage = {};
bool mIsGood = true;
- nlattr* add(nlattrtype_t type, const void* data = nullptr, size_t len = 0) {
+ nlattr* addInternal(nlattrtype_t type, const void* data = nullptr, size_t len = 0) {
if (!mIsGood) return nullptr;
auto attr = MessageFactoryBase::add(&mMessage.header, sizeof(mMessage), type, data, len);
if (attr == nullptr) mIsGood = false;
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/Socket.h b/automotive/can/1.0/default/libnl++/include/libnl++/Socket.h
index 7ec0f7b..996a350 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/Socket.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/Socket.h
@@ -55,6 +55,12 @@
Socket(int protocol, unsigned pid = 0, uint32_t groups = 0);
/**
+ * Attempt to clear POLLERR by recv-ing.
+ * TODO(224850481): determine if this is necessary, or if the socket is locked up anyway.
+ */
+ void clearPollErr();
+
+ /**
* Send Netlink message with incremented sequence number to the Kernel.
*
* \param msg Message to send. Its sequence number will be updated.
diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/bits.h b/automotive/can/1.0/default/libnl++/include/libnl++/bits.h
index 4c8f1aa..b6573b3 100644
--- a/automotive/can/1.0/default/libnl++/include/libnl++/bits.h
+++ b/automotive/can/1.0/default/libnl++/include/libnl++/bits.h
@@ -36,11 +36,19 @@
}
/**
+ * Equivalent to NLMSG_LENGTH(len).
+ */
+template <typename H>
+constexpr size_t length(size_t len) {
+ return align(sizeof(H)) + len;
+}
+
+/**
* Equivalent to NLMSG_SPACE(len).
*/
template <typename H>
constexpr size_t space(size_t len) {
- return align(align(sizeof(H)) + len);
+ return align(length<H>(len));
}
/**
diff --git a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
index aaf24a5..158d2a1 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
@@ -34,7 +34,7 @@
MessageDescriptor::MessageDescriptor(const std::string& name,
const MessageDetailsMap&& messageDetails,
- const AttributeMap&& attrTypes, size_t contentsSize)
+ const AttributeMap& attrTypes, size_t contentsSize)
: mName(name),
mContentsSize(contentsSize),
mMessageDetails(messageDetails),
diff --git a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
index 8bed5e7..33ded9a 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
@@ -163,7 +163,7 @@
protected:
MessageDescriptor(const std::string& name, const MessageDetailsMap&& messageDetails,
- const AttributeMap&& attrTypes, size_t contentsSize);
+ const AttributeMap& attrTypes, size_t contentsSize);
private:
const std::string mName;
@@ -183,7 +183,7 @@
MessageDefinition( //
const std::string& name,
const std::initializer_list<MessageDescriptor::MessageDetailsMap::value_type> msgDet,
- const std::initializer_list<AttributeMap::value_type> attrTypes = {})
+ const AttributeMap& attrTypes = {})
: MessageDescriptor(name, msgDet, attrTypes, sizeof(T)) {}
void dataToStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr) const override {
diff --git a/automotive/can/1.0/default/libnl++/protocols/all.cpp b/automotive/can/1.0/default/libnl++/protocols/all.cpp
index a398dc8..72c60f2 100644
--- a/automotive/can/1.0/default/libnl++/protocols/all.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/all.cpp
@@ -33,12 +33,12 @@
return map;
}
-static auto all = toMap({
- std::make_unique<generic::Generic>(),
- std::make_unique<route::Route>(),
-});
-
std::optional<std::reference_wrapper<NetlinkProtocol>> get(int protocol) {
+ static auto all = toMap({
+ std::make_unique<generic::Generic>(),
+ std::make_unique<route::Route>(),
+ });
+
if (all.count(protocol) == 0) return std::nullopt;
return *all.find(protocol)->second.get();
}
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/FamilyTracker.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/FamilyTracker.cpp
index 900560e..3ad101e 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/FamilyTracker.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/FamilyTracker.cpp
@@ -30,6 +30,7 @@
const auto familyName = msg.attributes.get<std::string>(CTRL_ATTR_FAMILY_NAME);
const auto familyId = msg.attributes.get<uint16_t>(CTRL_ATTR_FAMILY_ID);
+ // TODO(224845900): NETLINK_GENERIC == 16, and (erroneously?) sets off this warning
if (familyId < GENL_START_ALLOC) {
LOG(WARNING) << "Invalid family ID: " << familyId;
return true;
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/Addr.cpp b/automotive/can/1.0/default/libnl++/protocols/route/Addr.cpp
new file mode 100644
index 0000000..024d389
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Addr.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "Addr.h"
+
+#include "../structs.h"
+#include "attributes.h"
+#include "structs.h"
+
+namespace android::nl::protocols::route {
+
+using DataType = AttributeDefinition::DataType;
+
+// clang-format off
+Addr::Addr() : MessageDefinition<ifaddrmsg>("addr", {
+ {RTM_NEWADDR, {"NEWADDR", MessageGenre::New}},
+ {RTM_DELADDR, {"DELADDR", MessageGenre::Delete}},
+ {RTM_GETADDR, {"GETADDR", MessageGenre::Get}},
+}, gAttributes) {}
+
+static const FlagsMap ifaFlagsMap {
+ {IFA_F_SECONDARY, "SECONDARY"},
+ {IFA_F_NODAD, "NODAD"},
+ {IFA_F_OPTIMISTIC, "OPTIMISTIC"},
+ {IFA_F_DADFAILED, "DADFAILED"},
+ {IFA_F_HOMEADDRESS, "HOMEADDRESS"},
+ {IFA_F_DEPRECATED, "DEPRECATED"},
+ {IFA_F_TENTATIVE, "TENTATIVE"},
+ {IFA_F_PERMANENT, "PERMANENT"},
+ {IFA_F_MANAGETEMPADDR, "MANAGETEMPADDR"},
+ {IFA_F_NOPREFIXROUTE, "NOPREFIXROUTE"},
+ {IFA_F_MCAUTOJOIN, "MCAUTOJOIN"},
+ {IFA_F_STABLE_PRIVACY, "STABLE_PRIVACY"},
+};
+// clang-format on
+
+void Addr::toStream(std::stringstream& ss, const ifaddrmsg& data) const {
+ ss << "ifaddrmsg{"
+ << "family=" << familyToString(data.ifa_family)
+ << ", prefixlen=" << unsigned(data.ifa_prefixlen) << ", flags=";
+ flagsToStream(ss, ifaFlagsMap, data.ifa_flags);
+ ss << ", scope=" << unsigned(data.ifa_scope) << ", index=" << data.ifa_index << "}";
+}
+
+} // namespace android::nl::protocols::route
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/Addr.h b/automotive/can/1.0/default/libnl++/protocols/route/Addr.h
new file mode 100644
index 0000000..b6b8bdc
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Addr.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include "../MessageDefinition.h"
+
+#include <linux/rtnetlink.h>
+
+namespace android::nl::protocols::route {
+
+class Addr : public MessageDefinition<ifaddrmsg> {
+ public:
+ Addr();
+ void toStream(std::stringstream& ss, const ifaddrmsg& data) const override;
+};
+
+} // namespace android::nl::protocols::route
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp b/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
index 9cc05da..3dd0066 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
@@ -16,10 +16,7 @@
#include "Link.h"
-#include "../structs.h"
-#include "structs.h"
-
-#include <net/if.h>
+#include "attributes.h"
namespace android::nl::protocols::route {
@@ -30,83 +27,8 @@
{RTM_NEWLINK, {"NEWLINK", MessageGenre::New}},
{RTM_DELLINK, {"DELLINK", MessageGenre::Delete}},
{RTM_GETLINK, {"GETLINK", MessageGenre::Get}},
-}, {
- {IFLA_ADDRESS, {"ADDRESS"}},
- {IFLA_BROADCAST, {"BROADCAST"}},
- {IFLA_IFNAME, {"IFNAME", DataType::String}},
- {IFLA_MTU, {"MTU", DataType::Uint}},
- {IFLA_LINK, {"LINK", DataType::Uint}},
- {IFLA_QDISC, {"QDISC", DataType::String}},
- {IFLA_STATS, {"STATS", DataType::Struct, statsToStream<rtnl_link_stats>}},
- {IFLA_COST, {"COST"}},
- {IFLA_PRIORITY, {"PRIORITY"}},
- {IFLA_MASTER, {"MASTER", DataType::Uint}},
- {IFLA_WIRELESS, {"WIRELESS"}},
- {IFLA_PROTINFO, {"PROTINFO"}},
- {IFLA_TXQLEN, {"TXQLEN", DataType::Uint}},
- {IFLA_MAP, {"MAP", DataType::Struct, mapToStream}},
- {IFLA_WEIGHT, {"WEIGHT", DataType::Uint}},
- {IFLA_OPERSTATE, {"OPERSTATE", DataType::Uint}},
- {IFLA_LINKMODE, {"LINKMODE", DataType::Uint}},
- {IFLA_LINKINFO, {"LINKINFO", DataType::Nested, AttributeMap{
- {IFLA_INFO_KIND, {"INFO_KIND", DataType::String}},
- {IFLA_INFO_DATA, {"INFO_DATA", DataType::Nested}},
- {IFLA_INFO_XSTATS, {"INFO_XSTATS"}},
- {IFLA_INFO_SLAVE_KIND, {"INFO_SLAVE_KIND", DataType::String}},
- {IFLA_INFO_SLAVE_DATA, {"INFO_SLAVE_DATA"}},
- }}},
- {IFLA_NET_NS_PID, {"NET_NS_PID", DataType::Uint}},
- {IFLA_IFALIAS, {"IFALIAS", DataType::String}},
- {IFLA_NUM_VF, {"NUM_VF", DataType::Uint}},
- {IFLA_VFINFO_LIST, {"VFINFO_LIST"}},
- {IFLA_STATS64, {"STATS64", DataType::Struct, statsToStream<rtnl_link_stats64>}},
- {IFLA_VF_PORTS, {"VF_PORTS"}},
- {IFLA_PORT_SELF, {"PORT_SELF"}},
- {IFLA_AF_SPEC, {"AF_SPEC", DataType::Nested, AttributeMap{
- {AF_INET, {"AF_INET", DataType::Nested, AttributeMap{
- {IFLA_INET_CONF, {"INET_CONF", DataType::Struct, arrayToStream<int32_t>}},
- }}},
- {AF_INET6, {"AF_INET6", DataType::Nested, AttributeMap{
- {IFLA_INET6_FLAGS, {"INET6_FLAGS", DataType::Uint}},
- {IFLA_INET6_CONF, {"INET6_CONF", DataType::Struct, arrayToStream<int32_t>}},
- {IFLA_INET6_STATS, {"INET6_STATS", DataType::Struct, arrayToStream<uint64_t>}},
- {IFLA_INET6_MCAST, {"INET6_MCAST"}},
- {IFLA_INET6_CACHEINFO, {"INET6_CACHEINFO", DataType::Struct, ifla_cacheinfoToStream}},
- {IFLA_INET6_ICMP6STATS, {"INET6_ICMP6STATS", DataType::Struct, arrayToStream<uint64_t>}},
- {IFLA_INET6_TOKEN, {"INET6_TOKEN"}},
- {IFLA_INET6_ADDR_GEN_MODE, {"INET6_ADDR_GEN_MODE", DataType::Uint}},
- }}},
- }}},
- {IFLA_GROUP, {"GROUP", DataType::Uint}},
- {IFLA_NET_NS_FD, {"NET_NS_FD", DataType::Uint}},
- {IFLA_EXT_MASK, {"EXT_MASK", DataType::Uint}},
- {IFLA_PROMISCUITY, {"PROMISCUITY", DataType::Uint}},
- {IFLA_NUM_TX_QUEUES, {"NUM_TX_QUEUES", DataType::Uint}},
- {IFLA_NUM_RX_QUEUES, {"NUM_RX_QUEUES", DataType::Uint}},
- {IFLA_CARRIER, {"CARRIER", DataType::Uint}},
- {IFLA_PHYS_PORT_ID, {"PHYS_PORT_ID"}},
- {IFLA_CARRIER_CHANGES, {"CARRIER_CHANGES", DataType::Uint}},
- {IFLA_PHYS_SWITCH_ID, {"PHYS_SWITCH_ID"}},
- {IFLA_LINK_NETNSID, {"LINK_NETNSID"}}, // NLA_S32
- {IFLA_PHYS_PORT_NAME, {"PHYS_PORT_NAME", DataType::String}},
- {IFLA_PROTO_DOWN, {"PROTO_DOWN", DataType::Uint}},
- {IFLA_GSO_MAX_SEGS, {"GSO_MAX_SEGS", DataType::Uint}},
- {IFLA_GSO_MAX_SIZE, {"GSO_MAX_SIZE", DataType::Uint}},
- {IFLA_PAD, {"PAD"}},
- {IFLA_XDP, {"XDP"}},
- {IFLA_EVENT, {"EVENT", DataType::Uint}},
- {IFLA_NEW_NETNSID, {"NEW_NETNSID"}}, // NLA_S32
- {IFLA_TARGET_NETNSID, {"TARGET_NETNSID"}}, // NLA_S32
- {IFLA_CARRIER_UP_COUNT, {"CARRIER_UP_COUNT", DataType::Uint}},
- {IFLA_CARRIER_DOWN_COUNT, {"CARRIER_DOWN_COUNT", DataType::Uint}},
- {IFLA_NEW_IFINDEX, {"NEW_IFINDEX"}}, // NLA_S32
- {IFLA_MIN_MTU, {"MIN_MTU", DataType::Uint}},
- {IFLA_MAX_MTU, {"MAX_MTU", DataType::Uint}},
- {IFLA_PROP_LIST, {"PROP_LIST"}},
- {IFLA_ALT_IFNAME, {"ALT_IFNAME", DataType::String}},
- {IFLA_PERM_ADDRESS, {"PERM_ADDRESS"}},
-}) {}
-// clang-format off
+}, gAttributes) {}
+// clang-format on
void Link::toStream(std::stringstream& ss, const ifinfomsg& data) const {
ss << "ifinfomsg{"
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/Route.cpp b/automotive/can/1.0/default/libnl++/protocols/route/Route.cpp
index c134911..51e5b11 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/Route.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Route.cpp
@@ -16,10 +16,16 @@
#include "Route.h"
+#include "Addr.h"
#include "Link.h"
namespace android::nl::protocols::route {
-Route::Route() : NetlinkProtocol(NETLINK_ROUTE, "ROUTE", {std::make_shared<Link>()}) {}
+// clang-format off
+Route::Route() : NetlinkProtocol(NETLINK_ROUTE, "ROUTE", {
+ std::make_shared<Addr>(),
+ std::make_shared<Link>(),
+}) {}
+// clang-format on
} // namespace android::nl::protocols::route
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/attributes.cpp b/automotive/can/1.0/default/libnl++/protocols/route/attributes.cpp
new file mode 100644
index 0000000..69d9b81
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/route/attributes.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "attributes.h"
+
+#include "../structs.h"
+#include "structs.h"
+
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+namespace android::nl::protocols::route {
+
+using DataType = AttributeDefinition::DataType;
+using Flags = AttributeDefinition::Flags;
+
+// clang-format off
+AttributeMap gAttributes = {
+ {IFLA_ADDRESS, {"ADDRESS"}},
+ {IFLA_BROADCAST, {"BROADCAST"}},
+ {IFLA_IFNAME, {"IFNAME", DataType::StringNul}},
+ {IFLA_MTU, {"MTU", DataType::Uint}},
+ {IFLA_LINK, {"LINK", DataType::Uint}},
+ {IFLA_QDISC, {"QDISC", DataType::Raw, AttributeMap{}, Flags::Verbose}}, // should be DataType::String, but looks like binary blob
+ {IFLA_STATS, {"STATS", DataType::Struct, statsToStream<rtnl_link_stats>}},
+ {IFLA_COST, {"COST", DataType::Uint}},
+ {IFLA_PRIORITY, {"PRIORITY"}},
+ {IFLA_MASTER, {"MASTER", DataType::Uint}},
+ {IFLA_WIRELESS, {"WIRELESS"}},
+ {IFLA_PROTINFO, {"PROTINFO"}},
+ {IFLA_TXQLEN, {"TXQLEN", DataType::Uint}},
+ {IFLA_MAP, {"MAP", DataType::Struct, mapToStream}},
+ {IFLA_WEIGHT, {"WEIGHT", DataType::Uint}},
+ {IFLA_OPERSTATE, {"OPERSTATE", DataType::Uint}},
+ {IFLA_LINKMODE, {"LINKMODE", DataType::Uint}},
+ {IFLA_LINKINFO, {"LINKINFO", DataType::Nested, AttributeMap{
+ {IFLA_INFO_KIND, {"INFO_KIND", DataType::String}},
+ {IFLA_INFO_DATA, {"INFO_DATA", DataType::Nested}},
+ {IFLA_INFO_XSTATS, {"INFO_XSTATS"}},
+ {IFLA_INFO_SLAVE_KIND, {"INFO_SLAVE_KIND", DataType::String}},
+ {IFLA_INFO_SLAVE_DATA, {"INFO_SLAVE_DATA"}},
+ }}},
+ {IFLA_NET_NS_PID, {"NET_NS_PID", DataType::Uint}},
+ {IFLA_IFALIAS, {"IFALIAS", DataType::String}},
+ {IFLA_NUM_VF, {"NUM_VF", DataType::Uint}},
+ {IFLA_VFINFO_LIST, {"VFINFO_LIST"}},
+ {IFLA_STATS64, {"STATS64", DataType::Struct, statsToStream<rtnl_link_stats64>}},
+ {IFLA_VF_PORTS, {"VF_PORTS"}},
+ {IFLA_PORT_SELF, {"PORT_SELF"}},
+ {IFLA_AF_SPEC, {"AF_SPEC", DataType::Nested, AttributeMap{
+ {AF_INET, {"AF_INET", DataType::Nested, AttributeMap{
+ {IFLA_INET_CONF, {"INET_CONF", DataType::Struct, arrayToStream<int32_t>}},
+ }}},
+ {AF_INET6, {"AF_INET6", DataType::Nested, AttributeMap{
+ {IFLA_INET6_FLAGS, {"INET6_FLAGS", DataType::Uint}},
+ {IFLA_INET6_CONF, {"INET6_CONF", DataType::Struct, arrayToStream<int32_t>}},
+ {IFLA_INET6_STATS, {"INET6_STATS", DataType::Struct, arrayToStream<uint64_t>}},
+ {IFLA_INET6_MCAST, {"INET6_MCAST"}},
+ {IFLA_INET6_CACHEINFO, {"INET6_CACHEINFO", DataType::Struct, ifla_cacheinfoToStream}},
+ {IFLA_INET6_ICMP6STATS, {"INET6_ICMP6STATS", DataType::Struct, arrayToStream<uint64_t>}},
+ {IFLA_INET6_TOKEN, {"INET6_TOKEN"}},
+ {IFLA_INET6_ADDR_GEN_MODE, {"INET6_ADDR_GEN_MODE", DataType::Uint}},
+ }}},
+ }}},
+ {IFLA_GROUP, {"GROUP", DataType::Uint}},
+ {IFLA_NET_NS_FD, {"NET_NS_FD", DataType::Uint}},
+ {IFLA_EXT_MASK, {"EXT_MASK", DataType::Uint}},
+ {IFLA_PROMISCUITY, {"PROMISCUITY", DataType::Uint}},
+ {IFLA_NUM_TX_QUEUES, {"NUM_TX_QUEUES", DataType::Uint}},
+ {IFLA_NUM_RX_QUEUES, {"NUM_RX_QUEUES", DataType::Uint}},
+ {IFLA_CARRIER, {"CARRIER", DataType::Uint}},
+ {IFLA_PHYS_PORT_ID, {"PHYS_PORT_ID"}},
+ {IFLA_CARRIER_CHANGES, {"CARRIER_CHANGES", DataType::Uint}},
+ {IFLA_PHYS_SWITCH_ID, {"PHYS_SWITCH_ID"}},
+ {IFLA_LINK_NETNSID, {"LINK_NETNSID"}}, // NLA_S32
+ {IFLA_PHYS_PORT_NAME, {"PHYS_PORT_NAME", DataType::String}},
+ {IFLA_PROTO_DOWN, {"PROTO_DOWN", DataType::Uint}},
+ {IFLA_GSO_MAX_SEGS, {"GSO_MAX_SEGS", DataType::Uint}},
+ {IFLA_GSO_MAX_SIZE, {"GSO_MAX_SIZE", DataType::Uint}},
+ {IFLA_PAD, {"PAD"}},
+ {IFLA_XDP, {"XDP"}},
+ {IFLA_EVENT, {"EVENT", DataType::Uint}},
+ {IFLA_NEW_NETNSID, {"NEW_NETNSID"}}, // NLA_S32
+ {IFLA_TARGET_NETNSID, {"TARGET_NETNSID"}}, // NLA_S32
+ {IFLA_CARRIER_UP_COUNT, {"CARRIER_UP_COUNT", DataType::Uint}},
+ {IFLA_CARRIER_DOWN_COUNT, {"CARRIER_DOWN_COUNT", DataType::Uint}},
+ {IFLA_NEW_IFINDEX, {"NEW_IFINDEX"}}, // NLA_S32
+ {IFLA_MIN_MTU, {"MIN_MTU", DataType::Uint}},
+ {IFLA_MAX_MTU, {"MAX_MTU", DataType::Uint}},
+ {IFLA_PROP_LIST, {"PROP_LIST"}},
+ {IFLA_ALT_IFNAME, {"ALT_IFNAME", DataType::String}},
+ {IFLA_PERM_ADDRESS, {"PERM_ADDRESS"}},
+};
+// clang-format on
+
+} // namespace android::nl::protocols::route
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/attributes.h b/automotive/can/1.0/default/libnl++/protocols/route/attributes.h
new file mode 100644
index 0000000..ace9234
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/route/attributes.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include "../MessageDefinition.h"
+
+namespace android::nl::protocols::route {
+
+extern AttributeMap gAttributes;
+
+} // namespace android::nl::protocols::route
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp b/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp
index b62cec3..269771c 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/route/structs.cpp
@@ -46,4 +46,58 @@
<< data.retrans_time << '}';
}
+// clang-format off
+std::string familyToString(sa_family_t family) {
+ switch (family) {
+ case AF_UNSPEC: return "UNSPEC";
+ case AF_UNIX: return "UNIX";
+ case AF_INET: return "INET";
+ case AF_AX25: return "AX25";
+ case AF_IPX: return "IPX";
+ case AF_APPLETALK: return "APPLETALK";
+ case AF_NETROM: return "NETROM";
+ case AF_BRIDGE: return "BRIDGE";
+ case AF_ATMPVC: return "ATMPVC";
+ case AF_X25: return "X25";
+ case AF_INET6: return "INET6";
+ case AF_ROSE: return "ROSE";
+ case AF_DECnet: return "DECnet";
+ case AF_NETBEUI: return "NETBEUI";
+ case AF_SECURITY: return "SECURITY";
+ case AF_KEY: return "KEY";
+ case AF_NETLINK: return "NETLINK";
+ case AF_PACKET: return "PACKET";
+ case AF_ASH: return "ASH";
+ case AF_ECONET: return "ECONET";
+ case AF_ATMSVC: return "ATMSVC";
+ case AF_RDS: return "RDS";
+ case AF_SNA: return "SNA";
+ case AF_IRDA: return "IRDA";
+ case AF_PPPOX: return "PPPOX";
+ case AF_WANPIPE: return "WANPIPE";
+ case AF_LLC: return "LLC";
+ case 27 /*AF_IB*/: return "IB";
+ case 28 /*AF_MPLS*/: return "MPLS";
+ case AF_CAN: return "CAN";
+ case AF_TIPC: return "TIPC";
+ case AF_BLUETOOTH: return "BLUETOOTH";
+ case AF_IUCV: return "IUCV";
+ case AF_RXRPC: return "RXRPC";
+ case AF_ISDN: return "ISDN";
+ case AF_PHONET: return "PHONET";
+ case AF_IEEE802154: return "IEEE802154";
+ case AF_CAIF: return "CAIF";
+ case AF_ALG: return "ALG";
+ case AF_NFC: return "NFC";
+ case AF_VSOCK: return "VSOCK";
+ case AF_KCM: return "KCM";
+ case AF_QIPCRTR: return "QIPCRTR";
+ case 43 /*AF_SMC*/: return "SMC";
+ case 44 /*AF_XDP*/: return "XDP";
+ default:
+ return std::to_string(family);
+ }
+}
+// clang-format on
+
} // namespace android::nl::protocols::route
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/structs.h b/automotive/can/1.0/default/libnl++/protocols/route/structs.h
index fea2ce1..c969a6c 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/structs.h
+++ b/automotive/can/1.0/default/libnl++/protocols/route/structs.h
@@ -19,6 +19,7 @@
#include <libnl++/Buffer.h>
#include <linux/rtnetlink.h>
+#include <sys/socket.h>
#include <sstream>
@@ -30,6 +31,8 @@
// ifla_cacheinfo
void ifla_cacheinfoToStream(std::stringstream& ss, const Buffer<nlattr> attr);
+std::string familyToString(sa_family_t family);
+
// rtnl_link_stats or rtnl_link_stats64
template <typename T>
void statsToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
diff --git a/automotive/can/1.0/default/libnl++/protocols/structs.cpp b/automotive/can/1.0/default/libnl++/protocols/structs.cpp
index 8ff71f0..3f896bf 100644
--- a/automotive/can/1.0/default/libnl++/protocols/structs.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/structs.cpp
@@ -22,24 +22,27 @@
AttributeDefinition::ToStream flagsToStream(FlagsMap flags) {
return [flags](std::stringstream& ss, const Buffer<nlattr> attr) {
- auto val = attr.data<uint64_t>().copyFirst();
+ auto value = attr.data<uint64_t>().copyFirst();
+ flagsToStream(ss, flags, value);
+ };
+}
- bool first = true;
- for (const auto& [flag, name] : flags) {
- if ((val & flag) != flag) continue;
- val &= ~flag;
-
- if (!first) ss << '|';
- first = false;
-
- ss << name;
- }
-
- if (val == 0) return;
+void flagsToStream(std::stringstream& ss, const FlagsMap& flags, uint64_t val) {
+ bool first = true;
+ for (const auto& [flag, name] : flags) {
+ if ((val & flag) != flag) continue;
+ val &= ~flag;
if (!first) ss << '|';
- ss << std::hex << val << std::dec;
- };
+ first = false;
+
+ ss << name;
+ }
+
+ if (val == 0) return;
+
+ if (!first) ss << '|';
+ ss << std::hex << val << std::dec;
}
void hwaddrToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
diff --git a/automotive/can/1.0/default/libnl++/protocols/structs.h b/automotive/can/1.0/default/libnl++/protocols/structs.h
index f3a8c44..9cf6f1a 100644
--- a/automotive/can/1.0/default/libnl++/protocols/structs.h
+++ b/automotive/can/1.0/default/libnl++/protocols/structs.h
@@ -34,6 +34,7 @@
typedef std::map<uint64_t, std::string> FlagsMap;
AttributeDefinition::ToStream flagsToStream(FlagsMap flags);
+void flagsToStream(std::stringstream& ss, const FlagsMap& flags, uint64_t value);
void hwaddrToStream(std::stringstream& ss, const Buffer<nlattr> attr);
diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp
index 4c08ef3..4172e63 100644
--- a/automotive/evs/1.1/default/Android.bp
+++ b/automotive/evs/1.1/default/Android.bp
@@ -13,6 +13,7 @@
proprietary: true,
relative_install_path: "hw",
srcs: [
+ ":libgui_frame_event_aidl",
"*.cpp",
],
init_rc: ["android.hardware.automotive.evs@1.1-service.rc"],
diff --git a/automotive/evs/1.1/default/GlWrapper.cpp b/automotive/evs/1.1/default/GlWrapper.cpp
index 357b67c..9ad5729 100644
--- a/automotive/evs/1.1/default/GlWrapper.cpp
+++ b/automotive/evs/1.1/default/GlWrapper.cpp
@@ -37,28 +37,23 @@
constexpr float kDisplayAreaRatio = 0.8f;
constexpr const char vertexShaderSource[] =
- ""
- "#version 300 es \n"
- "layout(location = 0) in vec4 pos; \n"
- "layout(location = 1) in vec2 tex; \n"
- "out vec2 uv; \n"
- "void main() \n"
- "{ \n"
- " gl_Position = pos; \n"
- " uv = tex; \n"
- "} \n";
+ "attribute vec4 pos; \n"
+ "attribute vec2 tex; \n"
+ "varying vec2 uv; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = pos; \n"
+ " uv = tex; \n"
+ "} \n";
constexpr const char pixelShaderSource[] =
- "#version 300 es \n"
- "precision mediump float; \n"
- "uniform sampler2D tex; \n"
- "in vec2 uv; \n"
- "out vec4 color; \n"
- "void main() \n"
- "{ \n"
- " vec4 texel = texture(tex, uv); \n"
- " color = texel; \n"
- "} \n";
+ "precision mediump float; \n"
+ "uniform sampler2D tex; \n"
+ "varying vec2 uv; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = texture2D(tex, uv); \n"
+ "} \n";
const char* getEGLError(void) {
switch (eglGetError()) {
@@ -157,6 +152,9 @@
glAttachShader(program, vertexShader);
glAttachShader(program, pixelShader);
+ glBindAttribLocation(program, 0, "pos");
+ glBindAttribLocation(program, 1, "tex");
+
// Link the program
glLinkProgram(program);
GLint linked = 0;
@@ -235,7 +233,7 @@
return false;
}
- EGLint major = 3;
+ EGLint major = 2;
EGLint minor = 0;
if (!eglInitialize(mDisplay, &major, &minor)) {
LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
diff --git a/automotive/evs/common/utils/default/test/fuzz/FormatConvertFuzzer.cpp b/automotive/evs/common/utils/default/test/fuzz/FormatConvertFuzzer.cpp
index 58423c8..7f90501 100644
--- a/automotive/evs/common/utils/default/test/fuzz/FormatConvertFuzzer.cpp
+++ b/automotive/evs/common/utils/default/test/fuzz/FormatConvertFuzzer.cpp
@@ -32,9 +32,10 @@
// API have a requirement that width must be divied by 16 except yuyvtorgb
int min_height = 2;
- int max_height = (image_pixel_size / 16) & ~(1); // must be even number
+ int max_height = (image_pixel_size / 16);
int height = fdp.ConsumeIntegralInRange<uint32_t>(min_height, max_height);
- int width = (image_pixel_size / height) & ~(16); // must be divisible by 16
+ height &= ~(1); // must be even number
+ int width = (image_pixel_size / height) & ~(0xF); // must be divisible by 16
uint8_t* src = (uint8_t*)(data + 4);
uint32_t* tgt = (uint32_t*)malloc(sizeof(uint32_t) * image_pixel_size);
diff --git a/automotive/occupant_awareness/aidl/default/Android.bp b/automotive/occupant_awareness/aidl/default/Android.bp
index 66af9de..3dc7e0d 100644
--- a/automotive/occupant_awareness/aidl/default/Android.bp
+++ b/automotive/occupant_awareness/aidl/default/Android.bp
@@ -39,3 +39,28 @@
"android.hardware.automotive.occupant_awareness-V1-ndk",
],
}
+
+cc_fuzz {
+ name: "android.hardware.automotive.occupant_awareness-service.fuzzer",
+ static_libs: [
+ "android.hardware.automotive.occupant_awareness-V1-ndk",
+ "libbase",
+ "libbinder_random_parcel",
+ "libcutils",
+ "liblog",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ "libutils",
+ ],
+ srcs: [
+ "fuzzer.cpp",
+ "OccupantAwareness.cpp",
+ ],
+ fuzz_config: {
+ cc: [
+ "keithmok@google.com",
+ ],
+ },
+}
diff --git a/automotive/occupant_awareness/aidl/default/fuzzer.cpp b/automotive/occupant_awareness/aidl/default/fuzzer.cpp
new file mode 100644
index 0000000..551b83a
--- /dev/null
+++ b/automotive/occupant_awareness/aidl/default/fuzzer.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "OccupantAwareness.h"
+
+using ::aidl::android::hardware::automotive::occupant_awareness::IOccupantAwareness;
+using ::android::fuzzService;
+using ::android::hardware::automotive::occupant_awareness::V1_0::implementation::OccupantAwareness;
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto occupantAwareness = SharedRefBase::make<OccupantAwareness>();
+
+ fuzzService(occupantAwareness->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index e276a39..edc8949 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -374,6 +374,15 @@
{.config =
{
+ .prop = toInt(VehicleProperty::FUEL_VOLUME_DISPLAY_UNITS),
+ .access = VehiclePropertyAccess::READ_WRITE,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ .configArray = {(int)VehicleUnit::LITER, (int)VehicleUnit::US_GALLON},
+ },
+ .initialValue = {.int32Values = {(int)VehicleUnit::LITER}}},
+
+ {.config =
+ {
.prop = toInt(VehicleProperty::HW_KEY_INPUT),
.access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
index 9be9ea7..503afd2 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/GeneratorHub.cpp
@@ -28,11 +28,18 @@
namespace impl {
-GeneratorHub::GeneratorHub(const OnHalEvent& onHalEvent)
- : mOnHalEvent(onHalEvent), mThread(&GeneratorHub::run, this) {}
+GeneratorHub::GeneratorHub(const OnHalEvent& onHalEvent) : mOnHalEvent(onHalEvent) {
+ mThread = std::thread(&GeneratorHub::run, this);
+}
GeneratorHub::~GeneratorHub() {
- mShuttingDownFlag.store(true);
+ {
+ // Even if the shared variable is atomic, it must be modified under the
+ // mutex in order to correctly publish the modification to the waiting
+ // thread.
+ std::unique_lock<std::mutex> g(mLock);
+ mShuttingDownFlag.store(true);
+ }
mCond.notify_all();
if (mThread.joinable()) {
mThread.join();
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
index d35792d..74f5a7a 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp
@@ -141,7 +141,7 @@
TEST_F(DefaultVhalImplTest, testListProperties) {
std::vector<VehiclePropConfig> configs = mHal->listProperties();
- EXPECT_EQ((size_t)122, configs.size());
+ EXPECT_EQ((size_t)123, configs.size());
}
TEST_F(DefaultVhalImplTest, testGetDefaultPropertyFloat) {
@@ -402,7 +402,7 @@
gotValue->timestamp = 0;
std::string infoMake = toString(*gotValue);
- EXPECT_THAT(std::string(buf), HasSubstr(infoMake));
+ EXPECT_THAT(std::string(buf, sizeof(buf)), HasSubstr(infoMake));
}
TEST_F(DefaultVhalImplTest, testSetPropInvalidAreaId) {
diff --git a/automotive/vehicle/README.md b/automotive/vehicle/README.md
new file mode 100644
index 0000000..e0f03e2
--- /dev/null
+++ b/automotive/vehicle/README.md
@@ -0,0 +1,26 @@
+# Vehicle Hardware Abstraction Layer (VHAL)
+---
+
+This directory stores the VHAL interface definition and VHAL reference
+implementation.
+
+## 2.0 (deprecated)
+
+HIDL based VHAL interface and reference implementation.
+
+## aidl
+
+AIDL based VHAL interfadce and reference implementation.
+
+## proto
+
+Protobuf used to pass message between emulator VHAL and emulator.
+
+## tools
+
+Dev tools related to VHAL.
+
+## vts
+
+VTS test for VHAL. The VTS test works for both AIDL and HIDL VHAL
+implementation. Vendor implementation of VHAL must passes VTS.
diff --git a/automotive/vehicle/TEST_MAPPING b/automotive/vehicle/TEST_MAPPING
index 7e42554..da8416c 100644
--- a/automotive/vehicle/TEST_MAPPING
+++ b/automotive/vehicle/TEST_MAPPING
@@ -7,9 +7,24 @@
"name": "VehicleHalDefaultConfigTest"
},
{
+ "name": "VehicleHalDefaultConfigTestEnableTestProperties"
+ },
+ {
+ "name": "JsonConfigLoaderUnitTest"
+ },
+ {
+ "name": "JsonConfigLoaderUnitTestEnableTestProperties"
+ },
+ {
"name": "VehicleHalVehicleUtilsTest"
},
{
+ "name": "VehiclePropertyAnnotationCppTest"
+ },
+ {
+ "name": "VehiclePropertyAnnotationJavaTest"
+ },
+ {
"name": "FakeVehicleHardwareTest"
},
{
diff --git a/automotive/vehicle/aidl/README.md b/automotive/vehicle/aidl/README.md
new file mode 100644
index 0000000..09f03b4
--- /dev/null
+++ b/automotive/vehicle/aidl/README.md
@@ -0,0 +1,23 @@
+# AIDL Vehicle Hardware Abstraction Layer (VHAL)
+---
+
+This directory stores the AIDL VHAL interface and reference implementation.
+
+## aidl_api
+
+Auto-generated current and previous versions of AIDL VHAL api.
+
+## aidl_test
+
+Contains a test to test that all HIDL VHAL properties are supported in
+AIDL VHAL.
+
+## android
+
+Contains AIDL VHAL interface definition. The main interface file is
+`android/hardware/automotive/vehicle/IVehicle.aidl`.
+
+## impl
+
+Reference implementation for AIDL VHAL and useful libraries for implementing
+vendor AIDL VHAL.
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
new file mode 100644
index 0000000..0d3a061
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.automotive.vehicle;
+@Backing(type="int") @VintfStability
+enum GsrComplianceRequirementType {
+ GSR_COMPLIANCE_NOT_REQUIRED = 0,
+ GSR_COMPLIANCE_REQUIRED_THROUGH_SYSTEM_IMAGE = 1,
+}
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 04f8fa3..2d0cde5 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -206,4 +206,5 @@
EV_REGENERATIVE_BRAKING_STATE = 289410884,
TRAILER_PRESENT = 289410885,
VEHICLE_CURB_WEIGHT = 289410886,
+ GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT = 289410887,
}
diff --git a/automotive/vehicle/aidl/aidl_test/Android.bp b/automotive/vehicle/aidl/aidl_test/Android.bp
index 5284a0a..cf7ef1e 100644
--- a/automotive/vehicle/aidl/aidl_test/Android.bp
+++ b/automotive/vehicle/aidl/aidl_test/Android.bp
@@ -20,7 +20,7 @@
cc_test {
name: "VehicleHalAidlHidlCompatibilityTest",
- srcs: ["*.cpp"],
+ srcs: ["AidlHidlCompatibilityTest.cpp"],
shared_libs: [
"libbinder_ndk",
"libhidlbase",
@@ -35,3 +35,25 @@
test_suites: ["device-tests"],
vendor: true,
}
+
+cc_test {
+ name: "VehiclePropertyAnnotationCppTest",
+ srcs: ["VehiclePropertyAnnotationCppTest.cpp"],
+ header_libs: ["IVehicleGeneratedHeaders"],
+ defaults: ["VehicleHalInterfaceDefaults"],
+ test_suites: ["general-tests"],
+}
+
+android_test {
+ name: "VehiclePropertyAnnotationJavaTest",
+ srcs: [
+ "VehiclePropertyAnnotationJavaTest.java",
+ ":IVehicleGeneratedJavaFiles",
+ ],
+ static_libs: [
+ "android.hardware.automotive.vehicle-V2-java",
+ "androidx.test.runner",
+ "truth-prebuilt",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/automotive/vehicle/aidl/aidl_test/AndroidManifest.xml b/automotive/vehicle/aidl/aidl_test/AndroidManifest.xml
new file mode 100644
index 0000000..00fdf50
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_test/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.hardware.automotive.vehicle" >
+
+ <application/>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.hardware.automotive.vehicle"
+ android:label="test to verify VHAL annotation"/>
+
+</manifest>
\ No newline at end of file
diff --git a/automotive/vehicle/aidl/aidl_test/VehiclePropertyAnnotationCppTest.cpp b/automotive/vehicle/aidl/aidl_test/VehiclePropertyAnnotationCppTest.cpp
new file mode 100644
index 0000000..a4bbbe8
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_test/VehiclePropertyAnnotationCppTest.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <AccessForVehicleProperty.h>
+#include <ChangeModeForVehicleProperty.h>
+
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
+#include <gtest/gtest.h>
+#include <unordered_set>
+
+namespace aidl_vehicle = ::aidl::android::hardware::automotive::vehicle;
+using aidl_vehicle::AccessForVehicleProperty;
+using aidl_vehicle::ChangeModeForVehicleProperty;
+using aidl_vehicle::VehicleProperty;
+
+namespace {
+ template<class T>
+ bool doesAnnotationMapContainsAllProps(std::unordered_map<VehicleProperty, T> annotationMap) {
+ for (const VehicleProperty& v : ::ndk::enum_range<VehicleProperty>()) {
+ std::string name = aidl_vehicle::toString(v);
+ if (name == "INVALID") {
+ continue;
+ }
+ if (annotationMap.find(v) == annotationMap.end()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+} // namespace
+
+TEST(VehiclePropertyAnnotationCppTest, testChangeMode) {
+ ASSERT_TRUE(doesAnnotationMapContainsAllProps(ChangeModeForVehicleProperty))
+ << "Outdated annotation-generated AIDL files. Please run "
+ << "generate_annotation_enums.py to update.";
+}
+
+TEST(VehiclePropertyAnnotationCppTest, testAccess) {
+ ASSERT_TRUE(doesAnnotationMapContainsAllProps(AccessForVehicleProperty))
+ << "Outdated annotation-generated AIDL files. Please run "
+ << "generate_annotation_enums.py to update.";
+}
diff --git a/automotive/vehicle/aidl/aidl_test/VehiclePropertyAnnotationJavaTest.java b/automotive/vehicle/aidl/aidl_test/VehiclePropertyAnnotationJavaTest.java
new file mode 100644
index 0000000..ef49299
--- /dev/null
+++ b/automotive/vehicle/aidl/aidl_test/VehiclePropertyAnnotationJavaTest.java
@@ -0,0 +1,60 @@
+package android.hardware.automotive.vehicle;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public class VehiclePropertyAnnotationJavaTest {
+
+ private boolean doesAnnotationMapContainsAllProps(Map<Integer, Integer> annotationMap) {
+ for (Field field : VehicleProperty.class.getDeclaredFields()) {
+ int modifiers = field.getModifiers();
+ try {
+ if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+ && Modifier.isPublic(modifiers) && field.getType().equals(int.class)) {
+ int propId = field.getInt(/* obj= */ null);
+ if (propId == VehicleProperty.INVALID) {
+ // Skip INVALID_PROP.
+ continue;
+ }
+ if (annotationMap.get(propId) == null) {
+ return false;
+ }
+ }
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(
+ "Cannot access a member for VehicleProperty.class", e);
+ }
+ }
+ return true;
+ }
+
+ @Test
+ @SmallTest
+ public void testChangeMode() {
+ assertWithMessage("Outdated annotation-generated AIDL files. Please run "
+ + "generate_annotation_enums.py to update.")
+ .that(doesAnnotationMapContainsAllProps(ChangeModeForVehicleProperty.values))
+ .isTrue();
+ }
+
+ @Test
+ @SmallTest
+ public void testAccess() {
+ assertWithMessage("Outdated annotation-generated AIDL files. Please run "
+ + "generate_annotation_enums.py to update.")
+ .that(doesAnnotationMapContainsAllProps(AccessForVehicleProperty.values))
+ .isTrue();
+ }
+}
\ No newline at end of file
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
new file mode 100644
index 0000000..e0609d5
--- /dev/null
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.automotive.vehicle;
+
+/**
+ * Used by GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT to indicate what
+ * kind of general safety regulation compliance requirement is enforced.
+ */
+@VintfStability
+@Backing(type="int")
+enum GsrComplianceRequirementType {
+ /**
+ * GSR compliance is not required.
+ */
+ GSR_COMPLIANCE_NOT_REQUIRED = 0,
+ /**
+ * GSR compliance is required through system image.
+ */
+ GSR_COMPLIANCE_REQUIRED_THROUGH_SYSTEM_IMAGE = 1,
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 727b949..1cf8aff 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -46,32 +46,32 @@
/**
* VIN of vehicle
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
*/
INFO_VIN = 0x0100 + 0x10000000 + 0x01000000
+ 0x00100000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:STRING
/**
* Manufacturer of vehicle
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
*/
INFO_MAKE = 0x0101 + 0x10000000 + 0x01000000
+ 0x00100000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:STRING
/**
* Model of vehicle
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
*/
INFO_MODEL = 0x0102 + 0x10000000 + 0x01000000
+ 0x00100000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:STRING
/**
* Model year of vehicle.
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:YEAR
*/
INFO_MODEL_YEAR = 0x0103 + 0x10000000 + 0x01000000
@@ -79,8 +79,8 @@
/**
* Fuel capacity of the vehicle in milliliters
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:MILLILITER
*/
INFO_FUEL_CAPACITY = 0x0104 + 0x10000000 + 0x01000000
@@ -88,8 +88,8 @@
/**
* List of fuels the vehicle may use
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @data_enum FuelType
*/
INFO_FUEL_TYPE = 0x0105 + 0x10000000 + 0x01000000
@@ -98,8 +98,8 @@
* Battery capacity of the vehicle, if EV or hybrid. This is the nominal
* battery capacity when the vehicle is new.
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:WH
*/
INFO_EV_BATTERY_CAPACITY = 0x0106 + 0x10000000 + 0x01000000
@@ -107,26 +107,26 @@
/**
* List of connectors this EV may use
*
- * @change_mode VehiclePropertyChangeMode:STATIC
+ * @change_mode VehiclePropertyChangeMode.STATIC
* @data_enum EvConnectorType
- * @access VehiclePropertyAccess:READ
+ * @access VehiclePropertyAccess.READ
*/
INFO_EV_CONNECTOR_TYPE = 0x0107 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
/**
* Fuel door location
*
- * @change_mode VehiclePropertyChangeMode:STATIC
+ * @change_mode VehiclePropertyChangeMode.STATIC
* @data_enum PortLocationType
- * @access VehiclePropertyAccess:READ
+ * @access VehiclePropertyAccess.READ
*/
INFO_FUEL_DOOR_LOCATION = 0x0108 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
/**
* EV port location
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @data_enum PortLocationType
*/
INFO_EV_PORT_LOCATION = 0x0109 + 0x10000000 + 0x01000000
@@ -135,9 +135,9 @@
* Driver's seat location
* VHAL implementations must ignore the areaId. Use VehicleArea:GLOBAL.
*
- * @change_mode VehiclePropertyChangeMode:STATIC
+ * @change_mode VehiclePropertyChangeMode.STATIC
* @data_enum VehicleAreaSeat
- * @access VehiclePropertyAccess:READ
+ * @access VehiclePropertyAccess.READ
*/
INFO_DRIVER_SEAT = 0x010A + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -153,8 +153,8 @@
* int32Values[6] = track width rear
* int32Values[7] = curb to curb turning radius
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:MILLIMETER
*/
INFO_EXTERIOR_DIMENSIONS = 0x010B + 0x10000000 + 0x01000000
@@ -168,8 +168,8 @@
* int32Values[0] = PortLocationType::FRONT_LEFT
* int32Values[0] = PortLocationType::REAR_LEFT
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @data_enum PortLocationType
*/
INFO_MULTI_EV_PORT_LOCATIONS = 0x010C + 0x10000000 + 0x01000000
@@ -177,8 +177,8 @@
/**
* Current odometer value of the vehicle
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:KILOMETER
*/
PERF_ODOMETER = 0x0204 + 0x10000000 + 0x01000000
@@ -192,8 +192,8 @@
* PERF_VEHICLE_SPEED is positive when the vehicle is moving forward, negative when moving
* backward, and zero when not moving.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:METER_PER_SEC
*/
PERF_VEHICLE_SPEED = 0x0207 + 0x10000000 + 0x01000000
@@ -204,8 +204,8 @@
* Some cars display a slightly slower speed than the actual speed. This is
* usually displayed on the speedometer.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:METER_PER_SEC
*/
PERF_VEHICLE_SPEED_DISPLAY = 0x0208 + 0x10000000 + 0x01000000
@@ -215,8 +215,8 @@
*
* Angle is in degrees. Left is negative.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:DEGREES
*/
PERF_STEERING_ANGLE = 0x0209 + 0x10000000 + 0x01000000
@@ -226,8 +226,8 @@
*
* Angle is in degrees. Left is negative.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:DEGREES
*/
PERF_REAR_STEERING_ANGLE = 0x0210 + 0x10000000 + 0x01000000
@@ -235,8 +235,8 @@
/**
* Temperature of engine coolant
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:CELSIUS
*/
ENGINE_COOLANT_TEMP = 0x0301 + 0x10000000 + 0x01000000
@@ -244,8 +244,8 @@
/**
* Engine oil level
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleOilLevel
*/
ENGINE_OIL_LEVEL = 0x0303 + 0x10000000 + 0x01000000
@@ -253,8 +253,8 @@
/**
* Temperature of engine oil
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:CELSIUS
*/
ENGINE_OIL_TEMP = 0x0304 + 0x10000000 + 0x01000000
@@ -262,8 +262,8 @@
/**
* Engine rpm
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:RPM
*/
ENGINE_RPM = 0x0305 + 0x10000000 + 0x01000000
@@ -300,8 +300,8 @@
*
* VehiclePropValue.timestamp must be correctly filled in.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
*/
WHEEL_TICK = 0x0306 + 0x10000000 + 0x01000000
+ 0x00510000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT64_VEC
@@ -310,8 +310,8 @@
*
* Value may not exceed INFO_FUEL_CAPACITY
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:MILLILITER
*/
FUEL_LEVEL = 0x0307 + 0x10000000 + 0x01000000
@@ -319,8 +319,8 @@
/**
* Fuel door open
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
FUEL_DOOR_OPEN = 0x0308 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -329,8 +329,8 @@
*
* Value may not exceed INFO_EV_BATTERY_CAPACITY
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:WH
*/
EV_BATTERY_LEVEL = 0x0309 + 0x10000000 + 0x01000000
@@ -338,16 +338,16 @@
/**
* EV charge port open
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
EV_CHARGE_PORT_OPEN = 0x030A + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
/**
* EV charge port connected
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
EV_CHARGE_PORT_CONNECTED = 0x030B + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -357,8 +357,8 @@
* Positive value indicates battery is being charged.
* Negative value indicates battery being discharged.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:MW
*/
EV_BATTERY_INSTANTANEOUS_CHARGE_RATE = 0x030C + 0x10000000 + 0x01000000
@@ -370,8 +370,8 @@
* all energy sources in a vehicle. For example, a hybrid car's range will
* be the sum of the ranges based on fuel and battery.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ_WRITE
* @unit VehicleUnit:METER
*/
RANGE_REMAINING = 0x0308 + 0x10000000 + 0x01000000
@@ -397,8 +397,8 @@
* }
* },
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:KILOPASCAL
*/
TIRE_PRESSURE = 0x0309 + 0x10000000 + 0x07000000
@@ -412,8 +412,8 @@
* Minimum and maximum property values (that is, minFloatValue, maxFloatValue)
* are not applicable to this property.
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:KILOPASCAL
*/
CRITICALLY_LOW_TIRE_PRESSURE = 0x030A + 0x10000000 + 0x07000000
@@ -429,8 +429,8 @@
* GEAR_1, GEAR_2,...} and for manual transmission the list must be
* {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleGear
*/
GEAR_SELECTION = 0x0400 + 0x10000000 + 0x01000000
@@ -448,8 +448,8 @@
* {GEAR_NEUTRAL, GEAR_REVERSE, GEAR_1, GEAR_2,...}. This list need not be the
* same as that of the supported gears reported in GEAR_SELECTION.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleGear
*/
CURRENT_GEAR = 0x0401 + 0x10000000 + 0x01000000
@@ -457,16 +457,16 @@
/**
* Parking brake state.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
PARKING_BRAKE_ON = 0x0402 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
/**
* Auto-apply parking brake.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
PARKING_BRAKE_AUTO_APPLY = 0x0403 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -483,8 +483,8 @@
* For a hybrid vehicle, this property may be based on the combination of gas and battery
* levels, at the OEM's discretion.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
FUEL_LEVEL_LOW = 0x0405 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -495,16 +495,16 @@
* low light. The platform could use this, for example, to enable appropriate UI for
* better viewing in dark or low light environments.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
NIGHT_MODE = 0x0407 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
/**
* State of the vehicles turn signals
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleTurnSignal
*/
TURN_SIGNAL_STATE = 0x0408 + 0x10000000 + 0x01000000
@@ -512,8 +512,8 @@
/**
* Represents ignition state
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleIgnitionState
*/
IGNITION_STATE = 0x0409 + 0x10000000 + 0x01000000
@@ -525,8 +525,8 @@
* property may be intermittently set (pulsing) based on the real-time
* state of the ABS system.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
ABS_ACTIVE = 0x040A + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -537,12 +537,12 @@
* TC is off. This property may be intermittently set (pulsing) based on
* the real-time state of the TC system.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
TRACTION_CONTROL_ACTIVE = 0x040B + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
- /*
+ /**
* HVAC Properties
*
* Additional rules for mapping a zoned HVAC property (except
@@ -580,16 +580,16 @@
*
* Fan speed setting
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_FAN_SPEED = 0x0500 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
/**
* Fan direction setting
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleHvacFanDirection
*/
HVAC_FAN_DIRECTION = 0x0501 + 0x10000000 + 0x05000000
@@ -597,8 +597,8 @@
/**
* HVAC current temperature.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:CELSIUS
*/
HVAC_TEMPERATURE_CURRENT = 0x0502 + 0x10000000 + 0x05000000
@@ -624,8 +624,8 @@
* that property to get the suggested value before setting HVAC_TEMPERATURE_SET. Otherwise,
* the application may choose the value in HVAC_TEMPERATURE_SET configArray by itself.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @unit VehicleUnit:CELSIUS
*/
HVAC_TEMPERATURE_SET = 0x0503 + 0x10000000 + 0x05000000
@@ -633,16 +633,16 @@
/**
* Fan-based defrost for designated window.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_DEFROSTER = 0x0504 + 0x10000000 + 0x03000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:BOOLEAN
/**
* On/off AC for designated areaId
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @config_flags Supported areaIds
*/
HVAC_AC_ON = 0x0505 + 0x10000000 + 0x05000000
@@ -655,8 +655,8 @@
* Any parameters modified as a side effect of turning on/off the MAX AC
* parameter shall generate onPropertyEvent() callbacks to the VHAL.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_MAX_AC_ON = 0x0506 + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
@@ -674,8 +674,8 @@
* areaConfig.areaId = {ROW_1_LEFT | ROW_1_RIGHT} indicates HVAC_MAX_DEFROST_ON
* only can be controlled for the front rows.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_MAX_DEFROST_ON = 0x0507 + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
@@ -687,8 +687,8 @@
* Recirc “off” means the majority of the airflow into the cabin is coming
* from outside the car.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_RECIRC_ON = 0x0508 + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
@@ -722,16 +722,16 @@
* onPropertyEvent() callbacks (i.e. HVAC_DUAL_ON = false,
* HVAC_TEMPERATURE_SET[AreaID] = xxx, etc).
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_DUAL_ON = 0x0509 + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
/**
* On/off automatic mode
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_AUTO_ON = 0x050A + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
@@ -746,8 +746,8 @@
* min/max range defines the allowable range and number of steps in each
* direction.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_SEAT_TEMPERATURE = 0x050B + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -758,8 +758,8 @@
* The Max value in the config data represents the highest heating level.
* The Min value in the config data MUST be zero and indicates no heating.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_SIDE_MIRROR_HEAT = 0x050C + 0x10000000 + 0x04000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:MIRROR,VehiclePropertyType:INT32
@@ -772,8 +772,8 @@
* Negative value indicates cooling.
* 0 indicates temperature control is off.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_STEERING_WHEEL_HEAT = 0x050D + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -790,8 +790,8 @@
* Values must be one of VehicleUnit::CELSIUS or VehicleUnit::FAHRENHEIT
* Note that internally, all temperatures are represented in floating point Celsius.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
*/
HVAC_TEMPERATURE_DISPLAY_UNITS = 0x050E + 0x10000000 + 0x01000000
@@ -799,8 +799,8 @@
/**
* Actual fan speed
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
HVAC_ACTUAL_FAN_SPEED_RPM = 0x050F + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -841,8 +841,8 @@
* - ROW_1_LEFT | ROW_1_RIGHT
* - ROW_2_LEFT | ROW_2_CENTER | ROW_2_RIGHT | ROW_3_LEFT | ROW_3_CENTER | ROW_3_RIGHT
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_POWER_ON = 0x0510 + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
@@ -859,8 +859,8 @@
* - FAN_DIRECTION_DEFROST (0x4)
* - FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST (0x6)
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleHvacFanDirection
*/
HVAC_FAN_DIRECTION_AVAILABLE = 0x0511 + 0x10000000 + 0x05000000
@@ -872,8 +872,8 @@
* switch to recirculation mode if the vehicle detects poor incoming air
* quality.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_AUTO_RECIRC_ON = 0x0512 + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
@@ -887,16 +887,16 @@
* ventilation. This is different than seating cooling. It can be on at the
* same time as cooling, or not.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_SEAT_VENTILATION = 0x0513 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
/**
* Electric defrosters' status
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_ELECTRIC_DEFROSTER_ON = 0x0514 + 0x10000000 + 0x03000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:BOOLEAN
@@ -935,8 +935,8 @@
* callback with property value [21, (float)VehicleUnit:CELSIUS, 21.0, 70.0].
* In this case, the application can know that the value is 70.0 Fahrenheit in the car’s UI.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
HVAC_TEMPERATURE_VALUE_SUGGESTION = 0x0515 + 0x10000000 + 0x01000000
+ 0x00610000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:FLOAT_VEC
@@ -951,8 +951,8 @@
* For example: configArray[0] = METER
* configArray[1] = KILOMETER
* configArray[2] = MILE
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
*/
DISTANCE_DISPLAY_UNITS = 0x0600 + 0x10000000 + 0x01000000
@@ -967,8 +967,8 @@
* Volume units are defined in VehicleUnit.
* For example: configArray[0] = LITER
* configArray[1] = GALLON
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
*/
FUEL_VOLUME_DISPLAY_UNITS = 0x0601 + 0x10000000 + 0x01000000
@@ -984,8 +984,8 @@
* For example: configArray[0] = KILOPASCAL
* configArray[1] = PSI
* configArray[2] = BAR
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
*/
TIRE_PRESSURE_DISPLAY_UNITS = 0x0602 + 0x10000000 + 0x01000000
@@ -1001,8 +1001,8 @@
* For example: configArray[0] = WATT_HOUR
* configArray[1] = AMPERE_HOURS
* configArray[2] = KILOWATT_HOUR
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleUnit
*/
EV_BATTERY_DISPLAY_UNITS = 0x0603 + 0x10000000 + 0x01000000
@@ -1014,8 +1014,8 @@
* True indicates units are distance over volume such as MPG.
* False indicates units are volume over distance such as L/100KM.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME = 0x0604 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -1029,8 +1029,8 @@
* For example: configArray[0] = METER_PER_SEC
* configArray[1] = MILES_PER_HOUR
* configArray[2] = KILOMETERS_PER_HOUR
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
VEHICLE_SPEED_DISPLAY_UNITS = 0x0605 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -1072,8 +1072,8 @@
* WARNING: The value available through this property should not be dependent
* on value written by Android to ANDROID_EPOCH_TIME property in any way.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_ONLY
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:MILLI_SECS
*/
EXTERNAL_CAR_TIME = 0x0608 + 0x10000000 // VehiclePropertyGroup:SYSTEM
@@ -1101,8 +1101,8 @@
* drift = elapsedTime - PropValue.timestamp
* effectiveTime = PropValue.value.int64Values[0] + drift
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE_ONLY
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
* @unit VehicleUnit:MILLI_SECS
*/
ANDROID_EPOCH_TIME = 0x0606 + 0x10000000 + 0x01000000
@@ -1116,16 +1116,16 @@
* AAOS will then read the property on subsequent boots. The binding seed is expected to be
* reliably persisted. Any loss of the seed results in a factory reset of the IVI.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
STORAGE_ENCRYPTION_BINDING_SEED = 0x0607 + 0x10000000 + 0x01000000
+ 0x00700000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BYTES
/**
* Outside temperature
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:CELSIUS
*/
ENV_OUTSIDE_TEMPERATURE = 0x0703 + 0x10000000 + 0x01000000
@@ -1143,8 +1143,8 @@
* int32Values[1] : additional parameter relevant for each state,
* 0 if not used.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
AP_POWER_STATE_REQ = 0x0A00 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
@@ -1158,8 +1158,8 @@
* int32Values[1] : Time in ms to wake up, if necessary. Otherwise 0.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
AP_POWER_STATE_REPORT = 0x0A01 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
@@ -1172,8 +1172,8 @@
*
* int32Values[0] must be VehicleApPowerBootupReason.
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
*/
AP_POWER_BOOTUP_REASON = 0x0A02 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -1187,8 +1187,8 @@
* change display brightness from Settings, but that must not be reflected
* to other displays.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
DISPLAY_BRIGHTNESS = 0x0A03 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -1203,8 +1203,8 @@
* int32Values[3] : [optional] Number of ticks. The value must be equal or
* greater than 1. When omitted, Android will default to 1.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @config_flags
*/
HW_KEY_INPUT = 0x0A10 + 0x10000000 + 0x01000000
@@ -1226,9 +1226,9 @@
* detents is > 1 or < -1, this is when the
* first detent of rotation occurred.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @data_enum RotaryInputType
- * @access VehiclePropertyAccess:READ
+ * @access VehiclePropertyAccess.READ
*/
HW_ROTARY_INPUT = 0x0A20 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
@@ -1249,9 +1249,9 @@
* int32Values[2] : repeat counter, if 0 then event is not repeated. Values 1 or above means
* how many times this event repeated.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @data_enum CustomInputType
- * @access VehiclePropertyAccess:READ
+ * @access VehiclePropertyAccess.READ
*/
HW_CUSTOM_INPUT = 0X0A30 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
@@ -1282,16 +1282,16 @@
* Some vehicles (minivans) can open the door electronically. Hence, the
* ability to write this property.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
DOOR_POS = 0x0B00 + 0x10000000 + 0x06000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:DOOR,VehiclePropertyType:INT32
/**
* Door move
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
DOOR_MOVE = 0x0B01 + 0x10000000 + 0x06000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:DOOR,VehiclePropertyType:INT32
@@ -1300,8 +1300,8 @@
*
* 'true' indicates door is locked
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
DOOR_LOCK = 0x0B02 + 0x10000000 + 0x06000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:DOOR,VehiclePropertyType:BOOLEAN
@@ -1310,8 +1310,8 @@
*
* Positive value indicates tilt upwards, negative value is downwards
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
MIRROR_Z_POS = 0x0B40 + 0x10000000 + 0x04000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:MIRROR,VehiclePropertyType:INT32
@@ -1320,8 +1320,8 @@
*
* Positive value indicates tilt upwards, negative value is downwards
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
MIRROR_Z_MOVE = 0x0B41 + 0x10000000 + 0x04000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:MIRROR,VehiclePropertyType:INT32
@@ -1330,8 +1330,8 @@
*
* Positive value indicate tilt right, negative value is left
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
MIRROR_Y_POS = 0x0B42 + 0x10000000 + 0x04000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:MIRROR,VehiclePropertyType:INT32
@@ -1340,8 +1340,8 @@
*
* Positive value indicate tilt right, negative value is left
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
MIRROR_Y_MOVE = 0x0B43 + 0x10000000 + 0x04000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:MIRROR,VehiclePropertyType:INT32
@@ -1350,8 +1350,8 @@
*
* True indicates mirror positions are locked and not changeable
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
MIRROR_LOCK = 0x0B44 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -1360,8 +1360,8 @@
*
* True indicates mirrors are folded
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
MIRROR_FOLD = 0x0B45 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -1376,8 +1376,8 @@
* will be 3. When the user wants to select a preset, the desired preset
* number (1, 2, or 3) is set.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
SEAT_MEMORY_SELECT = 0x0B80 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1388,8 +1388,8 @@
* into the selected preset slot. The maxValue for each seat position
* must match the maxValue for SEAT_MEMORY_SELECT.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
SEAT_MEMORY_SET = 0x0B81 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1401,8 +1401,8 @@
* Write access indicates automatic seat buckling capabilities. There are
* no known cars at this time, but you never know...
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_BELT_BUCKLED = 0x0B82 + 0x10000000 + 0x05000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:BOOLEAN
@@ -1413,16 +1413,16 @@
* Max value indicates highest position
* Min value indicates lowest position
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_BELT_HEIGHT_POS = 0x0B83 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
/**
* Seatbelt height move
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_BELT_HEIGHT_MOVE = 0x0B84 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1433,8 +1433,8 @@
* Max value indicates closest to wheel, min value indicates most rearward
* position.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_FORE_AFT_POS = 0x0B85 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1443,8 +1443,8 @@
*
* Moves the seat position forward and aft.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_FORE_AFT_MOVE = 0x0B86 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1455,8 +1455,8 @@
* Max value indicates angling forward towards the steering wheel.
* Min value indicates full recline.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_BACKREST_ANGLE_1_POS = 0x0B87 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1465,8 +1465,8 @@
*
* Moves the backrest forward or recline.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_BACKREST_ANGLE_1_MOVE = 0x0B88 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1477,8 +1477,8 @@
* Max value indicates angling forward towards the steering wheel.
* Min value indicates full recline.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_BACKREST_ANGLE_2_POS = 0x0B89 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1487,8 +1487,8 @@
*
* Moves the backrest forward or recline.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_BACKREST_ANGLE_2_MOVE = 0x0B8A + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1499,8 +1499,8 @@
* Max value indicates highest position.
* Min value indicates lowest position.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEIGHT_POS = 0x0B8B + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1509,8 +1509,8 @@
*
* Moves the seat height.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEIGHT_MOVE = 0x0B8C + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1521,8 +1521,8 @@
* Max value indicates longest depth position.
* Min value indicates shortest position.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_DEPTH_POS = 0x0B8D + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1531,8 +1531,8 @@
*
* Adjusts the seat depth.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_DEPTH_MOVE = 0x0B8E + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1543,8 +1543,8 @@
* Max value indicates front edge of seat higher than back edge.
* Min value indicates front edge of seat lower than back edge.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_TILT_POS = 0x0B8F + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1553,8 +1553,8 @@
*
* Tilts the seat.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_TILT_MOVE = 0x0B90 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1565,8 +1565,8 @@
* Max value indicates most forward position.
* Min value indicates most rearward position.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_LUMBAR_FORE_AFT_POS = 0x0B91 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1575,8 +1575,8 @@
*
* Adjusts the lumbar support.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_LUMBAR_FORE_AFT_MOVE = 0x0B92 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1587,8 +1587,8 @@
* Max value indicates widest lumbar setting (i.e. least support)
* Min value indicates thinnest lumbar setting.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_LUMBAR_SIDE_SUPPORT_POS = 0x0B93 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1597,8 +1597,8 @@
*
* Adjusts the amount of lateral lumbar support.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 0x0B94 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1609,8 +1609,8 @@
* Max value indicates tallest setting.
* Min value indicates shortest setting.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEADREST_HEIGHT_POS = 0x0B95 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -1619,8 +1619,8 @@
*
* Moves the headrest up and down.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEADREST_HEIGHT_MOVE = 0x0B96 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1631,8 +1631,8 @@
* Max value indicates most upright angle.
* Min value indicates shallowest headrest angle.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEADREST_ANGLE_POS = 0x0B97 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1641,8 +1641,8 @@
*
* Adjusts the angle of the headrest
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEADREST_ANGLE_MOVE = 0x0B98 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1653,16 +1653,16 @@
* Max value indicates position closest to front of car.
* Min value indicates position closest to rear of car.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEADREST_FORE_AFT_POS = 0x0B99 + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
/**
* Headrest fore/aft move
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SEAT_HEADREST_FORE_AFT_MOVE = 0x0B9A + 0x10000000 + 0x05000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:SEAT,VehiclePropertyType:INT32
@@ -1672,8 +1672,8 @@
* Indicates whether a particular seat is occupied or not, to the best of the car's ability
* to determine. Valid values are from the VehicleSeatOccupancyState enum.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleSeatOccupancyState
*/
SEAT_OCCUPANCY = 0x0BB0 + 0x10000000 + 0x05000000
@@ -1692,8 +1692,8 @@
*
* Note that in this mode, 0 indicates the window is closed.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
WINDOW_POS = 0x0BC0 + 0x10000000 + 0x03000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
@@ -1720,8 +1720,8 @@
* Max = open the sunroof, automatically stop when sunroof is fully open.
* Min = open the vent, automatically stop when vent is fully open.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
WINDOW_MOVE = 0x0BC1 + 0x10000000 + 0x03000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:INT32
@@ -1730,8 +1730,8 @@
*
* True indicates windows are locked and can't be moved.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
WINDOW_LOCK = 0x0BC4 + 0x10000000 + 0x03000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:BOOLEAN
@@ -1748,8 +1748,8 @@
*
* IVehicle#get must always return StatusCode::NOT_AVAILABLE.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
VEHICLE_MAP_SERVICE = 0x0C00 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -1791,8 +1791,8 @@
* floatValues[4 and 5] are valid sensor values
* floatValues[6] is not a valid sensor value
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
OBD2_LIVE_FRAME = 0x0D00 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -1817,8 +1817,8 @@
* freeze frames, it is possible for a frame request to respond with NOT_AVAILABLE even if
* the associated timestamp has been recently obtained via OBD2_FREEZE_FRAME_INFO.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
OBD2_FREEZE_FRAME = 0x0D01 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -1834,8 +1834,8 @@
* such element can be used as the key to OBD2_FREEZE_FRAME to retrieve
* the corresponding freeze frame.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
OBD2_FREEZE_FRAME_INFO = 0x0D02 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -1856,8 +1856,8 @@
* vehicle not support selective clearing of freeze frames, this latter mode must
* return NOT_AVAILABLE.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
OBD2_FREEZE_FRAME_CLEAR = 0x0D03 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -1866,8 +1866,8 @@
*
* Return the current state of headlights.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
HEADLIGHTS_STATE = 0x0E00 + 0x10000000 + 0x01000000
@@ -1877,8 +1877,8 @@
*
* Return the current state of high beam lights.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
HIGH_BEAM_LIGHTS_STATE = 0x0E01 + 0x10000000 + 0x01000000
@@ -1904,8 +1904,8 @@
* Only one of FOG_LIGHTS_STATE or REAR_FOG_LIGHTS_STATE must be implemented and not both.
* FRONT_FOG_LIGHTS_STATE must not be implemented.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
FOG_LIGHTS_STATE = 0x0E02 + 0x10000000 + 0x01000000
@@ -1915,8 +1915,8 @@
*
* Return the current status of hazard lights.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
HAZARD_LIGHTS_STATE = 0x0E03 + 0x10000000 + 0x01000000
@@ -1926,8 +1926,8 @@
*
* The setting that the user wants.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
HEADLIGHTS_SWITCH = 0x0E10 + 0x10000000 + 0x01000000
@@ -1937,8 +1937,8 @@
*
* The setting that the user wants.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
HIGH_BEAM_LIGHTS_SWITCH = 0x0E11 + 0x10000000 + 0x01000000
@@ -1964,8 +1964,8 @@
* Only one of FOG_LIGHTS_SWITCH or REAR_FOG_LIGHTS_SWITCH must be implemented and not both.
* FRONT_FOG_LIGHTS_SWITCH must not be implemented.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
FOG_LIGHTS_SWITCH = 0x0E12 + 0x10000000 + 0x01000000
@@ -1975,8 +1975,8 @@
*
* The setting that the user wants.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
HAZARD_LIGHTS_SWITCH = 0x0E13 + 0x10000000 + 0x01000000
@@ -1986,8 +1986,8 @@
*
* Return current status of cabin lights.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
CABIN_LIGHTS_STATE = 0x0F01 + 0x10000000 + 0x01000000
@@ -2000,8 +2000,8 @@
* is open or because of a voice command.
* For example, while the switch is in the "off" or "automatic" position.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
CABIN_LIGHTS_SWITCH = 0x0F02 + 0x10000000 + 0x01000000
@@ -2011,8 +2011,8 @@
*
* Return current status of reading lights.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
READING_LIGHTS_STATE = 0x0F03 + 0x10000000 + 0x05000000
@@ -2025,8 +2025,8 @@
* is open or because of a voice command.
* For example, while the switch is in the "off" or "automatic" position.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
READING_LIGHTS_SWITCH = 0x0F04 + 0x10000000 + 0x05000000
@@ -2054,8 +2054,8 @@
* If vendor chose PERMISSION_NOT_ACCESSIBLE, android will not have access to the property. In
* the example, Android can not write value for vendor_prop_2.
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
*/
SUPPORT_CUSTOMIZE_VENDOR_PERMISSION = 0x0F05 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -2070,8 +2070,8 @@
*
* Value read should include all features disabled with ',' separation.
* ex) "com.android.car.user.CarUserNoticeService,storage_monitoring"
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
*/
DISABLED_OPTIONAL_FEATURES = 0x0F06 + 0x10000000 + 0x01000000
+ 0x00100000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:STRING
@@ -2120,8 +2120,8 @@
* by the Android System). But if it supports user management, then it must support all core
* user-related properties (INITIAL_USER_INFO, SWITCH_USER, CREATE_USER, and REMOVE_USER).
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
INITIAL_USER_INFO = 0x0F07 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -2286,8 +2286,8 @@
*
* Example: see VEHICLE_REQUEST section above.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
SWITCH_USER = 0x0F08 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -2332,8 +2332,8 @@
* string: "D'OH!" // The meaning is a blackbox - it's passed to the caller (like Settings UI),
* // which in turn can take the proper action.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
CREATE_USER = 0x0F09 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -2363,8 +2363,8 @@
* int32[8]: 10 // 2nd user (user 10)
* int32[9]: 0 // 2nd user flags (none)
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.WRITE
*/
REMOVE_USER = 0x0F0A + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -2438,8 +2438,8 @@
* int32[5]: 101 (2nd type: UserIdentificationAssociationType::CUSTOM_1)
* int32[6]: 1 (2nd value: UserIdentificationAssociationValue::ASSOCIATE_CURRENT_USER)
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
USER_IDENTIFICATION_ASSOCIATION = 0x0F0B + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -2457,8 +2457,8 @@
* For example, to enable rear view EVS service, android side can set the property value as
* [EvsServiceType::REAR_VIEW, EvsServiceState::ON].
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
EVS_SERVICE_REQUEST = 0x0F10 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
@@ -2473,8 +2473,8 @@
*
* string: "sample_policy_id" // power policy ID
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
POWER_POLICY_REQ = 0x0F21 + 0x10000000 + 0x01000000
+ 0x00100000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:STRING
@@ -2492,8 +2492,8 @@
*
* string: "sample_policy_group_id" // power policy group ID
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
POWER_POLICY_GROUP_REQ = 0x0F22 + 0x10000000 + 0x01000000
+ 0x00100000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:STRING
@@ -2504,8 +2504,8 @@
*
* string: "sample_policy_id" // power policy ID
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
CURRENT_POWER_POLICY = 0x0F23 + 0x10000000 + 0x01000000
+ 0x00100000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:STRING
@@ -2515,8 +2515,8 @@
* Car watchdog sets this property to system uptime in milliseconds at every 3 second.
* During the boot, the update may take longer time.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
WATCHDOG_ALIVE = 0xF31 + 0x10000000 + 0x01000000
+ 0x00500000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT64
@@ -2526,8 +2526,8 @@
* int32Values[0]: 1 // ProcessTerminationReason showing why a process is terminated.
* string: "/system/bin/log" // Process execution command.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
WATCHDOG_TERMINATED_PROCESS = 0x0F32 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -2541,8 +2541,8 @@
* VHAL unhealthy and terminates it.
* If this property is not supported by VHAL, car watchdog doesn't check VHAL health status.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
VHAL_HEARTBEAT = 0x0F33 + 0x10000000 + 0x01000000
+ 0x00500000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT64
@@ -2554,8 +2554,8 @@
* the default UI and a kind of launcher functionality for cluster display.
* the other values are followed by OEM's definition.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
CLUSTER_SWITCH_UI = 0x0F34 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -2578,8 +2578,8 @@
* int32[7]: Inset - right: same format with 'left'
* int32[8]: Inset - bottom: same format with 'left'
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
*/
CLUSTER_DISPLAY_STATE = 0x0F35 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
@@ -2613,8 +2613,8 @@
* and it only supports CALL UI only when the cellular network is available. Then, if the
* nework is avaibale, it'll send [1 1 1], and if it's out of network, it'll send [1 1 0].
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
CLUSTER_REPORT_STATE = 0x0F36 + 0x10000000 + 0x01000000
+ 0x00e00000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:MIXED
@@ -2627,8 +2627,8 @@
*
* int32: the type of ClusterUI to show
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
CLUSTER_REQUEST_DISPLAY = 0x0F37 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
@@ -2637,8 +2637,8 @@
*
* bytes: the serialized message of NavigationStateProto.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
*/
CLUSTER_NAVIGATION_STATE = 0x0F38 + 0x10000000 + 0x01000000
+ 0x00700000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BYTES
@@ -2649,8 +2649,8 @@
* If the head unit is aware of an ETC card attached to the vehicle, this property should
* return the type of card attached; otherwise, this property should be UNAVAILABLE.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum ElectronicTollCollectionCardType
*/
ELECTRONIC_TOLL_COLLECTION_CARD_TYPE = 0x0F39 + 0x10000000 + 0x01000000
@@ -2663,8 +2663,8 @@
* ELECTRONIC_TOLL_COLLECTION_CARD_TYPE gives that status of the card; otherwise,
* this property should be UNAVAILABLE.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum ElectronicTollCollectionCardStatus
*/
ELECTRONIC_TOLL_COLLECTION_CARD_STATUS = 0x0F3A + 0x10000000 + 0x01000000
@@ -2676,8 +2676,8 @@
* Only one of FOG_LIGHTS_STATE or FRONT_FOG_LIGHTS_STATE must be implemented. Please refer to
* the documentation on FOG_LIGHTS_STATE for more information.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
FRONT_FOG_LIGHTS_STATE = 0x0F3B + 0x10000000 + 0x01000000
@@ -2690,8 +2690,8 @@
* Only one of FOG_LIGHTS_SWITCH or FRONT_FOG_LIGHTS_SWITCH must be implemented. Please refer to
* the documentation on FOG_LIGHTS_SWITCH for more information.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
FRONT_FOG_LIGHTS_SWITCH = 0x0F3C + 0x10000000 + 0x01000000
@@ -2704,8 +2704,8 @@
* Only one of FOG_LIGHTS_STATE or REAR_FOG_LIGHTS_STATE must be implemented. Please refer to
* the documentation on FOG_LIGHTS_STATE for more information.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum VehicleLightState
*/
REAR_FOG_LIGHTS_STATE = 0x0F3D + 0x10000000 + 0x01000000
@@ -2718,8 +2718,8 @@
* Only one of FOG_LIGHTS_SWITCH or REAR_FOG_LIGHTS_SWITCH must be implemented. Please refer to
* the documentation on FOG_LIGHTS_SWITCH for more information.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @data_enum VehicleLightSwitch
*/
REAR_FOG_LIGHTS_SWITCH = 0x0F3E + 0x10000000 + 0x01000000
@@ -2731,8 +2731,8 @@
* configArray[0] is used to specify the max current draw allowed by
* the vehicle in Amperes.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
* @unit VehicleUnit:AMPERE
*/
EV_CHARGE_CURRENT_DRAW_LIMIT = 0x0F3F + 0x10000000 + 0x01000000
@@ -2749,8 +2749,8 @@
* then the configArray should be {20, 40, 60, 80, 100}
* If the configArray is empty then all values from 0 to 100 must be valid.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
EV_CHARGE_PERCENT_LIMIT = 0x0F40 + 0x10000000 + 0x01000000
+ 0x00600000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:FLOAT
@@ -2760,8 +2760,8 @@
*
* Returns the current charging state of the car.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum EvChargeState
*/
EV_CHARGE_STATE = 0x0F41 + 0x10000000 + 0x01000000
@@ -2773,8 +2773,8 @@
* The setting that the user wants. Setting this property to true starts the battery charging
* and setting to false stops charging.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
*/
EV_CHARGE_SWITCH = 0x0F42 + 0x10000000 + 0x01000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:BOOLEAN
@@ -2784,8 +2784,8 @@
*
* Returns 0 if the vehicle is not charging.
*
- * @change_mode VehiclePropertyChangeMode:CONTINUOUS
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.CONTINUOUS
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:SECS
*/
EV_CHARGE_TIME_REMAINING = 0x0F43 + 0x10000000 + 0x01000000
@@ -2797,8 +2797,8 @@
* Returns the current state associated with the regenerative braking
* setting in the car
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum EvRegenerativeBrakingState
*/
EV_REGENERATIVE_BRAKING_STATE = 0x0F44 + 0x10000000 + 0x01000000
@@ -2809,8 +2809,8 @@
*
* Returns the trailer state of the car.
*
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ
* @data_enum TrailerState
*/
TRAILER_PRESENT = 0x0F45 + 0x10000000 + 0x01000000
@@ -2832,12 +2832,25 @@
* engine fluids, fuel, accessories, driver, passengers and cargo but excluding
* that of any trailers.
*
- * @change_mode VehiclePropertyChangeMode:STATIC
- * @access VehiclePropertyAccess:READ
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:KILOGRAM
*/
VEHICLE_CURB_WEIGHT = 0x0F46 + 0x10000000 + 0x01000000
+ 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+ /**
+ * EU's General security regulation compliance requirement.
+ *
+ * Returns whether general security regulation compliance is required, if
+ * so, what type of requirement.
+ *
+ * @change_mode VehiclePropertyChangeMode.STATIC
+ * @access VehiclePropertyAccess.READ
+ * @data_enum GsrComplianceRequirementType
+ */
+ GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT = 0x0F47 + 0x10000000 + 0x01000000
+ + 0x00400000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32
+
}
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
new file mode 100644
index 0000000..14a694d
--- /dev/null
+++ b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+/**
+ * DO NOT EDIT MANUALLY!!!
+ *
+ * Generated by tools/generate_annotation_enums.py.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_generated_lib_AccessForVehicleProperty_H_
+#define android_hardware_automotive_vehicle_aidl_generated_lib_AccessForVehicleProperty_H_
+
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
+#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyAccess.h>
+
+#include <unordered_map>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehicleProperty = {
+ {VehicleProperty::INFO_VIN, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_MAKE, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_MODEL, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_MODEL_YEAR, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_FUEL_CAPACITY, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_FUEL_TYPE, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_EV_BATTERY_CAPACITY, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_EV_CONNECTOR_TYPE, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_FUEL_DOOR_LOCATION, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_EV_PORT_LOCATION, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_DRIVER_SEAT, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_EXTERIOR_DIMENSIONS, VehiclePropertyAccess::READ},
+ {VehicleProperty::INFO_MULTI_EV_PORT_LOCATIONS, VehiclePropertyAccess::READ},
+ {VehicleProperty::PERF_ODOMETER, VehiclePropertyAccess::READ},
+ {VehicleProperty::PERF_VEHICLE_SPEED, VehiclePropertyAccess::READ},
+ {VehicleProperty::PERF_VEHICLE_SPEED_DISPLAY, VehiclePropertyAccess::READ},
+ {VehicleProperty::PERF_STEERING_ANGLE, VehiclePropertyAccess::READ},
+ {VehicleProperty::PERF_REAR_STEERING_ANGLE, VehiclePropertyAccess::READ},
+ {VehicleProperty::ENGINE_COOLANT_TEMP, VehiclePropertyAccess::READ},
+ {VehicleProperty::ENGINE_OIL_LEVEL, VehiclePropertyAccess::READ},
+ {VehicleProperty::ENGINE_OIL_TEMP, VehiclePropertyAccess::READ},
+ {VehicleProperty::ENGINE_RPM, VehiclePropertyAccess::READ},
+ {VehicleProperty::WHEEL_TICK, VehiclePropertyAccess::READ},
+ {VehicleProperty::FUEL_LEVEL, VehiclePropertyAccess::READ},
+ {VehicleProperty::FUEL_DOOR_OPEN, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EV_BATTERY_LEVEL, VehiclePropertyAccess::READ},
+ {VehicleProperty::EV_CHARGE_PORT_OPEN, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EV_CHARGE_PORT_CONNECTED, VehiclePropertyAccess::READ},
+ {VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::RANGE_REMAINING, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::TIRE_PRESSURE, VehiclePropertyAccess::READ},
+ {VehicleProperty::CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyAccess::READ},
+ {VehicleProperty::GEAR_SELECTION, VehiclePropertyAccess::READ},
+ {VehicleProperty::CURRENT_GEAR, VehiclePropertyAccess::READ},
+ {VehicleProperty::PARKING_BRAKE_ON, VehiclePropertyAccess::READ},
+ {VehicleProperty::PARKING_BRAKE_AUTO_APPLY, VehiclePropertyAccess::READ},
+ {VehicleProperty::FUEL_LEVEL_LOW, VehiclePropertyAccess::READ},
+ {VehicleProperty::NIGHT_MODE, VehiclePropertyAccess::READ},
+ {VehicleProperty::TURN_SIGNAL_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::IGNITION_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::ABS_ACTIVE, VehiclePropertyAccess::READ},
+ {VehicleProperty::TRACTION_CONTROL_ACTIVE, VehiclePropertyAccess::READ},
+ {VehicleProperty::HVAC_FAN_SPEED, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_FAN_DIRECTION, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_TEMPERATURE_CURRENT, VehiclePropertyAccess::READ},
+ {VehicleProperty::HVAC_TEMPERATURE_SET, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_DEFROSTER, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_AC_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_MAX_AC_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_MAX_DEFROST_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_RECIRC_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_DUAL_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_AUTO_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_SEAT_TEMPERATURE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_SIDE_MIRROR_HEAT, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_STEERING_WHEEL_HEAT, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_ACTUAL_FAN_SPEED_RPM, VehiclePropertyAccess::READ},
+ {VehicleProperty::HVAC_POWER_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_FAN_DIRECTION_AVAILABLE, VehiclePropertyAccess::READ},
+ {VehicleProperty::HVAC_AUTO_RECIRC_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_SEAT_VENTILATION, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::DISTANCE_DISPLAY_UNITS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::FUEL_VOLUME_DISPLAY_UNITS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::TIRE_PRESSURE_DISPLAY_UNITS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EV_BATTERY_DISPLAY_UNITS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EXTERNAL_CAR_TIME, VehiclePropertyAccess::READ},
+ {VehicleProperty::ANDROID_EPOCH_TIME, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::STORAGE_ENCRYPTION_BINDING_SEED, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::ENV_OUTSIDE_TEMPERATURE, VehiclePropertyAccess::READ},
+ {VehicleProperty::AP_POWER_STATE_REQ, VehiclePropertyAccess::READ},
+ {VehicleProperty::AP_POWER_STATE_REPORT, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::AP_POWER_BOOTUP_REASON, VehiclePropertyAccess::READ},
+ {VehicleProperty::DISPLAY_BRIGHTNESS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HW_KEY_INPUT, VehiclePropertyAccess::READ},
+ {VehicleProperty::HW_ROTARY_INPUT, VehiclePropertyAccess::READ},
+ {VehicleProperty::HW_CUSTOM_INPUT, VehiclePropertyAccess::READ},
+ {VehicleProperty::DOOR_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::DOOR_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::DOOR_LOCK, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::MIRROR_Z_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::MIRROR_Z_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::MIRROR_Y_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::MIRROR_Y_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::MIRROR_LOCK, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::MIRROR_FOLD, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_MEMORY_SELECT, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::SEAT_MEMORY_SET, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::SEAT_BELT_BUCKLED, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_BELT_HEIGHT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_BELT_HEIGHT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_FORE_AFT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_FORE_AFT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_1_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_1_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_2_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_2_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEIGHT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEIGHT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_DEPTH_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_DEPTH_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_TILT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_TILT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_LUMBAR_FORE_AFT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_LUMBAR_FORE_AFT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEADREST_HEIGHT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEADREST_HEIGHT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEADREST_ANGLE_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEADREST_ANGLE_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_OCCUPANCY, VehiclePropertyAccess::READ},
+ {VehicleProperty::WINDOW_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::WINDOW_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::WINDOW_LOCK, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::VEHICLE_MAP_SERVICE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::OBD2_LIVE_FRAME, VehiclePropertyAccess::READ},
+ {VehicleProperty::OBD2_FREEZE_FRAME, VehiclePropertyAccess::READ},
+ {VehicleProperty::OBD2_FREEZE_FRAME_INFO, VehiclePropertyAccess::READ},
+ {VehicleProperty::OBD2_FREEZE_FRAME_CLEAR, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::HEADLIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::HIGH_BEAM_LIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::FOG_LIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::HAZARD_LIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::HEADLIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HIGH_BEAM_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::FOG_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::HAZARD_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::CABIN_LIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::CABIN_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::READING_LIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::READING_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION, VehiclePropertyAccess::READ},
+ {VehicleProperty::DISABLED_OPTIONAL_FEATURES, VehiclePropertyAccess::READ},
+ {VehicleProperty::INITIAL_USER_INFO, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SWITCH_USER, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::CREATE_USER, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::REMOVE_USER, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EVS_SERVICE_REQUEST, VehiclePropertyAccess::READ},
+ {VehicleProperty::POWER_POLICY_REQ, VehiclePropertyAccess::READ},
+ {VehicleProperty::POWER_POLICY_GROUP_REQ, VehiclePropertyAccess::READ},
+ {VehicleProperty::CURRENT_POWER_POLICY, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::WATCHDOG_ALIVE, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::WATCHDOG_TERMINATED_PROCESS, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::VHAL_HEARTBEAT, VehiclePropertyAccess::READ},
+ {VehicleProperty::CLUSTER_SWITCH_UI, VehiclePropertyAccess::READ},
+ {VehicleProperty::CLUSTER_DISPLAY_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::CLUSTER_REPORT_STATE, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::CLUSTER_REQUEST_DISPLAY, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::CLUSTER_NAVIGATION_STATE, VehiclePropertyAccess::WRITE},
+ {VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_TYPE, VehiclePropertyAccess::READ},
+ {VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_STATUS, VehiclePropertyAccess::READ},
+ {VehicleProperty::FRONT_FOG_LIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::FRONT_FOG_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::REAR_FOG_LIGHTS_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::REAR_FOG_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EV_CHARGE_CURRENT_DRAW_LIMIT, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EV_CHARGE_PERCENT_LIMIT, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EV_CHARGE_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::EV_CHARGE_SWITCH, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::EV_CHARGE_TIME_REMAINING, VehiclePropertyAccess::READ},
+ {VehicleProperty::EV_REGENERATIVE_BRAKING_STATE, VehiclePropertyAccess::READ},
+ {VehicleProperty::TRAILER_PRESENT, VehiclePropertyAccess::READ},
+ {VehicleProperty::VEHICLE_CURB_WEIGHT, VehiclePropertyAccess::READ},
+ {VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyAccess::READ},
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+} // aidl
+
+#endif // android_hardware_automotive_vehicle_aidl_generated_lib_AccessForVehicleProperty_H_
diff --git a/automotive/vehicle/aidl/impl/default_config/Android.bp b/automotive/vehicle/aidl/generated_lib/cpp/Android.bp
similarity index 61%
rename from automotive/vehicle/aidl/impl/default_config/Android.bp
rename to automotive/vehicle/aidl/generated_lib/cpp/Android.bp
index 0feaf23..11d3693 100644
--- a/automotive/vehicle/aidl/impl/default_config/Android.bp
+++ b/automotive/vehicle/aidl/generated_lib/cpp/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,13 +19,9 @@
}
cc_library_headers {
- name: "VehicleHalDefaultConfig",
- vendor: true,
- local_include_dirs: ["include"],
- export_include_dirs: ["include"],
- defaults: ["VehicleHalDefaults"],
- static_libs: ["VehicleHalUtils"],
- header_libs: ["VehicleHalTestUtilHeaders"],
- export_static_lib_headers: ["VehicleHalUtils"],
- export_header_lib_headers: ["VehicleHalTestUtilHeaders"],
+ name: "IVehicleGeneratedHeaders",
+ vendor_available: true,
+ local_include_dirs: ["."],
+ export_include_dirs: ["."],
+ defaults: ["VehicleHalInterfaceDefaults"],
}
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
new file mode 100644
index 0000000..c0416af
--- /dev/null
+++ b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+/**
+ * DO NOT EDIT MANUALLY!!!
+ *
+ * Generated by tools/generate_annotation_enums.py.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_generated_lib_ChangeModeForVehicleProperty_H_
+#define android_hardware_automotive_vehicle_aidl_generated_lib_ChangeModeForVehicleProperty_H_
+
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
+#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyChangeMode.h>
+
+#include <unordered_map>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehicleProperty = {
+ {VehicleProperty::INFO_VIN, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_MAKE, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_MODEL, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_MODEL_YEAR, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_FUEL_CAPACITY, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_FUEL_TYPE, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_EV_BATTERY_CAPACITY, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_EV_CONNECTOR_TYPE, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_FUEL_DOOR_LOCATION, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_EV_PORT_LOCATION, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_DRIVER_SEAT, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_EXTERIOR_DIMENSIONS, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INFO_MULTI_EV_PORT_LOCATIONS, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::PERF_ODOMETER, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::PERF_VEHICLE_SPEED, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::PERF_VEHICLE_SPEED_DISPLAY, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::PERF_STEERING_ANGLE, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::PERF_REAR_STEERING_ANGLE, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::ENGINE_COOLANT_TEMP, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::ENGINE_OIL_LEVEL, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::ENGINE_OIL_TEMP, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::ENGINE_RPM, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::WHEEL_TICK, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::FUEL_LEVEL, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::FUEL_DOOR_OPEN, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_BATTERY_LEVEL, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::EV_CHARGE_PORT_OPEN, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_CHARGE_PORT_CONNECTED, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::RANGE_REMAINING, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::TIRE_PRESSURE, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::GEAR_SELECTION, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CURRENT_GEAR, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::PARKING_BRAKE_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::PARKING_BRAKE_AUTO_APPLY, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::FUEL_LEVEL_LOW, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::NIGHT_MODE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::TURN_SIGNAL_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::IGNITION_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::ABS_ACTIVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::TRACTION_CONTROL_ACTIVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_FAN_SPEED, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_FAN_DIRECTION, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_TEMPERATURE_CURRENT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_TEMPERATURE_SET, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_DEFROSTER, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_AC_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_MAX_AC_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_MAX_DEFROST_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_RECIRC_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_DUAL_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_AUTO_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_SEAT_TEMPERATURE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_SIDE_MIRROR_HEAT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_STEERING_WHEEL_HEAT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_ACTUAL_FAN_SPEED_RPM, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_POWER_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_FAN_DIRECTION_AVAILABLE, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::HVAC_AUTO_RECIRC_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_SEAT_VENTILATION, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::DISTANCE_DISPLAY_UNITS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::FUEL_VOLUME_DISPLAY_UNITS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::TIRE_PRESSURE_DISPLAY_UNITS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_BATTERY_DISPLAY_UNITS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EXTERNAL_CAR_TIME, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::ANDROID_EPOCH_TIME, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::STORAGE_ENCRYPTION_BINDING_SEED, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::ENV_OUTSIDE_TEMPERATURE, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::AP_POWER_STATE_REQ, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::AP_POWER_STATE_REPORT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::AP_POWER_BOOTUP_REASON, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HW_KEY_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HW_ROTARY_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HW_CUSTOM_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::DOOR_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::DOOR_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::DOOR_LOCK, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::MIRROR_Z_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::MIRROR_Z_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::MIRROR_Y_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::MIRROR_Y_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::MIRROR_LOCK, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::MIRROR_FOLD, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_MEMORY_SELECT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_MEMORY_SET, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_BELT_BUCKLED, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_BELT_HEIGHT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_BELT_HEIGHT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_FORE_AFT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_FORE_AFT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_1_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_1_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_2_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_BACKREST_ANGLE_2_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEIGHT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEIGHT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_DEPTH_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_DEPTH_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_TILT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_TILT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_LUMBAR_FORE_AFT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_LUMBAR_FORE_AFT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEADREST_HEIGHT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEADREST_HEIGHT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEADREST_ANGLE_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEADREST_ANGLE_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_OCCUPANCY, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::WINDOW_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::WINDOW_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::WINDOW_LOCK, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::VEHICLE_MAP_SERVICE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::OBD2_LIVE_FRAME, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::OBD2_FREEZE_FRAME, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::OBD2_FREEZE_FRAME_INFO, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::OBD2_FREEZE_FRAME_CLEAR, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HEADLIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HIGH_BEAM_LIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::FOG_LIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HAZARD_LIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HEADLIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HIGH_BEAM_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::FOG_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::HAZARD_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CABIN_LIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CABIN_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::READING_LIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::READING_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::DISABLED_OPTIONAL_FEATURES, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::INITIAL_USER_INFO, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SWITCH_USER, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CREATE_USER, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::REMOVE_USER, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::USER_IDENTIFICATION_ASSOCIATION, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EVS_SERVICE_REQUEST, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::POWER_POLICY_REQ, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::POWER_POLICY_GROUP_REQ, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CURRENT_POWER_POLICY, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::WATCHDOG_ALIVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::WATCHDOG_TERMINATED_PROCESS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::VHAL_HEARTBEAT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CLUSTER_SWITCH_UI, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CLUSTER_DISPLAY_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CLUSTER_REPORT_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CLUSTER_REQUEST_DISPLAY, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::CLUSTER_NAVIGATION_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_TYPE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_STATUS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::FRONT_FOG_LIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::FRONT_FOG_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::REAR_FOG_LIGHTS_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::REAR_FOG_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_CHARGE_CURRENT_DRAW_LIMIT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_CHARGE_PERCENT_LIMIT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_CHARGE_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_CHARGE_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::EV_CHARGE_TIME_REMAINING, VehiclePropertyChangeMode::CONTINUOUS},
+ {VehicleProperty::EV_REGENERATIVE_BRAKING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::TRAILER_PRESENT, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::VEHICLE_CURB_WEIGHT, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyChangeMode::STATIC},
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+} // aidl
+
+#endif // android_hardware_automotive_vehicle_aidl_generated_lib_ChangeModeForVehicleProperty_H_
diff --git a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
new file mode 100644
index 0000000..0a17ba1
--- /dev/null
+++ b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+/**
+ * DO NOT EDIT MANUALLY!!!
+ *
+ * Generated by tools/generate_annotation_enums.py.
+ */
+
+package android.hardware.automotive.vehicle;
+
+import java.util.Map;
+
+public final class AccessForVehicleProperty {
+
+ public static final Map<Integer, Integer> values = Map.ofEntries(
+ Map.entry(VehicleProperty.INFO_VIN, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_MAKE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_MODEL, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_MODEL_YEAR, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_FUEL_CAPACITY, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_FUEL_TYPE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_EV_BATTERY_CAPACITY, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_EV_CONNECTOR_TYPE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_FUEL_DOOR_LOCATION, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_EV_PORT_LOCATION, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_DRIVER_SEAT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_EXTERIOR_DIMENSIONS, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INFO_MULTI_EV_PORT_LOCATIONS, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.PERF_ODOMETER, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.PERF_VEHICLE_SPEED, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.PERF_VEHICLE_SPEED_DISPLAY, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.PERF_STEERING_ANGLE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.PERF_REAR_STEERING_ANGLE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.ENGINE_COOLANT_TEMP, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.ENGINE_OIL_LEVEL, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.ENGINE_OIL_TEMP, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.ENGINE_RPM, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.WHEEL_TICK, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.FUEL_LEVEL, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.FUEL_DOOR_OPEN, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EV_BATTERY_LEVEL, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.EV_CHARGE_PORT_OPEN, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EV_CHARGE_PORT_CONNECTED, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.RANGE_REMAINING, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.TIRE_PRESSURE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.GEAR_SELECTION, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.CURRENT_GEAR, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.PARKING_BRAKE_ON, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.PARKING_BRAKE_AUTO_APPLY, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.FUEL_LEVEL_LOW, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.NIGHT_MODE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.TURN_SIGNAL_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.IGNITION_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.ABS_ACTIVE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.TRACTION_CONTROL_ACTIVE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HVAC_FAN_SPEED, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_FAN_DIRECTION, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_CURRENT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_SET, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_DEFROSTER, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_AC_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_MAX_AC_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_MAX_DEFROST_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_RECIRC_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_DUAL_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_AUTO_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_SEAT_TEMPERATURE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_SIDE_MIRROR_HEAT, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_STEERING_WHEEL_HEAT, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HVAC_POWER_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HVAC_AUTO_RECIRC_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_SEAT_VENTILATION, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_ELECTRIC_DEFROSTER_ON, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_VALUE_SUGGESTION, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.DISTANCE_DISPLAY_UNITS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EV_BATTERY_DISPLAY_UNITS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EXTERNAL_CAR_TIME, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.ANDROID_EPOCH_TIME, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.STORAGE_ENCRYPTION_BINDING_SEED, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.ENV_OUTSIDE_TEMPERATURE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.AP_POWER_STATE_REQ, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.AP_POWER_STATE_REPORT, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.AP_POWER_BOOTUP_REASON, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.DISPLAY_BRIGHTNESS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HW_KEY_INPUT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HW_ROTARY_INPUT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HW_CUSTOM_INPUT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.DOOR_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.DOOR_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.DOOR_LOCK, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.MIRROR_Z_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.MIRROR_Z_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.MIRROR_Y_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.MIRROR_Y_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.MIRROR_LOCK, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.MIRROR_FOLD, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_MEMORY_SELECT, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.SEAT_MEMORY_SET, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.SEAT_BELT_BUCKLED, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_BELT_HEIGHT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_BELT_HEIGHT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_FORE_AFT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_FORE_AFT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_1_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_1_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_2_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_2_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEIGHT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEIGHT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_DEPTH_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_DEPTH_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_TILT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_TILT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_FORE_AFT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_FORE_AFT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_HEIGHT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_HEIGHT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_ANGLE_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_ANGLE_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_OCCUPANCY, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.WINDOW_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.WINDOW_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.WINDOW_LOCK, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.VEHICLE_MAP_SERVICE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.OBD2_LIVE_FRAME, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.OBD2_FREEZE_FRAME, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.OBD2_FREEZE_FRAME_INFO, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.OBD2_FREEZE_FRAME_CLEAR, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.HEADLIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.FOG_LIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HAZARD_LIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.HEADLIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.FOG_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.HAZARD_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.CABIN_LIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.CABIN_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.READING_LIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.READING_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.DISABLED_OPTIONAL_FEATURES, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.INITIAL_USER_INFO, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SWITCH_USER, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.CREATE_USER, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.REMOVE_USER, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.USER_IDENTIFICATION_ASSOCIATION, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EVS_SERVICE_REQUEST, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.POWER_POLICY_REQ, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.POWER_POLICY_GROUP_REQ, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.CURRENT_POWER_POLICY, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.WATCHDOG_ALIVE, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.WATCHDOG_TERMINATED_PROCESS, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.VHAL_HEARTBEAT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.CLUSTER_SWITCH_UI, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.CLUSTER_DISPLAY_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.CLUSTER_REPORT_STATE, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.CLUSTER_REQUEST_DISPLAY, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.CLUSTER_NAVIGATION_STATE, VehiclePropertyAccess.WRITE),
+ Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_TYPE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_STATUS, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.REAR_FOG_LIGHTS_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.REAR_FOG_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EV_CHARGE_CURRENT_DRAW_LIMIT, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EV_CHARGE_PERCENT_LIMIT, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EV_CHARGE_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.EV_CHARGE_SWITCH, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.EV_CHARGE_TIME_REMAINING, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.EV_REGENERATIVE_BRAKING_STATE, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.TRAILER_PRESENT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.VEHICLE_CURB_WEIGHT, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyAccess.READ)
+ );
+
+}
diff --git a/automotive/vehicle/aidl/generated_lib/java/Android.bp b/automotive/vehicle/aidl/generated_lib/java/Android.bp
new file mode 100644
index 0000000..1d612e8
--- /dev/null
+++ b/automotive/vehicle/aidl/generated_lib/java/Android.bp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "IVehicleGeneratedJavaFiles",
+ srcs: [
+ "*.java",
+ ],
+}
diff --git a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
new file mode 100644
index 0000000..5dfcd22
--- /dev/null
+++ b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+/**
+ * DO NOT EDIT MANUALLY!!!
+ *
+ * Generated by tools/generate_annotation_enums.py.
+ */
+
+package android.hardware.automotive.vehicle;
+
+import java.util.Map;
+
+public final class ChangeModeForVehicleProperty {
+
+ public static final Map<Integer, Integer> values = Map.ofEntries(
+ Map.entry(VehicleProperty.INFO_VIN, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_MAKE, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_MODEL, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_MODEL_YEAR, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_FUEL_CAPACITY, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_FUEL_TYPE, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_EV_BATTERY_CAPACITY, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_EV_CONNECTOR_TYPE, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_FUEL_DOOR_LOCATION, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_EV_PORT_LOCATION, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_DRIVER_SEAT, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_EXTERIOR_DIMENSIONS, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INFO_MULTI_EV_PORT_LOCATIONS, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.PERF_ODOMETER, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.PERF_VEHICLE_SPEED, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.PERF_VEHICLE_SPEED_DISPLAY, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.PERF_STEERING_ANGLE, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.PERF_REAR_STEERING_ANGLE, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.ENGINE_COOLANT_TEMP, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.ENGINE_OIL_LEVEL, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.ENGINE_OIL_TEMP, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.ENGINE_RPM, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.WHEEL_TICK, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.FUEL_LEVEL, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.FUEL_DOOR_OPEN, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_BATTERY_LEVEL, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.EV_CHARGE_PORT_OPEN, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_CHARGE_PORT_CONNECTED, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.RANGE_REMAINING, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.TIRE_PRESSURE, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.GEAR_SELECTION, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CURRENT_GEAR, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.PARKING_BRAKE_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.PARKING_BRAKE_AUTO_APPLY, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.FUEL_LEVEL_LOW, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.NIGHT_MODE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.TURN_SIGNAL_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.IGNITION_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.ABS_ACTIVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.TRACTION_CONTROL_ACTIVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_FAN_SPEED, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_FAN_DIRECTION, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_CURRENT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_SET, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_DEFROSTER, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_AC_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_MAX_AC_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_MAX_DEFROST_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_RECIRC_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_DUAL_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_AUTO_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_SEAT_TEMPERATURE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_SIDE_MIRROR_HEAT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_STEERING_WHEEL_HEAT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_POWER_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.HVAC_AUTO_RECIRC_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_SEAT_VENTILATION, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_ELECTRIC_DEFROSTER_ON, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HVAC_TEMPERATURE_VALUE_SUGGESTION, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.DISTANCE_DISPLAY_UNITS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_BATTERY_DISPLAY_UNITS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EXTERNAL_CAR_TIME, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.ANDROID_EPOCH_TIME, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.STORAGE_ENCRYPTION_BINDING_SEED, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.ENV_OUTSIDE_TEMPERATURE, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.AP_POWER_STATE_REQ, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.AP_POWER_STATE_REPORT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.AP_POWER_BOOTUP_REASON, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HW_KEY_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HW_ROTARY_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HW_CUSTOM_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.DOOR_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.DOOR_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.DOOR_LOCK, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.MIRROR_Z_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.MIRROR_Z_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.MIRROR_Y_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.MIRROR_Y_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.MIRROR_LOCK, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.MIRROR_FOLD, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_MEMORY_SELECT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_MEMORY_SET, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_BELT_BUCKLED, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_BELT_HEIGHT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_BELT_HEIGHT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_FORE_AFT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_FORE_AFT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_1_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_1_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_2_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_BACKREST_ANGLE_2_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEIGHT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEIGHT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_DEPTH_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_DEPTH_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_TILT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_TILT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_FORE_AFT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_FORE_AFT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_SIDE_SUPPORT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_HEIGHT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_HEIGHT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_ANGLE_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_ANGLE_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_OCCUPANCY, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.WINDOW_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.WINDOW_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.WINDOW_LOCK, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.VEHICLE_MAP_SERVICE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.OBD2_LIVE_FRAME, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.OBD2_FREEZE_FRAME, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.OBD2_FREEZE_FRAME_INFO, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.OBD2_FREEZE_FRAME_CLEAR, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HEADLIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.FOG_LIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HAZARD_LIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HEADLIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.FOG_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.HAZARD_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CABIN_LIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CABIN_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.READING_LIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.READING_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.DISABLED_OPTIONAL_FEATURES, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.INITIAL_USER_INFO, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SWITCH_USER, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CREATE_USER, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.REMOVE_USER, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.USER_IDENTIFICATION_ASSOCIATION, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EVS_SERVICE_REQUEST, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.POWER_POLICY_REQ, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.POWER_POLICY_GROUP_REQ, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CURRENT_POWER_POLICY, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.WATCHDOG_ALIVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.WATCHDOG_TERMINATED_PROCESS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.VHAL_HEARTBEAT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CLUSTER_SWITCH_UI, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CLUSTER_DISPLAY_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CLUSTER_REPORT_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CLUSTER_REQUEST_DISPLAY, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.CLUSTER_NAVIGATION_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_TYPE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_STATUS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.REAR_FOG_LIGHTS_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.REAR_FOG_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_CHARGE_CURRENT_DRAW_LIMIT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_CHARGE_PERCENT_LIMIT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_CHARGE_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_CHARGE_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.EV_CHARGE_TIME_REMAINING, VehiclePropertyChangeMode.CONTINUOUS),
+ Map.entry(VehicleProperty.EV_REGENERATIVE_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.TRAILER_PRESENT, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.VEHICLE_CURB_WEIGHT, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyChangeMode.STATIC)
+ );
+
+}
diff --git a/automotive/vehicle/aidl/impl/Android.bp b/automotive/vehicle/aidl/impl/Android.bp
index d24a739..73f7df0 100644
--- a/automotive/vehicle/aidl/impl/Android.bp
+++ b/automotive/vehicle/aidl/impl/Android.bp
@@ -19,10 +19,16 @@
}
cc_defaults {
+ name: "VehicleHalInterfaceDefaults",
+ static_libs: [
+ "android.hardware.automotive.vehicle-V2-ndk",
+ ],
+}
+
+cc_defaults {
name: "VehicleHalDefaults",
static_libs: [
"android-automotive-large-parcelable-lib",
- "android.hardware.automotive.vehicle-V1-ndk",
"libmath",
],
shared_libs: [
@@ -37,6 +43,7 @@
"-Wthread-safety",
],
defaults: [
+ "VehicleHalInterfaceDefaults",
"android-automotive-large-parcelable-defaults",
],
}
diff --git a/automotive/vehicle/aidl/impl/README.md b/automotive/vehicle/aidl/impl/README.md
new file mode 100644
index 0000000..121ffd1
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/README.md
@@ -0,0 +1,58 @@
+# AIDL VHAL libraries and reference implementation.
+---
+
+This directory stores the libraries useful for implementing vendor AIDL VHAL.
+This directory also stores a reference fake implementation for AIDL VHAL.
+
+## default_config
+
+Stores the default vehicle property configurations for reference vehicle HAL.
+Vendor implementation could copy this library but must update the configuration
+to meet their own requirements, e.g. enable or disable certain properties or
+update the initial value for certain properties.
+
+## fake_impl
+
+Contains libraries used specifically for the fake reference VHAL implementation.
+These libraries are for test only and must not be directly used for vendor
+VHAL implementation.
+
+These libraries contain test-spcific logic and must not run directly on a real
+vehicle.
+
+## grpc
+
+Stores code for GRPC based VHAL implementation.
+
+## hardware
+
+Defines an interface `IVehicleHardware.h` which vendor must implement for
+vehicle-specific logic if they want to follow our reference VHAL design.
+
+## proto
+
+Stores Some protobuf files translated from AIDL VHAL interface types. These
+files are used in GRPC VHAL implementation.
+
+## utils
+
+Defines a library `VehicleHalUtils` which provides useful utility functions for
+VHAL implementation. Vendor VHAL could use this library.
+
+## vhal
+
+Defines a library `DefaultVehicleHal` which provides generic logic for all VHAL
+implementations (including reference VHAL). Vendor VHAL implementation could
+use this library, along with their own implementation for `IVehicleHardware`
+interface.
+
+Also defines a binary `android.hardware.automotive.vehicle@V1-default-service`
+which is the reference VHAL implementation. It implements `IVehicle.aidl`
+interface. It uses `DefaultVehicleHal`, along with `FakeVehicleHardware`
+(in fake_impl). It simulates the vehicle bus interaction by using an
+in-memory map. Meaning that all properties (except for some special ones) are
+just written into a hash map and read from a hash map without relying on any
+hardware. As a result, the reference implementation can run on emulator or
+any host environment.
+
+Vendor must not directly use the reference implementation for a real vehicle.
\ No newline at end of file
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp
new file mode 100644
index 0000000..6984d5e
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/Android.bp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "VehicleHalJsonConfigLoader",
+ vendor: true,
+ srcs: ["src/*.cpp"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ defaults: ["VehicleHalDefaults"],
+ static_libs: ["VehicleHalUtils"],
+ header_libs: [
+ "IVehicleGeneratedHeaders",
+ ],
+ shared_libs: ["libjsoncpp"],
+}
+
+cc_library {
+ name: "VehicleHalJsonConfigLoaderEnableTestProperties",
+ vendor: true,
+ srcs: ["src/*.cpp"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ defaults: ["VehicleHalDefaults"],
+ static_libs: ["VehicleHalUtils"],
+ header_libs: [
+ "VehicleHalTestUtilHeaders",
+ "IVehicleGeneratedHeaders",
+ ],
+ cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
+ shared_libs: ["libjsoncpp"],
+}
+
+cc_library_headers {
+ name: "VehicleHalJsonConfigLoaderHeaders",
+ vendor: true,
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ defaults: ["VehicleHalDefaults"],
+ static_libs: ["VehicleHalUtils"],
+ header_libs: [
+ "IVehicleGeneratedHeaders",
+ ],
+ shared_libs: ["libjsoncpp"],
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/ConfigDeclaration.h b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/ConfigDeclaration.h
new file mode 100644
index 0000000..40ac129
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/ConfigDeclaration.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_ConfigDeclaration_H_
+#define android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_ConfigDeclaration_H_
+
+#include <VehicleHalTypes.h>
+
+#include <unordered_map>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// ConfigDeclaration represents one property config, its optional initial value and its optional
+// area configs and initial values for each area.
+struct ConfigDeclaration {
+ aidl::android::hardware::automotive::vehicle::VehiclePropConfig config;
+
+ // This value will be used as an initial value for the property. If this field is specified for
+ // property that supports multiple areas then it will be used for all areas unless particular
+ // area is overridden in initialAreaValue field.
+ aidl::android::hardware::automotive::vehicle::RawPropValues initialValue;
+ // Use initialAreaValues if it is necessary to specify different values per each area.
+ std::unordered_map<int32_t, aidl::android::hardware::automotive::vehicle::RawPropValues>
+ initialAreaValues;
+
+ inline bool operator==(const ConfigDeclaration& other) const {
+ return (config == other.config && initialValue == other.initialValue &&
+ initialAreaValues == other.initialAreaValues);
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const ConfigDeclaration& c) {
+ return os << "Config Declaration for property: "
+ << aidl::android::hardware::automotive::vehicle::toString(
+ static_cast<
+ aidl::android::hardware::automotive::vehicle::VehicleProperty>(
+ c.config.prop));
+ }
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_ConfigDeclaration_H_
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h
new file mode 100644
index 0000000..f3bdbd2
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/include/JsonConfigLoader.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_JsonConfigLoader_H_
+#define android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_JsonConfigLoader_H_
+
+#include <ConfigDeclaration.h>
+#include <VehicleHalTypes.h>
+
+#include <android-base/result.h>
+#include <json/json.h>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+// private namespace
+namespace jsonconfigloader_impl {
+
+// An abstract interface that represents a ValueParser for any constant value types.
+class ConstantParserInterface {
+ public:
+ // Parses a constant variable name to its actual value.
+ virtual android::base::Result<int> parseValue(const std::string& name) const = 0;
+ virtual ~ConstantParserInterface() = default;
+};
+
+// A class to parse a value field in JSON config file.
+// If the field is a string and the field is in the format of "XX::XX", the value will be parsed
+// as a constant value in the format of "TYPE::NAME". Otherwise, the field will be return as is
+// converted to the expected type.
+class JsonValueParser final {
+ public:
+ JsonValueParser();
+
+ android::base::Result<std::string> parseStringValue(const std::string& fieldName,
+ const Json::Value& value) const;
+
+ template <class T>
+ android::base::Result<std::vector<T>> parseArray(const std::string& fieldName,
+ const Json::Value& value) const;
+
+ template <class T>
+ android::base::Result<T> parseValue(const std::string& fieldName,
+ const Json::Value& value) const;
+
+ private:
+ template <class T>
+ static android::base::Result<T> convertValueToType(const std::string& fieldName,
+ const Json::Value& value);
+
+ std::optional<std::pair<std::string, std::string>> maybeGetTypeAndValueName(
+ const std::string& jsonFieldValue) const;
+
+ android::base::Result<int> parseConstantValue(
+ const std::pair<std::string, std::string>& typeValueName) const;
+
+ const ConstantParserInterface* getParser(const std::string& type) const {
+ auto it = mConstantParsersByType.find(type);
+ if (it == mConstantParsersByType.end()) {
+ return nullptr;
+ }
+ return it->second.get();
+ }
+
+ private:
+ inline static const std::string DELIMITER = "::";
+ std::unordered_map<std::string, std::unique_ptr<ConstantParserInterface>>
+ mConstantParsersByType;
+};
+
+// The main class to parse a VHAL config file in JSON format.
+class JsonConfigParser {
+ public:
+ android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>> parseJsonConfig(
+ std::istream& is);
+
+ private:
+ JsonValueParser mValueParser;
+
+ // Parses configuration for each property.
+ std::optional<ConfigDeclaration> parseEachProperty(const Json::Value& propJsonValue,
+ std::vector<std::string>* errors);
+ // Tries to parse a JSON value to a specific type.
+ //
+ // If fieldIsOptional is True, then if the field specified by "fieldName" does not exist,
+ // this method will return true without doing anything, otherwise, it will return false.
+ //
+ // @param parentJsonNode The parent node of the field you are going to parse.
+ // @param fieldName The name for the field.
+ // @param fieldIsOptional Whether the field is optional.
+ // @param outPtr The pointer to output to if the field exists and parsing succeeded.
+ // @param errors The error array to append error to if errors are found.
+ // @return true if the field is optional and does not exist or parsed successfully.
+ template <class T>
+ bool tryParseJsonValueToVariable(const Json::Value& parentJsonNode,
+ const std::string& fieldName, bool fieldIsOptional, T* outPtr,
+ std::vector<std::string>* errors);
+ // Tries to parse a JSON value to an array of specific type.
+ //
+ // If fieldIsOptional is True, then if the field specified by "fieldName" does not exist,
+ // this method will return true without doing anything, otherwise, it will return false.
+ //
+ // @param parentJsonNode The parent node of the field you are going to parse.
+ // @param fieldName The name for the field.
+ // @param fieldIsOptional Whether the field is optional.
+ // @param outPtr The pointer to output to if the field exists and parsing succeeded.
+ // @param errors The error array to append error to if errors are found.
+ // @return true if the field is optional and does not exist or parsed successfully.
+ template <class T>
+ bool tryParseJsonArrayToVariable(const Json::Value& parentJsonNode,
+ const std::string& fieldName, bool fieldIsOptional,
+ std::vector<T>* outPtr, std::vector<std::string>* errors);
+ // Parses a JSON field to VehiclePropertyAccess or VehiclePropertyChangeMode.
+ template <class T>
+ void parseAccessChangeMode(
+ const Json::Value& parentJsonNode, const std::string& fieldName, int propId,
+ const std::string& propStr,
+ const std::unordered_map<aidl::android::hardware::automotive::vehicle::VehicleProperty,
+ T>& defaultMap,
+ T* outPtr, std::vector<std::string>* errors);
+
+ // Parses a JSON field to RawPropValues.
+ //
+ // @return True if the field exist and can be parsed to a RawPropValues.
+ bool parsePropValues(const Json::Value& parentJsonNode, const std::string& fieldName,
+ aidl::android::hardware::automotive::vehicle::RawPropValues* outPtr,
+ std::vector<std::string>* errors);
+
+ // Prase a JSON field as an array of area configs.
+ void parseAreas(const Json::Value& parentJsonNode, const std::string& fieldName,
+ ConfigDeclaration* outPtr, std::vector<std::string>* errors);
+};
+
+} // namespace jsonconfigloader_impl
+
+// A class to load vehicle property configs and initial values in JSON format.
+class JsonConfigLoader final {
+ public:
+ JsonConfigLoader();
+
+ // Loads a JSON file stream and parses it to a map from propId to ConfigDeclarations.
+ android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>> loadPropConfig(
+ std::istream& is);
+
+ // Loads a JSON config file and parses it to a map from propId to ConfigDeclarations.
+ android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>> loadPropConfig(
+ const std::string& configPath);
+
+ private:
+ std::unique_ptr<jsonconfigloader_impl::JsonConfigParser> mParser;
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_aidl_impl_default_config_JsonConfigLoader_include_JsonConfigLoader_H_
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
new file mode 100644
index 0000000..e6b7d14
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/src/JsonConfigLoader.cpp
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <JsonConfigLoader.h>
+
+#include <AccessForVehicleProperty.h>
+#include <ChangeModeForVehicleProperty.h>
+#include <PropertyUtils.h>
+
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+#include <TestPropertyUtils.h>
+#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
+#include <android-base/strings.h>
+#include <fstream>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+namespace jsonconfigloader_impl {
+
+using ::aidl::android::hardware::automotive::vehicle::AccessForVehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::ChangeModeForVehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::EvConnectorType;
+using ::aidl::android::hardware::automotive::vehicle::EvsServiceState;
+using ::aidl::android::hardware::automotive::vehicle::EvsServiceType;
+using ::aidl::android::hardware::automotive::vehicle::FuelType;
+using ::aidl::android::hardware::automotive::vehicle::GsrComplianceRequirementType;
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
+using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
+using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaMirror;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
+using ::aidl::android::hardware::automotive::vehicle::VehicleGear;
+using ::aidl::android::hardware::automotive::vehicle::VehicleHvacFanDirection;
+using ::aidl::android::hardware::automotive::vehicle::VehicleIgnitionState;
+using ::aidl::android::hardware::automotive::vehicle::VehicleOilLevel;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
+using ::aidl::android::hardware::automotive::vehicle::VehicleSeatOccupancyState;
+using ::aidl::android::hardware::automotive::vehicle::VehicleTurnSignal;
+using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
+using ::aidl::android::hardware::automotive::vehicle::VehicleVendorPermission;
+
+using ::android::base::Error;
+using ::android::base::Result;
+
+// Defines a map from constant names to constant values, the values defined here corresponds to
+// the "Constants::XXXX" used in JSON config file.
+const std::unordered_map<std::string, int> CONSTANTS_BY_NAME = {
+ {"DOOR_1_RIGHT", DOOR_1_RIGHT},
+ {"DOOR_1_LEFT", DOOR_1_LEFT},
+ {"DOOR_2_RIGHT", DOOR_2_RIGHT},
+ {"DOOR_2_LEFT", DOOR_2_LEFT},
+ {"DOOR_REAR", DOOR_REAR},
+ {"HVAC_ALL", HVAC_ALL},
+ {"HVAC_LEFT", HVAC_LEFT},
+ {"HVAC_RIGHT", HVAC_RIGHT},
+ {"VENDOR_EXTENSION_INT_PROPERTY", VENDOR_EXTENSION_INT_PROPERTY},
+ {"VENDOR_EXTENSION_BOOLEAN_PROPERTY", VENDOR_EXTENSION_BOOLEAN_PROPERTY},
+ {"VENDOR_EXTENSION_STRING_PROPERTY", VENDOR_EXTENSION_STRING_PROPERTY},
+ {"VENDOR_EXTENSION_FLOAT_PROPERTY", VENDOR_EXTENSION_FLOAT_PROPERTY},
+ {"WINDOW_1_LEFT", WINDOW_1_LEFT},
+ {"WINDOW_1_RIGHT", WINDOW_1_RIGHT},
+ {"WINDOW_2_LEFT", WINDOW_2_LEFT},
+ {"WINDOW_2_RIGHT", WINDOW_2_RIGHT},
+ {"WINDOW_ROOF_TOP_1", WINDOW_ROOF_TOP_1},
+ {"WINDOW_1_RIGHT_2_LEFT_2_RIGHT", WINDOW_1_RIGHT | WINDOW_2_LEFT | WINDOW_2_RIGHT},
+ {"SEAT_1_LEFT", SEAT_1_LEFT},
+ {"SEAT_1_RIGHT", SEAT_1_RIGHT},
+ {"SEAT_2_LEFT", SEAT_2_LEFT},
+ {"SEAT_2_RIGHT", SEAT_2_RIGHT},
+ {"SEAT_2_CENTER", SEAT_2_CENTER},
+ {"WHEEL_REAR_RIGHT", WHEEL_REAR_RIGHT},
+ {"WHEEL_REAR_LEFT", WHEEL_REAR_LEFT},
+ {"WHEEL_FRONT_RIGHT", WHEEL_FRONT_RIGHT},
+ {"WHEEL_FRONT_LEFT", WHEEL_FRONT_LEFT},
+ {"CHARGE_PORT_FRONT_LEFT", CHARGE_PORT_FRONT_LEFT},
+ {"CHARGE_PORT_REAR_LEFT", CHARGE_PORT_REAR_LEFT},
+ {"FAN_DIRECTION_FLOOR", FAN_DIRECTION_FLOOR},
+ {"FAN_DIRECTION_FACE", FAN_DIRECTION_FACE},
+ {"FAN_DIRECTION_DEFROST", FAN_DIRECTION_DEFROST},
+ {"FAN_DIRECTION_FACE_FLOOR", FAN_DIRECTION_FACE | FAN_DIRECTION_FLOOR},
+ {"FAN_DIRECTION_FACE_DEFROST", FAN_DIRECTION_FACE | FAN_DIRECTION_DEFROST},
+ {"FAN_DIRECTION_FLOOR_DEFROST", FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST},
+ {"FAN_DIRECTION_FLOOR_DEFROST_FACE",
+ FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST | FAN_DIRECTION_FACE},
+ {"FUEL_DOOR_REAR_LEFT", FUEL_DOOR_REAR_LEFT},
+ {"LIGHT_STATE_ON", LIGHT_STATE_ON},
+ {"LIGHT_SWITCH_AUTO", LIGHT_SWITCH_AUTO},
+ {"MIRROR_DRIVER_LEFT_RIGHT",
+ toInt(VehicleAreaMirror::DRIVER_LEFT) | toInt(VehicleAreaMirror::DRIVER_RIGHT)},
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+ // Following are test properties:
+ {"ECHO_REVERSE_BYTES", ECHO_REVERSE_BYTES},
+ {"kMixedTypePropertyForTest", kMixedTypePropertyForTest},
+ {"VENDOR_CLUSTER_NAVIGATION_STATE", VENDOR_CLUSTER_NAVIGATION_STATE},
+ {"VENDOR_CLUSTER_REQUEST_DISPLAY", VENDOR_CLUSTER_REQUEST_DISPLAY},
+ {"VENDOR_CLUSTER_SWITCH_UI", VENDOR_CLUSTER_SWITCH_UI},
+ {"VENDOR_CLUSTER_DISPLAY_STATE", VENDOR_CLUSTER_DISPLAY_STATE},
+ {"VENDOR_CLUSTER_REPORT_STATE", VENDOR_CLUSTER_REPORT_STATE},
+ {"PLACEHOLDER_PROPERTY_INT", PLACEHOLDER_PROPERTY_INT},
+ {"PLACEHOLDER_PROPERTY_FLOAT", PLACEHOLDER_PROPERTY_FLOAT},
+ {"PLACEHOLDER_PROPERTY_BOOLEAN", PLACEHOLDER_PROPERTY_BOOLEAN},
+ {"PLACEHOLDER_PROPERTY_STRING", PLACEHOLDER_PROPERTY_STRING}
+#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+};
+
+// A class to parse constant values for type T.
+template <class T>
+class ConstantParser final : public ConstantParserInterface {
+ public:
+ ConstantParser() {
+ for (const T& v : ndk::enum_range<T>()) {
+ std::string name = aidl::android::hardware::automotive::vehicle::toString(v);
+ // We use the same constant for both VehicleUnit::GALLON and VehicleUnit::US_GALLON,
+ // which caused toString() not work properly for US_GALLON. So we explicitly add the
+ // map here.
+ if (name == "GALLON") {
+ mValueByName["US_GALLON"] = toInt(v);
+ }
+ mValueByName[name] = toInt(v);
+ }
+ }
+
+ ~ConstantParser() = default;
+
+ Result<int> parseValue(const std::string& name) const override {
+ auto it = mValueByName.find(name);
+ if (it == mValueByName.end()) {
+ return Error() << "Constant name: " << name << " is not defined";
+ }
+ return it->second;
+ }
+
+ private:
+ std::unordered_map<std::string, int> mValueByName;
+};
+
+// A class to parse constant values defined in CONSTANTS_BY_NAME map.
+class LocalVariableParser final : public ConstantParserInterface {
+ public:
+ ~LocalVariableParser() = default;
+
+ Result<int> parseValue(const std::string& name) const override {
+ auto constantsIt = CONSTANTS_BY_NAME.find(name);
+ if (constantsIt == CONSTANTS_BY_NAME.end()) {
+ return Error() << "Constant variable name: " << name << " is not defined";
+ }
+ return constantsIt->second;
+ }
+};
+
+JsonValueParser::JsonValueParser() {
+ mConstantParsersByType["VehiclePropertyAccess"] =
+ std::make_unique<ConstantParser<VehiclePropertyAccess>>();
+ mConstantParsersByType["VehiclePropertyChangeMode"] =
+ std::make_unique<ConstantParser<VehiclePropertyChangeMode>>();
+ mConstantParsersByType["VehicleGear"] = std::make_unique<ConstantParser<VehicleGear>>();
+ mConstantParsersByType["VehicleAreaWindow"] =
+ std::make_unique<ConstantParser<VehicleAreaWindow>>();
+ mConstantParsersByType["VehicleAreaMirror"] =
+ std::make_unique<ConstantParser<VehicleAreaMirror>>();
+ mConstantParsersByType["VehicleOilLevel"] = std::make_unique<ConstantParser<VehicleOilLevel>>();
+ mConstantParsersByType["VehicleUnit"] = std::make_unique<ConstantParser<VehicleUnit>>();
+ mConstantParsersByType["VehicleSeatOccupancyState"] =
+ std::make_unique<ConstantParser<VehicleSeatOccupancyState>>();
+ mConstantParsersByType["VehicleHvacFanDirection"] =
+ std::make_unique<ConstantParser<VehicleHvacFanDirection>>();
+ mConstantParsersByType["VehicleApPowerStateReport"] =
+ std::make_unique<ConstantParser<VehicleApPowerStateReport>>();
+ mConstantParsersByType["VehicleTurnSignal"] =
+ std::make_unique<ConstantParser<VehicleTurnSignal>>();
+ mConstantParsersByType["VehicleVendorPermission"] =
+ std::make_unique<ConstantParser<VehicleVendorPermission>>();
+ mConstantParsersByType["EvsServiceType"] = std::make_unique<ConstantParser<EvsServiceType>>();
+ mConstantParsersByType["EvsServiceState"] = std::make_unique<ConstantParser<EvsServiceState>>();
+ mConstantParsersByType["EvConnectorType"] = std::make_unique<ConstantParser<EvConnectorType>>();
+ mConstantParsersByType["VehicleProperty"] = std::make_unique<ConstantParser<VehicleProperty>>();
+ mConstantParsersByType["GsrComplianceRequirementType"] =
+ std::make_unique<ConstantParser<GsrComplianceRequirementType>>();
+ mConstantParsersByType["VehicleIgnitionState"] =
+ std::make_unique<ConstantParser<VehicleIgnitionState>>();
+ mConstantParsersByType["FuelType"] = std::make_unique<ConstantParser<FuelType>>();
+ mConstantParsersByType["Constants"] = std::make_unique<LocalVariableParser>();
+}
+
+template <>
+Result<int32_t> JsonValueParser::convertValueToType<int32_t>(const std::string& fieldName,
+ const Json::Value& value) {
+ if (!value.isInt()) {
+ return Error() << "The value: " << value << " for field: " << fieldName
+ << " is not in correct type, expect int";
+ }
+ return static_cast<int32_t>(value.asInt());
+}
+
+template <>
+Result<float> JsonValueParser::convertValueToType<float>(const std::string& fieldName,
+ const Json::Value& value) {
+ // isFloat value does not exist, so we use isDouble here.
+ if (!value.isDouble()) {
+ return Error() << "The value: " << value << " for field: " << fieldName
+ << " is not in correct type, expect float";
+ }
+ return value.asFloat();
+}
+
+template <>
+Result<int64_t> JsonValueParser::convertValueToType<int64_t>(const std::string& fieldName,
+ const Json::Value& value) {
+ if (!value.isInt64()) {
+ return Error() << "The value: " << value << " for field: " << fieldName
+ << " is not in correct type, expect int64";
+ }
+ return static_cast<int64_t>(value.asInt64());
+}
+
+template <>
+Result<std::string> JsonValueParser::convertValueToType<std::string>(const std::string& fieldName,
+ const Json::Value& value) {
+ if (!value.isString()) {
+ return Error() << "The value: " << value << " for field: " << fieldName
+ << " is not in correct type, expect string";
+ }
+ return value.asString();
+}
+
+Result<std::string> JsonValueParser::parseStringValue(const std::string& fieldName,
+ const Json::Value& value) const {
+ return convertValueToType<std::string>(fieldName, value);
+}
+
+template <class T>
+Result<T> JsonValueParser::parseValue(const std::string& fieldName,
+ const Json::Value& value) const {
+ if (!value.isString()) {
+ return convertValueToType<T>(fieldName, value);
+ }
+ auto maybeTypeAndValue = maybeGetTypeAndValueName(value.asString());
+ if (!maybeTypeAndValue.has_value()) {
+ return Error() << "Invalid constant value: " << value << " for field: " << fieldName;
+ }
+ auto constantParseResult = parseConstantValue(maybeTypeAndValue.value());
+ if (!constantParseResult.ok()) {
+ return constantParseResult.error();
+ }
+ int constantValue = constantParseResult.value();
+ return static_cast<T>(constantValue);
+}
+
+template <>
+Result<std::string> JsonValueParser::parseValue<std::string>(const std::string& fieldName,
+ const Json::Value& value) const {
+ return parseStringValue(fieldName, value);
+}
+
+template <class T>
+Result<std::vector<T>> JsonValueParser::parseArray(const std::string& fieldName,
+ const Json::Value& value) const {
+ if (!value.isArray()) {
+ return Error() << "The value: " << value << " for field: " << fieldName
+ << " is not in correct type, expect array";
+ }
+ std::vector<T> parsedValues;
+ for (unsigned int i = 0; i < value.size(); i++) {
+ auto result = parseValue<T>(fieldName, value[i]);
+ if (!result.ok()) {
+ return result.error();
+ }
+ parsedValues.push_back(result.value());
+ }
+ return std::move(parsedValues);
+}
+
+std::optional<std::pair<std::string, std::string>> JsonValueParser::maybeGetTypeAndValueName(
+ const std::string& jsonFieldValue) const {
+ size_t pos = jsonFieldValue.find(DELIMITER);
+ if (pos == std::string::npos) {
+ return {};
+ }
+ std::string type = jsonFieldValue.substr(0, pos);
+ std::string valueName = jsonFieldValue.substr(pos + DELIMITER.length(), std::string::npos);
+ if (type != "Constants" && mConstantParsersByType.find(type) == mConstantParsersByType.end()) {
+ return {};
+ }
+ return std::make_pair(type, valueName);
+}
+
+Result<int> JsonValueParser::parseConstantValue(
+ const std::pair<std::string, std::string>& typeValueName) const {
+ const std::string& type = typeValueName.first;
+ const std::string& valueName = typeValueName.second;
+ auto it = mConstantParsersByType.find(type);
+ if (it == mConstantParsersByType.end()) {
+ return Error() << "Unrecognized type: " << type;
+ }
+ auto result = it->second->parseValue(valueName);
+ if (!result.ok()) {
+ return Error() << type << "::" << valueName << " undefined";
+ }
+ return result;
+}
+
+template <class T>
+bool JsonConfigParser::tryParseJsonValueToVariable(const Json::Value& parentJsonNode,
+ const std::string& fieldName,
+ bool fieldIsOptional, T* outPtr,
+ std::vector<std::string>* errors) {
+ if (!parentJsonNode.isObject()) {
+ errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+ return false;
+ }
+ if (!parentJsonNode.isMember(fieldName)) {
+ if (!fieldIsOptional) {
+ errors->push_back("Missing required field: " + fieldName +
+ " in node: " + parentJsonNode.toStyledString());
+ return false;
+ }
+ return true;
+ }
+ auto result = mValueParser.parseValue<T>(fieldName, parentJsonNode[fieldName]);
+ if (!result.ok()) {
+ errors->push_back(result.error().message());
+ return false;
+ }
+ *outPtr = std::move(result.value());
+ return true;
+}
+
+template <class T>
+bool JsonConfigParser::tryParseJsonArrayToVariable(const Json::Value& parentJsonNode,
+ const std::string& fieldName,
+ bool fieldIsOptional, std::vector<T>* outPtr,
+ std::vector<std::string>* errors) {
+ if (!parentJsonNode.isObject()) {
+ errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+ return false;
+ }
+ if (!parentJsonNode.isMember(fieldName)) {
+ if (!fieldIsOptional) {
+ errors->push_back("Missing required field: " + fieldName +
+ " in node: " + parentJsonNode.toStyledString());
+ return false;
+ }
+ return true;
+ }
+ auto result = mValueParser.parseArray<T>(fieldName, parentJsonNode[fieldName]);
+ if (!result.ok()) {
+ errors->push_back(result.error().message());
+ return false;
+ }
+ *outPtr = std::move(result.value());
+ return true;
+}
+
+template <class T>
+void JsonConfigParser::parseAccessChangeMode(
+ const Json::Value& parentJsonNode, const std::string& fieldName, int propId,
+ const std::string& propStr, const std::unordered_map<VehicleProperty, T>& defaultMap,
+ T* outPtr, std::vector<std::string>* errors) {
+ if (!parentJsonNode.isObject()) {
+ errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+ return;
+ }
+ if (parentJsonNode.isMember(fieldName)) {
+ auto result = mValueParser.parseValue<int32_t>(fieldName, parentJsonNode[fieldName]);
+ if (!result.ok()) {
+ errors->push_back(result.error().message());
+ return;
+ }
+ *outPtr = static_cast<T>(result.value());
+ return;
+ }
+ auto it = defaultMap.find(static_cast<VehicleProperty>(propId));
+ if (it == defaultMap.end()) {
+ errors->push_back("No " + fieldName + " specified for property: " + propStr);
+ return;
+ }
+ *outPtr = it->second;
+ return;
+}
+
+bool JsonConfigParser::parsePropValues(const Json::Value& parentJsonNode,
+ const std::string& fieldName, RawPropValues* outPtr,
+ std::vector<std::string>* errors) {
+ if (!parentJsonNode.isObject()) {
+ errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+ return false;
+ }
+ if (!parentJsonNode.isMember(fieldName)) {
+ return false;
+ }
+ const Json::Value& jsonValue = parentJsonNode[fieldName];
+ bool success = true;
+ success &= tryParseJsonArrayToVariable(jsonValue, "int32Values",
+ /*optional=*/true, &(outPtr->int32Values), errors);
+ success &= tryParseJsonArrayToVariable(jsonValue, "floatValues",
+ /*optional=*/true, &(outPtr->floatValues), errors);
+ success &= tryParseJsonArrayToVariable(jsonValue, "int64Values",
+ /*optional=*/true, &(outPtr->int64Values), errors);
+ // We don't support "byteValues" yet.
+ success &= tryParseJsonValueToVariable(jsonValue, "stringValue",
+ /*optional=*/true, &(outPtr->stringValue), errors);
+ return success;
+}
+
+void JsonConfigParser::parseAreas(const Json::Value& parentJsonNode, const std::string& fieldName,
+ ConfigDeclaration* config, std::vector<std::string>* errors) {
+ if (!parentJsonNode.isObject()) {
+ errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
+ return;
+ }
+ if (!parentJsonNode.isMember(fieldName)) {
+ return;
+ }
+ const Json::Value& jsonValue = parentJsonNode[fieldName];
+
+ if (!jsonValue.isArray()) {
+ errors->push_back("Field: " + fieldName + " is not an array");
+ return;
+ }
+ for (unsigned int i = 0; i < jsonValue.size(); i++) {
+ int32_t areaId;
+ const Json::Value& jsonAreaConfig = jsonValue[i];
+ if (!tryParseJsonValueToVariable(jsonAreaConfig, "areaId",
+ /*optional=*/false, &areaId, errors)) {
+ continue;
+ }
+ VehicleAreaConfig areaConfig = {};
+ areaConfig.areaId = areaId;
+ tryParseJsonValueToVariable(jsonAreaConfig, "minInt32Value", /*optional=*/true,
+ &areaConfig.minInt32Value, errors);
+ tryParseJsonValueToVariable(jsonAreaConfig, "maxInt32Value", /*optional=*/true,
+ &areaConfig.maxInt32Value, errors);
+ tryParseJsonValueToVariable(jsonAreaConfig, "minInt64Value", /*optional=*/true,
+ &areaConfig.minInt64Value, errors);
+ tryParseJsonValueToVariable(jsonAreaConfig, "maxInt64Value", /*optional=*/true,
+ &areaConfig.maxInt64Value, errors);
+ tryParseJsonValueToVariable(jsonAreaConfig, "minFloatValue", /*optional=*/true,
+ &areaConfig.minFloatValue, errors);
+ tryParseJsonValueToVariable(jsonAreaConfig, "maxFloatValue", /*optional=*/true,
+ &areaConfig.maxFloatValue, errors);
+ config->config.areaConfigs.push_back(std::move(areaConfig));
+
+ RawPropValues areaValue = {};
+ if (parsePropValues(jsonAreaConfig, "defaultValue", &areaValue, errors)) {
+ config->initialAreaValues[areaId] = std::move(areaValue);
+ }
+ }
+}
+
+std::optional<ConfigDeclaration> JsonConfigParser::parseEachProperty(
+ const Json::Value& propJsonValue, std::vector<std::string>* errors) {
+ size_t initialErrorCount = errors->size();
+ ConfigDeclaration configDecl = {};
+ int32_t propId;
+
+ if (!tryParseJsonValueToVariable(propJsonValue, "property", /*optional=*/false, &propId,
+ errors)) {
+ return std::nullopt;
+ }
+
+ configDecl.config.prop = propId;
+ std::string propStr = propJsonValue["property"].toStyledString();
+
+ parseAccessChangeMode(propJsonValue, "access", propId, propStr, AccessForVehicleProperty,
+ &configDecl.config.access, errors);
+
+ parseAccessChangeMode(propJsonValue, "changeMode", propId, propStr,
+ ChangeModeForVehicleProperty, &configDecl.config.changeMode, errors);
+
+ tryParseJsonValueToVariable(propJsonValue, "configString", /*optional=*/true,
+ &configDecl.config.configString, errors);
+
+ tryParseJsonArrayToVariable(propJsonValue, "configArray", /*optional=*/true,
+ &configDecl.config.configArray, errors);
+
+ parsePropValues(propJsonValue, "defaultValue", &configDecl.initialValue, errors);
+
+ tryParseJsonValueToVariable(propJsonValue, "minSampleRate", /*optional=*/true,
+ &configDecl.config.minSampleRate, errors);
+
+ tryParseJsonValueToVariable(propJsonValue, "maxSampleRate", /*optional=*/true,
+ &configDecl.config.maxSampleRate, errors);
+
+ parseAreas(propJsonValue, "areas", &configDecl, errors);
+
+ if (errors->size() != initialErrorCount) {
+ return std::nullopt;
+ }
+ return configDecl;
+}
+
+Result<std::unordered_map<int32_t, ConfigDeclaration>> JsonConfigParser::parseJsonConfig(
+ std::istream& is) {
+ Json::CharReaderBuilder builder;
+ Json::Value root;
+ std::unordered_map<int32_t, ConfigDeclaration> configsByPropId;
+ std::string errs;
+ if (!Json::parseFromStream(builder, is, &root, &errs)) {
+ return Error() << "Failed to parse property config file as JSON, error: " << errs;
+ }
+ if (!root.isObject()) {
+ return Error() << "root element must be an object";
+ }
+ if (!root.isMember("properties") || !root["properties"].isArray()) {
+ return Error() << "Missing 'properties' field in root or the field is not an array";
+ }
+ Json::Value properties = root["properties"];
+ std::vector<std::string> errors;
+ for (unsigned int i = 0; i < properties.size(); i++) {
+ if (auto maybeConfig = parseEachProperty(properties[i], &errors); maybeConfig.has_value()) {
+ configsByPropId[maybeConfig.value().config.prop] = std::move(maybeConfig.value());
+ }
+ }
+ if (!errors.empty()) {
+ return Error() << android::base::Join(errors, '\n');
+ }
+ return configsByPropId;
+}
+
+} // namespace jsonconfigloader_impl
+
+JsonConfigLoader::JsonConfigLoader() {
+ mParser = std::make_unique<jsonconfigloader_impl::JsonConfigParser>();
+}
+
+android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>>
+JsonConfigLoader::loadPropConfig(std::istream& is) {
+ return mParser->parseJsonConfig(is);
+}
+
+android::base::Result<std::unordered_map<int32_t, ConfigDeclaration>>
+JsonConfigLoader::loadPropConfig(const std::string& configPath) {
+ std::ifstream ifs(configPath.c_str());
+ if (!ifs) {
+ return android::base::Error() << "couldn't open " << configPath << " for parsing.";
+ }
+
+ return loadPropConfig(ifs);
+}
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp
new file mode 100644
index 0000000..dae37b9
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/Android.bp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "JsonConfigLoaderUnitTest",
+ vendor: true,
+ srcs: ["*.cpp"],
+ static_libs: [
+ "VehicleHalJsonConfigLoader",
+ "VehicleHalUtils",
+ "libgtest",
+ ],
+ shared_libs: [
+ "libjsoncpp",
+ ],
+ defaults: ["VehicleHalDefaults"],
+ test_suites: ["device-tests"],
+}
+
+cc_test {
+ name: "JsonConfigLoaderUnitTestEnableTestProperties",
+ vendor: true,
+ srcs: ["*.cpp"],
+ static_libs: [
+ "VehicleHalJsonConfigLoaderEnableTestProperties",
+ "VehicleHalUtils",
+ "libgtest",
+ ],
+ shared_libs: [
+ "libjsoncpp",
+ ],
+ defaults: ["VehicleHalDefaults"],
+ test_suites: ["device-tests"],
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp
new file mode 100644
index 0000000..9ff8c1b
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/JsonConfigLoader/test/JsonConfigLoaderUnitTest.cpp
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <JsonConfigLoader.h>
+
+#include <PropertyUtils.h>
+
+#include <gtest/gtest.h>
+#include <sstream>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
+using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
+
+class JsonConfigLoaderUnitTest : public ::testing::Test {
+ protected:
+ JsonConfigLoader mLoader;
+};
+
+TEST_F(JsonConfigLoaderUnitTest, testBasic) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": 291504388
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.config.prop, 291504388);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testRootNotObject) {
+ std::istringstream iss(R"(
+ []
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok()) << "root is not an object must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMissingPropertiesField) {
+ std::istringstream iss(R"(
+ {
+ "abcd": 1234
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok()) << "Missing 'properties' field must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertiesFieldNotArray) {
+ std::istringstream iss(R"(
+ {
+ "properties': {'a': 'b'}
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "'properties' field is not an array must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertyIsEnum) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY"
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.config.prop, toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertyEnum_FailInvalidEnum) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::BLAH"
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Invalid VehicleProperty enum must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testPropertyEnum_FailInvalidType) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "test"
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Invalid VehicleProperty type must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testProperty_FailInvalidJson) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok()) << "Invalid JSON format must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArray) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configArray": [1, 2, 3]
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.config.configArray, std::vector<int>({1, 2, 3}));
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArrayConstants) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configArray": [1, 2, "Constants::FUEL_DOOR_REAR_LEFT"]
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.config.configArray,
+ std::vector<int>({1, 2, FUEL_DOOR_REAR_LEFT}));
+}
+
+// We have special logic to deal with GALLON and US_GALLON since they share the same value.
+TEST_F(JsonConfigLoaderUnitTest, testConfigArrayUnitGallon) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configArray": [1, 2, "VehicleUnit::GALLON"]
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArrayUnitUsGallon) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configArray": [1, 2, "VehicleUnit::US_GALLON"]
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArray_FailInvalidEnum) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configArray": [1, 2, "VehicleUnits::BLAH"]
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Invalid enum in ConfigArray must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigArray_FailNotArray) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configArray": "123"
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "ConfigArray is not an array must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigString) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configString": "test"
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.config.configString, "test");
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testConfigString_FailNotString) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "configString": 1234
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "ConfigString is not a String must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCheckDefaultAccessChangeMode) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY"
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+
+ const VehiclePropConfig& propConfig = configs.begin()->second.config;
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::READ);
+ ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAccessOverride) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "access": "VehiclePropertyAccess::WRITE"
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+
+ const VehiclePropConfig& propConfig = configs.begin()->second.config;
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::WRITE);
+ ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testChangeModeOverride) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+
+ const VehiclePropConfig& propConfig = configs.begin()->second.config;
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::READ);
+ ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::ON_CHANGE);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCustomProp) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": 1234,
+ "access": "VehiclePropertyAccess::WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+
+ const VehiclePropConfig& propConfig = configs.begin()->second.config;
+ ASSERT_EQ(propConfig.access, VehiclePropertyAccess::WRITE);
+ ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::ON_CHANGE);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCustomProp_FailMissingAccess) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": 1234,
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Missing access for custom property must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testCustomProp_FailMissingChangeMode) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": 1234,
+ "access": "VehiclePropertyAccess::WRITE"
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Missing change mode for custom property must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMinSampleRate) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "minSampleRate": 1,
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.config.minSampleRate, 1);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMinSampleRate_FailInvalidType) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "minSampleRate": "abcd",
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Wrong type for MinSampleRate must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMaxSampleRate) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "maxSampleRate": 1,
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.config.maxSampleRate, 1);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testMaxSampleRate_FailInvalidType) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "maxSampleRate": "abcd",
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Wrong type for MaxSampleRate must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_Simple) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "defaultValue": {
+ "int32Values": [1, 2]
+ }
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+ ASSERT_EQ(configs.begin()->second.initialValue.int32Values, std::vector<int32_t>({1, 2}));
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_Mixed) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "defaultValue": {
+ "int32Values": [1, "Constants::FUEL_DOOR_REAR_LEFT"],
+ "int64Values": [2, "Constants::FUEL_DOOR_REAR_LEFT"],
+ "floatValues": [3.0, "Constants::FUEL_DOOR_REAR_LEFT"],
+ "stringValue": "abcd"
+ }
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+
+ const RawPropValues& initialValue = configs.begin()->second.initialValue;
+ ASSERT_EQ(initialValue.int32Values, std::vector<int32_t>({1, FUEL_DOOR_REAR_LEFT}));
+ ASSERT_EQ(initialValue.int64Values,
+ std::vector<int64_t>({2, static_cast<int64_t>(FUEL_DOOR_REAR_LEFT)}));
+ ASSERT_EQ(initialValue.floatValues,
+ std::vector<float>({3.0, static_cast<float>(FUEL_DOOR_REAR_LEFT)}));
+ ASSERT_EQ(initialValue.stringValue, "abcd");
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_FailNotObject) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "defaultValue": []
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "DefaultValue is not an object must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testDefaultValue_FailInvalidType) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "defaultValue": {
+ "int32Values": [1.1]
+ }
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Wrong type for DefaultValue must cause error";
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAreas_Simple) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "areas": [{
+ "areaId": "Constants::HVAC_ALL",
+ "minInt32Value": 1,
+ "maxInt32Value": 7
+ }]
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+
+ const VehiclePropConfig& config = configs.begin()->second.config;
+ ASSERT_EQ(config.areaConfigs.size(), 1u);
+ const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
+ ASSERT_EQ(areaConfig.minInt32Value, 1);
+ ASSERT_EQ(areaConfig.maxInt32Value, 7);
+ ASSERT_EQ(areaConfig.areaId, HVAC_ALL);
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAreas_DefaultValueForEachArea) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "areas": [{
+ "areaId": "Constants::HVAC_LEFT",
+ "defaultValue": {
+ "int32Values": [1]
+ }
+ }, {
+ "areaId": "Constants::HVAC_RIGHT",
+ "defaultValue": {
+ "int32Values": [2]
+ }
+ }]
+ }]
+ }
+ )");
+
+ auto result = mLoader.loadPropConfig(iss);
+
+ auto configs = result.value();
+ ASSERT_EQ(configs.size(), 1u);
+
+ const VehiclePropConfig& config = configs.begin()->second.config;
+ ASSERT_EQ(config.areaConfigs.size(), 2u);
+ ASSERT_EQ(config.areaConfigs[0].areaId, HVAC_LEFT);
+ ASSERT_EQ(config.areaConfigs[1].areaId, HVAC_RIGHT);
+ ASSERT_EQ(configs.begin()->second.initialAreaValues[HVAC_LEFT],
+ RawPropValues{.int32Values = {1}});
+ ASSERT_EQ(configs.begin()->second.initialAreaValues[HVAC_RIGHT],
+ RawPropValues{.int32Values = {2}});
+}
+
+TEST_F(JsonConfigLoaderUnitTest, testAreas_FailInvalidTypeForOneAreaValue) {
+ std::istringstream iss(R"(
+ {
+ "properties": [{
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "areas": [{
+ "areaId": "Constants::HVAC_LEFT",
+ "defaultValue": {
+ "int32Values": [1]
+ }
+ }, {
+ "areaId": "Constants::HVAC_RIGHT",
+ "defaultValue": {
+ "int32Values": [2.1]
+ }
+ }]
+ }]
+ }
+ )");
+
+ ASSERT_FALSE(mLoader.loadPropConfig(iss).ok())
+ << "Wrong type for DefaultValue for one area must cause error";
+}
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/vehicle/aidl/impl/default_config/config/Android.bp b/automotive/vehicle/aidl/impl/default_config/config/Android.bp
new file mode 100644
index 0000000..8f1c7d1
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/config/Android.bp
@@ -0,0 +1,47 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+filegroup {
+ name: "VehicleHalDefaultProperties_JSON",
+ srcs: ["DefaultProperties.json"],
+}
+
+filegroup {
+ name: "VehicleHalTestProperties_JSON",
+ srcs: ["TestProperties.json"],
+}
+
+filegroup {
+ name: "VehicleHalVendorClusterTestProperties_JSON",
+ srcs: ["VendorClusterTestProperties.json"],
+}
+
+prebuilt_etc {
+ name: "Prebuilt_VehicleHalDefaultProperties_JSON",
+ filename_from_src: true,
+ src: "DefaultProperties.json",
+ sub_dir: "automotive/vhalconfig/",
+ vendor: true,
+}
+
+prebuilt_etc {
+ name: "Prebuilt_VehicleHalTestProperties_JSON",
+ filename_from_src: true,
+ src: "TestProperties.json",
+ sub_dir: "automotive/vhalconfig/",
+ vendor: true,
+}
+
+prebuilt_etc {
+ name: "Prebuilt_VehicleHalVendorClusterTestProperties_JSON",
+ filename_from_src: true,
+ src: "VendorClusterTestProperties.json",
+ sub_dir: "automotive/vhalconfig/",
+ vendor: true,
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
new file mode 100644
index 0000000..137cb99
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -0,0 +1,2261 @@
+{
+ "apiVersion": 1,
+ "properties": [
+ {
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ "defaultValue": {
+ "floatValues": [
+ 15000.0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_FUEL_TYPE",
+ "defaultValue": {
+ "int32Values": [
+ "FuelType::FUEL_TYPE_UNLEADED"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_EV_BATTERY_CAPACITY",
+ "defaultValue": {
+ "floatValues": [
+ 150000.0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_EV_CONNECTOR_TYPE",
+ "defaultValue": {
+ "int32Values": [
+ "EvConnectorType::IEC_TYPE_1_AC"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_FUEL_DOOR_LOCATION",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::FUEL_DOOR_REAR_LEFT"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_EV_PORT_LOCATION",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::CHARGE_PORT_FRONT_LEFT"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_MULTI_EV_PORT_LOCATIONS",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::CHARGE_PORT_FRONT_LEFT",
+ "Constants::CHARGE_PORT_REAR_LEFT"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_MAKE",
+ "defaultValue": {
+ "stringValue": "Toy Vehicle"
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_MODEL",
+ "defaultValue": {
+ "stringValue": "Speedy Model"
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_MODEL_YEAR",
+ "defaultValue": {
+ "int32Values": [
+ 2020
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INFO_EXTERIOR_DIMENSIONS",
+ "defaultValue": {
+ "int32Values": [
+ 1776,
+ 4950,
+ 2008,
+ 2140,
+ 2984,
+ 1665,
+ 1667,
+ 11800
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::PERF_VEHICLE_SPEED",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleUnit::KILOMETERS_PER_HOUR"
+ ]
+ },
+ "configArray": [
+ "VehicleUnit::METER_PER_SEC",
+ "VehicleUnit::MILES_PER_HOUR",
+ "VehicleUnit::KILOMETERS_PER_HOUR"
+ ]
+ },
+ {
+ "property": "VehicleProperty::EV_BATTERY_DISPLAY_UNITS",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleUnit::KILOWATT_HOUR"
+ ]
+ },
+ "configArray": [
+ "VehicleUnit::WATT_HOUR",
+ "VehicleUnit::AMPERE_HOURS",
+ "VehicleUnit::KILOWATT_HOUR"
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_BELT_BUCKLED",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT"
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT"
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT"
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT"
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_BELT_HEIGHT_POS",
+ "defaultValue": {
+ "int32Values": [
+ 10
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_BELT_HEIGHT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_FORE_AFT_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_FORE_AFT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_BACKREST_ANGLE_1_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_BACKREST_ANGLE_1_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_BACKREST_ANGLE_2_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_BACKREST_ANGLE_2_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_HEIGHT_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_HEIGHT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_DEPTH_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_DEPTH_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_TILT_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_TILT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_LUMBAR_FORE_AFT_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_LUMBAR_FORE_AFT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_HEADREST_HEIGHT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_HEADREST_ANGLE_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_HEADREST_ANGLE_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_HEADREST_FORE_AFT_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_HEADREST_FORE_AFT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_OCCUPANCY",
+ "areas": [
+ {
+ "defaultValue": {
+ "int32Values": [
+ "VehicleSeatOccupancyState::VACANT"
+ ]
+ },
+ "areaId": "Constants::SEAT_1_LEFT"
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ "VehicleSeatOccupancyState::VACANT"
+ ]
+ },
+ "areaId": "Constants::SEAT_1_RIGHT"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::INFO_DRIVER_SEAT",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::SEAT_1_LEFT"
+ ]
+ },
+ "areas": [
+ {
+ "areaId": 0
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::PERF_ODOMETER",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::PERF_STEERING_ANGLE",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::PERF_REAR_STEERING_ANGLE",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::ENGINE_RPM",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::FUEL_LEVEL",
+ "defaultValue": {
+ "floatValues": [
+ 15000.0
+ ]
+ },
+ "maxSampleRate": 100.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::FUEL_DOOR_OPEN",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::EV_BATTERY_LEVEL",
+ "defaultValue": {
+ "floatValues": [
+ 150000.0
+ ]
+ },
+ "maxSampleRate": 100.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::EV_CHARGE_PORT_OPEN",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::EV_CHARGE_PORT_CONNECTED",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::EV_CHARGE_CURRENT_DRAW_LIMIT",
+ "defaultValue": {
+ "floatValues": [
+ 12.5
+ ]
+ },
+ "comment": "ConfigArray specifies Max current draw allowed by vehicle in amperes",
+ "configArray": [
+ 20
+ ]
+ },
+ {
+ "property": "VehicleProperty::EV_CHARGE_PERCENT_LIMIT",
+ "defaultValue": {
+ "floatValues": [
+ 40.0
+ ]
+ },
+ "configArray": [
+ 20,
+ 40,
+ 60,
+ 80,
+ 100
+ ]
+ },
+ {
+ "property": "VehicleProperty::EV_CHARGE_STATE",
+ "defaultValue": {
+ "int32Values": [
+ 2
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::EV_CHARGE_SWITCH",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::EV_CHARGE_TIME_REMAINING",
+ "defaultValue": {
+ "int32Values": [
+ 20
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::EV_REGENERATIVE_BRAKING_STATE",
+ "defaultValue": {
+ "int32Values": [
+ 2
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::TRAILER_PRESENT",
+ "defaultValue": {
+ "int32Values": [
+ 2
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::VEHICLE_CURB_WEIGHT",
+ "defaultValue": {
+ "int32Values": [
+ 2211
+ ]
+ },
+ "configArray": [
+ 2948
+ ],
+ "comment": "unit is kg"
+ },
+ {
+ "property": "VehicleProperty::RANGE_REMAINING",
+ "defaultValue": {
+ "floatValues": [
+ 50000.0
+ ]
+ },
+ "comment": "units in meter",
+ "maxSampleRate": 2.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::TIRE_PRESSURE",
+ "defaultValue": {
+ "floatValues": [
+ 200.0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::WHEEL_FRONT_LEFT",
+ "minFloatValue": 193.0,
+ "maxFloatValue": 300.0
+ },
+ {
+ "areaId": "Constants::WHEEL_FRONT_RIGHT",
+ "minFloatValue": 193.0,
+ "maxFloatValue": 300.0
+ },
+ {
+ "areaId": "Constants::WHEEL_REAR_LEFT",
+ "minFloatValue": 193.0,
+ "maxFloatValue": 300.0
+ },
+ {
+ "areaId": "Constants::WHEEL_REAR_RIGHT",
+ "minFloatValue": 193.0,
+ "maxFloatValue": 300.0
+ }
+ ],
+ "comment": "Units in kpa",
+ "maxSampleRate": 2.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::CRITICALLY_LOW_TIRE_PRESSURE",
+ "areas": [
+ {
+ "defaultValue": {
+ "floatValues": [
+ 137.0
+ ]
+ },
+ "areaId": "Constants::WHEEL_FRONT_LEFT"
+ },
+ {
+ "defaultValue": {
+ "floatValues": [
+ 137.0
+ ]
+ },
+ "areaId": "Constants::WHEEL_FRONT_RIGHT"
+ },
+ {
+ "defaultValue": {
+ "floatValues": [
+ 137.0
+ ]
+ },
+ "areaId": "Constants::WHEEL_REAR_RIGHT"
+ },
+ {
+ "defaultValue": {
+ "floatValues": [
+ 137.0
+ ]
+ },
+ "areaId": "Constants::WHEEL_REAR_LEFT"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::TIRE_PRESSURE_DISPLAY_UNITS",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleUnit::PSI"
+ ]
+ },
+ "configArray": [
+ "VehicleUnit::KILOPASCAL",
+ "VehicleUnit::PSI",
+ "VehicleUnit::BAR"
+ ]
+ },
+ {
+ "property": "VehicleProperty::CURRENT_GEAR",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleGear::GEAR_PARK"
+ ]
+ },
+ "configArray": [
+ "VehicleGear::GEAR_PARK",
+ "VehicleGear::GEAR_NEUTRAL",
+ "VehicleGear::GEAR_REVERSE",
+ "VehicleGear::GEAR_1",
+ "VehicleGear::GEAR_2",
+ "VehicleGear::GEAR_3",
+ "VehicleGear::GEAR_4",
+ "VehicleGear::GEAR_5"
+ ]
+ },
+ {
+ "property": "VehicleProperty::PARKING_BRAKE_ON",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::PARKING_BRAKE_AUTO_APPLY",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::FUEL_LEVEL_LOW",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::FUEL_VOLUME_DISPLAY_UNITS",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleUnit::LITER"
+ ]
+ },
+ "configArray": [
+ "VehicleUnit::LITER",
+ "VehicleUnit::US_GALLON"
+ ]
+ },
+ {
+ "property": "VehicleProperty::FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::HW_KEY_INPUT",
+ "defaultValue": {
+ "int32Values": [
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::HW_ROTARY_INPUT",
+ "defaultValue": {
+ "int32Values": [
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::HW_CUSTOM_INPUT",
+ "defaultValue": {
+ "int32Values": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "configArray": [
+ 0,
+ 0,
+ 0,
+ 3,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_ACTUAL_FAN_SPEED_RPM",
+ "defaultValue": {
+ "int32Values": [
+ 50
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_POWER_ON",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ],
+ "configArray": [
+ "VehicleProperty::HVAC_FAN_SPEED",
+ "VehicleProperty::HVAC_FAN_DIRECTION"
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_DEFROSTER",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "VehicleAreaWindow::FRONT_WINDSHIELD"
+ },
+ {
+ "areaId": "VehicleAreaWindow::REAR_WINDSHIELD"
+ }
+ ],
+ "comment": "0 means using for all areas"
+ },
+ {
+ "property": "VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "VehicleAreaWindow::FRONT_WINDSHIELD"
+ },
+ {
+ "areaId": "VehicleAreaWindow::REAR_WINDSHIELD"
+ }
+ ],
+ "comment": "0 means using for all areas"
+ },
+ {
+ "property": "VehicleProperty::HVAC_MAX_DEFROST_ON",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_RECIRC_ON",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_AUTO_RECIRC_ON",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_AC_ON",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_MAX_AC_ON",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_AUTO_ON",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_DUAL_ON",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_FAN_SPEED",
+ "defaultValue": {
+ "int32Values": [
+ 3
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL",
+ "minInt32Value": 1,
+ "maxInt32Value": 7
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_FAN_DIRECTION",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleHvacFanDirection::FACE"
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_FAN_DIRECTION_AVAILABLE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::FAN_DIRECTION_FACE",
+ "Constants::FAN_DIRECTION_FLOOR",
+ "Constants::FAN_DIRECTION_FACE_FLOOR",
+ "Constants::FAN_DIRECTION_DEFROST",
+ "Constants::FAN_DIRECTION_FACE_DEFROST",
+ "Constants::FAN_DIRECTION_FLOOR_DEFROST",
+ "Constants::FAN_DIRECTION_FLOOR_DEFROST_FACE"
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::HVAC_ALL"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_SEAT_VENTILATION",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": 0,
+ "maxInt32Value": 3
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 3
+ }
+ ],
+ "comment": "0 is off and +ve values indicate ventilation level."
+ },
+ {
+ "property": "VehicleProperty::HVAC_STEERING_WHEEL_HEAT",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": 0,
+ "minInt32Value": -2,
+ "maxInt32Value": 2
+ }
+ ],
+ "comment": "+ve values for heating and -ve for cooling"
+ },
+ {
+ "property": "VehicleProperty::HVAC_SEAT_TEMPERATURE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -2,
+ "maxInt32Value": 2
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -2,
+ "maxInt32Value": 2
+ }
+ ],
+ "comment": "+ve values for heating and -ve for cooling"
+ },
+ {
+ "property": "VehicleProperty::HVAC_SIDE_MIRROR_HEAT",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::MIRROR_DRIVER_LEFT_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 2
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_TEMPERATURE_CURRENT",
+ "areas": [
+ {
+ "defaultValue": {
+ "floatValues": [
+ 17.3
+ ]
+ },
+ "areaId": 49
+ },
+ {
+ "defaultValue": {
+ "floatValues": [
+ 19.1
+ ]
+ },
+ "areaId": 68
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_TEMPERATURE_SET",
+ "areas": [
+ {
+ "defaultValue": {
+ "floatValues": [
+ 16.0
+ ]
+ },
+ "areaId": 49,
+ "minFloatValue": 16.0,
+ "maxFloatValue": 32.0
+ },
+ {
+ "defaultValue": {
+ "floatValues": [
+ 20.0
+ ]
+ },
+ "areaId": 68,
+ "minFloatValue": 16.0,
+ "maxFloatValue": 32.0
+ }
+ ],
+ "configArray": [
+ 160,
+ 280,
+ 5,
+ 605,
+ 825,
+ 10
+ ]
+ },
+ {
+ "property": "VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION",
+ "defaultValue": {
+ "floatValues": [
+ 66.19999694824219,
+ "VehicleUnit::FAHRENHEIT",
+ 19.0,
+ 66.5
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::ENV_OUTSIDE_TEMPERATURE",
+ "defaultValue": {
+ "floatValues": [
+ 25.0
+ ]
+ },
+ "maxSampleRate": 2.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleUnit::FAHRENHEIT"
+ ]
+ },
+ "configArray": [
+ "VehicleUnit::FAHRENHEIT",
+ "VehicleUnit::CELSIUS"
+ ]
+ },
+ {
+ "property": "VehicleProperty::DISTANCE_DISPLAY_UNITS",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleUnit::MILE"
+ ]
+ },
+ "areas": [
+ {
+ "areaId": 0
+ }
+ ],
+ "configArray": [
+ "VehicleUnit::KILOMETER",
+ "VehicleUnit::MILE"
+ ]
+ },
+ {
+ "property": "VehicleProperty::NIGHT_MODE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::GEAR_SELECTION",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleGear::GEAR_PARK"
+ ]
+ },
+ "configArray": [
+ "VehicleGear::GEAR_PARK",
+ "VehicleGear::GEAR_NEUTRAL",
+ "VehicleGear::GEAR_REVERSE",
+ "VehicleGear::GEAR_DRIVE",
+ "VehicleGear::GEAR_1",
+ "VehicleGear::GEAR_2",
+ "VehicleGear::GEAR_3",
+ "VehicleGear::GEAR_4",
+ "VehicleGear::GEAR_5"
+ ]
+ },
+ {
+ "property": "VehicleProperty::TURN_SIGNAL_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleTurnSignal::NONE"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::IGNITION_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleIgnitionState::ON"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::ENGINE_COOLANT_TEMP",
+ "defaultValue": {
+ "floatValues": [
+ 75.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::ENGINE_OIL_LEVEL",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleOilLevel::NORMAL"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::ENGINE_OIL_TEMP",
+ "defaultValue": {
+ "floatValues": [
+ 101.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 0.10000000149011612
+ },
+ {
+ "property": "VehicleProperty::DOOR_LOCK",
+ "areas": [
+ {
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areaId": "Constants::DOOR_1_LEFT"
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areaId": "Constants::DOOR_1_RIGHT"
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areaId": "Constants::DOOR_2_LEFT"
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areaId": "Constants::DOOR_2_RIGHT"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::DOOR_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::DOOR_1_LEFT",
+ "minInt32Value": 0,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::DOOR_1_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::DOOR_2_LEFT",
+ "minInt32Value": 0,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::DOOR_2_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::DOOR_REAR",
+ "minInt32Value": 0,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::WINDOW_LOCK",
+ "areas": [
+ {
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areaId": "Constants::WINDOW_1_RIGHT_2_LEFT_2_RIGHT"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::WINDOW_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::WINDOW_1_LEFT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::WINDOW_1_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::WINDOW_2_LEFT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::WINDOW_2_RIGHT",
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::WINDOW_ROOF_TOP_1",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::WHEEL_TICK",
+ "defaultValue": {
+ "int64Values": [
+ 0,
+ 100000,
+ 200000,
+ 300000,
+ 400000
+ ]
+ },
+ "configArray": [
+ 15,
+ 50000,
+ 50000,
+ 50000,
+ 50000
+ ],
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
+ "property": "VehicleProperty::ABS_ACTIVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::TRACTION_CONTROL_ACTIVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::AP_POWER_STATE_REQ",
+ "configArray": [
+ 0
+ ]
+ },
+ {
+ "property": "VehicleProperty::AP_POWER_STATE_REPORT",
+ "defaultValue": {
+ "int32Values": [
+ "VehicleApPowerStateReport::WAIT_FOR_VHAL",
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::DISPLAY_BRIGHTNESS",
+ "defaultValue": {
+ "int32Values": [
+ 100
+ ]
+ },
+ "areas": [
+ {
+ "areaId": 0,
+ "minInt32Value": 0,
+ "maxInt32Value": 100
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::OBD2_LIVE_FRAME",
+ "configArray": [
+ 0,
+ 0
+ ]
+ },
+ {
+ "property": "VehicleProperty::OBD2_FREEZE_FRAME",
+ "configArray": [
+ 0,
+ 0
+ ]
+ },
+ {
+ "property": "VehicleProperty::OBD2_FREEZE_FRAME_INFO"
+ },
+ {
+ "property": "VehicleProperty::OBD2_FREEZE_FRAME_CLEAR",
+ "configArray": [
+ 1
+ ]
+ },
+ {
+ "property": "VehicleProperty::HEADLIGHTS_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_STATE_ON"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::HIGH_BEAM_LIGHTS_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_STATE_ON"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::FRONT_FOG_LIGHTS_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_STATE_ON"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::REAR_FOG_LIGHTS_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_STATE_ON"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::HAZARD_LIGHTS_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_STATE_ON"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::CABIN_LIGHTS_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_STATE_ON"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::READING_LIGHTS_STATE",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_STATE_ON"
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT"
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT"
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT"
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT"
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER"
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::HEADLIGHTS_SWITCH",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_SWITCH_AUTO"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::HIGH_BEAM_LIGHTS_SWITCH",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_SWITCH_AUTO"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::FRONT_FOG_LIGHTS_SWITCH",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_SWITCH_AUTO"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::REAR_FOG_LIGHTS_SWITCH",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_SWITCH_AUTO"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::HAZARD_LIGHTS_SWITCH",
+ "defaultValue": {
+ "int32Values": [
+ "Constants::LIGHT_SWITCH_AUTO"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::EVS_SERVICE_REQUEST",
+ "defaultValue": {
+ "int32Values": [
+ "EvsServiceType::REARVIEW",
+ "EvsServiceState::OFF"
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::VEHICLE_MAP_SERVICE"
+ },
+ {
+ "property": "VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_TYPE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_STATUS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ }
+ },
+ {
+ "property": "VehicleProperty::INITIAL_USER_INFO"
+ },
+ {
+ "property": "VehicleProperty::SWITCH_USER"
+ },
+ {
+ "property": "VehicleProperty::CREATE_USER"
+ },
+ {
+ "property": "VehicleProperty::REMOVE_USER"
+ },
+ {
+ "property": "VehicleProperty::USER_IDENTIFICATION_ASSOCIATION"
+ },
+ {
+ "property": "VehicleProperty::POWER_POLICY_REQ"
+ },
+ {
+ "property": "VehicleProperty::POWER_POLICY_GROUP_REQ"
+ },
+ {
+ "property": "VehicleProperty::CURRENT_POWER_POLICY"
+ },
+ {
+ "property": "VehicleProperty::ANDROID_EPOCH_TIME"
+ },
+ {
+ "property": "VehicleProperty::WATCHDOG_ALIVE"
+ },
+ {
+ "property": "VehicleProperty::WATCHDOG_TERMINATED_PROCESS"
+ },
+ {
+ "property": "VehicleProperty::VHAL_HEARTBEAT"
+ },
+ {
+ "property": "VehicleProperty::CLUSTER_SWITCH_UI",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "comment": "0 means ClusterHome"
+ },
+ {
+ "property": "VehicleProperty::CLUSTER_DISPLAY_STATE",
+ "defaultValue": {
+ "int32Values": [
+ 0,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1
+ ]
+ },
+ "comment":
+ "Value means: 0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1, -1, -1 /* Insets */"
+ },
+ {
+ "property": "VehicleProperty::CLUSTER_REPORT_STATE",
+ "configArray": [
+ 0,
+ 0,
+ 0,
+ 11,
+ 0,
+ 0,
+ 0,
+ 0,
+ 16
+ ]
+ },
+ {
+ "property": "VehicleProperty::CLUSTER_REQUEST_DISPLAY"
+ },
+ {
+ "property": "VehicleProperty::CLUSTER_NAVIGATION_STATE"
+ },
+ {
+ "property": "VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT",
+ "defaultValue": {
+ "int32Values": [
+ "GsrComplianceRequirementType::GSR_COMPLIANCE_REQUIRED_THROUGH_SYSTEM_IMAGE"
+ ]
+ }
+ }
+ ]
+}
diff --git a/automotive/vehicle/aidl/impl/default_config/config/README.md b/automotive/vehicle/aidl/impl/default_config/config/README.md
new file mode 100644
index 0000000..0e3fc5e
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/config/README.md
@@ -0,0 +1,151 @@
+# Property Configuration Files
+
+Each JSON file in this folder is a property configuration file for reference
+Vehicle HAL. They contain VehiclePropConfig information along with initial
+value information.
+
+## JSON schema
+
+Each JSON file must be in a schema like the following example:
+(The comment starting with "//" is for documentation only and must be removed
+from the actual JSON file. The "comment" field is used for comment in the
+actual JSON file and will be ignored by the parser)
+
+```
+{
+ // (number) The version for the JSON schema.
+ "apiVersion": 1,
+ // (non-empty array of objects) The property configuration list.
+ //
+ // Each object is a configuration for one property.
+ "properties": [
+ {
+ // (number/string) The ID for the property.
+ // This value is defined in a string value
+ // which represents a constant value, see the "JSON Number-type
+ // Field Values" section for detail.
+ "property": "VehicleProperty::INFO_FUEL_CAPACITY",
+ // (optional, number/string) The access mode for the property.
+ // If specified, this overwrite the default access mode specified in
+ // VehicleProperty.aidl. Must be specified for vendor properties.
+ "access": "VehiclePropertyAccess::READ",
+ // (optional, number/string) The change mode for the property.
+ // If specified, this overwrite the default change mode specified in
+ // VehicleProperty.aidl. Must be specified for vendor properties.
+ "changeMode": "VehiclePropertyChangeMode::STATIC",
+ // (optional, string) The config string.
+ "configString": "blahblah",
+ // (optional, array of number/string) The config array.
+ "configArray": [1, 2, "Constants::HVAC_ALL"],
+ // (optional, object) The default value for the property.
+ // If not specified, the property will be shown as unavailable
+ // until its value is set.
+ "defaultValue": {
+ // (optional, array of int number/string) Int values.
+ "int32Values": [1, 2, "Constants::HVAC_ALL"],
+ // (optional, array of int number/string) Long values.
+ "int64Values": [1, 2],
+ // (optional, array of float number/string) Float values.
+ "floatValues": [1.1, 2.2],
+ // (optional, string) String value.
+ "stringValue": "test"
+ },
+ // (optional, number/string) The minimum sample rate in HZ.
+ // Only work for VehiclePropertyChangeMode::CONTINUOUS property.
+ // Must be specified for continuous property.
+ "minSampleRate": 1,
+ // (optional, number/string) The maximum sample rate in HZ.
+ // Only work for VehiclePropertyChangeMode::CONTINUOUS property.
+ // Must be specified for continuous property.
+ "maxSampleRate": 10,
+ // (optional, array of objects) The area configs.
+ "areas:" [
+ {
+ // (number/string) The area ID.
+ "areaId": "Constants::DOOR_1_LEFT",
+ // (optional number/string) The minimum int value.
+ "minInt32Value": 1,
+ // (optional number/string) The maximum int value.
+ "maxInt32Value": 10,
+ // (optional number/string) The minimum long value.
+ "minInt64Value": 1,
+ // (optional number/string) The maximum long value.
+ "maxInt64Value": 10,
+ // (optional number/string) The minimum float value.
+ "minFloatValue": 1,
+ // (optional number/string) The maximum float value.
+ "maxFloatValue": 10,
+ // (optional object) The default value for this area.
+ // Uses the same format as the "defaultValue" field for
+ // property object. If specified, this overwrite the global
+ // defaultValue.
+ "defaultValue": {
+ "int32Values": [1, 2, "Constants::HVAC_ALL"],
+ "int64Values": [1, 2],
+ "floatValues": [1.1, 2.2],
+ "stringValue": "test"
+ }
+ }
+ ]
+ }
+ ]
+}
+```
+
+## JSON Number-type Field Values
+
+For number type field values, they can either be defined as a numeric number,
+e.g., `{"minInt32Value": 1}` or be defined as a string which represents a
+defined constant value, e.g.,
+`{"property": "VehicleProperty::INFO_FUEL_CAPACITY"}`.
+
+For constant values, they must be a string in the format of `XXX::XXX`, where
+the field before `::` is the constant type, and the field after `::` is the
+variable name.
+
+We support the following constant types:
+
+* VehiclePropertyAccess
+
+* VehiclePropertyChangeMode
+
+* VehicleGear
+
+* VehicleAreaWindow
+
+* VehicleOilLevel
+
+* VehicleUnit
+
+* VehicleSeatOccupancyState
+
+* VehicleHvacFanDirection
+
+* VehicleApPowerStateReport
+
+* VehicleTurnSignal
+
+* VehicleVendorPermission
+
+* EvsServiceType
+
+* EvsServiceState
+
+* EvConnectorType
+
+* VehicleProperty
+
+* GsrComplianceRequirementType
+
+* VehicleIgnitionState
+
+* FuelType
+
+* Constants
+
+Every constant type except "Constants" corresponds to a enum defined in Vehicle
+HAL interfac. E.g. "VehicleProperty" corresponds to the enums defined in
+"VehicleProperty.aidl".
+
+"Constants" type refers to the constant variables defined in the paresr.
+Specifically, the "CONSTANTS_BY_NAME" map defined in "JsonConfigLoader.cpp".
diff --git a/automotive/vehicle/aidl/impl/default_config/config/TestProperties.json b/automotive/vehicle/aidl/impl/default_config/config/TestProperties.json
new file mode 100644
index 0000000..33c6cc2
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/config/TestProperties.json
@@ -0,0 +1,204 @@
+{
+ "properties": [
+ {
+ "property": "Constants::kMixedTypePropertyForTest",
+ "defaultValue": {
+ "floatValues": [
+ 4.5
+ ],
+ "int32Values": [
+ 1,
+ 2,
+ 3
+ ],
+ "stringValue": "MIXED property"
+ },
+ "configArray": [
+ 1,
+ 1,
+ 0,
+ 2,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0
+ ],
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::VENDOR_EXTENSION_BOOLEAN_PROPERTY",
+ "areas": [
+ {
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areaId": "Constants::DOOR_1_LEFT"
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areaId": "Constants::DOOR_1_RIGHT"
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areaId": "Constants::DOOR_2_LEFT"
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areaId": "Constants::DOOR_2_RIGHT"
+ }
+ ],
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::VENDOR_EXTENSION_FLOAT_PROPERTY",
+ "areas": [
+ {
+ "defaultValue": {
+ "floatValues": [
+ 1.0
+ ]
+ },
+ "areaId": "Constants::HVAC_LEFT",
+ "minFloatValue": -10.0,
+ "maxFloatValue": 10.0
+ },
+ {
+ "defaultValue": {
+ "floatValues": [
+ 2.0
+ ]
+ },
+ "areaId": "Constants::HVAC_RIGHT",
+ "minFloatValue": -10.0,
+ "maxFloatValue": 10.0
+ }
+ ],
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::VENDOR_EXTENSION_INT_PROPERTY",
+ "areas": [
+ {
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areaId": "VehicleAreaWindow::FRONT_WINDSHIELD",
+ "minInt32Value": -100,
+ "maxInt32Value": 100
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areaId": "VehicleAreaWindow::REAR_WINDSHIELD",
+ "minInt32Value": -100,
+ "maxInt32Value": 100
+ },
+ {
+ "defaultValue": {
+ "int32Values": [
+ -1
+ ]
+ },
+ "areaId": "VehicleAreaWindow::ROOF_TOP_1",
+ "minInt32Value": -100,
+ "maxInt32Value": 100
+ }
+ ],
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::VENDOR_EXTENSION_STRING_PROPERTY",
+ "defaultValue": {
+ "stringValue": "Vendor String Property"
+ },
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::PLACEHOLDER_PROPERTY_INT",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::PLACEHOLDER_PROPERTY_FLOAT",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::PLACEHOLDER_PROPERTY_BOOLEAN",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::PLACEHOLDER_PROPERTY_STRING",
+ "defaultValue": {
+ "stringValue": "Test"
+ },
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::ECHO_REVERSE_BYTES",
+ "access": "VehiclePropertyAccess::READ_WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "configArray": [
+ "Constants::kMixedTypePropertyForTest",
+ "VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_INFO",
+ "VehicleVendorPermission::PERMISSION_SET_VENDOR_CATEGORY_INFO",
+ "Constants::VENDOR_EXTENSION_INT_PROPERTY",
+ "VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_SEAT",
+ "VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE",
+ "Constants::VENDOR_EXTENSION_FLOAT_PROPERTY",
+ "VehicleVendorPermission::PERMISSION_DEFAULT",
+ "VehicleVendorPermission::PERMISSION_DEFAULT"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/automotive/vehicle/aidl/impl/default_config/config/VendorClusterTestProperties.json b/automotive/vehicle/aidl/impl/default_config/config/VendorClusterTestProperties.json
new file mode 100644
index 0000000..3a1a783
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/default_config/config/VendorClusterTestProperties.json
@@ -0,0 +1,63 @@
+{
+ "properties": [
+ {
+ "property": "Constants::VENDOR_CLUSTER_SWITCH_UI",
+ "access": "VehiclePropertyAccess::WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::VENDOR_CLUSTER_DISPLAY_STATE",
+ "access": "VehiclePropertyAccess::WRITE",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ },
+ {
+ "property": "Constants::VENDOR_CLUSTER_REPORT_STATE",
+ "defaultValue": {
+ "int32Values": [
+ 0,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ 0,
+ -1
+ ]
+ },
+ "configArray": [
+ 0,
+ 0,
+ 0,
+ 11,
+ 0,
+ 0,
+ 0,
+ 0,
+ 16
+ ],
+ "access": "VehiclePropertyAccess::READ",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE",
+ "comment":
+ "Value means 0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1, -1, -1 /* Insets */, 0 /* ClusterHome */, -1 /* ClusterNone */"
+ },
+ {
+ "property": "Constants::VENDOR_CLUSTER_REQUEST_DISPLAY",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "access": "VehiclePropertyAccess::READ",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE",
+ "comment": "0 means ClusterHome"
+ },
+ {
+ "property": "Constants::VENDOR_CLUSTER_NAVIGATION_STATE",
+ "access": "VehiclePropertyAccess::READ",
+ "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h b/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
deleted file mode 100644
index 384b87d..0000000
--- a/automotive/vehicle/aidl/impl/default_config/include/DefaultConfig.h
+++ /dev/null
@@ -1,1953 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#ifndef android_hardware_automotive_vehicle_aidl_impl_default_config_include_DefaultConfig_H_
-#define android_hardware_automotive_vehicle_aidl_impl_default_config_include_DefaultConfig_H_
-
-#include <PropertyUtils.h>
-#include <TestPropertyUtils.h>
-#include <VehicleHalTypes.h>
-
-#include <map>
-#include <vector>
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-
-// Types used in configs, not to be exposed as public API.
-namespace defaultconfig_impl {
-
-using ::aidl::android::hardware::automotive::vehicle::EvConnectorType;
-using ::aidl::android::hardware::automotive::vehicle::EvsServiceState;
-using ::aidl::android::hardware::automotive::vehicle::EvsServiceType;
-using ::aidl::android::hardware::automotive::vehicle::FuelType;
-using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
-using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
-using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
-using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
-using ::aidl::android::hardware::automotive::vehicle::VehicleAreaMirror;
-using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
-using ::aidl::android::hardware::automotive::vehicle::VehicleGear;
-using ::aidl::android::hardware::automotive::vehicle::VehicleHvacFanDirection;
-using ::aidl::android::hardware::automotive::vehicle::VehicleIgnitionState;
-using ::aidl::android::hardware::automotive::vehicle::VehicleOilLevel;
-using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
-using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
-using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
-using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
-using ::aidl::android::hardware::automotive::vehicle::VehicleSeatOccupancyState;
-using ::aidl::android::hardware::automotive::vehicle::VehicleTurnSignal;
-using ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
-using ::aidl::android::hardware::automotive::vehicle::VehicleVendorPermission;
-
-struct ConfigDeclaration {
- VehiclePropConfig config;
-
- // This value will be used as an initial value for the property. If this field is specified for
- // property that supports multiple areas then it will be used for all areas unless particular
- // area is overridden in initialAreaValue field.
- RawPropValues initialValue;
- // Use initialAreaValues if it is necessary to specify different values per each area.
- std::map<int32_t, RawPropValues> initialAreaValues;
-};
-
-const std::vector<ConfigDeclaration> kVehicleProperties = {
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.floatValues = {15000.0f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_FUEL_TYPE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.int32Values = {toInt(FuelType::FUEL_TYPE_UNLEADED)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_EV_BATTERY_CAPACITY),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.floatValues = {150000.0f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_EV_CONNECTOR_TYPE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.int32Values = {toInt(EvConnectorType::IEC_TYPE_1_AC)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_FUEL_DOOR_LOCATION),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.int32Values = {FUEL_DOOR_REAR_LEFT}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_EV_PORT_LOCATION),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.int32Values = {CHARGE_PORT_FRONT_LEFT}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_MULTI_EV_PORT_LOCATIONS),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.int32Values = {CHARGE_PORT_FRONT_LEFT, CHARGE_PORT_REAR_LEFT}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_MAKE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.stringValue = "Toy Vehicle"}},
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_MODEL),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.stringValue = "Speedy Model"}},
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_MODEL_YEAR),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.int32Values = {2020}}},
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_EXTERIOR_DIMENSIONS),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- },
- .initialValue = {.int32Values = {1776, 4950, 2008, 2140, 2984, 1665, 1667, 11800}}},
- {.config =
- {
- .prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.floatValues = {0.0f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {toInt(VehicleUnit::METER_PER_SEC),
- toInt(VehicleUnit::MILES_PER_HOUR),
- toInt(VehicleUnit::KILOMETERS_PER_HOUR)},
- },
- .initialValue = {.int32Values = {toInt(VehicleUnit::KILOMETERS_PER_HOUR)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_BATTERY_DISPLAY_UNITS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {toInt(VehicleUnit::WATT_HOUR),
- toInt(VehicleUnit::AMPERE_HOURS),
- toInt(VehicleUnit::KILOWATT_HOUR)},
- },
- .initialValue = {.int32Values = {toInt(VehicleUnit::KILOWATT_HOUR)}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_BELT_BUCKLED),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_BELT_HEIGHT_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = 0,
- .maxInt32Value = 10}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {10}}},
- {SEAT_1_RIGHT, {.int32Values = {10}}},
- {SEAT_2_LEFT, {.int32Values = {10}}},
- {SEAT_2_RIGHT, {.int32Values = {10}}},
- {SEAT_2_CENTER, {.int32Values = {10}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_BELT_HEIGHT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_FORE_AFT_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_FORE_AFT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_BACKREST_ANGLE_1_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_BACKREST_ANGLE_1_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_BACKREST_ANGLE_2_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_BACKREST_ANGLE_2_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialAreaValues = {{SEAT_1_LEFT, {.int32Values = {0}}},
- {SEAT_1_RIGHT, {.int32Values = {0}}},
- {SEAT_2_LEFT, {.int32Values = {0}}},
- {SEAT_2_RIGHT, {.int32Values = {0}}},
- {SEAT_2_CENTER, {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_HEIGHT_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_HEIGHT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_DEPTH_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_DEPTH_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_TILT_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_TILT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_LUMBAR_FORE_AFT_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -10,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_LUMBAR_FORE_AFT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = 0,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_LUMBAR_SIDE_SUPPORT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_HEADREST_HEIGHT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_HEADREST_ANGLE_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = 0,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_HEADREST_ANGLE_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_HEADREST_FORE_AFT_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = 0,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::SEAT_HEADREST_FORE_AFT_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT,
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER,
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::SEAT_OCCUPANCY),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = (SEAT_1_LEFT)},
- VehicleAreaConfig{.areaId = (SEAT_1_RIGHT)}},
- },
- .initialAreaValues = {{SEAT_1_LEFT,
- {.int32Values = {toInt(VehicleSeatOccupancyState::VACANT)}}},
- {SEAT_1_RIGHT,
- {.int32Values = {toInt(VehicleSeatOccupancyState::VACANT)}}}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::INFO_DRIVER_SEAT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- // this was a zoned property on an old vhal, but it is meant to be global
- .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
- },
- .initialValue = {.int32Values = {SEAT_1_LEFT}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::PERF_ODOMETER),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.floatValues = {0.0f}}},
- {.config =
- {
- .prop = toInt(VehicleProperty::PERF_STEERING_ANGLE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.floatValues = {0.0f}}},
- {.config =
- {
- .prop = toInt(VehicleProperty::PERF_REAR_STEERING_ANGLE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.floatValues = {0.0f}}},
- {
- .config =
- {
- .prop = toInt(VehicleProperty::ENGINE_RPM),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.floatValues = {0.0f}},
- },
-
- {.config =
- {
- .prop = toInt(VehicleProperty::FUEL_LEVEL),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 100.0f,
- },
- .initialValue = {.floatValues = {15000.0f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::FUEL_DOOR_OPEN),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_BATTERY_LEVEL),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 100.0f,
- },
- .initialValue = {.floatValues = {150000.0f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_CHARGE_PORT_OPEN),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_CHARGE_PORT_CONNECTED),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.floatValues = {0.0f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_CHARGE_CURRENT_DRAW_LIMIT),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {/*max current draw allowed by vehicle in amperes=*/20},
- },
- .initialValue = {.floatValues = {(float)12.5}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_CHARGE_PERCENT_LIMIT),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {20, 40, 60, 80, 100},
- },
- .initialValue = {.floatValues = {40}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_CHARGE_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {2}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_CHARGE_SWITCH),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0 /* false */}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_CHARGE_TIME_REMAINING),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.int32Values = {20}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EV_REGENERATIVE_BRAKING_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {2}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::TRAILER_PRESENT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {2}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::VEHICLE_CURB_WEIGHT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- .configArray = {/*gross weight kg=*/2948},
- },
- .initialValue = {.int32Values = {2211 /*kg*/}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::RANGE_REMAINING),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 2.0f,
- },
- .initialValue = {.floatValues = {50000.0f}}}, // units in meters
-
- {.config =
- {
- .prop = toInt(VehicleProperty::TIRE_PRESSURE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .areaConfigs = {VehicleAreaConfig{
- .areaId = WHEEL_FRONT_LEFT,
- .minFloatValue = 193.0f,
- .maxFloatValue = 300.0f,
- },
- VehicleAreaConfig{
- .areaId = WHEEL_FRONT_RIGHT,
- .minFloatValue = 193.0f,
- .maxFloatValue = 300.0f,
- },
- VehicleAreaConfig{
- .areaId = WHEEL_REAR_LEFT,
- .minFloatValue = 193.0f,
- .maxFloatValue = 300.0f,
- },
- VehicleAreaConfig{
- .areaId = WHEEL_REAR_RIGHT,
- .minFloatValue = 193.0f,
- .maxFloatValue = 300.0f,
- }},
- .minSampleRate = 1.0f,
- .maxSampleRate = 2.0f,
- },
- .initialValue = {.floatValues = {200.0f}}}, // units in kPa
-
- {.config =
- {
- .prop = toInt(VehicleProperty::CRITICALLY_LOW_TIRE_PRESSURE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- .areaConfigs = {VehicleAreaConfig{.areaId = WHEEL_FRONT_LEFT},
- VehicleAreaConfig{.areaId = WHEEL_FRONT_RIGHT},
- VehicleAreaConfig{.areaId = WHEEL_REAR_RIGHT},
- VehicleAreaConfig{.areaId = WHEEL_REAR_LEFT}},
- },
- .initialAreaValues = {{WHEEL_FRONT_LEFT, {.floatValues = {137.0f}}},
- {WHEEL_FRONT_RIGHT, {.floatValues = {137.0f}}},
- {WHEEL_REAR_RIGHT, {.floatValues = {137.0f}}},
- {WHEEL_REAR_LEFT, {.floatValues = {137.0f}}}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::TIRE_PRESSURE_DISPLAY_UNITS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {toInt(VehicleUnit::KILOPASCAL), toInt(VehicleUnit::PSI),
- toInt(VehicleUnit::BAR)},
- },
- .initialValue = {.int32Values = {toInt(VehicleUnit::PSI)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::CURRENT_GEAR),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {toInt(VehicleGear::GEAR_PARK),
- toInt(VehicleGear::GEAR_NEUTRAL),
- toInt(VehicleGear::GEAR_REVERSE),
- toInt(VehicleGear::GEAR_1), toInt(VehicleGear::GEAR_2),
- toInt(VehicleGear::GEAR_3), toInt(VehicleGear::GEAR_4),
- toInt(VehicleGear::GEAR_5)},
- },
- .initialValue = {.int32Values = {toInt(VehicleGear::GEAR_PARK)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::PARKING_BRAKE_ON),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {1}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::PARKING_BRAKE_AUTO_APPLY),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {1}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::FUEL_LEVEL_LOW),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::FUEL_VOLUME_DISPLAY_UNITS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {(int)VehicleUnit::LITER, (int)VehicleUnit::US_GALLON},
- },
- .initialValue = {.int32Values = {(int)VehicleUnit::LITER}}},
-
- {.config =
- {
- .prop = toInt(
- VehicleProperty::FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {1}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HW_KEY_INPUT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0, 0, 0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HW_ROTARY_INPUT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0, 0, 0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HW_CUSTOM_INPUT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {0, 0, 0, 3, 0, 0, 0, 0, 0},
- },
- .initialValue =
- {
- .int32Values = {0, 0, 0},
- }},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_ACTUAL_FAN_SPEED_RPM),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {50}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_POWER_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}},
- // TODO(bryaneyler): Ideally, this is generated dynamically from
- // kHvacPowerProperties.
- .configArray = {toInt(VehicleProperty::HVAC_FAN_SPEED),
- toInt(VehicleProperty::HVAC_FAN_DIRECTION)}},
- .initialValue = {.int32Values = {1}}},
-
- {
- .config = {.prop = toInt(VehicleProperty::HVAC_DEFROSTER),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{
- .areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD)},
- VehicleAreaConfig{
- .areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}},
- .initialValue = {.int32Values = {0}} // Will be used for all areas.
- },
- {
- .config = {.prop = toInt(VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{
- .areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD)},
- VehicleAreaConfig{
- .areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}},
- .initialValue = {.int32Values = {0}} // Will be used for all areas.
- },
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_MAX_DEFROST_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_RECIRC_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {1}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_AUTO_RECIRC_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_AC_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {1}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_MAX_AC_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_AUTO_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {1}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_DUAL_ON),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_SPEED),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{
- .areaId = HVAC_ALL, .minInt32Value = 1, .maxInt32Value = 7}}},
- .initialValue = {.int32Values = {3}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {toInt(VehicleHvacFanDirection::FACE)}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION_AVAILABLE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::STATIC,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_ALL}}},
- .initialValue = {.int32Values = {FAN_DIRECTION_FACE, FAN_DIRECTION_FLOOR,
- FAN_DIRECTION_FACE | FAN_DIRECTION_FLOOR,
- FAN_DIRECTION_DEFROST,
- FAN_DIRECTION_FACE | FAN_DIRECTION_DEFROST,
- FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST,
- FAN_DIRECTION_FLOOR | FAN_DIRECTION_DEFROST |
- FAN_DIRECTION_FACE}}},
- {.config = {.prop = toInt(VehicleProperty::HVAC_SEAT_VENTILATION),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{
- .areaId = SEAT_1_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 3,
- },
- VehicleAreaConfig{
- .areaId = SEAT_1_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 3,
- }}},
- .initialValue =
- {.int32Values = {0}}}, // 0 is off and +ve values indicate ventilation level.
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_STEERING_WHEEL_HEAT),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{
- .areaId = (0), .minInt32Value = -2, .maxInt32Value = 2}}},
- .initialValue = {.int32Values = {0}}}, // +ve values for heating and -ve for cooling
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{
- .areaId = SEAT_1_LEFT,
- .minInt32Value = -2,
- .maxInt32Value = 2,
- },
- VehicleAreaConfig{
- .areaId = SEAT_1_RIGHT,
- .minInt32Value = -2,
- .maxInt32Value = 2,
- }}},
- .initialValue = {.int32Values = {0}}}, // +ve values for heating and -ve for cooling
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_SIDE_MIRROR_HEAT),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{
- .areaId = toInt(VehicleAreaMirror::DRIVER_LEFT) |
- toInt(VehicleAreaMirror::DRIVER_RIGHT),
- .minInt32Value = 0,
- .maxInt32Value = 2,
- }}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_CURRENT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_LEFT},
- VehicleAreaConfig{.areaId = HVAC_RIGHT}}},
- .initialAreaValues = {{HVAC_LEFT, {.floatValues = {17.3f}}},
- {HVAC_RIGHT, {.floatValues = {19.1f}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {160, 280, 5, 605, 825, 10},
- .areaConfigs = {VehicleAreaConfig{
- .areaId = HVAC_LEFT,
- .minFloatValue = 16,
- .maxFloatValue = 32,
- },
- VehicleAreaConfig{
- .areaId = HVAC_RIGHT,
- .minFloatValue = 16,
- .maxFloatValue = 32,
- }}},
- .initialAreaValues = {{HVAC_LEFT, {.floatValues = {16}}},
- {HVAC_RIGHT, {.floatValues = {20}}}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.floatValues = {66.2f, (float)VehicleUnit::FAHRENHEIT, 19.0f, 66.5f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::ENV_OUTSIDE_TEMPERATURE),
- .access = VehiclePropertyAccess::READ,
- // TODO(bryaneyler): Support ON_CHANGE as well.
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 2.0f,
- },
- .initialValue = {.floatValues = {25.0f}}},
-
- {.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {toInt(VehicleUnit::FAHRENHEIT), toInt(VehicleUnit::CELSIUS)}},
- .initialValue = {.int32Values = {toInt(VehicleUnit::FAHRENHEIT)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::DISTANCE_DISPLAY_UNITS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
- .configArray = {toInt(VehicleUnit::KILOMETER), toInt(VehicleUnit::MILE)},
- },
- .initialValue = {.int32Values = {toInt(VehicleUnit::MILE)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::NIGHT_MODE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::GEAR_SELECTION),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {toInt(VehicleGear::GEAR_PARK),
- toInt(VehicleGear::GEAR_NEUTRAL),
- toInt(VehicleGear::GEAR_REVERSE),
- toInt(VehicleGear::GEAR_DRIVE), toInt(VehicleGear::GEAR_1),
- toInt(VehicleGear::GEAR_2), toInt(VehicleGear::GEAR_3),
- toInt(VehicleGear::GEAR_4), toInt(VehicleGear::GEAR_5)},
- },
- .initialValue = {.int32Values = {toInt(VehicleGear::GEAR_PARK)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::TURN_SIGNAL_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {toInt(VehicleTurnSignal::NONE)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::IGNITION_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {toInt(VehicleIgnitionState::ON)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::ENGINE_COOLANT_TEMP),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.floatValues = {75.0f}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {toInt(VehicleOilLevel::NORMAL)}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::ENGINE_OIL_TEMP),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .minSampleRate = 0.1, // 0.1 Hz, every 10 seconds
- .maxSampleRate = 10, // 10 Hz, every 100 ms
- },
- .initialValue = {.floatValues = {101.0f}}},
-
- {
- .config = {.prop = kMixedTypePropertyForTest,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {1, 1, 0, 2, 0, 0, 1, 0, 0}},
- .initialValue =
- {
- .int32Values = {1 /* indicate TRUE boolean value */, 2, 3},
- .floatValues = {4.5f},
- .stringValue = "MIXED property",
- },
- },
-
- {.config = {.prop = toInt(VehicleProperty::DOOR_LOCK),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = DOOR_1_LEFT},
- VehicleAreaConfig{.areaId = DOOR_1_RIGHT},
- VehicleAreaConfig{.areaId = DOOR_2_LEFT},
- VehicleAreaConfig{.areaId = DOOR_2_RIGHT}}},
- .initialAreaValues = {{DOOR_1_LEFT, {.int32Values = {1}}},
- {DOOR_1_RIGHT, {.int32Values = {1}}},
- {DOOR_2_LEFT, {.int32Values = {1}}},
- {DOOR_2_RIGHT, {.int32Values = {1}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::DOOR_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{
- .areaId = DOOR_1_LEFT, .minInt32Value = 0, .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = DOOR_1_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 1},
- VehicleAreaConfig{
- .areaId = DOOR_2_LEFT, .minInt32Value = 0, .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = DOOR_2_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 1},
- VehicleAreaConfig{
- .areaId = DOOR_REAR, .minInt32Value = 0, .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::MIRROR_Z_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_LEFT),
- .minInt32Value = -3,
- .maxInt32Value = 3},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_RIGHT),
- .minInt32Value = -3,
- .maxInt32Value = 3},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_CENTER),
- .minInt32Value = -3,
- .maxInt32Value = 3}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::MIRROR_Z_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_LEFT),
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_RIGHT),
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_CENTER),
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::MIRROR_Y_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_LEFT),
- .minInt32Value = -3,
- .maxInt32Value = 3},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_RIGHT),
- .minInt32Value = -3,
- .maxInt32Value = 3},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_CENTER),
- .minInt32Value = -3,
- .maxInt32Value = 3}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::MIRROR_Y_MOVE),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_LEFT),
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_RIGHT),
- .minInt32Value = -1,
- .maxInt32Value = 1},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaMirror::DRIVER_CENTER),
- .minInt32Value = -1,
- .maxInt32Value = 1}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::MIRROR_LOCK),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.int32Values = {1}}},
-
- {.config = {.prop = toInt(VehicleProperty::MIRROR_FOLD),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.int32Values = {1}}},
-
- {.config = {.prop = toInt(VehicleProperty::WINDOW_LOCK),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = WINDOW_1_RIGHT | WINDOW_2_LEFT |
- WINDOW_2_RIGHT}}},
- .initialAreaValues = {{WINDOW_1_RIGHT | WINDOW_2_LEFT | WINDOW_2_RIGHT,
- {.int32Values = {0}}}}},
-
- {.config = {.prop = toInt(VehicleProperty::WINDOW_POS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = WINDOW_1_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = WINDOW_1_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = WINDOW_2_LEFT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = WINDOW_2_RIGHT,
- .minInt32Value = 0,
- .maxInt32Value = 10},
- VehicleAreaConfig{.areaId = WINDOW_ROOF_TOP_1,
- .minInt32Value = -10,
- .maxInt32Value = 10}}},
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = WHEEL_TICK,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
- .configArray = {ALL_WHEELS, 50000, 50000, 50000, 50000},
- .minSampleRate = 1.0f,
- .maxSampleRate = 10.0f,
- },
- .initialValue = {.int64Values = {0, 100000, 200000, 300000, 400000}}},
-
- {.config = {.prop = ABS_ACTIVE,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = TRACTION_CONTROL_ACTIVE,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::AP_POWER_STATE_REQ),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {3}}},
-
- {.config = {.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.int32Values = {toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL), 0}}},
-
- {.config = {.prop = toInt(VehicleProperty::DISPLAY_BRIGHTNESS),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.minInt32Value = 0, .maxInt32Value = 100}}},
- .initialValue = {.int32Values = {100}}},
-
- {
- .config = {.prop = OBD2_LIVE_FRAME,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {0, 0}},
- },
-
- {
- .config = {.prop = OBD2_FREEZE_FRAME,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {0, 0}},
- },
-
- {
- .config = {.prop = OBD2_FREEZE_FRAME_INFO,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- },
-
- {
- .config = {.prop = OBD2_FREEZE_FRAME_CLEAR,
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {1}},
- },
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HEADLIGHTS_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HIGH_BEAM_LIGHTS_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::FRONT_FOG_LIGHTS_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::REAR_FOG_LIGHTS_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HAZARD_LIGHTS_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::CABIN_LIGHTS_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
- {.config = {.prop = toInt(VehicleProperty::READING_LIGHTS_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = SEAT_1_LEFT},
- VehicleAreaConfig{.areaId = SEAT_1_RIGHT},
- VehicleAreaConfig{.areaId = SEAT_2_LEFT},
- VehicleAreaConfig{.areaId = SEAT_2_RIGHT},
- VehicleAreaConfig{.areaId = SEAT_2_CENTER}}},
- .initialValue = {.int32Values = {LIGHT_STATE_ON}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HEADLIGHTS_SWITCH),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HIGH_BEAM_LIGHTS_SWITCH),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
- // FOG_LIGHTS_SWITCH must not be implemented when FRONT_FOG_LIGHTS_SWITCH is implemented
- {.config =
- {
- .prop = toInt(VehicleProperty::FRONT_FOG_LIGHTS_SWITCH),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
- // FOG_LIGHTS_SWITCH must not be implemented when REAR_FOG_LIGHTS_SWITCH is implemented
- {.config =
- {
- .prop = toInt(VehicleProperty::REAR_FOG_LIGHTS_SWITCH),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::HAZARD_LIGHTS_SWITCH),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {LIGHT_SWITCH_AUTO}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::EVS_SERVICE_REQUEST),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {toInt(EvsServiceType::REARVIEW),
- toInt(EvsServiceState::OFF)}}},
-
- {.config = {.prop = VEHICLE_MAP_SERVICE,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE}},
-
- // Example Vendor Extension properties for testing
- {.config = {.prop = VENDOR_EXTENSION_BOOLEAN_PROPERTY,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = DOOR_1_LEFT},
- VehicleAreaConfig{.areaId = DOOR_1_RIGHT},
- VehicleAreaConfig{.areaId = DOOR_2_LEFT},
- VehicleAreaConfig{.areaId = DOOR_2_RIGHT}}},
- .initialAreaValues = {{DOOR_1_LEFT, {.int32Values = {1}}},
- {DOOR_1_RIGHT, {.int32Values = {1}}},
- {DOOR_2_LEFT, {.int32Values = {0}}},
- {DOOR_2_RIGHT, {.int32Values = {0}}}}},
-
- {.config = {.prop = VENDOR_EXTENSION_FLOAT_PROPERTY,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs = {VehicleAreaConfig{.areaId = HVAC_LEFT,
- .minFloatValue = -10,
- .maxFloatValue = 10},
- VehicleAreaConfig{.areaId = HVAC_RIGHT,
- .minFloatValue = -10,
- .maxFloatValue = 10}}},
- .initialAreaValues = {{HVAC_LEFT, {.floatValues = {1}}},
- {HVAC_RIGHT, {.floatValues = {2}}}}},
-
- {.config = {.prop = VENDOR_EXTENSION_INT_PROPERTY,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .areaConfigs =
- {VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD),
- .minInt32Value = -100,
- .maxInt32Value = 100},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD),
- .minInt32Value = -100,
- .maxInt32Value = 100},
- VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::ROOF_TOP_1),
- .minInt32Value = -100,
- .maxInt32Value = 100}}},
- .initialAreaValues = {{toInt(VehicleAreaWindow::FRONT_WINDSHIELD), {.int32Values = {1}}},
- {toInt(VehicleAreaWindow::REAR_WINDSHIELD), {.int32Values = {0}}},
- {toInt(VehicleAreaWindow::ROOF_TOP_1), {.int32Values = {-1}}}}},
-
- {.config = {.prop = VENDOR_EXTENSION_STRING_PROPERTY,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.stringValue = "Vendor String Property"}},
-
- {.config = {.prop = toInt(VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_TYPE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.int32Values = {0}}},
-
- {.config = {.prop = toInt(VehicleProperty::ELECTRONIC_TOLL_COLLECTION_CARD_STATUS),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE},
- .initialValue = {.int32Values = {0}}},
-
- {.config =
- {
- .prop = toInt(VehicleProperty::SUPPORT_CUSTOMIZE_VENDOR_PERMISSION),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {kMixedTypePropertyForTest,
- toInt(VehicleVendorPermission::
- PERMISSION_GET_VENDOR_CATEGORY_INFO),
- toInt(VehicleVendorPermission::
- PERMISSION_SET_VENDOR_CATEGORY_INFO),
- VENDOR_EXTENSION_INT_PROPERTY,
- toInt(VehicleVendorPermission::
- PERMISSION_GET_VENDOR_CATEGORY_SEAT),
- toInt(VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE),
- VENDOR_EXTENSION_FLOAT_PROPERTY,
- toInt(VehicleVendorPermission::PERMISSION_DEFAULT),
- toInt(VehicleVendorPermission::PERMISSION_DEFAULT)},
- },
- .initialValue = {.int32Values = {1}}},
-
- {
- .config =
- {
- .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::SWITCH_USER),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::CREATE_USER),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::REMOVE_USER),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::POWER_POLICY_REQ),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::POWER_POLICY_GROUP_REQ),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::CURRENT_POWER_POLICY),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::ANDROID_EPOCH_TIME),
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::WATCHDOG_ALIVE),
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::WATCHDOG_TERMINATED_PROCESS),
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::CLUSTER_SWITCH_UI),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0 /* ClusterHome */}},
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::CLUSTER_DISPLAY_STATE),
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1,
- -1, -1 /* Insets */}},
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::CLUSTER_REPORT_STATE),
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {0, 0, 0, 11, 0, 0, 0, 0, 16},
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::CLUSTER_REQUEST_DISPLAY),
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = toInt(VehicleProperty::CLUSTER_NAVIGATION_STATE),
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = PLACEHOLDER_PROPERTY_INT,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0}},
- },
- {
- .config =
- {
- .prop = PLACEHOLDER_PROPERTY_FLOAT,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.floatValues = {0.0f}},
- },
- {
- .config =
- {
- .prop = PLACEHOLDER_PROPERTY_BOOLEAN,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0 /* false */}},
- },
- {
- .config =
- {
- .prop = PLACEHOLDER_PROPERTY_STRING,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.stringValue = {"Test"}},
- },
- {
- .config =
- {
- .prop = ECHO_REVERSE_BYTES,
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
-#ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
- // Vendor propetry for E2E ClusterHomeService testing.
- {
- .config =
- {
- .prop = VENDOR_CLUSTER_SWITCH_UI,
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = VENDOR_CLUSTER_DISPLAY_STATE,
- .access = VehiclePropertyAccess::WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
- {
- .config =
- {
- .prop = VENDOR_CLUSTER_REPORT_STATE,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .configArray = {0, 0, 0, 11, 0, 0, 0, 0, 16},
- },
- .initialValue = {.int32Values = {0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1,
- -1, -1 /* Insets */, 0 /* ClusterHome */,
- -1 /* ClusterNone */}},
- },
- {
- .config =
- {
- .prop = VENDOR_CLUSTER_REQUEST_DISPLAY,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- .initialValue = {.int32Values = {0 /* ClusterHome */}},
- },
- {
- .config =
- {
- .prop = VENDOR_CLUSTER_NAVIGATION_STATE,
- .access = VehiclePropertyAccess::READ,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- },
- },
-#endif // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
-};
-
-} // namespace defaultconfig_impl
-
-// public namespace
-namespace defaultconfig {
-
-typedef defaultconfig_impl::ConfigDeclaration ConfigDeclaration;
-
-inline constexpr const std::vector<ConfigDeclaration>& getDefaultConfigs() {
- return defaultconfig_impl::kVehicleProperties;
-}
-
-} // namespace defaultconfig
-
-} // namespace vehicle
-} // namespace automotive
-} // namespace hardware
-} // namespace android
-
-#endif // android_hardware_automotive_vehicle_aidl_impl_default_config_include_DefaultConfig_H_
diff --git a/automotive/vehicle/aidl/impl/default_config/test/Android.bp b/automotive/vehicle/aidl/impl/default_config/test/Android.bp
index 771472c..8702eae 100644
--- a/automotive/vehicle/aidl/impl/default_config/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/default_config/test/Android.bp
@@ -24,11 +24,47 @@
defaults: ["VehicleHalDefaults"],
srcs: ["*.cpp"],
static_libs: [
+ "VehicleHalJsonConfigLoader",
"VehicleHalUtils",
+ "libgmock",
"libgtest",
],
header_libs: [
- "VehicleHalDefaultConfig",
+ "IVehicleGeneratedHeaders",
+ ],
+ shared_libs: [
+ "libjsoncpp",
+ ],
+ data: [
+ ":VehicleHalDefaultProperties_JSON",
+ ],
+ test_suites: ["device-tests"],
+}
+
+cc_test {
+ name: "VehicleHalDefaultConfigTestEnableTestProperties",
+ vendor: true,
+ defaults: ["VehicleHalDefaults"],
+ srcs: ["*.cpp"],
+ static_libs: [
+ "VehicleHalJsonConfigLoaderEnableTestProperties",
+ "VehicleHalUtils",
+ "libgmock",
+ "libgtest",
+ ],
+ cflags: [
+ "-DENABLE_VEHICLE_HAL_TEST_PROPERTIES",
+ ],
+ header_libs: [
+ "IVehicleGeneratedHeaders",
+ ],
+ shared_libs: [
+ "libjsoncpp",
+ ],
+ data: [
+ ":VehicleHalDefaultProperties_JSON",
+ ":VehicleHalTestProperties_JSON",
+ ":VehicleHalVendorClusterTestProperties_JSON",
],
test_suites: ["device-tests"],
}
diff --git a/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp b/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp
index baaae75..95fecfe 100644
--- a/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp
+++ b/automotive/vehicle/aidl/impl/default_config/test/DefaultConfigTest.cpp
@@ -14,27 +14,75 @@
* limitations under the License.
*/
-#include <DefaultConfig.h>
+#include <JsonConfigLoader.h>
#include <VehicleUtils.h>
+#include <android-base/file.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <fstream>
+#include <unordered_map>
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
-namespace defaultconfig {
namespace test {
-TEST(DefaultConfigTest, loadDefaultConfigs) {
- for (ConfigDeclaration config : getDefaultConfigs()) {
- ASSERT_NE(0, config.config.prop);
- }
+using ::android::base::Error;
+using ::android::base::Result;
+using ::testing::UnorderedElementsAreArray;
+
+constexpr char kDefaultPropertiesConfigFile[] = "DefaultProperties.json";
+
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+constexpr char kTestPropertiesConfigFile[] = "TestProperties.json";
+constexpr char kVendorClusterTestPropertiesConfigFile[] = "VendorClusterTestProperties.json";
+#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
+std::string getTestFilePath(const char* filename) {
+ static std::string baseDir = android::base::GetExecutableDirectory();
+ return baseDir + "/" + filename;
}
+Result<std::unordered_map<int32_t, ConfigDeclaration>> loadConfig(JsonConfigLoader& loader,
+ const char* path) {
+ std::string configPath = getTestFilePath(path);
+ std::ifstream ifs(configPath.c_str());
+ if (!ifs) {
+ return Error() << "couldn't open %s for parsing." << configPath;
+ }
+
+ return loader.loadPropConfig(ifs);
+}
+
+TEST(DefaultConfigTest, TestloadDefaultProperties) {
+ JsonConfigLoader loader;
+ auto result = loadConfig(loader, kDefaultPropertiesConfigFile);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
+TEST(DefaultConfigTest, TestloadTestProperties) {
+ JsonConfigLoader loader;
+ auto result = loadConfig(loader, kTestPropertiesConfigFile);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+TEST(DefaultConfigTest, TestloadVendorClusterTestProperties) {
+ JsonConfigLoader loader;
+ auto result = loadConfig(loader, kVendorClusterTestPropertiesConfigFile);
+
+ ASSERT_TRUE(result.ok()) << result.error().message();
+}
+
+#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
+
} // namespace test
-} // namespace defaultconfig
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h
index 9f112ae..f96b6ec 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h
@@ -54,8 +54,8 @@
void registerGenerator(int32_t generatorId, std::unique_ptr<FakeValueGenerator> generator);
// Unregister a generator with the generatorId. If no registered generator is found, this
- // function does nothing.
- void unregisterGenerator(int32_t generatorId);
+ // function does nothing. Returns true if the generator is unregistered.
+ bool unregisterGenerator(int32_t generatorId);
private:
struct VhalEvent {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h
index d421ac5..42b1bd3 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h
@@ -45,6 +45,9 @@
// Create a new JSON fake value generator using the specified JSON file path. All the events
// in the JSON file would be generated once.
explicit JsonFakeValueGenerator(const std::string& path);
+ // Create a new JSON fake value generator using the JSON content. The first argument is just
+ // used to differentiate this function with the one that takes path as input.
+ explicit JsonFakeValueGenerator(bool unused, const std::string& content, int32_t iteration);
~JsonFakeValueGenerator() = default;
@@ -53,6 +56,9 @@
const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
getAllEvents();
+ // Whether there are events left to replay for this generator.
+ bool hasNext();
+
private:
size_t mEventIndex = 0;
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue> mEvents;
@@ -60,7 +66,8 @@
int32_t mNumOfIterations = 0;
void setBit(std::vector<uint8_t>& bytes, size_t idx);
- void init(const std::string& path, int32_t iteration);
+ void initWithPath(const std::string& path, int32_t iteration);
+ void initWithStream(std::istream& is, int32_t iteration);
};
} // namespace fake
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp
index 0c182d9..d815456 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp
@@ -29,11 +29,18 @@
using ::android::base::ScopedLockAssertion;
-GeneratorHub::GeneratorHub(OnHalEvent&& onHalEvent)
- : mOnHalEvent(onHalEvent), mThread(&GeneratorHub::run, this) {}
+GeneratorHub::GeneratorHub(OnHalEvent&& onHalEvent) : mOnHalEvent(onHalEvent) {
+ mThread = std::thread(&GeneratorHub::run, this);
+}
GeneratorHub::~GeneratorHub() {
- mShuttingDownFlag.store(true);
+ {
+ // Even if the shared variable is atomic, it must be modified under the
+ // mutex in order to correctly publish the modification to the waiting
+ // thread.
+ std::unique_lock<std::mutex> lock(mGeneratorsLock);
+ mShuttingDownFlag.store(true);
+ }
mCond.notify_all();
if (mThread.joinable()) {
mThread.join();
@@ -58,13 +65,15 @@
mCond.notify_one();
}
-void GeneratorHub::unregisterGenerator(int32_t id) {
+bool GeneratorHub::unregisterGenerator(int32_t id) {
+ bool removed;
{
std::scoped_lock<std::mutex> lockGuard(mGeneratorsLock);
- mGenerators.erase(id);
+ removed = mGenerators.erase(id);
}
mCond.notify_one();
ALOGI("%s: Unregistered generator, id: %d", __func__, id);
+ return removed;
}
void GeneratorHub::run() {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp
index d4d52a5..cb42317 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp
@@ -173,12 +173,11 @@
} // namespace
-JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path) {
- init(path, 1);
-}
+JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path)
+ : JsonFakeValueGenerator(path, /*iteration=*/1) {}
JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path, int32_t iteration) {
- init(path, iteration);
+ initWithPath(path, iteration);
}
JsonFakeValueGenerator::JsonFakeValueGenerator(const VehiclePropValue& request) {
@@ -186,16 +185,26 @@
// Iterate infinitely if iteration number is not provided
int32_t numOfIterations = v.int32Values.size() < 2 ? -1 : v.int32Values[1];
- init(v.stringValue, numOfIterations);
+ initWithPath(v.stringValue, numOfIterations);
}
-void JsonFakeValueGenerator::init(const std::string& path, int32_t iteration) {
+JsonFakeValueGenerator::JsonFakeValueGenerator([[maybe_unused]] bool unused,
+ const std::string& content, int32_t iteration) {
+ std::istringstream iss(content);
+ initWithStream(iss, iteration);
+}
+
+void JsonFakeValueGenerator::initWithPath(const std::string& path, int32_t iteration) {
std::ifstream ifs(path);
if (!ifs) {
ALOGE("%s: couldn't open %s for parsing.", __func__, path.c_str());
return;
}
- mEvents = parseFakeValueJson(ifs);
+ initWithStream(ifs, iteration);
+}
+
+void JsonFakeValueGenerator::initWithStream(std::istream& is, int32_t iteration) {
+ mEvents = parseFakeValueJson(is);
mNumOfIterations = iteration;
}
@@ -235,12 +244,15 @@
mNumOfIterations--;
}
}
-
generatedValue.timestamp = mLastEventTimestamp;
return generatedValue;
}
+bool JsonFakeValueGenerator::hasNext() {
+ return mNumOfIterations != 0 && mEvents.size() > 0;
+}
+
} // namespace fake
} // namespace vehicle
} // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/fake_impl/README.md b/automotive/vehicle/aidl/impl/fake_impl/README.md
new file mode 100644
index 0000000..e86dcee
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/README.md
@@ -0,0 +1,28 @@
+# Fake reference AIDL VHAL implementation libraries
+---
+
+This directory stores libraries for implementing a fake reference AIDL VHAL.
+
+WARNING: All the libraries here are for TEST ONLY.
+
+## GeneratorHub
+
+Defines a library `FakeVehicleHalValueGenerators` that could generate fake
+vehicle property values for testing.
+
+## hardware
+
+Defines a fake implementation for device-specifc interface `IVehicleHardware`:
+`FakeVehicleHardware`. This implementation uses a in-memory map for storing
+property values and does not communicate with or depending on any specific
+vehicle bus.
+
+## obd2frame
+
+Defines a library `FakeObd2Frame` that generates fake OBD2 frame for OBD2
+properties.
+
+## userhal
+
+Defines a library `FakeUserHal` that emulates a real User HAL behavior by
+parsing debug commands.
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
index dcd9208..4c17cde 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/Android.bp
@@ -24,6 +24,7 @@
srcs: ["src/*.cpp"],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
+ cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
defaults: [
"VehicleHalDefaults",
"FakeVehicleHardwareDefaults",
@@ -32,18 +33,23 @@
cc_defaults {
name: "FakeVehicleHardwareDefaults",
- cflags: ["-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING"],
header_libs: [
"IVehicleHardware",
- "VehicleHalDefaultConfig",
+ "VehicleHalTestUtilHeaders",
],
export_header_lib_headers: ["IVehicleHardware"],
static_libs: [
+ "VehicleHalJsonConfigLoaderEnableTestProperties",
"VehicleHalUtils",
"FakeVehicleHalValueGenerators",
"FakeObd2Frame",
"FakeUserHal",
],
+ required: [
+ "Prebuilt_VehicleHalDefaultProperties_JSON",
+ "Prebuilt_VehicleHalTestProperties_JSON",
+ "Prebuilt_VehicleHalVendorClusterTestProperties_JSON",
+ ],
shared_libs: [
"libjsoncpp",
],
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index 34b2b24..1636cb6 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -18,21 +18,24 @@
#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
#include <ConcurrentQueue.h>
-#include <DefaultConfig.h>
+#include <ConfigDeclaration.h>
#include <FakeObd2Frame.h>
#include <FakeUserHal.h>
+#include <GeneratorHub.h>
#include <IVehicleHardware.h>
+#include <JsonConfigLoader.h>
#include <RecurrentTimer.h>
#include <VehicleHalTypes.h>
#include <VehiclePropertyStore.h>
+#include <aidl/android/hardware/automotive/vehicle/VehicleHwKeyInputAction.h>
#include <android-base/parseint.h>
#include <android-base/result.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
-#include <map>
#include <memory>
#include <mutex>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -47,7 +50,8 @@
FakeVehicleHardware();
- explicit FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool);
+ FakeVehicleHardware(std::string defaultConfigDir, std::string overrideConfigDir,
+ bool forceOverride);
~FakeVehicleHardware();
@@ -132,11 +136,15 @@
const std::unique_ptr<FakeUserHal> mFakeUserHal;
// RecurrentTimer is thread-safe.
std::unique_ptr<RecurrentTimer> mRecurrentTimer;
+ // GeneratorHub is thread-safe.
+ std::unique_ptr<GeneratorHub> mGeneratorHub;
std::mutex mLock;
std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mLock);
std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash>
mRecurrentActions GUARDED_BY(mLock);
+ std::unordered_map<PropIdAreaId, VehiclePropValuePool::RecyclableType, PropIdAreaIdHash>
+ mSavedProps GUARDED_BY(mLock);
// PendingRequestHandler is thread-safe.
mutable PendingRequestHandler<GetValuesCallback,
aidl::android::hardware::automotive::vehicle::GetValueRequest>
@@ -145,17 +153,27 @@
aidl::android::hardware::automotive::vehicle::SetValueRequest>
mPendingSetValueRequests;
+ const std::string mDefaultConfigDir;
+ const std::string mOverrideConfigDir;
+ const bool mForceOverride;
+
+ // Only used during initialization.
+ JsonConfigLoader mLoader;
+
void init();
// Stores the initial value to property store.
- void storePropInitialValue(const defaultconfig::ConfigDeclaration& config);
+ void storePropInitialValue(const ConfigDeclaration& config);
// The callback that would be called when a vehicle property value change happens.
void onValueChangeCallback(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
- // If property "persist.vendor.vhal_init_value_override" is set to true, override the properties
- // using config files in 'overrideDir'.
- void maybeOverrideProperties(const char* overrideDir);
- // Override the properties using config files in 'overrideDir'.
- void overrideProperties(const char* overrideDir);
+ // Load the config files in format '*.json' from the directory and parse the config files
+ // into a map from property ID to ConfigDeclarations.
+ void loadPropConfigsFromDir(const std::string& dirPath,
+ std::unordered_map<int32_t, ConfigDeclaration>* configs);
+ // Function to be called when a value change event comes from vehicle bus. In our fake
+ // implementation, this function is only called during "--inject-event" dump command.
+ void eventFromVehicleBus(
+ const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
VhalResult<void> maybeSetSpecialValue(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
@@ -175,6 +193,8 @@
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
bool isHvacPropAndHvacNotAvailable(int32_t propId);
+ std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations();
+
std::string dumpAllProperties();
std::string dumpOnePropertyByConfig(
int rowNumber,
@@ -184,6 +204,10 @@
std::string dumpListProperties();
std::string dumpSpecificProperty(const std::vector<std::string>& options);
std::string dumpSetProperties(const std::vector<std::string>& options);
+ std::string dumpGetPropertyWithArg(const std::vector<std::string>& options);
+ std::string dumpSaveProperty(const std::vector<std::string>& options);
+ std::string dumpRestoreProperty(const std::vector<std::string>& options);
+ std::string dumpInjectEvent(const std::vector<std::string>& options);
template <typename T>
android::base::Result<T> safelyParseInt(int index, const std::string& s) {
@@ -198,7 +222,7 @@
std::vector<std::string> getOptionValues(const std::vector<std::string>& options,
size_t* index);
android::base::Result<aidl::android::hardware::automotive::vehicle::VehiclePropValue>
- parseSetPropOptions(const std::vector<std::string>& options);
+ parsePropOptions(const std::vector<std::string>& options);
android::base::Result<std::vector<uint8_t>> parseHexString(const std::string& s);
android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options,
@@ -207,6 +231,14 @@
const aidl::android::hardware::automotive::vehicle::GetValueRequest& request);
aidl::android::hardware::automotive::vehicle::SetValueResult handleSetValueRequest(
const aidl::android::hardware::automotive::vehicle::SetValueRequest& request);
+
+ std::string genFakeDataCommand(const std::vector<std::string>& options);
+
+ static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp(
+ aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action,
+ int32_t keyCode, int32_t targetDisplay);
+ static std::string genFakeDataHelp();
+ static std::string parseErrMsg(std::string fieldName, std::string value, std::string type);
};
} // namespace fake
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 20c34aa..d87e5aa 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -19,13 +19,15 @@
#include "FakeVehicleHardware.h"
-#include <DefaultConfig.h>
#include <FakeObd2Frame.h>
#include <JsonFakeValueGenerator.h>
+#include <LinearFakeValueGenerator.h>
#include <PropertyUtils.h>
#include <TestPropertyUtils.h>
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
+
+#include <android-base/file.h>
#include <android-base/parsedouble.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
@@ -33,6 +35,7 @@
#include <utils/SystemClock.h>
#include <dirent.h>
+#include <inttypes.h>
#include <sys/types.h>
#include <fstream>
#include <regex>
@@ -55,6 +58,7 @@
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
+using ::aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup;
@@ -64,14 +68,23 @@
using ::android::base::EqualsIgnoreCase;
using ::android::base::Error;
+using ::android::base::GetIntProperty;
using ::android::base::ParseFloat;
using ::android::base::Result;
using ::android::base::ScopedLockAssertion;
using ::android::base::StartsWith;
using ::android::base::StringPrintf;
-const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
-const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";
+// The directory for default property configuration file.
+// For config file format, see impl/default_config/config/README.md.
+constexpr char DEFAULT_CONFIG_DIR[] = "/vendor/etc/automotive/vhalconfig/";
+// The directory for property configuration file that overrides the default configuration file.
+// For config file format, see impl/default_config/config/README.md.
+constexpr char OVERRIDE_CONFIG_DIR[] = "/vendor/etc/automotive/vhaloverride/";
+// If OVERRIDE_PROPERTY is set, we will use the configuration files from OVERRIDE_CONFIG_DIR to
+// overwrite the default configs.
+constexpr char OVERRIDE_PROPERTY[] = "persist.vendor.vhal_init_value_override";
+constexpr char POWER_STATE_REQ_CONFIG_PROPERTY[] = "ro.vendor.fake_vhal.ap_power_state_req.config";
// A list of supported options for "--set" command.
const std::unordered_set<std::string> SET_PROP_OPTIONS = {
@@ -86,11 +99,13 @@
// bytes in hex format, e.g. 0xDEADBEEF.
"-b",
// Area id in integer.
- "-a"};
+ "-a",
+ // Timestamp in int64.
+ "-t"};
} // namespace
-void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
+void FakeVehicleHardware::storePropInitialValue(const ConfigDeclaration& config) {
const VehiclePropConfig& vehiclePropConfig = config.config;
int propId = vehiclePropConfig.prop;
@@ -132,30 +147,50 @@
}
FakeVehicleHardware::FakeVehicleHardware()
- : FakeVehicleHardware(std::make_unique<VehiclePropValuePool>()) {}
+ : FakeVehicleHardware(DEFAULT_CONFIG_DIR, OVERRIDE_CONFIG_DIR, false) {}
-FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool)
- : mValuePool(std::move(valuePool)),
+FakeVehicleHardware::FakeVehicleHardware(std::string defaultConfigDir,
+ std::string overrideConfigDir, bool forceOverride)
+ : mValuePool(std::make_unique<VehiclePropValuePool>()),
mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)),
mFakeUserHal(new FakeUserHal(mValuePool)),
mRecurrentTimer(new RecurrentTimer()),
+ mGeneratorHub(new GeneratorHub(
+ [this](const VehiclePropValue& value) { eventFromVehicleBus(value); })),
mPendingGetValueRequests(this),
- mPendingSetValueRequests(this) {
+ mPendingSetValueRequests(this),
+ mDefaultConfigDir(defaultConfigDir),
+ mOverrideConfigDir(overrideConfigDir),
+ mForceOverride(forceOverride) {
init();
}
FakeVehicleHardware::~FakeVehicleHardware() {
mPendingGetValueRequests.stop();
mPendingSetValueRequests.stop();
+ mGeneratorHub.reset();
+}
+
+std::unordered_map<int32_t, ConfigDeclaration> FakeVehicleHardware::loadConfigDeclarations() {
+ std::unordered_map<int32_t, ConfigDeclaration> configsByPropId;
+ loadPropConfigsFromDir(mDefaultConfigDir, &configsByPropId);
+ if (mForceOverride ||
+ android::base::GetBoolProperty(OVERRIDE_PROPERTY, /*default_value=*/false)) {
+ loadPropConfigsFromDir(mOverrideConfigDir, &configsByPropId);
+ }
+ return configsByPropId;
}
void FakeVehicleHardware::init() {
- for (auto& it : defaultconfig::getDefaultConfigs()) {
- VehiclePropConfig cfg = it.config;
+ for (auto& [_, configDeclaration] : loadConfigDeclarations()) {
+ VehiclePropConfig cfg = configDeclaration.config;
VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
- if (cfg.prop == OBD2_FREEZE_FRAME) {
+ if (cfg.prop == toInt(VehicleProperty::AP_POWER_STATE_REQ)) {
+ int config = GetIntProperty(POWER_STATE_REQ_CONFIG_PROPERTY, /*default_value=*/0);
+ cfg.configArray[0] = config;
+ } else if (cfg.prop == OBD2_FREEZE_FRAME) {
tokenFunction = [](const VehiclePropValue& propValue) { return propValue.timestamp; };
}
@@ -165,15 +200,18 @@
// logic.
continue;
}
- storePropInitialValue(it);
+ storePropInitialValue(configDeclaration);
}
- maybeOverrideProperties(VENDOR_OVERRIDE_DIR);
-
// OBD2_LIVE_FRAME and OBD2_FREEZE_FRAME must be configured in default configs.
- mFakeObd2Frame->initObd2LiveFrame(*mServerSidePropStore->getConfig(OBD2_LIVE_FRAME).value());
- mFakeObd2Frame->initObd2FreezeFrame(
- *mServerSidePropStore->getConfig(OBD2_FREEZE_FRAME).value());
+ auto maybeObd2LiveFrame = mServerSidePropStore->getConfig(OBD2_LIVE_FRAME);
+ if (maybeObd2LiveFrame.has_value()) {
+ mFakeObd2Frame->initObd2LiveFrame(*maybeObd2LiveFrame.value());
+ }
+ auto maybeObd2FreezeFrame = mServerSidePropStore->getConfig(OBD2_FREEZE_FRAME);
+ if (maybeObd2FreezeFrame.has_value()) {
+ mFakeObd2Frame->initObd2FreezeFrame(*maybeObd2FreezeFrame.value());
+ }
mServerSidePropStore->setOnValueChangeCallback(
[this](const VehiclePropValue& value) { return onValueChangeCallback(value); });
@@ -395,7 +433,7 @@
*isSpecialValue = true;
return mFakeObd2Frame->clearObd2FreezeFrames(value);
-#ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
+#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
case toInt(VehicleProperty::CLUSTER_REPORT_STATE):
[[fallthrough]];
case toInt(VehicleProperty::CLUSTER_REQUEST_DISPLAY):
@@ -423,7 +461,7 @@
<< getErrorMsg(writeResult);
}
return {};
-#endif // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
+#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
default:
break;
@@ -574,34 +612,237 @@
result.buffer = dumpListProperties();
} else if (EqualsIgnoreCase(option, "--get")) {
result.buffer = dumpSpecificProperty(options);
+ } else if (EqualsIgnoreCase(option, "--getWithArg")) {
+ result.buffer = dumpGetPropertyWithArg(options);
} else if (EqualsIgnoreCase(option, "--set")) {
result.buffer = dumpSetProperties(options);
+ } else if (EqualsIgnoreCase(option, "--save-prop")) {
+ result.buffer = dumpSaveProperty(options);
+ } else if (EqualsIgnoreCase(option, "--restore-prop")) {
+ result.buffer = dumpRestoreProperty(options);
+ } else if (EqualsIgnoreCase(option, "--inject-event")) {
+ result.buffer = dumpInjectEvent(options);
} else if (EqualsIgnoreCase(option, kUserHalDumpOption)) {
if (options.size() == 1) {
result.buffer = mFakeUserHal->showDumpHelp();
} else {
result.buffer = mFakeUserHal->dump(options[1]);
}
+ } else if (EqualsIgnoreCase(option, "--genfakedata")) {
+ result.buffer = genFakeDataCommand(options);
} else {
result.buffer = StringPrintf("Invalid option: %s\n", option.c_str());
}
return result;
}
+std::string FakeVehicleHardware::genFakeDataHelp() {
+ return R"(
+Generate Fake Data Usage:
+--genfakedata --startlinear [propID] [mValue] [cValue] [dispersion] [increment] [interval]: "
+Start a linear generator that generates event with floatValue within range:
+[mValue - disperson, mValue + dispersion].
+propID(int32): ID for the property to generate event for.
+mValue(float): The middle of the possible values for the property.
+cValue(float): The start value for the property, must be within the range.
+dispersion(float): The range the value can change.
+increment(float): The step the value would increase by for each generated event,
+if exceed the range, the value would loop back.
+interval(int64): The interval in nanoseconds the event would generate by.
+
+--genfakedata --stoplinear [propID(int32)]: Stop a linear generator
+
+--genfakedata --startjson --path [jsonFilePath] [repetition]:
+Start a JSON generator that would generate events according to a JSON file.
+jsonFilePath(string): The path to a JSON file. The JSON content must be in the format of
+[{
+ "timestamp": 1000000,
+ "areaId": 0,
+ "value": 8,
+ "prop": 289408000
+}, {...}]
+Each event in the JSON file would be generated by the same interval their timestamp is relative to
+the first event's timestamp.
+repetition(int32, optional): how many iterations the events would be generated. If it is not
+provided, it would iterate indefinitely.
+
+--genfakedata --startjson --content [jsonContent]: Start a JSON generator using the content.
+
+--genfakedata --stopjson [generatorID(string)]: Stop a JSON generator.
+
+--genfakedata --keypress [keyCode(int32)] [display[int32]]: Generate key press.
+
+)";
+}
+
+std::string FakeVehicleHardware::parseErrMsg(std::string fieldName, std::string value,
+ std::string type) {
+ return StringPrintf("failed to parse %s as %s: \"%s\"\n%s", fieldName.c_str(), type.c_str(),
+ value.c_str(), genFakeDataHelp().c_str());
+}
+
+std::string FakeVehicleHardware::genFakeDataCommand(const std::vector<std::string>& options) {
+ if (options.size() < 2) {
+ return "No subcommand specified for genfakedata\n" + genFakeDataHelp();
+ }
+
+ std::string command = options[1];
+ if (command == "--startlinear") {
+ // --genfakedata --startlinear [propID(int32)] [middleValue(float)]
+ // [currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)]
+ if (options.size() != 8) {
+ return "incorrect argument count, need 8 arguments for --genfakedata --startlinear\n" +
+ genFakeDataHelp();
+ }
+ int32_t propId;
+ float middleValue;
+ float currentValue;
+ float dispersion;
+ float increment;
+ int64_t interval;
+ if (!android::base::ParseInt(options[2], &propId)) {
+ return parseErrMsg("propId", options[2], "int");
+ }
+ if (!android::base::ParseFloat(options[3], &middleValue)) {
+ return parseErrMsg("middleValue", options[3], "float");
+ }
+ if (!android::base::ParseFloat(options[4], ¤tValue)) {
+ return parseErrMsg("currentValue", options[4], "float");
+ }
+ if (!android::base::ParseFloat(options[5], &dispersion)) {
+ return parseErrMsg("dispersion", options[5], "float");
+ }
+ if (!android::base::ParseFloat(options[6], &increment)) {
+ return parseErrMsg("increment", options[6], "float");
+ }
+ if (!android::base::ParseInt(options[7], &interval)) {
+ return parseErrMsg("interval", options[7], "int");
+ }
+ auto generator = std::make_unique<LinearFakeValueGenerator>(
+ propId, middleValue, currentValue, dispersion, increment, interval);
+ mGeneratorHub->registerGenerator(propId, std::move(generator));
+ return "Linear event generator started successfully";
+ } else if (command == "--stoplinear") {
+ // --genfakedata --stoplinear [propID(int32)]
+ if (options.size() != 3) {
+ return "incorrect argument count, need 3 arguments for --genfakedata --stoplinear\n" +
+ genFakeDataHelp();
+ }
+ int32_t propId;
+ if (!android::base::ParseInt(options[2], &propId)) {
+ return parseErrMsg("propId", options[2], "int");
+ }
+ if (mGeneratorHub->unregisterGenerator(propId)) {
+ return "Linear event generator stopped successfully";
+ }
+ return StringPrintf("No linear event generator found for property: %d", propId);
+ } else if (command == "--startjson") {
+ // --genfakedata --startjson --path path repetition
+ // or
+ // --genfakedata --startjson --content content repetition.
+ if (options.size() != 4 && options.size() != 5) {
+ return "incorrect argument count, need 4 or 5 arguments for --genfakedata "
+ "--startjson\n";
+ }
+ // Iterate infinitely if repetition number is not provided
+ int32_t repetition = -1;
+ if (options.size() == 5) {
+ if (!android::base::ParseInt(options[4], &repetition)) {
+ return parseErrMsg("repetition", options[4], "int");
+ }
+ }
+ std::unique_ptr<JsonFakeValueGenerator> generator;
+ if (options[2] == "--path") {
+ const std::string& fileName = options[3];
+ generator = std::make_unique<JsonFakeValueGenerator>(fileName, repetition);
+ if (!generator->hasNext()) {
+ return "invalid JSON file, no events";
+ }
+ } else if (options[2] == "--content") {
+ const std::string& content = options[3];
+ generator =
+ std::make_unique<JsonFakeValueGenerator>(/*unused=*/true, content, repetition);
+ if (!generator->hasNext()) {
+ return "invalid JSON content, no events";
+ }
+ }
+ int32_t cookie = std::hash<std::string>()(options[3]);
+ mGeneratorHub->registerGenerator(cookie, std::move(generator));
+ return StringPrintf("JSON event generator started successfully, ID: %" PRId32, cookie);
+ } else if (command == "--stopjson") {
+ // --genfakedata --stopjson [generatorID(string)]
+ if (options.size() != 3) {
+ return "incorrect argument count, need 3 arguments for --genfakedata --stopjson\n";
+ }
+ int32_t cookie;
+ if (!android::base::ParseInt(options[2], &cookie)) {
+ return parseErrMsg("cookie", options[2], "int");
+ }
+ if (mGeneratorHub->unregisterGenerator(cookie)) {
+ return "JSON event generator stopped successfully";
+ } else {
+ return StringPrintf("No JSON event generator found for ID: %s", options[2].c_str());
+ }
+ } else if (command == "--keypress") {
+ int32_t keyCode;
+ int32_t display;
+ // --genfakedata --keypress [keyCode(int32)] [display[int32]]
+ if (options.size() != 4) {
+ return "incorrect argument count, need 4 arguments for --genfakedata --keypress\n";
+ }
+ if (!android::base::ParseInt(options[2], &keyCode)) {
+ return parseErrMsg("keyCode", options[2], "int");
+ }
+ if (!android::base::ParseInt(options[3], &display)) {
+ return parseErrMsg("display", options[3], "int");
+ }
+ // Send back to HAL
+ onValueChangeCallback(
+ createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display));
+ onValueChangeCallback(
+ createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display));
+ return "keypress event generated successfully";
+ }
+
+ return StringPrintf("Unknown command: \"%s\"\n%s", command.c_str(), genFakeDataHelp().c_str());
+}
+
+VehiclePropValue FakeVehicleHardware::createHwInputKeyProp(VehicleHwKeyInputAction action,
+ int32_t keyCode, int32_t targetDisplay) {
+ VehiclePropValue value = {
+ .prop = toInt(VehicleProperty::HW_KEY_INPUT),
+ .areaId = 0,
+ .timestamp = elapsedRealtimeNano(),
+ .status = VehiclePropertyStatus::AVAILABLE,
+ .value.int32Values = {toInt(action), keyCode, targetDisplay},
+ };
+ return value;
+}
+
+void FakeVehicleHardware::eventFromVehicleBus(const VehiclePropValue& value) {
+ mServerSidePropStore->writeValue(mValuePool->obtain(value));
+}
+
std::string FakeVehicleHardware::dumpHelp() {
return "Usage: \n\n"
"[no args]: dumps (id and value) all supported properties \n"
"--help: shows this help\n"
"--list: lists the ids of all supported properties\n"
- "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n"
- "--set <PROP> [-i INT_VALUE [INT_VALUE ...]] [-i64 INT64_VALUE [INT64_VALUE ...]] "
- "[-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] "
- "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. "
+ "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties. \n"
+ "--getWithArg <PROP> [ValueArguments]: gets the value for a specific property with "
+ "arguments. \n"
+ "--set <PROP> [ValueArguments]: sets the value of property PROP. \n"
+ "--save-prop <prop> [-a AREA_ID]: saves the current value for PROP, integration test"
+ " that modifies prop value must call this before test and restore-prop after test. \n"
+ "--restore-prop <prop> [-a AREA_ID]: restores a previously saved property value. \n"
+ "--inject-event <PROP> [ValueArguments]: inject a property update event from car\n\n"
+ "ValueArguments are in the format of [-i INT_VALUE [INT_VALUE ...]] "
+ "[-i64 INT64_VALUE [INT64_VALUE ...]] [-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] "
+ "[-b BYTES_VALUE] [-a AREA_ID].\n"
"Notice that the string, bytes and area value can be set just once, while the other can"
" have multiple values (so they're used in the respective array), "
- "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n\n"
- "Fake user HAL usage: \n" +
- mFakeUserHal->showDumpHelp();
+ "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n" +
+ genFakeDataHelp() + "Fake user HAL usage: \n" + mFakeUserHal->showDumpHelp();
}
std::string FakeVehicleHardware::dumpAllProperties() {
@@ -719,10 +960,11 @@
return std::move(values);
}
-Result<VehiclePropValue> FakeVehicleHardware::parseSetPropOptions(
+Result<VehiclePropValue> FakeVehicleHardware::parsePropOptions(
const std::vector<std::string>& options) {
// Options format:
- // --set PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a]
+ // --set/get/inject-event PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...]
+ // [-b b1 b2...] [-a a] [-t timestamp]
size_t optionIndex = 1;
auto result = safelyParseInt<int32_t>(optionIndex, options[optionIndex]);
if (!result.ok()) {
@@ -736,83 +978,98 @@
std::unordered_set<std::string> parsedOptions;
while (optionIndex < options.size()) {
- std::string type = options[optionIndex];
+ std::string argType = options[optionIndex];
optionIndex++;
+
size_t currentIndex = optionIndex;
- std::vector<std::string> values = getOptionValues(options, &optionIndex);
- if (parsedOptions.find(type) != parsedOptions.end()) {
- return Error() << StringPrintf("Duplicate \"%s\" options\n", type.c_str());
+ std::vector<std::string> argValues = getOptionValues(options, &optionIndex);
+ if (parsedOptions.find(argType) != parsedOptions.end()) {
+ return Error() << StringPrintf("Duplicate \"%s\" options\n", argType.c_str());
}
- parsedOptions.insert(type);
- if (EqualsIgnoreCase(type, "-i")) {
- if (values.size() == 0) {
+ parsedOptions.insert(argType);
+ size_t argValuesSize = argValues.size();
+ if (EqualsIgnoreCase(argType, "-i")) {
+ if (argValuesSize == 0) {
return Error() << "No values specified when using \"-i\"\n";
}
- prop.value.int32Values.resize(values.size());
- for (size_t i = 0; i < values.size(); i++) {
- auto int32Result = safelyParseInt<int32_t>(currentIndex + i, values[i]);
+ prop.value.int32Values.resize(argValuesSize);
+ for (size_t i = 0; i < argValuesSize; i++) {
+ auto int32Result = safelyParseInt<int32_t>(currentIndex + i, argValues[i]);
if (!int32Result.ok()) {
return Error()
<< StringPrintf("Value: \"%s\" is not a valid int: %s\n",
- values[i].c_str(), getErrorMsg(int32Result).c_str());
+ argValues[i].c_str(), getErrorMsg(int32Result).c_str());
}
prop.value.int32Values[i] = int32Result.value();
}
- } else if (EqualsIgnoreCase(type, "-i64")) {
- if (values.size() == 0) {
+ } else if (EqualsIgnoreCase(argType, "-i64")) {
+ if (argValuesSize == 0) {
return Error() << "No values specified when using \"-i64\"\n";
}
- prop.value.int64Values.resize(values.size());
- for (size_t i = 0; i < values.size(); i++) {
- auto int64Result = safelyParseInt<int64_t>(currentIndex + i, values[i]);
+ prop.value.int64Values.resize(argValuesSize);
+ for (size_t i = 0; i < argValuesSize; i++) {
+ auto int64Result = safelyParseInt<int64_t>(currentIndex + i, argValues[i]);
if (!int64Result.ok()) {
return Error()
<< StringPrintf("Value: \"%s\" is not a valid int64: %s\n",
- values[i].c_str(), getErrorMsg(int64Result).c_str());
+ argValues[i].c_str(), getErrorMsg(int64Result).c_str());
}
prop.value.int64Values[i] = int64Result.value();
}
- } else if (EqualsIgnoreCase(type, "-f")) {
- if (values.size() == 0) {
+ } else if (EqualsIgnoreCase(argType, "-f")) {
+ if (argValuesSize == 0) {
return Error() << "No values specified when using \"-f\"\n";
}
- prop.value.floatValues.resize(values.size());
- for (size_t i = 0; i < values.size(); i++) {
- auto floatResult = safelyParseFloat(currentIndex + i, values[i]);
+ prop.value.floatValues.resize(argValuesSize);
+ for (size_t i = 0; i < argValuesSize; i++) {
+ auto floatResult = safelyParseFloat(currentIndex + i, argValues[i]);
if (!floatResult.ok()) {
return Error()
<< StringPrintf("Value: \"%s\" is not a valid float: %s\n",
- values[i].c_str(), getErrorMsg(floatResult).c_str());
+ argValues[i].c_str(), getErrorMsg(floatResult).c_str());
}
prop.value.floatValues[i] = floatResult.value();
}
- } else if (EqualsIgnoreCase(type, "-s")) {
- if (values.size() != 1) {
+ } else if (EqualsIgnoreCase(argType, "-s")) {
+ if (argValuesSize != 1) {
return Error() << "Expect exact one value when using \"-s\"\n";
}
- prop.value.stringValue = values[0];
- } else if (EqualsIgnoreCase(type, "-b")) {
- if (values.size() != 1) {
+ prop.value.stringValue = argValues[0];
+ } else if (EqualsIgnoreCase(argType, "-b")) {
+ if (argValuesSize != 1) {
return Error() << "Expect exact one value when using \"-b\"\n";
}
- auto bytesResult = parseHexString(values[0]);
+ auto bytesResult = parseHexString(argValues[0]);
if (!bytesResult.ok()) {
return Error() << StringPrintf("value: \"%s\" is not a valid hex string: %s\n",
- values[0].c_str(), getErrorMsg(bytesResult).c_str());
+ argValues[0].c_str(),
+ getErrorMsg(bytesResult).c_str());
}
prop.value.byteValues = std::move(bytesResult.value());
- } else if (EqualsIgnoreCase(type, "-a")) {
- if (values.size() != 1) {
+ } else if (EqualsIgnoreCase(argType, "-a")) {
+ if (argValuesSize != 1) {
return Error() << "Expect exact one value when using \"-a\"\n";
}
- auto int32Result = safelyParseInt<int32_t>(currentIndex, values[0]);
+ auto int32Result = safelyParseInt<int32_t>(currentIndex, argValues[0]);
if (!int32Result.ok()) {
return Error() << StringPrintf("Area ID: \"%s\" is not a valid int: %s\n",
- values[0].c_str(), getErrorMsg(int32Result).c_str());
+ argValues[0].c_str(),
+ getErrorMsg(int32Result).c_str());
}
prop.areaId = int32Result.value();
+ } else if (EqualsIgnoreCase(argType, "-t")) {
+ if (argValuesSize != 1) {
+ return Error() << "Expect exact one value when using \"-t\"\n";
+ }
+ auto int64Result = safelyParseInt<int64_t>(currentIndex, argValues[0]);
+ if (!int64Result.ok()) {
+ return Error() << StringPrintf("Timestamp: \"%s\" is not a valid int64: %s\n",
+ argValues[0].c_str(),
+ getErrorMsg(int64Result).c_str());
+ }
+ prop.timestamp = int64Result.value();
} else {
- return Error() << StringPrintf("Unknown option: %s\n", type.c_str());
+ return Error() << StringPrintf("Unknown option: %s\n", argType.c_str());
}
}
@@ -824,7 +1081,7 @@
return getErrorMsg(result);
}
- auto parseResult = parseSetPropOptions(options);
+ auto parseResult = parsePropOptions(options);
if (!parseResult.ok()) {
return getErrorMsg(parseResult);
}
@@ -847,6 +1104,123 @@
getErrorMsg(setResult).c_str());
}
+std::string FakeVehicleHardware::dumpGetPropertyWithArg(const std::vector<std::string>& options) {
+ if (auto result = checkArgumentsSize(options, 3); !result.ok()) {
+ return getErrorMsg(result);
+ }
+
+ auto parseResult = parsePropOptions(options);
+ if (!parseResult.ok()) {
+ return getErrorMsg(parseResult);
+ }
+ VehiclePropValue prop = std::move(parseResult.value());
+ ALOGD("Dump: Getting property: %s", prop.toString().c_str());
+
+ bool isSpecialValue = false;
+ auto result = maybeGetSpecialValue(prop, &isSpecialValue);
+
+ if (!isSpecialValue) {
+ result = mServerSidePropStore->readValue(prop);
+ }
+
+ if (!result.ok()) {
+ return StringPrintf("failed to read property value: %d, error: %s, code: %d\n", prop.prop,
+ getErrorMsg(result).c_str(), getIntErrorCode(result));
+ }
+ return StringPrintf("Get property result: %s\n", result.value()->toString().c_str());
+}
+
+std::string FakeVehicleHardware::dumpSaveProperty(const std::vector<std::string>& options) {
+ // Format: --save-prop PROP [-a areaID]
+ if (auto result = checkArgumentsSize(options, 2); !result.ok()) {
+ return getErrorMsg(result);
+ }
+
+ auto parseResult = parsePropOptions(options);
+ if (!parseResult.ok()) {
+ return getErrorMsg(parseResult);
+ }
+ // We are only using the prop and areaId option.
+ VehiclePropValue value = std::move(parseResult.value());
+ int32_t propId = value.prop;
+ int32_t areaId = value.areaId;
+
+ auto readResult = mServerSidePropStore->readValue(value);
+ if (!readResult.ok()) {
+ return StringPrintf("Failed to save current property value, error: %s",
+ getErrorMsg(readResult).c_str());
+ }
+
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mSavedProps[PropIdAreaId{
+ .propId = propId,
+ .areaId = areaId,
+ }] = std::move(readResult.value());
+
+ return StringPrintf("Property: %" PRId32 ", areaID: %" PRId32 " saved", propId, areaId);
+}
+
+std::string FakeVehicleHardware::dumpRestoreProperty(const std::vector<std::string>& options) {
+ // Format: --restore-prop PROP [-a areaID]
+ if (auto result = checkArgumentsSize(options, 2); !result.ok()) {
+ return getErrorMsg(result);
+ }
+
+ auto parseResult = parsePropOptions(options);
+ if (!parseResult.ok()) {
+ return getErrorMsg(parseResult);
+ }
+ // We are only using the prop and areaId option.
+ VehiclePropValue value = std::move(parseResult.value());
+ int32_t propId = value.prop;
+ int32_t areaId = value.areaId;
+ VehiclePropValuePool::RecyclableType savedValue;
+
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ auto it = mSavedProps.find(PropIdAreaId{
+ .propId = propId,
+ .areaId = areaId,
+ });
+ if (it == mSavedProps.end()) {
+ return StringPrintf("No saved property for property: %" PRId32 ", areaID: %" PRId32,
+ propId, areaId);
+ }
+
+ savedValue = std::move(it->second);
+ // Remove the saved property after restoring it.
+ mSavedProps.erase(it);
+ }
+
+ // Update timestamp.
+ savedValue->timestamp = elapsedRealtimeNano();
+
+ auto writeResult = mServerSidePropStore->writeValue(std::move(savedValue));
+ if (!writeResult.ok()) {
+ return StringPrintf("Failed to restore property value, error: %s",
+ getErrorMsg(writeResult).c_str());
+ }
+
+ return StringPrintf("Property: %" PRId32 ", areaID: %" PRId32 " restored", propId, areaId);
+}
+
+std::string FakeVehicleHardware::dumpInjectEvent(const std::vector<std::string>& options) {
+ if (auto result = checkArgumentsSize(options, 3); !result.ok()) {
+ return getErrorMsg(result);
+ }
+
+ auto parseResult = parsePropOptions(options);
+ if (!parseResult.ok()) {
+ return getErrorMsg(parseResult);
+ }
+ VehiclePropValue prop = std::move(parseResult.value());
+ ALOGD("Dump: Injecting event from vehicle bus: %s", prop.toString().c_str());
+
+ eventFromVehicleBus(prop);
+
+ return StringPrintf("Event for property: %d injected", prop.prop);
+}
+
StatusCode FakeVehicleHardware::checkHealth() {
// Always return OK for checkHealth.
return StatusCode::OK;
@@ -915,33 +1289,26 @@
(*mOnPropertyChangeCallback)(std::move(updatedValues));
}
-void FakeVehicleHardware::maybeOverrideProperties(const char* overrideDir) {
- if (android::base::GetBoolProperty(OVERRIDE_PROPERTY, false)) {
- overrideProperties(overrideDir);
- }
-}
-
-void FakeVehicleHardware::overrideProperties(const char* overrideDir) {
- ALOGI("loading vendor override properties from %s", overrideDir);
- if (auto dir = opendir(overrideDir); dir != NULL) {
+void FakeVehicleHardware::loadPropConfigsFromDir(
+ const std::string& dirPath,
+ std::unordered_map<int32_t, ConfigDeclaration>* configsByPropId) {
+ ALOGI("loading properties from %s", dirPath.c_str());
+ if (auto dir = opendir(dirPath.c_str()); dir != NULL) {
std::regex regJson(".*[.]json", std::regex::icase);
while (auto f = readdir(dir)) {
if (!std::regex_match(f->d_name, regJson)) {
continue;
}
- std::string file = overrideDir + std::string(f->d_name);
- JsonFakeValueGenerator tmpGenerator(file);
-
- std::vector<VehiclePropValue> propValues = tmpGenerator.getAllEvents();
- for (const VehiclePropValue& prop : propValues) {
- auto propToStore = mValuePool->obtain(prop);
- propToStore->timestamp = elapsedRealtimeNano();
- if (auto result = mServerSidePropStore->writeValue(std::move(propToStore),
- /*updateStatus=*/true);
- !result.ok()) {
- ALOGW("failed to write vendor override properties: %d, error: %s, code: %d",
- prop.prop, getErrorMsg(result).c_str(), getIntErrorCode(result));
- }
+ std::string filePath = dirPath + "/" + std::string(f->d_name);
+ ALOGI("loading properties from %s", filePath.c_str());
+ auto result = mLoader.loadPropConfig(filePath);
+ if (!result.ok()) {
+ ALOGE("failed to load config file: %s, error: %s", filePath.c_str(),
+ result.error().message().c_str());
+ continue;
+ }
+ for (auto& [propId, configDeclaration] : result.value()) {
+ (*configsByPropId)[propId] = std::move(configDeclaration);
}
}
closedir(dir);
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
index 90d1516..8d8fcf5 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp
@@ -22,13 +22,13 @@
name: "FakeVehicleHardwareTest",
vendor: true,
srcs: ["*.cpp"],
- cflags: ["-DENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING"],
+ cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
header_libs: [
"IVehicleHardware",
- "VehicleHalDefaultConfig",
"VehicleHalTestUtilHeaders",
],
static_libs: [
+ "VehicleHalJsonConfigLoaderEnableTestProperties",
"VehicleHalUtils",
"FakeVehicleHardware",
"FakeVehicleHalValueGenerators",
@@ -41,7 +41,11 @@
"libjsoncpp",
],
data: [
+ ":VehicleHalDefaultProperties_JSON",
+ ":VehicleHalTestProperties_JSON",
+ ":VehicleHalVendorClusterTestProperties_JSON",
":FakeVehicleHardwareTestOverrideJson",
+ ":FakeVehicleHardwareTestPropJson",
],
defaults: ["VehicleHalDefaults"],
test_suites: ["device-tests"],
@@ -51,3 +55,8 @@
name: "FakeVehicleHardwareTestOverrideJson",
srcs: ["override/*"],
}
+
+filegroup {
+ name: "FakeVehicleHardwareTestPropJson",
+ srcs: ["fakedata/prop.json"],
+}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index 3e8f634..c230c51 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -16,7 +16,6 @@
#include <FakeVehicleHardware.h>
-#include <DefaultConfig.h>
#include <FakeObd2Frame.h>
#include <FakeUserHal.h>
#include <PropertyUtils.h>
@@ -53,6 +52,7 @@
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
+using ::aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
@@ -64,6 +64,7 @@
using ::testing::ContainerEq;
using ::testing::ContainsRegex;
using ::testing::Eq;
+using ::testing::HasSubstr;
using ::testing::WhenSortedBy;
using std::chrono::milliseconds;
@@ -78,7 +79,9 @@
public:
FakeVehicleHardwareTestHelper(FakeVehicleHardware* hardware) { mHardware = hardware; }
- void overrideProperties(const char* overrideDir) { mHardware->overrideProperties(overrideDir); }
+ std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations() {
+ return mHardware->loadConfigDeclarations();
+ }
private:
FakeVehicleHardware* mHardware;
@@ -87,6 +90,9 @@
class FakeVehicleHardwareTest : public ::testing::Test {
protected:
void SetUp() override {
+ mHardware = std::make_unique<FakeVehicleHardware>(android::base::GetExecutableDirectory(),
+ /*overrideConfigDir=*/"",
+ /*forceOverride=*/false);
auto callback = std::make_unique<IVehicleHardware::PropertyChangeCallback>(
[this](const std::vector<VehiclePropValue>& values) {
onPropertyChangeEvent(values);
@@ -98,7 +104,17 @@
[this](std::vector<GetValueResult> results) { onGetValues(results); });
}
- FakeVehicleHardware* getHardware() { return &mHardware; }
+ void TearDown() override {
+ // mHardware uses callback which contains reference to 'this', so it has to be destroyed
+ // before 'this'.
+ mHardware.reset();
+ }
+
+ FakeVehicleHardware* getHardware() { return mHardware.get(); }
+
+ void setHardware(std::unique_ptr<FakeVehicleHardware> hardware) {
+ mHardware = std::move(hardware);
+ }
StatusCode setValues(const std::vector<SetValueRequest>& requests) {
{
@@ -251,6 +267,14 @@
return mChangedProperties;
}
+ bool waitForChangedProperties(size_t count, milliseconds timeout) {
+ std::unique_lock<std::mutex> lk(mLock);
+ return mCv.wait_for(lk, timeout, [this, count] {
+ ScopedLockAssertion lockAssertion(mLock);
+ return mChangedProperties.size() >= count;
+ });
+ }
+
bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count,
milliseconds timeout) {
PropIdAreaId propIdAreaId{
@@ -270,6 +294,15 @@
mChangedProperties.clear();
}
+ size_t getEventCount(int32_t propId, int32_t areaId) {
+ PropIdAreaId propIdAreaId{
+ .propId = propId,
+ .areaId = areaId,
+ };
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ return mEventCount[propIdAreaId];
+ }
+
static void addSetValueRequest(std::vector<SetValueRequest>& requests,
std::vector<SetValueResult>& expectedResults, int64_t requestId,
const VehiclePropValue& value, StatusCode expectedStatus) {
@@ -332,7 +365,7 @@
} mPropValueCmp;
private:
- FakeVehicleHardware mHardware;
+ std::unique_ptr<FakeVehicleHardware> mHardware;
std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback;
std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback;
std::condition_variable mCv;
@@ -348,7 +381,8 @@
TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) {
std::vector<VehiclePropConfig> configs = getHardware()->getAllPropertyConfigs();
- ASSERT_EQ(configs.size(), defaultconfig::getDefaultConfigs().size());
+ FakeVehicleHardwareTestHelper helper(getHardware());
+ ASSERT_EQ(configs.size(), helper.loadConfigDeclarations().size());
}
TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) {
@@ -356,7 +390,8 @@
std::vector<GetValueResult> expectedGetValueResults;
int64_t requestId = 1;
- for (auto& config : defaultconfig::getDefaultConfigs()) {
+ FakeVehicleHardwareTestHelper helper(getHardware());
+ for (auto& [propId, config] : helper.loadConfigDeclarations()) {
if (obd2frame::FakeObd2Frame::isDiagnosticProperty(config.config)) {
// Ignore storing default value for diagnostic property. They have special get/set
// logic.
@@ -368,12 +403,11 @@
continue;
}
- if (config.config.prop == ECHO_REVERSE_BYTES) {
+ if (propId == ECHO_REVERSE_BYTES) {
// Ignore ECHO_REVERSE_BYTES, it has special logic.
continue;
}
- int propId = config.config.prop;
if (isGlobalProp(propId)) {
if (config.initialValue == RawPropValues{}) {
addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++,
@@ -631,10 +665,12 @@
}
TEST_F(FakeVehicleHardwareTest, testVendorOverrideProperties) {
- std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
+ std::string currentDir = android::base::GetExecutableDirectory();
+ std::string overrideDir = currentDir + "/override/";
// Set vendor override directory.
- FakeVehicleHardwareTestHelper helper(getHardware());
- helper.overrideProperties(overrideDir.c_str());
+ std::unique_ptr<FakeVehicleHardware> hardware =
+ std::make_unique<FakeVehicleHardware>(currentDir, overrideDir, /*forceOverride=*/true);
+ setHardware(std::move(hardware));
// This is the same as the prop in 'gear_selection.json'.
int gearProp = toInt(VehicleProperty::GEAR_SELECTION);
@@ -669,10 +705,12 @@
}
TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesMultipleAreas) {
- std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
+ std::string currentDir = android::base::GetExecutableDirectory();
+ std::string overrideDir = currentDir + "/override/";
// Set vendor override directory.
- FakeVehicleHardwareTestHelper helper(getHardware());
- helper.overrideProperties(overrideDir.c_str());
+ std::unique_ptr<FakeVehicleHardware> hardware =
+ std::make_unique<FakeVehicleHardware>(currentDir, overrideDir, /*forceOverride=*/true);
+ setHardware(std::move(hardware));
// This is the same as the prop in 'hvac_temperature_set.json'.
int hvacProp = toInt(VehicleProperty::HVAC_TEMPERATURE_SET);
@@ -685,22 +723,16 @@
ASSERT_TRUE(result.ok()) << "expect to get the overridden property ok: " << getStatus(result);
ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size());
ASSERT_EQ(30.0f, result.value().value.floatValues[0]);
-
- // HVAC_RIGHT should not be affected and return the default value.
- result = getValue(VehiclePropValue{
- .prop = hvacProp,
- .areaId = HVAC_RIGHT,
- });
-
- ASSERT_TRUE(result.ok()) << "expect to get the default property ok: " << getStatus(result);
- ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size());
- ASSERT_EQ(20.0f, result.value().value.floatValues[0]);
}
TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesDirDoesNotExist) {
- // Set vendor override directory to a non-existing dir
- FakeVehicleHardwareTestHelper helper(getHardware());
- helper.overrideProperties("123");
+ std::string currentDir = android::base::GetExecutableDirectory();
+ std::string overrideDir = currentDir + "/override/";
+ // Set vendor override directory to a non-existing dir.
+ std::unique_ptr<FakeVehicleHardware> hardware =
+ std::make_unique<FakeVehicleHardware>(currentDir, "1234", /*forceOverride=*/true);
+ setHardware(std::move(hardware));
+
auto result = getValue(VehiclePropValue{
.prop = toInt(VehicleProperty::GEAR_SELECTION),
});
@@ -1395,6 +1427,85 @@
ASSERT_THAT(result.buffer, ContainsRegex("Invalid number of arguments"));
}
+TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertyWithArg) {
+ auto getValueResult = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO});
+ ASSERT_TRUE(getValueResult.ok());
+ auto propValue = getValueResult.value();
+ ASSERT_EQ(propValue.value.int64Values.size(), static_cast<size_t>(3))
+ << "expect 3 obd2 freeze frames stored";
+
+ std::string propIdStr = StringPrintf("%d", OBD2_FREEZE_FRAME);
+ DumpResult result;
+ for (int64_t timestamp : propValue.value.int64Values) {
+ result = getHardware()->dump(
+ {"--getWithArg", propIdStr, "-i64", StringPrintf("%" PRId64, timestamp)});
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("Get property result:"));
+ }
+
+ // Set the timestamp argument to 0.
+ result = getHardware()->dump({"--getWithArg", propIdStr, "-i64", "0"});
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ // There is no freeze obd2 frame at timestamp 0.
+ ASSERT_THAT(result.buffer, ContainsRegex("failed to read property value"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testSaveRestoreProp) {
+ int32_t prop = toInt(VehicleProperty::TIRE_PRESSURE);
+ std::string propIdStr = std::to_string(prop);
+ std::string areaIdStr = std::to_string(WHEEL_FRONT_LEFT);
+
+ DumpResult result = getHardware()->dump({"--save-prop", propIdStr, "-a", areaIdStr});
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, ContainsRegex("saved"));
+
+ ASSERT_EQ(setValue(VehiclePropValue{
+ .prop = prop,
+ .areaId = WHEEL_FRONT_LEFT,
+ .value =
+ {
+ .floatValues = {210.0},
+ },
+ }),
+ StatusCode::OK);
+
+ result = getHardware()->dump({"--restore-prop", propIdStr, "-a", areaIdStr});
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, ContainsRegex("restored"));
+
+ auto getResult = getValue(VehiclePropValue{.prop = prop, .areaId = WHEEL_FRONT_LEFT});
+
+ ASSERT_TRUE(getResult.ok());
+ // The default value is 200.0.
+ ASSERT_EQ(getResult.value().value.floatValues, std::vector<float>{200.0});
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpInjectEvent) {
+ int32_t prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
+ std::string propIdStr = std::to_string(prop);
+
+ int64_t timestamp = elapsedRealtimeNano();
+ // Inject an event with float value 123.4 and timestamp.
+ DumpResult result = getHardware()->dump(
+ {"--inject-event", propIdStr, "-f", "123.4", "-t", std::to_string(timestamp)});
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer,
+ ContainsRegex(StringPrintf("Event for property: %d injected", prop)));
+ ASSERT_TRUE(waitForChangedProperties(prop, 0, /*count=*/1, milliseconds(1000)))
+ << "No changed event received for injected event from vehicle bus";
+ auto events = getChangedProperties();
+ ASSERT_EQ(events.size(), 1u);
+ auto event = events[0];
+ ASSERT_EQ(event.timestamp, timestamp);
+ ASSERT_EQ(event.value.floatValues, std::vector<float>({123.4}));
+}
+
TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
std::vector<std::string> options;
options.push_back("--invalid");
@@ -1606,6 +1717,260 @@
ASSERT_EQ(3.402823466E+38f, value.value.floatValues[2]);
}
+struct OptionsTestCase {
+ std::string name;
+ std::vector<std::string> options;
+ std::string expectMsg;
+};
+
+class FakeVehicleHardwareOptionsTest : public FakeVehicleHardwareTest,
+ public testing::WithParamInterface<OptionsTestCase> {};
+
+std::vector<OptionsTestCase> GenInvalidOptions() {
+ return {{"unknown_command", {"--unknown"}, "Invalid option: --unknown"},
+ {"help", {"--help"}, "Usage:"},
+ {"genfakedata_no_subcommand",
+ {"--genfakedata"},
+ "No subcommand specified for genfakedata"},
+ {"genfakedata_unknown_subcommand",
+ {"--genfakedata", "--unknown"},
+ "Unknown command: \"--unknown\""},
+ {"genfakedata_start_linear_no_args",
+ {"--genfakedata", "--startlinear"},
+ "incorrect argument count"},
+ {"genfakedata_start_linear_invalid_propId",
+ {"--genfakedata", "--startlinear", "abcd", "0.1", "0.1", "0.1", "0.1", "100000000"},
+ "failed to parse propId as int: \"abcd\""},
+ {"genfakedata_start_linear_invalid_middleValue",
+ {"--genfakedata", "--startlinear", "1", "abcd", "0.1", "0.1", "0.1", "100000000"},
+ "failed to parse middleValue as float: \"abcd\""},
+ {"genfakedata_start_linear_invalid_currentValue",
+ {"--genfakedata", "--startlinear", "1", "0.1", "abcd", "0.1", "0.1", "100000000"},
+ "failed to parse currentValue as float: \"abcd\""},
+ {"genfakedata_start_linear_invalid_dispersion",
+ {"--genfakedata", "--startlinear", "1", "0.1", "0.1", "abcd", "0.1", "100000000"},
+ "failed to parse dispersion as float: \"abcd\""},
+ {"genfakedata_start_linear_invalid_increment",
+ {"--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "abcd", "100000000"},
+ "failed to parse increment as float: \"abcd\""},
+ {"genfakedata_start_linear_invalid_interval",
+ {"--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "0.1", "0.1"},
+ "failed to parse interval as int: \"0.1\""},
+ {"genfakedata_stop_linear_no_args",
+ {"--genfakedata", "--stoplinear"},
+ "incorrect argument count"},
+ {"genfakedata_stop_linear_invalid_propId",
+ {"--genfakedata", "--stoplinear", "abcd"},
+ "failed to parse propId as int: \"abcd\""},
+ {"genfakedata_startjson_no_args",
+ {"--genfakedata", "--startjson"},
+ "incorrect argument count"},
+ {"genfakedata_startjson_invalid_repetition",
+ {"--genfakedata", "--startjson", "--path", "file", "0.1"},
+ "failed to parse repetition as int: \"0.1\""},
+ {"genfakedata_startjson_invalid_json_file",
+ {"--genfakedata", "--startjson", "--path", "file", "1"},
+ "invalid JSON file"},
+ {"genfakedata_stopjson_no_args",
+ {"--genfakedata", "--stopjson"},
+ "incorrect argument count"},
+ {"genfakedata_keypress_no_args",
+ {"--genfakedata", "--keypress"},
+ "incorrect argument count"},
+ {"genfakedata_keypress_invalid_keyCode",
+ {"--genfakedata", "--keypress", "0.1", "1"},
+ "failed to parse keyCode as int: \"0.1\""},
+ {"genfakedata_keypress_invalid_display",
+ {"--genfakedata", "--keypress", "1", "0.1"},
+ "failed to parse display as int: \"0.1\""}};
+}
+
+TEST_P(FakeVehicleHardwareOptionsTest, testInvalidOptions) {
+ auto tc = GetParam();
+
+ DumpResult result = getHardware()->dump(tc.options);
+
+ EXPECT_FALSE(result.callerShouldDumpState);
+ EXPECT_THAT(result.buffer, HasSubstr(tc.expectMsg));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FakeVehicleHardwareOptionsTests, FakeVehicleHardwareOptionsTest,
+ testing::ValuesIn(GenInvalidOptions()),
+ [](const testing::TestParamInfo<FakeVehicleHardwareOptionsTest::ParamType>& info) {
+ return info.param.name;
+ });
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) {
+ // Start a fake linear data generator for vehicle speed at 0.1s interval.
+ // range: 0 - 100, current value: 30, step: 20.
+ std::string propIdString = StringPrintf("%d", toInt(VehicleProperty::PERF_VEHICLE_SPEED));
+ std::vector<std::string> options = {"--genfakedata", "--startlinear", propIdString,
+ /*middleValue=*/"50",
+ /*currentValue=*/"30",
+ /*dispersion=*/"50",
+ /*increment=*/"20",
+ /*interval=*/"100000000"};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+ ASSERT_TRUE(waitForChangedProperties(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0, /*count=*/5,
+ milliseconds(1000)))
+ << "not enough events generated for linear data generator";
+
+ int32_t value = 30;
+ auto events = getChangedProperties();
+ for (size_t i = 0; i < 5; i++) {
+ ASSERT_EQ(1u, events[i].value.floatValues.size());
+ EXPECT_EQ(static_cast<float>(value), events[i].value.floatValues[0]);
+ value = (value + 20) % 100;
+ }
+
+ // Stop the linear generator.
+ options = {"--genfakedata", "--stoplinear", propIdString};
+
+ result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+ clearChangedProperties();
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+
+ // There should be no new events generated.
+ EXPECT_EQ(0u, getEventCount(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0));
+}
+
+std::string getTestFilePath(const char* filename) {
+ static std::string baseDir = android::base::GetExecutableDirectory();
+ return baseDir + "/fakedata/" + filename;
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) {
+ std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
+ getTestFilePath("prop.json"), "2"};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+ ASSERT_TRUE(waitForChangedProperties(/*count=*/8, milliseconds(1000)))
+ << "not enough events generated for JSON data generator";
+
+ auto events = getChangedProperties();
+ ASSERT_EQ(8u, events.size());
+ // First set of events, we test 1st and the last.
+ EXPECT_EQ(1u, events[0].value.int32Values.size());
+ EXPECT_EQ(8, events[0].value.int32Values[0]);
+ EXPECT_EQ(1u, events[3].value.int32Values.size());
+ EXPECT_EQ(10, events[3].value.int32Values[0]);
+ // Second set of the same events.
+ EXPECT_EQ(1u, events[4].value.int32Values.size());
+ EXPECT_EQ(8, events[4].value.int32Values[0]);
+ EXPECT_EQ(1u, events[7].value.int32Values.size());
+ EXPECT_EQ(10, events[7].value.int32Values[0]);
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonByContent) {
+ std::vector<std::string> options = {
+ "--genfakedata", "--startjson", "--content",
+ "[{\"timestamp\":1000000,\"areaId\":0,\"value\":8,\"prop\":289408000}]", "1"};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+ ASSERT_TRUE(waitForChangedProperties(/*count=*/1, milliseconds(1000)))
+ << "not enough events generated for JSON data generator";
+
+ auto events = getChangedProperties();
+ ASSERT_EQ(1u, events.size());
+ EXPECT_EQ(1u, events[0].value.int32Values.size());
+ EXPECT_EQ(8, events[0].value.int32Values[0]);
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonInvalidContent) {
+ std::vector<std::string> options = {"--genfakedata", "--startjson", "--content", "[{", "2"};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("invalid JSON content"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonInvalidFile) {
+ std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
+ getTestFilePath("blahblah.json"), "2"};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("invalid JSON file"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStop) {
+ // No iteration number provided, would loop indefinitely.
+ std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
+ getTestFilePath("prop.json")};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+ std::string id = result.buffer.substr(result.buffer.find("ID: ") + 4);
+
+ result = getHardware()->dump({"--genfakedata", "--stopjson", id});
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStopInvalidFile) {
+ // No iteration number provided, would loop indefinitely.
+ std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
+ getTestFilePath("prop.json")};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+ result = getHardware()->dump({"--genfakedata", "--stopjson", "1234"});
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("No JSON event generator found"));
+
+ // TearDown function should destroy the generator which stops the iteration.
+}
+
+TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) {
+ std::vector<std::string> options = {"--genfakedata", "--keypress", "1", "2"};
+
+ DumpResult result = getHardware()->dump(options);
+
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_THAT(result.buffer, HasSubstr("successfully"));
+
+ auto events = getChangedProperties();
+ ASSERT_EQ(2u, events.size());
+ EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[0].prop);
+ EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[1].prop);
+ ASSERT_EQ(3u, events[0].value.int32Values.size());
+ ASSERT_EQ(3u, events[1].value.int32Values.size());
+ EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_DOWN), events[0].value.int32Values[0]);
+ EXPECT_EQ(1, events[0].value.int32Values[1]);
+ EXPECT_EQ(2, events[0].value.int32Values[2]);
+ EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_UP), events[1].value.int32Values[0]);
+ EXPECT_EQ(1, events[1].value.int32Values[1]);
+ EXPECT_EQ(2, events[1].value.int32Values[2]);
+}
+
TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) {
ASSERT_EQ(setValue(VehiclePropValue{
.prop = ECHO_REVERSE_BYTES,
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/fakedata/prop.json b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/fakedata/prop.json
new file mode 100644
index 0000000..7123a00
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/fakedata/prop.json
@@ -0,0 +1,26 @@
+[
+ {
+ "timestamp": 1000000,
+ "areaId": 0,
+ "value": 8,
+ "prop": 289408000
+ },
+ {
+ "timestamp": 2000000,
+ "areaId": 0,
+ "value": 4,
+ "prop": 289408000
+ },
+ {
+ "timestamp": 3000000,
+ "areaId": 0,
+ "value": 16,
+ "prop": 289408000
+ },
+ {
+ "timestamp": 4000000,
+ "areaId": 0,
+ "value": 10,
+ "prop": 289408000
+ }
+]
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json
index 59666b8..693f1e2 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/gear_selection.json
@@ -1,9 +1,13 @@
-[
- {
- "timestamp": 1000000,
- "areaId": 0,
- "value": 8,
- // GEAR_SELECTION
- "prop": 289408000
- }
-]
+{
+ "apiVersion": 1,
+ "properties": [
+ {
+ "property": "VehicleProperty::GEAR_SELECTION",
+ "defaultValue": {
+ "int32Values": [
+ 8
+ ]
+ }
+ }
+ ]
+}
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json
index 93a97ed..07cfebb 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/override/hvac_temperature_set.json
@@ -1,10 +1,20 @@
-[
- {
- "timestamp": 1000000,
- // HVAC_LEFT
- "areaId": 49,
- "value": 30,
- // HVAC_TEMPERATURE_SET
- "prop": 358614275
- }
-]
+{
+ "apiVersion": 1,
+ "properties": [
+ {
+ "property": "VehicleProperty::HVAC_TEMPERATURE_SET",
+ "areas": [
+ {
+ "defaultValue": {
+ "floatValues": [
+ 30.0
+ ]
+ },
+ "areaId": 49,
+ "minFloatValue": 16.0,
+ "maxFloatValue": 32.0
+ }
+ ]
+ }
+ ]
+}
diff --git a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
index 7670c25..2b4059c 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/Android.bp
@@ -47,13 +47,19 @@
],
vendor: true,
defaults: ["VehicleHalDefaults"],
- shared_libs: ["libprotobuf-cpp-full"],
+ shared_libs: [
+ "libprotobuf-cpp-full",
+ "libjsoncpp",
+ ],
static_libs: [
+ "VehicleHalJsonConfigLoaderEnableTestProperties",
"VehicleHalProtoMessageConverter",
"VehicleHalProtos",
"VehicleHalUtils",
"libgtest",
],
- header_libs: ["VehicleHalDefaultConfig"],
+ data: [
+ ":VehicleHalDefaultProperties_JSON",
+ ],
test_suites: ["device-tests"],
}
diff --git a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
index c742db5..308be46 100644
--- a/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
+++ b/automotive/vehicle/aidl/impl/grpc/utils/proto_message_converter/test/proto_message_converter_test.cpp
@@ -16,9 +16,11 @@
#include <vector>
-#include <DefaultConfig.h>
+#include <JsonConfigLoader.h>
#include <ProtoMessageConverter.h>
#include <VehicleHalTypes.h>
+
+#include <android-base/file.h>
#include <android-base/format.h>
#include <android/hardware/automotive/vehicle/VehiclePropConfig.pb.h>
#include <android/hardware/automotive/vehicle/VehiclePropValue.pb.h>
@@ -35,23 +37,39 @@
namespace proto = ::android::hardware::automotive::vehicle::proto;
namespace aidl_vehicle = ::aidl::android::hardware::automotive::vehicle;
+constexpr char DEFAULT_PROPERTIES_CONFIG[] = "DefaultProperties.json";
+
+inline std::string getConfigPath(const std::string& name) {
+ return android::base::GetExecutableDirectory() + "/" + name;
+}
+
std::vector<aidl_vehicle::VehiclePropConfig> prepareTestConfigs() {
+ JsonConfigLoader loader;
+ auto result = loader.loadPropConfig(getConfigPath(DEFAULT_PROPERTIES_CONFIG));
+ if (!result.ok()) {
+ return {};
+ }
std::vector<aidl_vehicle::VehiclePropConfig> configs;
- for (auto& property : defaultconfig::getDefaultConfigs()) {
- configs.push_back(property.config);
+ for (auto& [_, configDeclaration] : result.value()) {
+ configs.push_back(configDeclaration.config);
}
return configs;
}
std::vector<aidl_vehicle::VehiclePropValue> prepareTestValues() {
+ JsonConfigLoader loader;
+ auto result = loader.loadPropConfig(getConfigPath(DEFAULT_PROPERTIES_CONFIG));
+ if (!result.ok()) {
+ return {};
+ }
std::vector<aidl_vehicle::VehiclePropValue> values;
int64_t timestamp = 1;
- for (auto& property : defaultconfig::getDefaultConfigs()) {
+ for (auto& [_, configDeclaration] : result.value()) {
values.push_back({
.timestamp = timestamp,
.areaId = 123,
- .prop = property.config.prop,
- .value = property.initialValue,
+ .prop = configDeclaration.config.prop,
+ .value = configDeclaration.initialValue,
.status = aidl_vehicle::VehiclePropertyStatus::ERROR,
});
}
diff --git a/automotive/vehicle/aidl/impl/utils/README.md b/automotive/vehicle/aidl/impl/utils/README.md
new file mode 100644
index 0000000..87bb7e3
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/utils/README.md
@@ -0,0 +1,62 @@
+# Utility classes for VHAL implementation
+---
+
+This directory stores utility classes for VHAL implementation. Vendor
+implementation could use utility classes from `common` folder in their
+VHAL implementation.
+
+## common
+
+Defines common utility libraries.
+
+### ConcurrentQueue
+
+Provides a thread-safe concurrent queue object. Useful for adding object to
+a queue in one thread (usually binder thread) and handle the objects in a
+separate handler thread.
+
+### ParcelableUtils
+
+Provides functions to convert between a regular parcelable and a
+`LargeParcelabe`.
+
+A `LargeParcelable` is a parcelable that marshals the payload
+into a shared memory file if the payload is too large to pass across binder.
+It is used to pass large data across binder. Before sending the data, VHAL
+impl should convert a regular parcelabe to a `LargeParcelable`. After receving
+data, VHAL impl should convert a `LargeParcelable` back to regular parcelabe.
+
+### PendingRequestPool
+
+Defines A class for managing pending requests and automatically call timeout
+callback if the request timed-out.
+
+### PropertyUtils
+
+Defines some useful constants.
+
+### RecurrentTimer
+
+Defines a thread-safe recurrent timer that can call a function periodically.
+
+### VehicleHalTypes
+
+Provides a header file that includes many commonly used header files. Useful
+when you are using multiple types defined in VHAL interface.
+
+### VehicleObjectPool
+
+Defines a reusable in-memory pool for `VehiclePropValue`.
+
+### VehiclePropertyStore
+
+Defines an in-memory map for storing vehicle properties. Allows easier insert,
+delete and lookup.
+
+### VehicleUtils
+
+Defines many useful utility functions.
+
+## test
+
+Defines utility libraries for test only.
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/PendingRequestPool.h b/automotive/vehicle/aidl/impl/utils/common/include/PendingRequestPool.h
index 3f8db93..28cf08e 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/PendingRequestPool.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/PendingRequestPool.h
@@ -21,7 +21,6 @@
#include <android-base/result.h>
#include <android-base/thread_annotations.h>
-#include <atomic>
#include <list>
#include <mutex>
#include <thread>
@@ -85,7 +84,7 @@
std::unordered_map<const void*, std::list<PendingRequest>> mPendingRequestsByClient
GUARDED_BY(mLock);
std::thread mThread;
- std::atomic<bool> mThreadStop = false;
+ bool mThreadStop = false;
std::condition_variable mCv;
std::mutex mCvLock;
diff --git a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
index 8bc3c20..08e1990 100644
--- a/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
+++ b/automotive/vehicle/aidl/impl/utils/common/include/VehicleHalTypes.h
@@ -26,6 +26,7 @@
#include <aidl/android/hardware/automotive/vehicle/GetValueRequest.h>
#include <aidl/android/hardware/automotive/vehicle/GetValueResult.h>
#include <aidl/android/hardware/automotive/vehicle/GetValueResults.h>
+#include <aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.h>
#include <aidl/android/hardware/automotive/vehicle/Obd2CommonIgnitionMonitors.h>
#include <aidl/android/hardware/automotive/vehicle/Obd2FuelSystemStatus.h>
#include <aidl/android/hardware/automotive/vehicle/Obd2FuelType.h>
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/PendingRequestPool.cpp b/automotive/vehicle/aidl/impl/utils/common/src/PendingRequestPool.cpp
index 0196edd..ab50499 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/PendingRequestPool.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/PendingRequestPool.cpp
@@ -39,20 +39,27 @@
} // namespace
-PendingRequestPool::PendingRequestPool(int64_t timeoutInNano)
- : mTimeoutInNano(timeoutInNano), mThread([this] {
- // [this] must be alive within this thread because destructor would wait for this thread
- // to exit.
- int64_t sleepTime = std::min(mTimeoutInNano, static_cast<int64_t>(CHECK_TIME_IN_NANO));
- std::unique_lock<std::mutex> lk(mCvLock);
- while (!mCv.wait_for(lk, std::chrono::nanoseconds(sleepTime),
- [this] { return mThreadStop.load(); })) {
- checkTimeout();
- }
- }) {}
+PendingRequestPool::PendingRequestPool(int64_t timeoutInNano) : mTimeoutInNano(timeoutInNano) {
+ mThread = std::thread([this] {
+ // [this] must be alive within this thread because destructor would wait for this thread
+ // to exit.
+ int64_t sleepTime = std::min(mTimeoutInNano, static_cast<int64_t>(CHECK_TIME_IN_NANO));
+ std::unique_lock<std::mutex> lk(mCvLock);
+ while (!mCv.wait_for(lk, std::chrono::nanoseconds(sleepTime),
+ [this] { return mThreadStop; })) {
+ checkTimeout();
+ }
+ });
+}
PendingRequestPool::~PendingRequestPool() {
- mThreadStop = true;
+ {
+ // Even if the shared variable is atomic, it must be modified under the
+ // mutex in order to correctly publish the modification to the waiting
+ // thread.
+ std::unique_lock<std::mutex> lk(mCvLock);
+ mThreadStop = true;
+ }
mCv.notify_all();
if (mThread.joinable()) {
mThread.join();
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
index 2eca6b7..43f5d69 100644
--- a/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
+++ b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
@@ -29,7 +29,9 @@
using ::android::base::ScopedLockAssertion;
-RecurrentTimer::RecurrentTimer() : mThread(&RecurrentTimer::loop, this) {}
+RecurrentTimer::RecurrentTimer() {
+ mThread = std::thread(&RecurrentTimer::loop, this);
+}
RecurrentTimer::~RecurrentTimer() {
{
diff --git a/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h b/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h
index d512713..e6ea6fe 100644
--- a/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h
+++ b/automotive/vehicle/aidl/impl/utils/test/include/TestPropertyUtils.h
@@ -35,7 +35,6 @@
} // namespace testpropertyutils_impl
-#ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
// Converts the system property to the vendor property.
// WARNING: This is only for the end-to-end testing, Should NOT include in the user build.
inline constexpr int32_t toVendor(
@@ -55,7 +54,6 @@
toVendor(testpropertyutils_impl::VehicleProperty::CLUSTER_REQUEST_DISPLAY);
constexpr int32_t VENDOR_CLUSTER_NAVIGATION_STATE =
toVendor(testpropertyutils_impl::VehicleProperty::CLUSTER_NAVIGATION_STATE);
-#endif // ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
// These properties are placeholder properties for developers to test new features without
// implementing a real property.
diff --git a/automotive/vehicle/aidl/impl/vhal/Android.bp b/automotive/vehicle/aidl/impl/vhal/Android.bp
index 5abcaf6..6fec2c9 100644
--- a/automotive/vehicle/aidl/impl/vhal/Android.bp
+++ b/automotive/vehicle/aidl/impl/vhal/Android.bp
@@ -66,3 +66,32 @@
"libbinder_ndk",
],
}
+
+cc_fuzz {
+ name: "android.hardware.automotive.vehicle@V1-default-service_fuzzer",
+ vendor: true,
+ defaults: [
+ "FakeVehicleHardwareDefaults",
+ "VehicleHalDefaults",
+ "android-automotive-large-parcelable-defaults",
+ ],
+ static_libs: [
+ "DefaultVehicleHal",
+ "FakeVehicleHardware",
+ "VehicleHalUtils",
+ "libbase",
+ "libbinder_random_parcel",
+ "libcutils",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ "libutils",
+ ],
+ srcs: ["src/fuzzer.cpp"],
+ fuzz_config: {
+ cc: [
+ "keithmok@google.com",
+ ],
+ },
+}
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index 9c29816..0439ac6 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -123,10 +123,10 @@
std::shared_ptr<PendingRequestPool> mPendingRequestPool;
};
- // A wrapper for binder operations to enable stubbing for test.
- class IBinder {
+ // A wrapper for binder lifecycle operations to enable stubbing for test.
+ class BinderLifecycleInterface {
public:
- virtual ~IBinder() = default;
+ virtual ~BinderLifecycleInterface() = default;
virtual binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
void* cookie) = 0;
@@ -134,8 +134,8 @@
virtual bool isAlive(const AIBinder* binder) = 0;
};
- // A real implementation for IBinder.
- class AIBinderImpl final : public IBinder {
+ // A real implementation for BinderLifecycleInterface.
+ class BinderLifecycleHandler final : public BinderLifecycleInterface {
public:
binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
void* cookie) override;
@@ -154,7 +154,7 @@
// BinderDiedUnlinkedEvent represents either an onBinderDied or an onBinderUnlinked event.
struct BinderDiedUnlinkedEvent {
// true for onBinderDied, false for onBinderUnlinked.
- bool onBinderDied;
+ bool forOnBinderDied;
const AIBinder* clientId;
};
@@ -186,8 +186,8 @@
GUARDED_BY(mLock);
// SubscriptionClients is thread-safe.
std::shared_ptr<SubscriptionClients> mSubscriptionClients;
- // mBinderImpl is only going to be changed in test.
- std::unique_ptr<IBinder> mBinderImpl;
+ // mBinderLifecycleHandler is only going to be changed in test.
+ std::unique_ptr<BinderLifecycleInterface> mBinderLifecycleHandler;
// Only initialized once.
std::shared_ptr<std::function<void()>> mRecurrentAction;
@@ -263,7 +263,7 @@
void setTimeout(int64_t timeoutInNano);
// Test-only
- void setBinderImpl(std::unique_ptr<IBinder> impl);
+ void setBinderLifecycleHandler(std::unique_ptr<BinderLifecycleInterface> impl);
};
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
index 7c8f1b4..14799d9 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
@@ -41,15 +41,15 @@
public:
using ClientIdType = const AIBinder*;
- void addClient(const ClientIdType& clientId, float sampleRate);
+ void addClient(const ClientIdType& clientId, float sampleRateHz);
void removeClient(const ClientIdType& clientId);
- float getMaxSampleRate();
+ float getMaxSampleRateHz() const;
private:
- float mMaxSampleRate = 0.;
- std::unordered_map<ClientIdType, float> mSampleRates;
+ float mMaxSampleRateHz = 0.;
+ std::unordered_map<ClientIdType, float> mSampleRateHzByClient;
- void refreshMaxSampleRate();
+ void refreshMaxSampleRateHz();
};
// A thread-safe subscription manager that manages all VHAL subscriptions.
@@ -59,7 +59,7 @@
using CallbackType =
std::shared_ptr<aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
- explicit SubscriptionManager(IVehicleHardware* hardware);
+ explicit SubscriptionManager(IVehicleHardware* vehicleHardware);
~SubscriptionManager();
// Subscribes to properties according to {@code SubscribeOptions}. Note that all option must
@@ -99,13 +99,8 @@
const std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
updatedValues);
- // Gets the sample rate for the continuous property. Returns {@code std::nullopt} if the
- // property has not been subscribed before or is not a continuous property.
- std::optional<float> getSampleRate(const ClientIdType& clientId, int32_t propId,
- int32_t areaId);
-
// Checks whether the sample rate is valid.
- static bool checkSampleRate(float sampleRate);
+ static bool checkSampleRateHz(float sampleRateHz);
private:
// Friend class for testing.
@@ -122,17 +117,21 @@
std::unordered_map<PropIdAreaId, ContSubConfigs, PropIdAreaIdHash> mContSubConfigsByPropIdArea
GUARDED_BY(mLock);
- VhalResult<void> updateSampleRateLocked(const ClientIdType& clientId,
- const PropIdAreaId& propIdAreaId, float sampleRate)
+ VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
+ const PropIdAreaId& propIdAreaId,
+ float sampleRateHz) REQUIRES(mLock);
+ VhalResult<void> removeContinuousSubscriberLocked(const ClientIdType& clientId,
+ const PropIdAreaId& propIdAreaId)
REQUIRES(mLock);
- VhalResult<void> removeSampleRateLocked(const ClientIdType& clientId,
- const PropIdAreaId& propIdAreaId) REQUIRES(mLock);
+
+ VhalResult<void> updateContSubConfigs(const PropIdAreaId& PropIdAreaId,
+ const ContSubConfigs& newConfig) REQUIRES(mLock);
// Checks whether the manager is empty. For testing purpose.
bool isEmpty();
// Get the interval in nanoseconds accroding to sample rate.
- static android::base::Result<int64_t> getInterval(float sampleRate);
+ static android::base::Result<int64_t> getIntervalNanos(float sampleRateHz);
};
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index b191aef..d447bf8 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -78,14 +78,14 @@
return str;
}
-float getDefaultSampleRate(float sampleRate, float minSampleRate, float maxSampleRate) {
- if (sampleRate < minSampleRate) {
- return minSampleRate;
+float getDefaultSampleRateHz(float sampleRateHz, float minSampleRateHz, float maxSampleRateHz) {
+ if (sampleRateHz < minSampleRateHz) {
+ return minSampleRateHz;
}
- if (sampleRate > maxSampleRate) {
- return maxSampleRate;
+ if (sampleRateHz > maxSampleRateHz) {
+ return maxSampleRateHz;
}
- return sampleRate;
+ return sampleRateHz;
}
} // namespace
@@ -123,8 +123,8 @@
return mClients.size();
}
-DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware)
- : mVehicleHardware(std::move(hardware)),
+DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> vehicleHardware)
+ : mVehicleHardware(std::move(vehicleHardware)),
mPendingRequestPool(std::make_shared<PendingRequestPool>(TIMEOUT_IN_NANO)) {
auto configs = mVehicleHardware->getAllPropertyConfigs();
for (auto& config : configs) {
@@ -144,11 +144,10 @@
}
mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool);
- mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool);
auto subscribeIdByClient = std::make_shared<SubscribeIdByClient>();
- IVehicleHardware* hardwarePtr = mVehicleHardware.get();
- mSubscriptionManager = std::make_shared<SubscriptionManager>(hardwarePtr);
+ IVehicleHardware* vehicleHardwarePtr = mVehicleHardware.get();
+ mSubscriptionManager = std::make_shared<SubscriptionManager>(vehicleHardwarePtr);
std::weak_ptr<SubscriptionManager> subscriptionManagerCopy = mSubscriptionManager;
mVehicleHardware->registerOnPropertyChangeEvent(
@@ -158,13 +157,13 @@
}));
// Register heartbeat event.
- mRecurrentAction =
- std::make_shared<std::function<void()>>([hardwarePtr, subscriptionManagerCopy]() {
- checkHealth(hardwarePtr, subscriptionManagerCopy);
+ mRecurrentAction = std::make_shared<std::function<void()>>(
+ [vehicleHardwarePtr, subscriptionManagerCopy]() {
+ checkHealth(vehicleHardwarePtr, subscriptionManagerCopy);
});
mRecurrentTimer.registerTimerCallback(HEART_BEAT_INTERVAL_IN_NANO, mRecurrentAction);
- mBinderImpl = std::make_unique<AIBinderImpl>();
+ mBinderLifecycleHandler = std::make_unique<BinderLifecycleHandler>();
mOnBinderDiedUnlinkedHandlerThread = std::thread([this] { onBinderDiedUnlinkedHandler(); });
mDeathRecipient = ScopedAIBinder_DeathRecipient(
AIBinder_DeathRecipient_new(&DefaultVehicleHal::onBinderDied));
@@ -220,7 +219,7 @@
bool DefaultVehicleHal::monitorBinderLifeCycleLocked(const AIBinder* clientId) {
OnBinderDiedContext* contextPtr = nullptr;
if (mOnBinderDiedContexts.find(clientId) != mOnBinderDiedContexts.end()) {
- return mBinderImpl->isAlive(clientId);
+ return mBinderLifecycleHandler->isAlive(clientId);
} else {
std::unique_ptr<OnBinderDiedContext> context = std::make_unique<OnBinderDiedContext>(
OnBinderDiedContext{.vhal = this, .clientId = clientId});
@@ -232,7 +231,7 @@
}
// If this function fails, onBinderUnlinked would be called to remove the added context.
- binder_status_t status = mBinderImpl->linkToDeath(
+ binder_status_t status = mBinderLifecycleHandler->linkToDeath(
const_cast<AIBinder*>(clientId), mDeathRecipient.get(), static_cast<void*>(contextPtr));
if (status == STATUS_OK) {
return true;
@@ -246,7 +245,8 @@
OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie);
// To be handled in mOnBinderDiedUnlinkedHandlerThread. We cannot handle the event in the same
// thread because we might be holding the mLock the handler requires.
- context->vhal->mBinderEvents.push(BinderDiedUnlinkedEvent{true, context->clientId});
+ context->vhal->mBinderEvents.push(
+ BinderDiedUnlinkedEvent{/*forOnBinderDied=*/true, context->clientId});
}
void DefaultVehicleHal::onBinderDiedWithContext(const AIBinder* clientId) {
@@ -262,7 +262,8 @@
OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie);
// To be handled in mOnBinderDiedUnlinkedHandlerThread. We cannot handle the event in the same
// thread because we might be holding the mLock the handler requires.
- context->vhal->mBinderEvents.push(BinderDiedUnlinkedEvent{false, context->clientId});
+ context->vhal->mBinderEvents.push(
+ BinderDiedUnlinkedEvent{/*forOnBinderDied=*/false, context->clientId});
}
void DefaultVehicleHal::onBinderUnlinkedWithContext(const AIBinder* clientId) {
@@ -275,7 +276,7 @@
void DefaultVehicleHal::onBinderDiedUnlinkedHandler() {
while (mBinderEvents.waitForItems()) {
for (BinderDiedUnlinkedEvent& event : mBinderEvents.flush()) {
- if (event.onBinderDied) {
+ if (event.forOnBinderDied) {
onBinderDiedWithContext(event.clientId);
} else {
onBinderUnlinkedWithContext(event.clientId);
@@ -349,6 +350,9 @@
ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback,
const GetValueRequests& requests) {
+ if (callback == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
expected<LargeParcelableBase::BorrowedOwnedObject<GetValueRequests>, ScopedAStatus>
deserializedResults = fromStableLargeParcelable(requests);
if (!deserializedResults.ok()) {
@@ -432,6 +436,9 @@
ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
const SetValueRequests& requests) {
+ if (callback == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
expected<LargeParcelableBase::BorrowedOwnedObject<SetValueRequests>, ScopedAStatus>
deserializedResults = fromStableLargeParcelable(requests);
if (!deserializedResults.ok()) {
@@ -589,18 +596,20 @@
}
if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
- float sampleRate = option.sampleRate;
- float minSampleRate = config.minSampleRate;
- float maxSampleRate = config.maxSampleRate;
- if (sampleRate < minSampleRate || sampleRate > maxSampleRate) {
- float defaultRate = getDefaultSampleRate(sampleRate, minSampleRate, maxSampleRate);
- ALOGW("sample rate: %f out of range, must be within %f and %f, set to %f",
- sampleRate, minSampleRate, maxSampleRate, defaultRate);
- sampleRate = defaultRate;
+ float sampleRateHz = option.sampleRate;
+ float minSampleRateHz = config.minSampleRate;
+ float maxSampleRateHz = config.maxSampleRate;
+ float defaultRateHz =
+ getDefaultSampleRateHz(sampleRateHz, minSampleRateHz, maxSampleRateHz);
+ if (sampleRateHz != defaultRateHz) {
+ ALOGW("sample rate: %f HZ out of range, must be within %f HZ and %f HZ , set to %f "
+ "HZ",
+ sampleRateHz, minSampleRateHz, maxSampleRateHz, defaultRateHz);
+ sampleRateHz = defaultRateHz;
}
- if (!SubscriptionManager::checkSampleRate(sampleRate)) {
+ if (!SubscriptionManager::checkSampleRateHz(sampleRateHz)) {
return StatusError(StatusCode::INVALID_ARG)
- << "invalid sample rate: " << sampleRate;
+ << "invalid sample rate: " << sampleRateHz << " HZ";
}
}
@@ -625,11 +634,13 @@
const std::vector<SubscribeOptions>& options,
[[maybe_unused]] int32_t maxSharedMemoryFileCount) {
// TODO(b/205189110): Use shared memory file count.
+ if (callback == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
if (auto result = checkSubscribeOptions(options); !result.ok()) {
ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str());
return toScopedAStatus(result);
}
-
std::vector<SubscribeOptions> onChangeSubscriptions;
std::vector<SubscribeOptions> continuousSubscriptions;
for (const auto& option : options) {
@@ -650,7 +661,7 @@
}
if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
- optionCopy.sampleRate = getDefaultSampleRate(
+ optionCopy.sampleRate = getDefaultSampleRateHz(
optionCopy.sampleRate, config.minSampleRate, config.maxSampleRate);
continuousSubscriptions.push_back(std::move(optionCopy));
} else {
@@ -685,6 +696,9 @@
ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType& callback,
const std::vector<int32_t>& propIds) {
+ if (callback == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds));
}
@@ -729,9 +743,9 @@
return {};
}
-void DefaultVehicleHal::checkHealth(IVehicleHardware* hardware,
+void DefaultVehicleHal::checkHealth(IVehicleHardware* vehicleHardware,
std::weak_ptr<SubscriptionManager> subscriptionManager) {
- StatusCode status = hardware->checkHealth();
+ StatusCode status = vehicleHardware->checkHealth();
if (status != StatusCode::OK) {
ALOGE("VHAL check health returns non-okay status");
return;
@@ -746,18 +760,18 @@
return;
}
-binder_status_t DefaultVehicleHal::AIBinderImpl::linkToDeath(AIBinder* binder,
- AIBinder_DeathRecipient* recipient,
- void* cookie) {
+binder_status_t DefaultVehicleHal::BinderLifecycleHandler::linkToDeath(
+ AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) {
return AIBinder_linkToDeath(binder, recipient, cookie);
}
-bool DefaultVehicleHal::AIBinderImpl::isAlive(const AIBinder* binder) {
+bool DefaultVehicleHal::BinderLifecycleHandler::isAlive(const AIBinder* binder) {
return AIBinder_isAlive(binder);
}
-void DefaultVehicleHal::setBinderImpl(std::unique_ptr<IBinder> impl) {
- mBinderImpl = std::move(impl);
+void DefaultVehicleHal::setBinderLifecycleHandler(
+ std::unique_ptr<BinderLifecycleInterface> handler) {
+ mBinderLifecycleHandler = std::move(handler);
}
bool DefaultVehicleHal::checkDumpPermission() {
diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
index 2694401..bba730f 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
@@ -42,7 +42,8 @@
using ::android::base::StringPrintf;
using ::ndk::ScopedAStatus;
-SubscriptionManager::SubscriptionManager(IVehicleHardware* hardware) : mVehicleHardware(hardware) {}
+SubscriptionManager::SubscriptionManager(IVehicleHardware* vehicleHardware)
+ : mVehicleHardware(vehicleHardware) {}
SubscriptionManager::~SubscriptionManager() {
std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -51,94 +52,82 @@
mSubscribedPropsByClient.clear();
}
-bool SubscriptionManager::checkSampleRate(float sampleRate) {
- return getInterval(sampleRate).ok();
+bool SubscriptionManager::checkSampleRateHz(float sampleRateHz) {
+ return getIntervalNanos(sampleRateHz).ok();
}
-Result<int64_t> SubscriptionManager::getInterval(float sampleRate) {
- int64_t interval = 0;
- if (sampleRate <= 0) {
+Result<int64_t> SubscriptionManager::getIntervalNanos(float sampleRateHz) {
+ int64_t intervalNanos = 0;
+ if (sampleRateHz <= 0) {
return Error() << "invalid sample rate, must be a positive number";
}
- if (sampleRate <= (ONE_SECOND_IN_NANO / static_cast<float>(INT64_MAX))) {
- return Error() << "invalid sample rate: " << sampleRate << ", too small";
+ if (sampleRateHz <= (ONE_SECOND_IN_NANO / static_cast<float>(INT64_MAX))) {
+ return Error() << "invalid sample rate: " << sampleRateHz << ", too small";
}
- interval = static_cast<int64_t>(ONE_SECOND_IN_NANO / sampleRate);
- return interval;
+ intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANO / sampleRateHz);
+ return intervalNanos;
}
-void ContSubConfigs::refreshMaxSampleRate() {
- float maxSampleRate = 0.;
+void ContSubConfigs::refreshMaxSampleRateHz() {
+ float maxSampleRateHz = 0.;
// This is not called frequently so a brute-focre is okay. More efficient way exists but this
// is simpler.
- for (const auto& [_, sampleRate] : mSampleRates) {
- if (sampleRate > maxSampleRate) {
- maxSampleRate = sampleRate;
+ for (const auto& [_, sampleRateHz] : mSampleRateHzByClient) {
+ if (sampleRateHz > maxSampleRateHz) {
+ maxSampleRateHz = sampleRateHz;
}
}
- mMaxSampleRate = maxSampleRate;
+ mMaxSampleRateHz = maxSampleRateHz;
}
-void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRate) {
- mSampleRates[clientId] = sampleRate;
- refreshMaxSampleRate();
+void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRateHz) {
+ mSampleRateHzByClient[clientId] = sampleRateHz;
+ refreshMaxSampleRateHz();
}
void ContSubConfigs::removeClient(const ClientIdType& clientId) {
- mSampleRates.erase(clientId);
- refreshMaxSampleRate();
+ mSampleRateHzByClient.erase(clientId);
+ refreshMaxSampleRateHz();
}
-float ContSubConfigs::getMaxSampleRate() {
- return mMaxSampleRate;
+float ContSubConfigs::getMaxSampleRateHz() const {
+ return mMaxSampleRateHz;
}
-VhalResult<void> SubscriptionManager::updateSampleRateLocked(const ClientIdType& clientId,
- const PropIdAreaId& propIdAreaId,
- float sampleRate) {
+VhalResult<void> SubscriptionManager::addContinuousSubscriberLocked(
+ const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz) {
// Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
- ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId];
- infoCopy.addClient(clientId, sampleRate);
- if (infoCopy.getMaxSampleRate() ==
- mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) {
- mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
+ ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
+ newConfig.addClient(clientId, sampleRateHz);
+ return updateContSubConfigs(propIdAreaId, newConfig);
+}
+
+VhalResult<void> SubscriptionManager::removeContinuousSubscriberLocked(
+ const ClientIdType& clientId, const PropIdAreaId& propIdAreaId) {
+ // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
+ ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
+ newConfig.removeClient(clientId);
+ return updateContSubConfigs(propIdAreaId, newConfig);
+}
+
+VhalResult<void> SubscriptionManager::updateContSubConfigs(const PropIdAreaId& propIdAreaId,
+ const ContSubConfigs& newConfig) {
+ if (newConfig.getMaxSampleRateHz() ==
+ mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRateHz()) {
+ mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
return {};
}
- float newRate = infoCopy.getMaxSampleRate();
+ float newRateHz = newConfig.getMaxSampleRateHz();
int32_t propId = propIdAreaId.propId;
int32_t areaId = propIdAreaId.areaId;
- if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate);
+ if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
status != StatusCode::OK) {
return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
", area"
- ": %" PRId32 ", sample rate: %f",
- propId, areaId, newRate);
+ ": %" PRId32 ", sample rate: %f HZ",
+ propId, areaId, newRateHz);
}
- mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
- return {};
-}
-
-VhalResult<void> SubscriptionManager::removeSampleRateLocked(const ClientIdType& clientId,
- const PropIdAreaId& propIdAreaId) {
- // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
- ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId];
- infoCopy.removeClient(clientId);
- if (infoCopy.getMaxSampleRate() ==
- mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) {
- mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
- return {};
- }
- float newRate = infoCopy.getMaxSampleRate();
- int32_t propId = propIdAreaId.propId;
- int32_t areaId = propIdAreaId.areaId;
- if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate);
- status != StatusCode::OK) {
- return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
- ", area"
- ": %" PRId32 ", sample rate: %f",
- propId, areaId, newRate);
- }
- mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy;
+ mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
return {};
}
@@ -147,14 +136,12 @@
bool isContinuousProperty) {
std::scoped_lock<std::mutex> lockGuard(mLock);
- std::vector<int64_t> intervals;
for (const auto& option : options) {
- float sampleRate = option.sampleRate;
+ float sampleRateHz = option.sampleRate;
if (isContinuousProperty) {
- auto intervalResult = getInterval(sampleRate);
- if (!intervalResult.ok()) {
- return StatusError(StatusCode::INVALID_ARG) << intervalResult.error().message();
+ if (auto result = getIntervalNanos(sampleRateHz); !result.ok()) {
+ return StatusError(StatusCode::INVALID_ARG) << result.error().message();
}
}
@@ -176,7 +163,8 @@
.areaId = areaId,
};
if (isContinuousProperty) {
- if (auto result = updateSampleRateLocked(clientId, propIdAreaId, option.sampleRate);
+ if (auto result = addContinuousSubscriberLocked(clientId, propIdAreaId,
+ option.sampleRate);
!result.ok()) {
return result;
}
@@ -214,7 +202,7 @@
while (it != propIdAreaIds.end()) {
int32_t propId = it->propId;
if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
- if (auto result = removeSampleRateLocked(clientId, *it); !result.ok()) {
+ if (auto result = removeContinuousSubscriberLocked(clientId, *it); !result.ok()) {
return result;
}
@@ -244,7 +232,7 @@
auto& subscriptions = mSubscribedPropsByClient[clientId];
for (auto const& propIdAreaId : subscriptions) {
- if (auto result = removeSampleRateLocked(clientId, propIdAreaId); !result.ok()) {
+ if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
return result;
}
diff --git a/automotive/vehicle/aidl/impl/vhal/src/fuzzer.cpp b/automotive/vehicle/aidl/impl/vhal/src/fuzzer.cpp
new file mode 100644
index 0000000..ac1e3b1
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/vhal/src/fuzzer.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <DefaultVehicleHal.h>
+#include <FakeVehicleHardware.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using ::android::fuzzService;
+using ::android::hardware::automotive::vehicle::DefaultVehicleHal;
+using ::android::hardware::automotive::vehicle::fake::FakeVehicleHardware;
+using ::ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::unique_ptr<FakeVehicleHardware> hardware = std::make_unique<FakeVehicleHardware>();
+ std::shared_ptr<DefaultVehicleHal> vhal =
+ ::ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+
+ fuzzService(vhal->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index f48b906..36fa5e6 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -324,9 +324,9 @@
mCallbackClient = IVehicleCallback::fromBinder(mBinder);
// Set the linkToDeath to a fake implementation that always returns OK.
- auto binderImpl = std::make_unique<TestBinderImpl>();
- mBinderImpl = binderImpl.get();
- mVhal->setBinderImpl(std::move(binderImpl));
+ auto handler = std::make_unique<TestBinderLifecycleHandler>();
+ mBinderLifecycleHandler = handler.get();
+ mVhal->setBinderLifecycleHandler(std::move(handler));
}
void TearDown() override {
@@ -370,7 +370,7 @@
bool hasNoSubscriptions() { return mVhal->mSubscriptionManager->isEmpty(); }
- void setBinderAlive(bool isAlive) { mBinderImpl->setAlive(isAlive); };
+ void setBinderAlive(bool isAlive) { mBinderLifecycleHandler->setAlive(isAlive); };
static Result<void> getValuesTestCases(size_t size, GetValueRequests& requests,
std::vector<GetValueResult>& expectedResults,
@@ -444,7 +444,7 @@
}
private:
- class TestBinderImpl final : public DefaultVehicleHal::IBinder {
+ class TestBinderLifecycleHandler final : public DefaultVehicleHal::BinderLifecycleInterface {
public:
binder_status_t linkToDeath(AIBinder*, AIBinder_DeathRecipient*, void*) override {
if (mIsAlive) {
@@ -468,7 +468,7 @@
std::shared_ptr<MockVehicleCallback> mCallback;
std::shared_ptr<IVehicleCallback> mCallbackClient;
SpAIBinder mBinder;
- TestBinderImpl* mBinderImpl;
+ TestBinderLifecycleHandler* mBinderLifecycleHandler;
};
TEST_F(DefaultVehicleHalTest, testGetAllPropConfigsSmall) {
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
index 3f59363..eb3c663 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
@@ -477,16 +477,16 @@
ASSERT_THAT(clients[getCallbackClient()], ElementsAre(&updatedValues[1]));
}
-TEST_F(SubscriptionManagerTest, testCheckSampleRateValid) {
- ASSERT_TRUE(SubscriptionManager::checkSampleRate(1.0));
+TEST_F(SubscriptionManagerTest, testCheckSampleRateHzValid) {
+ ASSERT_TRUE(SubscriptionManager::checkSampleRateHz(1.0));
}
-TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidTooSmall) {
- ASSERT_FALSE(SubscriptionManager::checkSampleRate(FLT_MIN));
+TEST_F(SubscriptionManagerTest, testCheckSampleRateHzInvalidTooSmall) {
+ ASSERT_FALSE(SubscriptionManager::checkSampleRateHz(FLT_MIN));
}
-TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidZero) {
- ASSERT_FALSE(SubscriptionManager::checkSampleRate(0));
+TEST_F(SubscriptionManagerTest, testCheckSampleRateHzInvalidZero) {
+ ASSERT_FALSE(SubscriptionManager::checkSampleRateHz(0));
}
} // namespace vehicle
diff --git a/automotive/vehicle/tools/generate_annotation_enums.py b/automotive/vehicle/tools/generate_annotation_enums.py
new file mode 100644
index 0000000..fc6f157
--- /dev/null
+++ b/automotive/vehicle/tools/generate_annotation_enums.py
@@ -0,0 +1,243 @@
+#!/usr/bin/python
+
+# Copyright (C) 2022 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.
+#
+"""A script to generate Java files and CPP header files based on annotations in VehicleProperty.aidl
+
+ Need ANDROID_BUILD_TOP environmental variable to be set. This script will update
+ ChangeModeForVehicleProperty.h and AccessForVehicleProperty.h under generated_lib/cpp and
+ ChangeModeForVehicleProperty.java and AccessForVehicleProperty.java under generated_lib/java.
+
+ Usage:
+ $ python generate_annotation_enums.py
+"""
+import os
+import re
+import sys
+
+PROP_AIDL_FILE_PATH = ("hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/" +
+ "vehicle/VehicleProperty.aidl")
+CHANGE_MODE_CPP_FILE_PATH = ("hardware/interfaces/automotive/vehicle/aidl/generated_lib/cpp/" +
+ "ChangeModeForVehicleProperty.h")
+ACCESS_CPP_FILE_PATH = ("hardware/interfaces/automotive/vehicle/aidl/generated_lib/cpp/" +
+ "AccessForVehicleProperty.h")
+CHANGE_MODE_JAVA_FILE_PATH = ("hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/" +
+ "ChangeModeForVehicleProperty.java")
+ACCESS_JAVA_FILE_PATH = ("hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/" +
+ "AccessForVehicleProperty.java")
+
+TAB = " "
+RE_ENUM_START = re.compile("\s*enum VehicleProperty \{")
+RE_ENUM_END = re.compile("\s*\}\;")
+RE_COMMENT_BEGIN = re.compile("\s*\/\*\*?")
+RE_COMMENT_END = re.compile("\s*\*\/")
+RE_CHANGE_MODE = re.compile("\s*\* @change_mode (\S+)\s*")
+RE_ACCESS = re.compile("\s*\* @access (\S+)\s*")
+RE_VALUE = re.compile("\s*(\w+)\s*=(.*)")
+
+LICENSE = """/*
+ * Copyright (C) 2022 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.
+ */
+
+/**
+ * DO NOT EDIT MANUALLY!!!
+ *
+ * Generated by tools/generate_annotation_enums.py.
+ */
+
+"""
+
+CHANGE_MODE_CPP_HEADER = """#ifndef android_hardware_automotive_vehicle_aidl_generated_lib_ChangeModeForVehicleProperty_H_
+#define android_hardware_automotive_vehicle_aidl_generated_lib_ChangeModeForVehicleProperty_H_
+
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
+#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyChangeMode.h>
+
+#include <unordered_map>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehicleProperty = {
+"""
+
+CHANGE_MODE_CPP_FOOTER = """
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+} // aidl
+
+#endif // android_hardware_automotive_vehicle_aidl_generated_lib_ChangeModeForVehicleProperty_H_
+"""
+
+ACCESS_CPP_HEADER = """#ifndef android_hardware_automotive_vehicle_aidl_generated_lib_AccessForVehicleProperty_H_
+#define android_hardware_automotive_vehicle_aidl_generated_lib_AccessForVehicleProperty_H_
+
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
+#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyAccess.h>
+
+#include <unordered_map>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehicleProperty = {
+"""
+
+ACCESS_CPP_FOOTER = """
+};
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+} // aidl
+
+#endif // android_hardware_automotive_vehicle_aidl_generated_lib_AccessForVehicleProperty_H_
+"""
+
+CHANGE_MODE_JAVA_HEADER = """package android.hardware.automotive.vehicle;
+
+import java.util.Map;
+
+public final class ChangeModeForVehicleProperty {
+
+ public static final Map<Integer, Integer> values = Map.ofEntries(
+"""
+
+CHANGE_MODE_JAVA_FOOTER = """
+ );
+
+}
+"""
+
+ACCESS_JAVA_HEADER = """package android.hardware.automotive.vehicle;
+
+import java.util.Map;
+
+public final class AccessForVehicleProperty {
+
+ public static final Map<Integer, Integer> values = Map.ofEntries(
+"""
+
+ACCESS_JAVA_FOOTER = """
+ );
+
+}
+"""
+
+
+class Converter:
+
+ def __init__(self, name, annotation_re):
+ self.name = name
+ self.annotation_re = annotation_re
+
+ def convert(self, input, output, header, footer, cpp):
+ processing = False
+ in_comment = False
+ content = LICENSE + header
+ annotation = None
+ id = 0
+ with open(input, 'r') as f:
+ for line in f.readlines():
+ if RE_ENUM_START.match(line):
+ processing = True
+ annotation = None
+ elif RE_ENUM_END.match(line):
+ processing = False
+ if not processing:
+ continue
+ if RE_COMMENT_BEGIN.match(line):
+ in_comment = True
+ if RE_COMMENT_END.match(line):
+ in_comment = False
+ if in_comment:
+ match = self.annotation_re.match(line)
+ if match:
+ annotation = match.group(1)
+ else:
+ match = RE_VALUE.match(line)
+ if match:
+ prop_name = match.group(1)
+ if prop_name == "INVALID":
+ continue
+ if not annotation:
+ print("No @" + self.name + " annotation for property: " + prop_name)
+ sys.exit(1)
+ if id != 0:
+ content += "\n"
+ if cpp:
+ annotation = annotation.replace(".", "::")
+ content += (TAB + TAB + "{VehicleProperty::" + prop_name + ", " +
+ annotation + "},")
+ else:
+ content += (TAB + TAB + "Map.entry(VehicleProperty." + prop_name + ", " +
+ annotation + "),")
+ id += 1
+
+ # Remove the additional "," at the end for the Java file.
+ if not cpp:
+ content = content[:-1]
+
+ content += footer
+
+ with open(output, 'w') as f:
+ f.write(content)
+
+
+def main():
+ android_top = os.environ['ANDROID_BUILD_TOP']
+ if not android_top:
+ print("ANDROID_BUILD_TOP is not in envorinmental variable, please run source and lunch " +
+ "at the android root")
+
+ aidl_file = os.path.join(android_top, PROP_AIDL_FILE_PATH)
+ change_mode_cpp_output = os.path.join(android_top, CHANGE_MODE_CPP_FILE_PATH);
+ access_cpp_output = os.path.join(android_top, ACCESS_CPP_FILE_PATH);
+ change_mode_java_output = os.path.join(android_top, CHANGE_MODE_JAVA_FILE_PATH);
+ access_java_output = os.path.join(android_top, ACCESS_JAVA_FILE_PATH);
+
+ c = Converter("change_mode", RE_CHANGE_MODE);
+ c.convert(aidl_file, change_mode_cpp_output, CHANGE_MODE_CPP_HEADER, CHANGE_MODE_CPP_FOOTER, True)
+ c.convert(aidl_file, change_mode_java_output, CHANGE_MODE_JAVA_HEADER, CHANGE_MODE_JAVA_FOOTER, False)
+ c = Converter("access", RE_ACCESS)
+ c.convert(aidl_file, access_cpp_output, ACCESS_CPP_HEADER, ACCESS_CPP_FOOTER, True)
+ c.convert(aidl_file, access_java_output, ACCESS_JAVA_HEADER, ACCESS_JAVA_FOOTER, False)
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/automotive/vehicle/vts/Android.bp b/automotive/vehicle/vts/Android.bp
index b78e0ff..1cfd542 100644
--- a/automotive/vehicle/vts/Android.bp
+++ b/automotive/vehicle/vts/Android.bp
@@ -44,6 +44,7 @@
test_suites: [
"general-tests",
"vts",
+ "automotive-tests",
],
require_root: true,
}
diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
index c431d85..5de206b 100644
--- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
+++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
@@ -48,6 +48,7 @@
using ::android::getAidlHalInstanceNames;
using ::android::base::ScopedLockAssertion;
using ::android::base::StringPrintf;
+using ::android::frameworks::automotive::vhal::ErrorCode;
using ::android::frameworks::automotive::vhal::HalPropError;
using ::android::frameworks::automotive::vhal::IHalPropConfig;
using ::android::frameworks::automotive::vhal::IHalPropValue;
@@ -287,7 +288,7 @@
auto setValueResult = mVhalClient->setValueSync(*getValueResult.value());
ASSERT_FALSE(setValueResult.ok()) << "Expect set a read-only value to fail";
- ASSERT_EQ(setValueResult.error().code(), StatusCode::ACCESS_DENIED);
+ ASSERT_EQ(setValueResult.error().code(), ErrorCode::ACCESS_DENIED_FROM_VHAL);
}
// Test subscribe() and unsubscribe().
diff --git a/biometrics/common/thread/Android.bp b/biometrics/common/thread/Android.bp
new file mode 100644
index 0000000..a497d01
--- /dev/null
+++ b/biometrics/common/thread/Android.bp
@@ -0,0 +1,26 @@
+cc_library {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ name: "android.hardware.biometrics.common.thread",
+ export_include_dirs: ["include"],
+ vendor: true,
+ srcs: [
+ "WorkerThread.cpp",
+ ],
+}
+
+cc_test_host {
+ name: "android.hardware.biometrics.common.WorkerThreadTest",
+ local_include_dirs: ["include"],
+ srcs: [
+ "tests/WorkerThreadTest.cpp",
+ "WorkerThread.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/biometrics/fingerprint/aidl/default/WorkerThread.cpp b/biometrics/common/thread/WorkerThread.cpp
similarity index 89%
rename from biometrics/fingerprint/aidl/default/WorkerThread.cpp
rename to biometrics/common/thread/WorkerThread.cpp
index d1a63d0..61d1a13 100644
--- a/biometrics/fingerprint/aidl/default/WorkerThread.cpp
+++ b/biometrics/common/thread/WorkerThread.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include "WorkerThread.h"
+#include "thread/WorkerThread.h"
-namespace aidl::android::hardware::biometrics::fingerprint {
+namespace aidl::android::hardware::biometrics {
// It's important that mThread is initialized after everything else because it runs a member
// function that may use any member of this class.
@@ -31,7 +31,10 @@
WorkerThread::~WorkerThread() {
// This is a signal for threadFunc to terminate as soon as possible, and a hint for schedule
// that it doesn't need to do any work.
- mIsDestructing = true;
+ {
+ std::unique_lock<std::mutex> lock(mQueueMutex);
+ mIsDestructing = true;
+ }
mQueueCond.notify_all();
mThread.join();
}
@@ -65,4 +68,4 @@
}
}
-} // namespace aidl::android::hardware::biometrics::fingerprint
+} // namespace aidl::android::hardware::biometrics
diff --git a/biometrics/fingerprint/aidl/default/include/Callable.h b/biometrics/common/thread/include/thread/Callable.h
similarity index 92%
rename from biometrics/fingerprint/aidl/default/include/Callable.h
rename to biometrics/common/thread/include/thread/Callable.h
index c629511..6eeff76 100644
--- a/biometrics/fingerprint/aidl/default/include/Callable.h
+++ b/biometrics/common/thread/include/thread/Callable.h
@@ -16,7 +16,7 @@
#pragma once
-namespace aidl::android::hardware::biometrics::fingerprint {
+namespace aidl::android::hardware::biometrics {
// Interface for representing parameterless functions. Unlike std::function<void()>, this can also
// represent move-only lambdas.
@@ -51,4 +51,4 @@
return std::make_unique<AnyFuncWrapper<T>>(std::move(func));
}
-} // namespace aidl::android::hardware::biometrics::fingerprint
\ No newline at end of file
+} // namespace aidl::android::hardware::biometrics
\ No newline at end of file
diff --git a/biometrics/fingerprint/aidl/default/include/WorkerThread.h b/biometrics/common/thread/include/thread/WorkerThread.h
similarity index 95%
rename from biometrics/fingerprint/aidl/default/include/WorkerThread.h
rename to biometrics/common/thread/include/thread/WorkerThread.h
index 6fff4f2..5f89a7f 100644
--- a/biometrics/fingerprint/aidl/default/include/WorkerThread.h
+++ b/biometrics/common/thread/include/thread/WorkerThread.h
@@ -23,7 +23,7 @@
#include "Callable.h"
-namespace aidl::android::hardware::biometrics::fingerprint {
+namespace aidl::android::hardware::biometrics {
// A class that encapsulates a worker thread and a task queue, and provides a convenient interface
// for a Session to schedule its tasks for asynchronous execution.
@@ -76,4 +76,4 @@
std::thread mThread;
};
-} // namespace aidl::android::hardware::biometrics::fingerprint
+} // namespace aidl::android::hardware::biometrics
diff --git a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp b/biometrics/common/thread/tests/WorkerThreadTest.cpp
similarity index 96%
rename from biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
rename to biometrics/common/thread/tests/WorkerThreadTest.cpp
index 902fb40..5bb9e7e 100644
--- a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
+++ b/biometrics/common/thread/tests/WorkerThreadTest.cpp
@@ -21,12 +21,11 @@
#include <gtest/gtest.h>
-#include "WorkerThread.h"
+#include "thread/WorkerThread.h"
namespace {
-using aidl::android::hardware::biometrics::fingerprint::Callable;
-using aidl::android::hardware::biometrics::fingerprint::WorkerThread;
+using namespace aidl::android::hardware::biometrics;
using namespace std::chrono_literals;
TEST(WorkerThreadTest, ScheduleReturnsTrueWhenQueueHasSpace) {
diff --git a/biometrics/common/util/Android.bp b/biometrics/common/util/Android.bp
new file mode 100644
index 0000000..918ef72
--- /dev/null
+++ b/biometrics/common/util/Android.bp
@@ -0,0 +1,18 @@
+cc_library {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ name: "android.hardware.biometrics.common.util",
+ export_include_dirs: ["include"],
+ vendor: true,
+ srcs: [
+ "CancellationSignal.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.biometrics.common-V2-ndk",
+ ],
+}
diff --git a/biometrics/fingerprint/aidl/default/CancellationSignal.cpp b/biometrics/common/util/CancellationSignal.cpp
similarity index 87%
rename from biometrics/fingerprint/aidl/default/CancellationSignal.cpp
rename to biometrics/common/util/CancellationSignal.cpp
index 6598316..7888838 100644
--- a/biometrics/fingerprint/aidl/default/CancellationSignal.cpp
+++ b/biometrics/common/util/CancellationSignal.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "CancellationSignal.h"
+#include "util/CancellationSignal.h"
#include <android-base/logging.h>
#include <chrono>
-namespace aidl::android::hardware::biometrics::fingerprint {
+namespace aidl::android::hardware::biometrics {
CancellationSignal::CancellationSignal(std::promise<void>&& cancellationPromise)
: mCancellationPromise(std::move(cancellationPromise)) {}
@@ -34,4 +34,4 @@
return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}
-} // namespace aidl::android::hardware::biometrics::fingerprint
+} // namespace aidl::android::hardware::biometrics
diff --git a/biometrics/fingerprint/aidl/default/include/CancellationSignal.h b/biometrics/common/util/include/util/CancellationSignal.h
similarity index 83%
rename from biometrics/fingerprint/aidl/default/include/CancellationSignal.h
rename to biometrics/common/util/include/util/CancellationSignal.h
index 99f2fba..be77e29 100644
--- a/biometrics/fingerprint/aidl/default/include/CancellationSignal.h
+++ b/biometrics/common/util/include/util/CancellationSignal.h
@@ -17,13 +17,10 @@
#pragma once
#include <aidl/android/hardware/biometrics/common/BnCancellationSignal.h>
-#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
#include <functional>
#include <future>
-#include "WorkerThread.h"
-
-namespace aidl::android::hardware::biometrics::fingerprint {
+namespace aidl::android::hardware::biometrics {
class CancellationSignal : public common::BnCancellationSignal {
public:
@@ -39,4 +36,4 @@
// to this future should be cancelled.
bool shouldCancel(const std::future<void>& cancellationFuture);
-} // namespace aidl::android::hardware::biometrics::fingerprint
+} // namespace aidl::android::hardware::biometrics
diff --git a/biometrics/common/util/include/util/Util.h b/biometrics/common/util/include/util/Util.h
new file mode 100644
index 0000000..29ec0f8
--- /dev/null
+++ b/biometrics/common/util/include/util/Util.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+
+#include <chrono>
+#include <regex>
+#include <thread>
+#include <vector>
+
+namespace aidl::android::hardware::biometrics {
+
+#define SLEEP_MS(x) \
+ if (x > 0) std::this_thread::sleep_for(std::chrono::milliseconds(x))
+#define BEGIN_OP(x) \
+ do { \
+ LOG(INFO) << __func__; \
+ SLEEP_MS(x); \
+ } while (0)
+#define IS_TRUE(x) ((x == "1") || (x == "true"))
+
+// This is for non-test situations, such as casual cuttlefish users, that don't
+// set an explicit value.
+// Some operations (i.e. enroll, authenticate) will be executed in tight loops
+// by parts of the UI or fail if there is no latency. For example, the
+// Face settings page constantly runs auth and the enrollment UI uses a
+// cancel/restart cycle that requires some latency while the activities change.
+#define DEFAULT_LATENCY 800
+
+class Util {
+ public:
+ static int64_t getSystemNanoTime() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec * 1000000000LL + now.tv_nsec;
+ }
+
+ static bool hasElapsed(int64_t start, int64_t durationMillis) {
+ auto now = getSystemNanoTime();
+ if (now < start) return true;
+ if (durationMillis <= 0) return true;
+ return ((now - start) / 1000000LL) > durationMillis;
+ }
+
+ static std::vector<std::string> split(const std::string& str, const std::string& sep) {
+ std::regex regex(sep);
+ std::vector<std::string> parts(
+ std::sregex_token_iterator(str.begin(), str.end(), regex, -1),
+ std::sregex_token_iterator());
+ return parts;
+ }
+};
+
+} // namespace aidl::android::hardware::biometrics
\ No newline at end of file
diff --git a/biometrics/face/aidl/default/Android.bp b/biometrics/face/aidl/default/Android.bp
index 7f66eca..63a3645 100644
--- a/biometrics/face/aidl/default/Android.bp
+++ b/biometrics/face/aidl/default/Android.bp
@@ -18,10 +18,43 @@
"libbinder_ndk",
"android.hardware.biometrics.face-V2-ndk",
"android.hardware.biometrics.common-V2-ndk",
+ "android.hardware.biometrics.common.thread",
+ "android.hardware.biometrics.common.util",
],
srcs: [
"main.cpp",
"Face.cpp",
+ "FakeFaceEngine.cpp",
"Session.cpp",
],
+ static_libs: ["libandroid.hardware.biometrics.face.VirtualProps"],
+}
+
+sysprop_library {
+ name: "android.hardware.biometrics.face.VirtualProps",
+ srcs: ["face.sysprop"],
+ property_owner: "Vendor",
+ vendor: true,
+}
+
+cc_test {
+ name: "android.hardware.biometrics.face.FakeFaceEngineTest",
+ srcs: [
+ "tests/FakeFaceEngineTest.cpp",
+ "FakeFaceEngine.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "libandroid.hardware.biometrics.face.VirtualProps",
+ "android.hardware.biometrics.face-V2-ndk",
+ "android.hardware.biometrics.common-V2-ndk",
+ "android.hardware.keymaster-V3-ndk",
+ "android.hardware.biometrics.common.util",
+ ],
+ vendor: true,
+ test_suites: ["general-tests"],
+ require_root: true,
}
diff --git a/biometrics/face/aidl/default/Face.cpp b/biometrics/face/aidl/default/Face.cpp
index aca3e13..652a7e1 100644
--- a/biometrics/face/aidl/default/Face.cpp
+++ b/biometrics/face/aidl/default/Face.cpp
@@ -17,12 +17,14 @@
#include "Face.h"
#include "Session.h"
+#include "FakeFaceEngine.h"
+
namespace aidl::android::hardware::biometrics::face {
const int kSensorId = 4;
-const common::SensorStrength kSensorStrength = common::SensorStrength::STRONG;
+const common::SensorStrength kSensorStrength = FakeFaceEngine::GetSensorStrength();
const int kMaxEnrollmentsPerUser = 5;
-const FaceSensorType kSensorType = FaceSensorType::RGB;
+const FaceSensorType kSensorType = FakeFaceEngine::GetSensorType();
const bool kHalControlsPreview = true;
const std::string kHwComponentId = "faceSensor";
const std::string kHardwareVersion = "vendor/model/revision";
@@ -69,7 +71,7 @@
ndk::ScopedAStatus Face::createSession(int32_t /*sensorId*/, int32_t /*userId*/,
const std::shared_ptr<ISessionCallback>& cb,
std::shared_ptr<ISession>* return_val) {
- *return_val = SharedRefBase::make<Session>(cb);
+ *return_val = SharedRefBase::make<Session>(std::make_unique<FakeFaceEngine>(), cb);
return ndk::ScopedAStatus::ok();
}
diff --git a/biometrics/face/aidl/default/FakeFaceEngine.cpp b/biometrics/face/aidl/default/FakeFaceEngine.cpp
new file mode 100644
index 0000000..0f088f4
--- /dev/null
+++ b/biometrics/face/aidl/default/FakeFaceEngine.cpp
@@ -0,0 +1,318 @@
+#include "FakeFaceEngine.h"
+
+#include <android-base/logging.h>
+
+#include <face.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::face::virt;
+
+namespace aidl::android::hardware::biometrics::face {
+
+FaceSensorType FakeFaceEngine::GetSensorType() {
+ std::string type = FaceHalProperties::type().value_or("");
+ if (type == "IR") {
+ return FaceSensorType::IR;
+ } else {
+ FaceHalProperties::type("RGB");
+ return FaceSensorType::RGB;
+ }
+}
+
+common::SensorStrength FakeFaceEngine::GetSensorStrength() {
+ std::string strength = FaceHalProperties::strength().value_or("");
+ if (strength == "convenience") {
+ return common::SensorStrength::CONVENIENCE;
+ } else if (strength == "weak") {
+ return common::SensorStrength::WEAK;
+ } else {
+ FaceHalProperties::strength("strong");
+ return common::SensorStrength::STRONG;
+ }
+}
+
+void FakeFaceEngine::generateChallengeImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+ std::uniform_int_distribution<int64_t> dist;
+ auto challenge = dist(mRandom);
+ FaceHalProperties::challenge(challenge);
+ cb->onChallengeGenerated(challenge);
+}
+
+void FakeFaceEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
+ BEGIN_OP(0);
+ FaceHalProperties::challenge({});
+ cb->onChallengeRevoked(challenge);
+}
+void FakeFaceEngine::getEnrollmentConfigImpl(ISessionCallback* /*cb*/,
+ std::vector<EnrollmentStageConfig>* /*return_val*/) {}
+void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ EnrollmentType /*enrollmentType*/,
+ const std::vector<Feature>& /*features*/,
+ const std::future<void>& cancel) {
+ BEGIN_OP(FaceHalProperties::operation_start_enroll_latency().value_or(0));
+ // format is "<id>,<bucket_id>:<delay>:<succeeds>,<bucket_id>:<delay>:<succeeds>...
+ auto nextEnroll = FaceHalProperties::next_enrollment().value_or("");
+ // Erase the next enrollment
+ FaceHalProperties::next_enrollment({});
+
+ AuthenticationFrame frame;
+ frame.data.acquiredInfo = AcquiredInfo::START;
+ frame.data.vendorCode = 0;
+ cb->onAuthenticationFrame(frame);
+
+ // Do proper HAT verification in the real implementation.
+ if (hat.mac.empty()) {
+ LOG(ERROR) << "Fail: hat";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
+ if (FaceHalProperties::operation_enroll_fails().value_or(false)) {
+ LOG(ERROR) << "Fail: operation_enroll_fails";
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+
+ auto parts = Util::split(nextEnroll, ",");
+ if (parts.size() < 2) {
+ LOG(ERROR) << "Fail: invalid next_enrollment for : " << nextEnroll;
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+
+ auto enrollmentId = std::stoi(parts[0]);
+ const int numBuckets = parts.size() - 1;
+ for (size_t i = 1; i < parts.size(); i++) {
+ auto enrollHit = Util::split(parts[i], ":");
+ if (enrollHit.size() != 3) {
+ LOG(ERROR) << "Error when unpacking enrollment hit: " << parts[i];
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ }
+ std::string bucket = enrollHit[0];
+ std::string delay = enrollHit[1];
+ std::string succeeds = enrollHit[2];
+
+ SLEEP_MS(std::stoi(delay));
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+
+ if (!IS_TRUE(succeeds)) { // end and failed
+ LOG(ERROR) << "Fail: requested by caller: " << parts[i];
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ return;
+ }
+
+ EnrollmentFrame frame;
+
+ frame.data.acquiredInfo = AcquiredInfo::GOOD;
+ frame.data.vendorCode = 0;
+ cb->onEnrollmentFrame(frame);
+
+ frame.data.acquiredInfo = AcquiredInfo::VENDOR;
+ frame.data.vendorCode = std::stoi(bucket);
+ cb->onEnrollmentFrame(frame);
+
+ int remainingBuckets = numBuckets - i;
+ if (remainingBuckets > 0) {
+ cb->onEnrollmentProgress(enrollmentId, remainingBuckets);
+ }
+ }
+
+ auto enrollments = FaceHalProperties::enrollments();
+ enrollments.push_back(enrollmentId);
+ FaceHalProperties::enrollments(enrollments);
+ LOG(INFO) << "enrolled : " << enrollmentId;
+ cb->onEnrollmentProgress(enrollmentId, 0);
+}
+
+void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/,
+ const std::future<void>& cancel) {
+ BEGIN_OP(FaceHalProperties::operation_authenticate_latency().value_or(0));
+
+ // Signal to the framework that we have begun authenticating.
+ AuthenticationFrame frame;
+ frame.data.acquiredInfo = AcquiredInfo::START;
+ frame.data.vendorCode = 0;
+ cb->onAuthenticationFrame(frame);
+
+ // Also signal that we have opened the camera.
+ frame = {};
+ frame.data.acquiredInfo = AcquiredInfo::FIRST_FRAME_RECEIVED;
+ frame.data.vendorCode = 0;
+ cb->onAuthenticationFrame(frame);
+
+ auto now = Util::getSystemNanoTime();
+ int64_t duration = FaceHalProperties::operation_authenticate_duration().value_or(0);
+ if (duration > 0) {
+ do {
+ SLEEP_MS(5);
+ } while (!Util::hasElapsed(now, duration));
+ }
+
+ if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
+ LOG(ERROR) << "Fail: operation_authenticate_fails";
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+
+ if (FaceHalProperties::lockout().value_or(false)) {
+ LOG(ERROR) << "Fail: lockout";
+ cb->onLockoutPermanent();
+ cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
+ return;
+ }
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+
+ auto id = FaceHalProperties::enrollment_hit().value_or(0);
+ auto enrolls = FaceHalProperties::enrollments();
+ auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
+ if (id < 0 || !isEnrolled) {
+ LOG(ERROR) << (isEnrolled ? "invalid enrollment hit" : "Fail: not enrolled");
+ cb->onAuthenticationFailed();
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
+ cb->onAuthenticationSucceeded(id, {} /* hat */);
+}
+
+void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel) {
+ BEGIN_OP(FaceHalProperties::operation_detect_interaction_latency().value_or(0));
+
+ if (FaceHalProperties::operation_detect_interaction_fails().value_or(false)) {
+ LOG(ERROR) << "Fail: operation_detect_interaction_fails";
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+
+ auto id = FaceHalProperties::enrollment_hit().value_or(0);
+ auto enrolls = FaceHalProperties::enrollments();
+ auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
+ if (id <= 0 || !isEnrolled) {
+ LOG(ERROR) << "Fail: not enrolled";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
+ cb->onInteractionDetected();
+}
+
+void FakeFaceEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+ std::vector<int32_t> enrollments;
+ for (const auto& enrollmentId : FaceHalProperties::enrollments()) {
+ if (enrollmentId) {
+ enrollments.push_back(*enrollmentId);
+ }
+ }
+ cb->onEnrollmentsEnumerated(enrollments);
+}
+
+void FakeFaceEngine::removeEnrollmentsImpl(ISessionCallback* cb,
+ const std::vector<int32_t>& enrollmentIds) {
+ BEGIN_OP(0);
+
+ std::vector<std::optional<int32_t>> newEnrollments;
+ for (const auto& enrollment : FaceHalProperties::enrollments()) {
+ auto id = enrollment.value_or(0);
+ if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) == enrollmentIds.end()) {
+ newEnrollments.emplace_back(id);
+ }
+ }
+ FaceHalProperties::enrollments(newEnrollments);
+ cb->onEnrollmentsRemoved(enrollmentIds);
+}
+
+void FakeFaceEngine::getFeaturesImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+
+ if (FaceHalProperties::enrollments().empty()) {
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ return;
+ }
+
+ std::vector<Feature> featuresToReturn = {};
+ for (const auto& feature : FaceHalProperties::features()) {
+ if (feature) {
+ featuresToReturn.push_back((Feature)(*feature));
+ }
+ }
+ cb->onFeaturesRetrieved(featuresToReturn);
+}
+
+void FakeFaceEngine::setFeatureImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ Feature feature, bool enabled) {
+ BEGIN_OP(0);
+
+ if (FaceHalProperties::enrollments().empty()) {
+ LOG(ERROR) << "Unable to set feature, enrollments are empty";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ return;
+ }
+
+ if (hat.mac.empty()) {
+ LOG(ERROR) << "Unable to set feature, invalid hat";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ return;
+ }
+
+ auto features = FaceHalProperties::features();
+
+ auto itr = std::find_if(features.begin(), features.end(), [feature](const auto& theFeature) {
+ return *theFeature == (int)feature;
+ });
+
+ if (!enabled && (itr != features.end())) {
+ features.erase(itr);
+ } else if (enabled && (itr == features.end())) {
+ features.push_back((int)feature);
+ }
+
+ FaceHalProperties::features(features);
+ cb->onFeatureSet(feature);
+}
+
+void FakeFaceEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+ // If this is a weak HAL return 0 per the spec.
+ if (GetSensorStrength() != common::SensorStrength::STRONG) {
+ cb->onAuthenticatorIdRetrieved(0);
+ } else {
+ cb->onAuthenticatorIdRetrieved(FaceHalProperties::authenticator_id().value_or(0));
+ }
+}
+
+void FakeFaceEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+ int64_t authenticatorId = FaceHalProperties::authenticator_id().value_or(0);
+ int64_t newId = authenticatorId + 1;
+ FaceHalProperties::authenticator_id(newId);
+ cb->onAuthenticatorIdInvalidated(newId);
+}
+
+void FakeFaceEngine::resetLockoutImpl(ISessionCallback* cb,
+ const keymaster::HardwareAuthToken& /*hat*/) {
+ BEGIN_OP(0);
+ FaceHalProperties::lockout(false);
+ cb->onLockoutCleared();
+}
+
+} // namespace aidl::android::hardware::biometrics::face
\ No newline at end of file
diff --git a/biometrics/face/aidl/default/FakeFaceEngine.h b/biometrics/face/aidl/default/FakeFaceEngine.h
new file mode 100644
index 0000000..edb54ce
--- /dev/null
+++ b/biometrics/face/aidl/default/FakeFaceEngine.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/biometrics/common/SensorStrength.h>
+#include <aidl/android/hardware/biometrics/face/BnSession.h>
+#include <aidl/android/hardware/biometrics/face/FaceSensorType.h>
+#include <aidl/android/hardware/biometrics/face/ISessionCallback.h>
+
+#include <random>
+
+#include <future>
+#include <vector>
+
+namespace aidl::android::hardware::biometrics::face {
+
+namespace face = aidl::android::hardware::biometrics::face;
+namespace common = aidl::android::hardware::biometrics::common;
+namespace keymaster = aidl::android::hardware::keymaster;
+
+using aidl::android::hardware::common::NativeHandle;
+// A fake engine that is backed by system properties instead of hardware.
+class FakeFaceEngine {
+ public:
+ FakeFaceEngine() : mRandom(std::mt19937::default_seed) {}
+
+ static face::FaceSensorType GetSensorType();
+ static common::SensorStrength GetSensorStrength();
+ void generateChallengeImpl(ISessionCallback* cb);
+ void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge);
+ void getEnrollmentConfigImpl(ISessionCallback* cb,
+ std::vector<EnrollmentStageConfig>* return_val);
+ void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ EnrollmentType enrollmentType, const std::vector<Feature>& features,
+ const std::future<void>& cancel);
+ void authenticateImpl(ISessionCallback* cb, int64_t operationId,
+ const std::future<void>& cancel);
+ void detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel);
+ void enumerateEnrollmentsImpl(ISessionCallback* cb);
+ void removeEnrollmentsImpl(ISessionCallback* cb, const std::vector<int32_t>& enrollmentIds);
+ void getFeaturesImpl(ISessionCallback* cb);
+ void setFeatureImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ Feature feature, bool enabled);
+ void getAuthenticatorIdImpl(ISessionCallback* cb);
+ void invalidateAuthenticatorIdImpl(ISessionCallback* cb);
+ void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);
+
+ std::mt19937 mRandom;
+};
+
+} // namespace aidl::android::hardware::biometrics::face
\ No newline at end of file
diff --git a/biometrics/face/aidl/default/README.md b/biometrics/face/aidl/default/README.md
new file mode 100644
index 0000000..1655973
--- /dev/null
+++ b/biometrics/face/aidl/default/README.md
@@ -0,0 +1,77 @@
+# Virtual Face HAL
+
+This is a virtual HAL implementation that is backed by system properties
+instead of actual hardware. It's intended for testing and UI development
+on debuggable builds to allow devices to masquerade as alternative device
+types and for emulators.
+
+## Device Selection
+
+You can either run the FakeFaceEngine on a [real device](#actual-device) or a [virtual device/cuttlefish](#getting-started-on-a-virtual-device-cuttlefish). This document should
+help you to get started on either one.
+
+After setting up a device, go ahead and try out [enrolling](#enrolling) & [authenticating](#authenticating)
+
+### Getting started on a Virtual Device (cuttlefish)
+
+
+Note, I'm running this via a cloudtop virtual device.
+
+1. Setup cuttlefish on cloudtop, See [this](https://g3doc.corp.google.com/company/teams/android/teampages/acloud/getting_started.md?cl=head) for more details.
+2. acloud create --local-image
+3. Enter in the shell command to disable hidl
+
+```shell
+$ adb root
+$ adb shell settings put secure com.android.server.biometrics.AuthService.hidlDisabled 1
+$ adb reboot
+```
+4. You should now be able to do fake enrollments and authentications (as seen down below)
+
+### Actual Device
+
+1. Modify your real devices make file (I.E. vendor/google/products/{YOUR_DEVICE}.mk)
+2. Ensure that there is no other face HAL that is being included by the device
+3. Add the following
+```
+PRODUCT_COPY_FILES += \
+ frameworks/native/data/etc/android.hardware.biometrics.face.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/permissions/android.hardware.biometrics.face.xml
+
+PRODUCT_PACKAGES += \
+ android.hardware.biometrics.face-service.example \
+
+```
+4. Now build and flash m -j120 && flash
+5. Run the following commands
+
+```shell
+# This is a temporary workaround
+$ adb root
+$ adb shell setprop persist.vendor.face.virtual.type RGB
+$ adb shell setprop persist.vendor.face.virtual.strength strong
+$ adb shell locksettings set-pin 0000
+$ adb reboot
+```
+
+## Enrolling
+
+```shell
+# authenticar_id,bucket_id:duration:(true|false)....
+$ adb shell setprop vendor.face.virtual.next_enrollment 1,0:500:true,5:250:true,10:150:true,15:500:true
+$ adb shell am start -n com.android.settings/.biometrics.face.FaceEnrollIntroduction
+# If you would like to get rid of the enrollment, run the follwoing command
+$ adb shell setprop persist.vendor.face.virtual.enrollments \"\"
+```
+
+## Authenticating
+
+```shell
+# If enrollment hasn't been setup
+$ adb shell setprop persist.vendor.face.virtual.enrollments 1
+$ adb shell cmd face sync
+# After enrollment has been setup
+$ adb shell setprop vendor.face.virtual.operation_authenticate_duration 800
+$ adb shell setprop vendor.face.virtual.enrollment_hit 1
+# Power button press to simulate auth
+$ adb shell input keyevent 26
+```
diff --git a/biometrics/face/aidl/default/Session.cpp b/biometrics/face/aidl/default/Session.cpp
index 984a1a9..1188459 100644
--- a/biometrics/face/aidl/default/Session.cpp
+++ b/biometrics/face/aidl/default/Session.cpp
@@ -14,139 +14,135 @@
* limitations under the License.
*/
-#include <aidl/android/hardware/biometrics/common/BnCancellationSignal.h>
#include <android-base/logging.h>
#include "Session.h"
namespace aidl::android::hardware::biometrics::face {
-class CancellationSignal : public common::BnCancellationSignal {
- private:
- std::shared_ptr<ISessionCallback> cb_;
+constexpr size_t MAX_WORKER_QUEUE_SIZE = 5;
- public:
- explicit CancellationSignal(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
-
- ndk::ScopedAStatus cancel() override {
- cb_->onError(Error::CANCELED, 0 /* vendorCode */);
- return ndk::ScopedAStatus::ok();
- }
-};
-
-Session::Session(std::shared_ptr<ISessionCallback> cb)
- : cb_(std::move(cb)), mRandom(std::mt19937::default_seed) {}
+Session::Session(std::unique_ptr<FakeFaceEngine> engine, std::shared_ptr<ISessionCallback> cb)
+ : mEngine(std::move(engine)), mCb(std::move(cb)), mRandom(std::mt19937::default_seed) {
+ mThread = std::make_unique<WorkerThread>(MAX_WORKER_QUEUE_SIZE);
+}
ndk::ScopedAStatus Session::generateChallenge() {
LOG(INFO) << "generateChallenge";
- if (cb_) {
- std::uniform_int_distribution<int64_t> dist;
- auto challenge = dist(mRandom);
- cb_->onChallengeGenerated(challenge);
- }
+ mThread->schedule(Callable::from([this] { mEngine->generateChallengeImpl(mCb.get()); }));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::revokeChallenge(int64_t challenge) {
LOG(INFO) << "revokeChallenge";
- if (cb_) {
- cb_->onChallengeRevoked(challenge);
- }
+ mThread->schedule(Callable::from(
+ [this, challenge] { mEngine->revokeChallengeImpl(mCb.get(), challenge); }));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::getEnrollmentConfig(EnrollmentType /*enrollmentType*/,
- std::vector<EnrollmentStageConfig>* return_val) {
- *return_val = {};
+ndk::ScopedAStatus Session::getEnrollmentConfig(
+ EnrollmentType /*enrollmentType*/, std::vector<EnrollmentStageConfig>* cancellationSignal) {
+ *cancellationSignal = {};
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::enroll(
- const keymaster::HardwareAuthToken& /*hat*/, EnrollmentType /*enrollmentType*/,
- const std::vector<Feature>& /*features*/,
- const std::optional<NativeHandle>& /*previewSurface*/,
- std::shared_ptr<biometrics::common::ICancellationSignal>* /*return_val*/) {
+ const keymaster::HardwareAuthToken& hat, EnrollmentType enrollmentType,
+ const std::vector<Feature>& features, const std::optional<NativeHandle>& /*previewSurface*/,
+ std::shared_ptr<biometrics::common::ICancellationSignal>* cancellationSignal) {
LOG(INFO) << "enroll";
- if (cb_) {
- cb_->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
- }
+ std::promise<void> cancellationPromise;
+ auto cancFuture = cancellationPromise.get_future();
+
+ mThread->schedule(Callable::from(
+ [this, hat, enrollmentType, features, cancFuture = std::move(cancFuture)] {
+ mEngine->enrollImpl(mCb.get(), hat, enrollmentType, features, cancFuture);
+ }));
+
+ *cancellationSignal = SharedRefBase::make<CancellationSignal>(std::move(cancellationPromise));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::authenticate(int64_t /*keystoreOperationId*/,
- std::shared_ptr<common::ICancellationSignal>* return_val) {
+ndk::ScopedAStatus Session::authenticate(
+ int64_t keystoreOperationId,
+ std::shared_ptr<common::ICancellationSignal>* cancellationSignal) {
LOG(INFO) << "authenticate";
- if (cb_) {
- cb_->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
- }
- *return_val = SharedRefBase::make<CancellationSignal>(cb_);
+ std::promise<void> cancellationPromise;
+ auto cancFuture = cancellationPromise.get_future();
+
+ mThread->schedule(
+ Callable::from([this, keystoreOperationId, cancFuture = std::move(cancFuture)] {
+ mEngine->authenticateImpl(mCb.get(), keystoreOperationId, cancFuture);
+ }));
+
+ *cancellationSignal = SharedRefBase::make<CancellationSignal>(std::move(cancellationPromise));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::detectInteraction(
- std::shared_ptr<common::ICancellationSignal>* /*return_val*/) {
+ std::shared_ptr<common::ICancellationSignal>* cancellationSignal) {
LOG(INFO) << "detectInteraction";
+ std::promise<void> cancellationPromise;
+ auto cancFuture = cancellationPromise.get_future();
+
+ mThread->schedule(Callable::from([this, cancFuture = std::move(cancFuture)] {
+ mEngine->detectInteractionImpl(mCb.get(), cancFuture);
+ }));
+
+ *cancellationSignal = SharedRefBase::make<CancellationSignal>(std::move(cancellationPromise));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::enumerateEnrollments() {
LOG(INFO) << "enumerateEnrollments";
- if (cb_) {
- cb_->onEnrollmentsEnumerated(std::vector<int32_t>());
- }
+ mThread->schedule(Callable::from([this] { mEngine->enumerateEnrollmentsImpl(mCb.get()); }));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::removeEnrollments(const std::vector<int32_t>& /*enrollmentIds*/) {
+ndk::ScopedAStatus Session::removeEnrollments(const std::vector<int32_t>& enrollmentIds) {
LOG(INFO) << "removeEnrollments";
- if (cb_) {
- cb_->onEnrollmentsRemoved(std::vector<int32_t>());
- }
+ mThread->schedule(Callable::from(
+ [this, enrollmentIds] { mEngine->removeEnrollmentsImpl(mCb.get(), enrollmentIds); }));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::getFeatures() {
LOG(INFO) << "getFeatures";
- if (cb_) {
- // Must error out with UNABLE_TO_PROCESS when no faces are enrolled.
- cb_->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
- }
+ mThread->schedule(Callable::from([this] { mEngine->getFeaturesImpl(mCb.get()); }));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::setFeature(const keymaster::HardwareAuthToken& /*hat*/,
- Feature /*feature*/, bool /*enabled*/) {
+ndk::ScopedAStatus Session::setFeature(const keymaster::HardwareAuthToken& hat, Feature feature,
+ bool enabled) {
LOG(INFO) << "setFeature";
+ mThread->schedule(Callable::from([this, hat, feature, enabled] {
+ mEngine->setFeatureImpl(mCb.get(), hat, feature, enabled);
+ }));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::getAuthenticatorId() {
LOG(INFO) << "getAuthenticatorId";
- if (cb_) {
- cb_->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
- }
+ mThread->schedule(Callable::from([this] { mEngine->getAuthenticatorIdImpl(mCb.get()); }));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::invalidateAuthenticatorId() {
LOG(INFO) << "invalidateAuthenticatorId";
- if (cb_) {
- cb_->onAuthenticatorIdInvalidated(0);
- }
+ mThread->schedule(
+ Callable::from([this] { mEngine->invalidateAuthenticatorIdImpl(mCb.get()); }));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::resetLockout(const keymaster::HardwareAuthToken& /*hat*/) {
+ndk::ScopedAStatus Session::resetLockout(const keymaster::HardwareAuthToken& hat) {
LOG(INFO) << "resetLockout";
- if (cb_) {
- cb_->onLockoutCleared();
- }
+ mThread->schedule(Callable::from([this, hat] { mEngine->resetLockoutImpl(mCb.get(), hat); }));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Session::close() {
- if (cb_) {
- cb_->onSessionClosed();
+ if (mCb) {
+ mCb->onSessionClosed();
}
return ndk::ScopedAStatus::ok();
}
diff --git a/biometrics/face/aidl/default/Session.h b/biometrics/face/aidl/default/Session.h
index 9db17d2..7ca6a1f 100644
--- a/biometrics/face/aidl/default/Session.h
+++ b/biometrics/face/aidl/default/Session.h
@@ -21,6 +21,10 @@
#include <aidl/android/hardware/biometrics/face/BnSession.h>
#include <aidl/android/hardware/biometrics/face/ISessionCallback.h>
+#include "FakeFaceEngine.h"
+#include "thread/WorkerThread.h"
+#include "util/CancellationSignal.h"
+
namespace aidl::android::hardware::biometrics::face {
namespace common = aidl::android::hardware::biometrics::common;
@@ -30,7 +34,7 @@
class Session : public BnSession {
public:
- explicit Session(std::shared_ptr<ISessionCallback> cb);
+ explicit Session(std::unique_ptr<FakeFaceEngine> engine, std::shared_ptr<ISessionCallback> cb);
ndk::ScopedAStatus generateChallenge() override;
@@ -85,8 +89,11 @@
ndk::ScopedAStatus onContextChanged(const common::OperationContext& context) override;
private:
- std::shared_ptr<ISessionCallback> cb_;
+ std::unique_ptr<FakeFaceEngine> mEngine;
+ std::shared_ptr<ISessionCallback> mCb;
std::mt19937 mRandom;
+ std::unique_ptr<WorkerThread> mThread;
+ std::shared_ptr<CancellationSignal> mCancellationSignal;
};
} // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/default/api/android.hardware.biometrics.face.VirtualProps-current.txt b/biometrics/face/aidl/default/api/android.hardware.biometrics.face.VirtualProps-current.txt
new file mode 100644
index 0000000..9548920
--- /dev/null
+++ b/biometrics/face/aidl/default/api/android.hardware.biometrics.face.VirtualProps-current.txt
@@ -0,0 +1,98 @@
+props {
+ owner: Vendor
+ module: "android.face.virt.FaceHalProperties"
+ prop {
+ api_name: "authenticator_id"
+ type: Long
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.authenticator_id"
+ }
+ prop {
+ api_name: "challenge"
+ type: Long
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.challenge"
+ }
+ prop {
+ api_name: "enrollment_hit"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.enrollment_hit"
+ }
+ prop {
+ api_name: "enrollments"
+ type: IntegerList
+ access: ReadWrite
+ prop_name: "persist.vendor.face.virtual.enrollments"
+ }
+ prop {
+ api_name: "features"
+ type: IntegerList
+ access: ReadWrite
+ prop_name: "persist.vendor.face.virtual.features"
+ }
+ prop {
+ api_name: "lockout"
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.lockout"
+ }
+ prop {
+ api_name: "next_enrollment"
+ type: String
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.next_enrollment"
+ }
+ prop {
+ api_name: "operation_authenticate_duration"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.operation_authenticate_duration"
+ }
+ prop {
+ api_name: "operation_authenticate_fails"
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.operation_authenticate_fails"
+ }
+ prop {
+ api_name: "operation_authenticate_latency"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.operation_authenticate_latency"
+ }
+ prop {
+ api_name: "operation_detect_interaction_fails"
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.operation_detect_interaction_fails"
+ }
+ prop {
+ api_name: "operation_detect_interaction_latency"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.operation_detect_interaction_latency"
+ }
+ prop {
+ api_name: "operation_enroll_fails"
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.operation_enroll_fails"
+ }
+ prop {
+ api_name: "operation_start_enroll_latency"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.face.virtual.operation_start_enroll_latency"
+ }
+ prop {
+ api_name: "strength"
+ type: String
+ access: ReadWrite
+ prop_name: "persist.vendor.face.virtual.strength"
+ enum_values: "convenience|weak|strong"
+ }
+ prop {
+ api_name: "type"
+ type: String
+ access: ReadWrite
+ prop_name: "persist.vendor.face.virtual.type"
+ enum_values: "IR|RGB"
+ }
+}
diff --git a/biometrics/face/aidl/default/face.sysprop b/biometrics/face/aidl/default/face.sysprop
new file mode 100644
index 0000000..6b0f37f
--- /dev/null
+++ b/biometrics/face/aidl/default/face.sysprop
@@ -0,0 +1,159 @@
+# face.sysprop
+# module becomes static class (Java) / namespace (C++) for serving API
+module: "android.face.virt.FaceHalProperties"
+owner: Vendor
+
+# type of face sensor
+prop {
+ prop_name: "persist.vendor.face.virtual.type"
+ type: String
+ scope: Public
+ access: ReadWrite
+ enum_values: "IR|RGB"
+ api_name: "type"
+}
+
+# the strength of the sensor
+prop {
+ prop_name: "persist.vendor.face.virtual.strength"
+ type: String
+ scope: Public
+ access: ReadWrite
+ enum_values: "convenience|weak|strong"
+ api_name: "strength"
+}
+
+# ids of current enrollments
+prop {
+ prop_name: "persist.vendor.face.virtual.enrollments"
+ type: IntegerList
+ scope: Public
+ access: ReadWrite
+ api_name: "enrollments"
+}
+
+# List of features
+prop {
+ prop_name: "persist.vendor.face.virtual.features"
+ type: IntegerList
+ scope: Public
+ access: ReadWrite
+ api_name: "features"
+}
+
+# authenticate and detectInteraction will succeed with this
+# enrollment id, when present, otherwise they will error
+prop {
+ prop_name: "vendor.face.virtual.enrollment_hit"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "enrollment_hit"
+}
+
+# The initial latency for enrollment
+prop {
+ prop_name: "vendor.face.virtual.operation_start_enroll_latency"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_start_enroll_latency"
+}
+
+# the next enrollment in the format:
+# "<id>,<bucket_id>:<delay>:<succeeds>,<bucket_id>..."
+# for example: "0:1,0:100:1,1:200:1" indicating that bucket 0 took
+# 50 milliseconds, bucket 1 took 100 milliseconds, bucket 2 took 200 milliseconds.
+# Note that it is up to the configuration to determine how many buckets are required
+# to complete an enrollment
+prop {
+ prop_name: "vendor.face.virtual.next_enrollment"
+ type: String
+ scope: Public
+ access: ReadWrite
+ api_name: "next_enrollment"
+}
+
+# value for getAuthenticatorId or 0
+prop {
+ prop_name: "vendor.face.virtual.authenticator_id"
+ type: Long
+ scope: Public
+ access: ReadWrite
+ api_name: "authenticator_id"
+}
+
+# value for generateChallenge
+prop {
+ prop_name: "vendor.face.virtual.challenge"
+ type: Long
+ scope: Public
+ access: ReadWrite
+ api_name: "challenge"
+}
+
+# if locked out
+prop {
+ prop_name: "vendor.face.virtual.lockout"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "lockout"
+}
+
+# force all authenticate operations to fail
+prop {
+ prop_name: "vendor.face.virtual.operation_authenticate_fails"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_fails"
+}
+
+# force all detectInteraction operations to fail
+prop {
+ prop_name: "vendor.face.virtual.operation_detect_interaction_fails"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_detect_interaction_fails"
+}
+
+# force all enroll operations to fail
+prop {
+ prop_name: "vendor.face.virtual.operation_enroll_fails"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_enroll_fails"
+}
+
+# add a latency to authentication operations
+# Note that this latency is the initial authentication latency that occurs before
+# the HAL will send AcquiredInfo::START and AcquiredInfo::FIRST_FRAME_RECEIVED
+prop {
+ prop_name: "vendor.face.virtual.operation_authenticate_latency"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_latency"
+}
+
+# add a latency to detectInteraction operations
+prop {
+ prop_name: "vendor.face.virtual.operation_detect_interaction_latency"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_detect_interaction_latency"
+}
+
+# millisecond duration for authenticate operations
+# (waits for changes to enrollment_hit)
+prop {
+ prop_name: "vendor.face.virtual.operation_authenticate_duration"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_duration"
+}
diff --git a/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
new file mode 100644
index 0000000..c8ad6b7
--- /dev/null
+++ b/biometrics/face/aidl/default/tests/FakeFaceEngineTest.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <android/binder_process.h>
+#include <face.sysprop.h>
+#include <gtest/gtest.h>
+
+#include <aidl/android/hardware/biometrics/face/BnSessionCallback.h>
+#include <android-base/logging.h>
+
+#include "FakeFaceEngine.h"
+
+using namespace ::android::face::virt;
+using namespace ::aidl::android::hardware::biometrics::face;
+using namespace ::aidl::android::hardware::keymaster;
+
+namespace aidl::android::hardware::biometrics::face {
+
+class TestSessionCallback : public BnSessionCallback {
+ public:
+ ndk::ScopedAStatus onChallengeGenerated(int64_t challenge) override {
+ mLastChallenge = challenge;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onChallengeRevoked(int64_t challenge) override {
+ mLastChallengeRevoked = challenge;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onError(Error error, int32_t) override {
+ mError = error;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override {
+ if (remaining == 0) mLastEnrolled = enrollmentId;
+ return ndk::ScopedAStatus::ok();
+ };
+
+ ::ndk::ScopedAStatus onAuthenticationSucceeded(int32_t enrollmentId,
+ const HardwareAuthToken&) override {
+ mLastAuthenticated = enrollmentId;
+ mAuthenticateFailed = false;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticationFailed() override {
+ mLastAuthenticated = 0;
+ mAuthenticateFailed = true;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onInteractionDetected() override {
+ mInteractionDetectedCount++;
+ return ndk::ScopedAStatus::ok();
+ };
+
+ ::ndk::ScopedAStatus onEnrollmentFrame(const EnrollmentFrame& frame) override {
+ mEnrollmentFrames.push_back(frame.data.vendorCode);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ ::ndk::ScopedAStatus onEnrollmentsEnumerated(
+ const std::vector<int32_t>& enrollmentIds) override {
+ mLastEnrollmentsEnumerated = enrollmentIds;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentsRemoved(const std::vector<int32_t>& enrollmentIds) override {
+ mLastEnrollmentRemoved = enrollmentIds;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t authenticatorId) override {
+ mLastAuthenticatorId = authenticatorId;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t authenticatorId) override {
+ mLastAuthenticatorId = authenticatorId;
+ mAuthenticatorIdInvalidated = true;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticationFrame(const AuthenticationFrame& /*authFrame*/) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onLockoutPermanent() override {
+ mLockoutPermanent = true;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onLockoutTimed(int64_t /* timeout */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onLockoutCleared() override {
+ mLockoutPermanent = false;
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
+
+ ::ndk::ScopedAStatus onFeaturesRetrieved(const std::vector<Feature>& features) override {
+ mFeatures = features;
+ return ndk::ScopedAStatus::ok();
+ }
+
+ ::ndk::ScopedAStatus onFeatureSet(Feature feature) override {
+ mLastFeatureSet = feature;
+ return ndk::ScopedAStatus::ok();
+ }
+
+ Error mError = Error::UNKNOWN;
+ int64_t mLastChallenge = -1;
+ int64_t mLastChallengeRevoked = -1;
+ int32_t mLastEnrolled = -1;
+ int32_t mLastAuthenticated = -1;
+ int64_t mLastAuthenticatorId = -1;
+ std::vector<int32_t> mLastEnrollmentsEnumerated;
+ std::vector<int32_t> mLastEnrollmentRemoved;
+ std::vector<Feature> mFeatures;
+ Feature mLastFeatureSet;
+ std::vector<int32_t> mEnrollmentFrames;
+ bool mAuthenticateFailed = false;
+ bool mAuthenticatorIdInvalidated = false;
+ bool mLockoutPermanent = false;
+ int mInteractionDetectedCount = 0;
+};
+
+class FakeFaceEngineTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ LOG(ERROR) << "JRM SETUP";
+ mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
+ FaceHalProperties::enrollments({});
+ FaceHalProperties::challenge({});
+ FaceHalProperties::features({});
+ FaceHalProperties::authenticator_id({});
+ FaceHalProperties::strength("");
+ }
+
+ FakeFaceEngine mEngine;
+ std::shared_ptr<TestSessionCallback> mCallback;
+ std::promise<void> mCancel;
+};
+
+TEST_F(FakeFaceEngineTest, one_eq_one) {
+ ASSERT_EQ(1, 1);
+}
+
+TEST_F(FakeFaceEngineTest, GenerateChallenge) {
+ mEngine.generateChallengeImpl(mCallback.get());
+ ASSERT_EQ(FaceHalProperties::challenge().value(), mCallback->mLastChallenge);
+}
+
+TEST_F(FakeFaceEngineTest, RevokeChallenge) {
+ auto challenge = FaceHalProperties::challenge().value_or(10);
+ mEngine.revokeChallengeImpl(mCallback.get(), challenge);
+ ASSERT_FALSE(FaceHalProperties::challenge().has_value());
+ ASSERT_EQ(challenge, mCallback->mLastChallengeRevoked);
+}
+
+TEST_F(FakeFaceEngineTest, ResetLockout) {
+ FaceHalProperties::lockout(true);
+ mEngine.resetLockoutImpl(mCallback.get(), {});
+ ASSERT_FALSE(mCallback->mLockoutPermanent);
+ ASSERT_FALSE(FaceHalProperties::lockout().value_or(true));
+}
+
+TEST_F(FakeFaceEngineTest, AuthenticatorId) {
+ FaceHalProperties::authenticator_id(50);
+ mEngine.getAuthenticatorIdImpl(mCallback.get());
+ ASSERT_EQ(50, mCallback->mLastAuthenticatorId);
+ ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
+}
+
+TEST_F(FakeFaceEngineTest, GetAuthenticatorIdWeakReturnsZero) {
+ FaceHalProperties::strength("weak");
+ FaceHalProperties::authenticator_id(500);
+ mEngine.getAuthenticatorIdImpl(mCallback.get());
+ ASSERT_EQ(0, mCallback->mLastAuthenticatorId);
+ ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
+}
+
+TEST_F(FakeFaceEngineTest, AuthenticatorIdInvalidate) {
+ FaceHalProperties::authenticator_id(500);
+ mEngine.invalidateAuthenticatorIdImpl(mCallback.get());
+ ASSERT_NE(500, FaceHalProperties::authenticator_id().value());
+ ASSERT_TRUE(mCallback->mAuthenticatorIdInvalidated);
+}
+
+TEST_F(FakeFaceEngineTest, Enroll) {
+ FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:true");
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
+ mCancel.get_future());
+ ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
+ ASSERT_EQ(1, FaceHalProperties::enrollments().size());
+ ASSERT_EQ(1, FaceHalProperties::enrollments()[0].value());
+ ASSERT_EQ(1, mCallback->mLastEnrolled);
+}
+
+TEST_F(FakeFaceEngineTest, EnrollFails) {
+ FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:false");
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
+ mCancel.get_future());
+ ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
+ ASSERT_EQ(0, FaceHalProperties::enrollments().size());
+}
+
+TEST_F(FakeFaceEngineTest, EnrollCancel) {
+ FaceHalProperties::next_enrollment("1,0:30:true,1:0:true,2:0:true,3:0:true,4:0:false");
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mCancel.set_value();
+ mEngine.enrollImpl(mCallback.get(), hat, {} /*enrollmentType*/, {} /*features*/,
+ mCancel.get_future());
+ ASSERT_EQ(Error::CANCELED, mCallback->mError);
+ ASSERT_EQ(-1, mCallback->mLastEnrolled);
+ ASSERT_EQ(0, FaceHalProperties::enrollments().size());
+ ASSERT_FALSE(FaceHalProperties::next_enrollment().has_value());
+}
+
+TEST_F(FakeFaceEngineTest, Authenticate) {
+ FaceHalProperties::enrollments({100});
+ FaceHalProperties::enrollment_hit(100);
+ mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
+
+ ASSERT_EQ(100, mCallback->mLastAuthenticated);
+ ASSERT_FALSE(mCallback->mAuthenticateFailed);
+}
+
+TEST_F(FakeFaceEngineTest, AuthenticateCancel) {
+ FaceHalProperties::enrollments({100});
+ FaceHalProperties::enrollment_hit(100);
+ mCancel.set_value();
+ mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
+ ASSERT_EQ(Error::CANCELED, mCallback->mError);
+}
+
+TEST_F(FakeFaceEngineTest, AuthenticateFailedForUnEnrolled) {
+ FaceHalProperties::enrollments({3});
+ FaceHalProperties::enrollment_hit(100);
+ mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, mCancel.get_future());
+ ASSERT_EQ(Error::UNABLE_TO_PROCESS, mCallback->mError);
+ ASSERT_TRUE(mCallback->mAuthenticateFailed);
+}
+
+TEST_F(FakeFaceEngineTest, DetectInteraction) {
+ FaceHalProperties::enrollments({100});
+ FaceHalProperties::enrollment_hit(100);
+ ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
+}
+
+TEST_F(FakeFaceEngineTest, DetectInteractionCancel) {
+ FaceHalProperties::enrollments({100});
+ FaceHalProperties::enrollment_hit(100);
+ mCancel.set_value();
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(Error::CANCELED, mCallback->mError);
+}
+
+TEST_F(FakeFaceEngineTest, GetFeatureEmpty) {
+ mEngine.getFeaturesImpl(mCallback.get());
+ ASSERT_TRUE(mCallback->mFeatures.empty());
+}
+
+TEST_F(FakeFaceEngineTest, SetFeature) {
+ FaceHalProperties::enrollments({1});
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
+ auto features = mCallback->mFeatures;
+ ASSERT_TRUE(features.empty());
+ ASSERT_EQ(Feature::REQUIRE_ATTENTION, mCallback->mLastFeatureSet);
+
+ mEngine.getFeaturesImpl(mCallback.get());
+ features = mCallback->mFeatures;
+ ASSERT_FALSE(features.empty());
+ ASSERT_NE(features.end(),
+ std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
+}
+
+TEST_F(FakeFaceEngineTest, ToggleFeature) {
+ FaceHalProperties::enrollments({1});
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
+ mEngine.getFeaturesImpl(mCallback.get());
+ auto features = mCallback->mFeatures;
+ ASSERT_FALSE(features.empty());
+ ASSERT_NE(features.end(),
+ std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
+
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, false);
+ mEngine.getFeaturesImpl(mCallback.get());
+ features = mCallback->mFeatures;
+ ASSERT_TRUE(features.empty());
+}
+
+TEST_F(FakeFaceEngineTest, TurningOffNonExistentFeatureDoesNothing) {
+ FaceHalProperties::enrollments({1});
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, false);
+ mEngine.getFeaturesImpl(mCallback.get());
+ auto features = mCallback->mFeatures;
+ ASSERT_TRUE(features.empty());
+}
+
+TEST_F(FakeFaceEngineTest, SetMultipleFeatures) {
+ FaceHalProperties::enrollments({1});
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::DEBUG, true);
+ mEngine.getFeaturesImpl(mCallback.get());
+ auto features = mCallback->mFeatures;
+ ASSERT_EQ(3, features.size());
+ ASSERT_NE(features.end(),
+ std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
+ ASSERT_NE(features.end(),
+ std::find(features.begin(), features.end(), Feature::REQUIRE_DIVERSE_POSES));
+ ASSERT_NE(features.end(), std::find(features.begin(), features.end(), Feature::DEBUG));
+}
+
+TEST_F(FakeFaceEngineTest, SetMultipleFeaturesAndTurnOffSome) {
+ FaceHalProperties::enrollments({1});
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_ATTENTION, true);
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::REQUIRE_DIVERSE_POSES, true);
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::DEBUG, true);
+ mEngine.setFeatureImpl(mCallback.get(), hat, Feature::DEBUG, false);
+ mEngine.getFeaturesImpl(mCallback.get());
+ auto features = mCallback->mFeatures;
+ ASSERT_EQ(2, features.size());
+ ASSERT_NE(features.end(),
+ std::find(features.begin(), features.end(), Feature::REQUIRE_ATTENTION));
+ ASSERT_NE(features.end(),
+ std::find(features.begin(), features.end(), Feature::REQUIRE_DIVERSE_POSES));
+ ASSERT_EQ(features.end(), std::find(features.begin(), features.end(), Feature::DEBUG));
+}
+
+TEST_F(FakeFaceEngineTest, Enumerate) {
+ FaceHalProperties::enrollments({120, 3});
+ mEngine.enumerateEnrollmentsImpl(mCallback.get());
+ auto enrolls = mCallback->mLastEnrollmentsEnumerated;
+ ASSERT_FALSE(enrolls.empty());
+ ASSERT_NE(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 120));
+ ASSERT_NE(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 3));
+}
+
+TEST_F(FakeFaceEngineTest, RemoveEnrollments) {
+ FaceHalProperties::enrollments({120, 3, 100});
+ mEngine.removeEnrollmentsImpl(mCallback.get(), {120, 100});
+ mEngine.enumerateEnrollmentsImpl(mCallback.get());
+ auto enrolls = mCallback->mLastEnrollmentsEnumerated;
+ ASSERT_FALSE(enrolls.empty());
+ ASSERT_EQ(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 120));
+ ASSERT_NE(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 3));
+ ASSERT_EQ(enrolls.end(), std::find(enrolls.begin(), enrolls.end(), 100));
+}
+
+TEST_F(FakeFaceEngineTest, ResetLockoutWithAuth) {
+ FaceHalProperties::lockout(true);
+ FaceHalProperties::enrollments({33});
+ FaceHalProperties::enrollment_hit(33);
+ auto cancelFuture = mCancel.get_future();
+ mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
+
+ ASSERT_TRUE(mCallback->mLockoutPermanent);
+
+ mEngine.resetLockoutImpl(mCallback.get(), {} /* hat */);
+ ASSERT_FALSE(mCallback->mLockoutPermanent);
+ FaceHalProperties::enrollment_hit(33);
+ mEngine.authenticateImpl(mCallback.get(), 0 /* operationId*/, cancelFuture);
+ ASSERT_EQ(33, mCallback->mLastAuthenticated);
+ ASSERT_FALSE(mCallback->mAuthenticateFailed);
+}
+
+} // namespace aidl::android::hardware::biometrics::face
\ No newline at end of file
diff --git a/biometrics/fingerprint/aidl/OWNERS b/biometrics/fingerprint/aidl/OWNERS
index 36d7261..e162d2d 100644
--- a/biometrics/fingerprint/aidl/OWNERS
+++ b/biometrics/fingerprint/aidl/OWNERS
@@ -1,2 +1,4 @@
ilyamaty@google.com
-kchyn@google.com
+jeffpu@google.com
+jbolinger@google.com
+joshmccloskey@google.com
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index 430bf3c..2aa7bbd 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -11,14 +11,13 @@
name: "android.hardware.biometrics.fingerprint-service.example",
vendor: true,
relative_install_path: "hw",
- init_rc: ["fingerprint-default.rc"],
- vintf_fragments: ["fingerprint-default.xml"],
+ init_rc: ["fingerprint-example.rc"],
+ vintf_fragments: ["fingerprint-example.xml"],
local_include_dirs: ["include"],
srcs: [
- "CancellationSignal.cpp",
+ "FakeFingerprintEngine.cpp",
"Fingerprint.cpp",
"Session.cpp",
- "WorkerThread.cpp",
"main.cpp",
],
shared_libs: [
@@ -26,18 +25,38 @@
"libbinder_ndk",
"android.hardware.biometrics.fingerprint-V2-ndk",
"android.hardware.biometrics.common-V2-ndk",
+ "android.hardware.biometrics.common.thread",
+ "android.hardware.biometrics.common.util",
],
+ static_libs: ["libandroid.hardware.biometrics.fingerprint.VirtualProps"],
}
-cc_test_host {
- name: "android.hardware.biometrics.fingerprint.WorkerThreadTest",
+cc_test {
+ name: "android.hardware.biometrics.fingerprint.FakeFingerprintEngineTest",
local_include_dirs: ["include"],
srcs: [
- "tests/WorkerThreadTest.cpp",
- "WorkerThread.cpp",
+ "tests/FakeFingerprintEngineTest.cpp",
+ "FakeFingerprintEngine.cpp",
],
shared_libs: [
- "libcutils",
+ "libbase",
+ "libbinder_ndk",
],
+ static_libs: [
+ "libandroid.hardware.biometrics.fingerprint.VirtualProps",
+ "android.hardware.biometrics.fingerprint-V2-ndk",
+ "android.hardware.biometrics.common-V2-ndk",
+ "android.hardware.keymaster-V3-ndk",
+ "android.hardware.biometrics.common.util",
+ ],
+ vendor: true,
test_suites: ["general-tests"],
+ require_root: true,
+}
+
+sysprop_library {
+ name: "android.hardware.biometrics.fingerprint.VirtualProps",
+ srcs: ["fingerprint.sysprop"],
+ property_owner: "Vendor",
+ vendor: true,
}
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
new file mode 100644
index 0000000..138caa0
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "FakeFingerprintEngine.h"
+
+#include <android-base/logging.h>
+
+#include <fingerprint.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+void FakeFingerprintEngine::generateChallengeImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+ std::uniform_int_distribution<int64_t> dist;
+ auto challenge = dist(mRandom);
+ FingerprintHalProperties::challenge(challenge);
+ cb->onChallengeGenerated(challenge);
+}
+
+void FakeFingerprintEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
+ BEGIN_OP(0);
+ FingerprintHalProperties::challenge({});
+ cb->onChallengeRevoked(challenge);
+}
+
+void FakeFingerprintEngine::enrollImpl(ISessionCallback* cb,
+ const keymaster::HardwareAuthToken& hat,
+ const std::future<void>& cancel) {
+ BEGIN_OP(FingerprintHalProperties::operation_enroll_latency().value_or(DEFAULT_LATENCY));
+
+ // Do proper HAT verification in the real implementation.
+ if (hat.mac.empty()) {
+ LOG(ERROR) << "Fail: hat";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
+ if (FingerprintHalProperties::operation_enroll_fails().value_or(false)) {
+ LOG(ERROR) << "Fail: operation_enroll_fails";
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+
+ // format is "<id>:<progress_ms>,<progress_ms>,...:<result>
+ auto nextEnroll = FingerprintHalProperties::next_enrollment().value_or("");
+ auto parts = Util::split(nextEnroll, ":");
+ if (parts.size() != 3) {
+ LOG(ERROR) << "Fail: invalid next_enrollment";
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+ auto enrollmentId = std::stoi(parts[0]);
+ auto progress = Util::split(parts[1], ",");
+ for (size_t i = 0; i < progress.size(); i++) {
+ auto left = progress.size() - i - 1;
+ SLEEP_MS(std::stoi(progress[i]));
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+
+ cb->onAcquired(AcquiredInfo::GOOD, 0 /* vendorCode */);
+ if (left == 0 && !IS_TRUE(parts[2])) { // end and failed
+ LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
+ FingerprintHalProperties::next_enrollment({});
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ } else { // progress and update props if last time
+ if (left == 0) {
+ auto enrollments = FingerprintHalProperties::enrollments();
+ enrollments.emplace_back(enrollmentId);
+ FingerprintHalProperties::enrollments(enrollments);
+ FingerprintHalProperties::next_enrollment({});
+ LOG(INFO) << "Enrolled: " << enrollmentId;
+ }
+ cb->onEnrollmentProgress(enrollmentId, left);
+ }
+ }
+}
+
+void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* operationId */,
+ const std::future<void>& cancel) {
+ BEGIN_OP(FingerprintHalProperties::operation_authenticate_latency().value_or(DEFAULT_LATENCY));
+
+ auto now = Util::getSystemNanoTime();
+ int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(0);
+ do {
+ if (FingerprintHalProperties::operation_authenticate_fails().value_or(false)) {
+ LOG(ERROR) << "Fail: operation_authenticate_fails";
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+
+ if (FingerprintHalProperties::lockout().value_or(false)) {
+ LOG(ERROR) << "Fail: lockout";
+ cb->onLockoutPermanent();
+ cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
+ return;
+ }
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+
+ auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
+ auto enrolls = FingerprintHalProperties::enrollments();
+ auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
+ if (id > 0 && isEnrolled) {
+ cb->onAuthenticationSucceeded(id, {} /* hat */);
+ return;
+ }
+
+ SLEEP_MS(100);
+ } while (!Util::hasElapsed(now, duration));
+
+ LOG(ERROR) << "Fail: not enrolled";
+ cb->onAuthenticationFailed();
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+}
+
+void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb,
+ const std::future<void>& cancel) {
+ BEGIN_OP(FingerprintHalProperties::operation_detect_interaction_latency().value_or(
+ DEFAULT_LATENCY));
+
+ if (FingerprintHalProperties::operation_detect_interaction_fails().value_or(false)) {
+ LOG(ERROR) << "Fail: operation_detect_interaction_fails";
+ cb->onError(Error::VENDOR, 0 /* vendorError */);
+ return;
+ }
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+
+ auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
+ auto enrolls = FingerprintHalProperties::enrollments();
+ auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
+ if (id <= 0 || !isEnrolled) {
+ LOG(ERROR) << "Fail: not enrolled";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
+ cb->onInteractionDetected();
+}
+
+void FakeFingerprintEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+
+ std::vector<int32_t> ids;
+ for (auto& enrollment : FingerprintHalProperties::enrollments()) {
+ auto id = enrollment.value_or(0);
+ if (id > 0) {
+ ids.push_back(id);
+ }
+ }
+
+ cb->onEnrollmentsEnumerated(ids);
+}
+
+void FakeFingerprintEngine::removeEnrollmentsImpl(ISessionCallback* cb,
+ const std::vector<int32_t>& enrollmentIds) {
+ BEGIN_OP(0);
+
+ std::vector<std::optional<int32_t>> newEnrollments;
+ std::vector<int32_t> removed;
+ for (auto& enrollment : FingerprintHalProperties::enrollments()) {
+ auto id = enrollment.value_or(0);
+ if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) != enrollmentIds.end()) {
+ removed.push_back(id);
+ } else if (id > 0) {
+ newEnrollments.emplace_back(id);
+ }
+ }
+ FingerprintHalProperties::enrollments(newEnrollments);
+
+ cb->onEnrollmentsRemoved(enrollmentIds);
+}
+
+void FakeFingerprintEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+ cb->onAuthenticatorIdRetrieved(FingerprintHalProperties::authenticator_id().value_or(0));
+}
+
+void FakeFingerprintEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
+ BEGIN_OP(0);
+ auto id = FingerprintHalProperties::authenticator_id().value_or(0);
+ auto newId = id + 1;
+ FingerprintHalProperties::authenticator_id(newId);
+ cb->onAuthenticatorIdInvalidated(newId);
+}
+
+void FakeFingerprintEngine::resetLockoutImpl(ISessionCallback* cb,
+ const keymaster::HardwareAuthToken& /*hat*/) {
+ BEGIN_OP(0);
+ FingerprintHalProperties::lockout(false);
+ cb->onLockoutCleared();
+}
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 1f14de6..71dc660 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -16,15 +16,19 @@
#include "Fingerprint.h"
+#include <fingerprint.sysprop.h>
#include "Session.h"
+#include <android-base/logging.h>
+
+using namespace ::android::fingerprint::virt;
+
namespace aidl::android::hardware::biometrics::fingerprint {
namespace {
constexpr size_t MAX_WORKER_QUEUE_SIZE = 5;
constexpr int SENSOR_ID = 1;
constexpr common::SensorStrength SENSOR_STRENGTH = common::SensorStrength::STRONG;
constexpr int MAX_ENROLLMENTS_PER_USER = 5;
-constexpr FingerprintSensorType SENSOR_TYPE = FingerprintSensorType::REAR;
constexpr bool SUPPORTS_NAVIGATION_GESTURES = true;
constexpr char HW_COMPONENT_ID[] = "fingerprintSensor";
constexpr char HW_VERSION[] = "vendor/model/revision";
@@ -51,8 +55,18 @@
0 /* sensorLocationY */, 0 /* sensorRadius */,
"" /* display */};
+ FingerprintSensorType sensorType = FingerprintSensorType::UNKNOWN;
+ std::string sensorTypeProp = FingerprintHalProperties::type().value_or("");
+ if (sensorTypeProp == "" || sensorTypeProp == "default" || sensorTypeProp == "rear") {
+ sensorType = FingerprintSensorType::REAR;
+ }
+ if (sensorType == FingerprintSensorType::UNKNOWN) {
+ UNIMPLEMENTED(FATAL) << "unrecognized or unimplemented fingerprint behavior: "
+ << sensorTypeProp;
+ }
+
*out = {{commonProps,
- SENSOR_TYPE,
+ sensorType,
{sensorLocation},
SUPPORTS_NAVIGATION_GESTURES,
false /* supportsDetectInteraction */}};
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
new file mode 100644
index 0000000..a6e6b81
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -0,0 +1,88 @@
+# Virtual Fingerprint HAL
+
+This is a virtual HAL implementation that is backed by system properties instead
+of actual hardware. It's intended for testing and UI development on debuggable
+builds to allow devices to masquerade as alternative device types and for
+emulators.
+
+## Supported Devices
+
+This HAL can be used on emulators, like cuttlefish, or on real devices. Add the
+following to your device's `.mk` file to include it:
+
+```
+PRODUCT_PACKAGES_DEBUG += android.hardware.biometrics.fingerprint-service.example
+```
+
+The virtual HAL will be ignored if a real HAL is also installed on the target
+device. Set the `biometric_virtual_enabled` settings and reboot the device to
+switch to the virtual HAL. Unset it and reboot again to switch back.
+
+## Getting Started
+
+First, set the type of sensor the device should use, enable the virtual
+extensions in the framework, and reboot.
+
+This doesn't work with HIDL and you typically need to have a PIN or password set
+for things to work correctly, so this is a good time to set those too.
+
+```shell
+$ adb root
+$ adb shell settings put secure biometric_virtual_enabled 1
+$ adb shell setprop persist.vendor.fingerprint.virtual.type rear
+$ adb shell locksettings set-pin 0000
+$ adb shell settings put secure com.android.server.biometrics.AuthService.hidlDisabled 1
+$ adb reboot
+```
+
+### Enrollments
+
+Next, setup enrollments on the device. This can either be done through the UI,
+or via adb.
+
+#### UI Enrollment
+
+1. Tee up the results of the enrollment before starting the process:
+
+ ```shell
+ $ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
+ ```
+2. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the
+ prompts.
+3. Verify the enrollments in the UI:
+
+ ```shell
+ $ adb shell getprop persist.vendor.fingerprint.virtual.enrollments
+ ```
+
+#### Direct Enrollment
+
+To set enrollment directly without the UI:
+
+```shell
+$ adb root
+$ adb shell setprop persist.vendor.fingerprint.virtual.enrollments 1
+$ adb shell cmd fingerprint sync
+```
+
+**Note: You may need to do this twice.** The templates are checked as part of
+some lazy operations, like user switching and startup, which can cause the
+framework to delete the enrollments before the sync operation runs. Until this
+is fixed, just run the commands twice as a workaround.
+
+### Authenticate
+
+To authenticate successfully set the enrolled id that should succeed. Unset it
+or change the value to make authenticate operations fail:
+
+````shell
+$ adb shell setprop vendor.fingerprint.virtual.enrollment_hit 1
+````
+
+### View HAL State
+
+To view all the properties of the HAL (see `fingerprint.sysprop` for the API):
+
+```shell
+$ adb shell getprop | grep vendor.fingerprint.virtual
+```
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index 452ed12..ab91e98 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -18,7 +18,7 @@
#include <android-base/logging.h>
-#include "CancellationSignal.h"
+#include "util/CancellationSignal.h"
namespace aidl::android::hardware::biometrics::fingerprint {
@@ -101,7 +101,7 @@
if (shouldCancel(cancFuture)) {
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
} else {
- mEngine->enrollImpl(mCb.get(), hat);
+ mEngine->enrollImpl(mCb.get(), hat, cancFuture);
}
enterIdling();
}));
@@ -123,7 +123,7 @@
if (shouldCancel(cancFuture)) {
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
} else {
- mEngine->authenticateImpl(mCb.get(), operationId);
+ mEngine->authenticateImpl(mCb.get(), operationId, cancFuture);
}
enterIdling();
}));
@@ -144,7 +144,7 @@
if (shouldCancel(cancFuture)) {
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
} else {
- mEngine->detectInteractionImpl(mCb.get());
+ mEngine->detectInteractionImpl(mCb.get(), cancFuture);
}
enterIdling();
}));
diff --git a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
new file mode 100644
index 0000000..4724ff4
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
@@ -0,0 +1,85 @@
+props {
+ owner: Vendor
+ module: "android.fingerprint.virt.FingerprintHalProperties"
+ prop {
+ api_name: "authenticator_id"
+ type: Long
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.authenticator_id"
+ }
+ prop {
+ api_name: "challenge"
+ type: Long
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.challenge"
+ }
+ prop {
+ api_name: "enrollment_hit"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.enrollment_hit"
+ }
+ prop {
+ api_name: "enrollments"
+ type: IntegerList
+ access: ReadWrite
+ prop_name: "persist.vendor.fingerprint.virtual.enrollments"
+ }
+ prop {
+ api_name: "lockout"
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.lockout"
+ }
+ prop {
+ api_name: "next_enrollment"
+ type: String
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.next_enrollment"
+ }
+ prop {
+ api_name: "operation_authenticate_duration"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
+ }
+ prop {
+ api_name: "operation_authenticate_fails"
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
+ }
+ prop {
+ api_name: "operation_authenticate_latency"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
+ }
+ prop {
+ api_name: "operation_detect_interaction_fails"
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails"
+ }
+ prop {
+ api_name: "operation_detect_interaction_latency"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
+ }
+ prop {
+ api_name: "operation_enroll_fails"
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.operation_enroll_fails"
+ }
+ prop {
+ api_name: "operation_enroll_latency"
+ type: Integer
+ access: ReadWrite
+ prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
+ }
+ prop {
+ api_name: "type"
+ type: String
+ access: ReadWrite
+ prop_name: "persist.vendor.fingerprint.virtual.type"
+ enum_values: "default|rear|udfps|side"
+ }
+}
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-default.rc b/biometrics/fingerprint/aidl/default/fingerprint-default.rc
deleted file mode 100644
index eb62c56..0000000
--- a/biometrics/fingerprint/aidl/default/fingerprint-default.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service vendor.fingerprint-default /vendor/bin/hw/android.hardware.biometrics.fingerprint-service.example
- class hal
- user nobody
- group nobody
-
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-example.rc b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
new file mode 100644
index 0000000..574438e
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/fingerprint-example.rc
@@ -0,0 +1,4 @@
+service vendor.fingerprint-example /vendor/bin/hw/android.hardware.biometrics.fingerprint-service.example
+ class hal
+ user nobody
+ group nobody
diff --git a/biometrics/fingerprint/aidl/default/fingerprint-default.xml b/biometrics/fingerprint/aidl/default/fingerprint-example.xml
similarity index 79%
rename from biometrics/fingerprint/aidl/default/fingerprint-default.xml
rename to biometrics/fingerprint/aidl/default/fingerprint-example.xml
index d322eb6..05d1279 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint-default.xml
+++ b/biometrics/fingerprint/aidl/default/fingerprint-example.xml
@@ -2,6 +2,6 @@
<hal format="aidl">
<name>android.hardware.biometrics.fingerprint</name>
<version>2</version>
- <fqname>IFingerprint/default</fqname>
+ <fqname>IFingerprint/virtual</fqname>
</hal>
</manifest>
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
new file mode 100644
index 0000000..12c8648
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -0,0 +1,135 @@
+# fingerprint.sysprop
+# module becomes static class (Java) / namespace (C++) for serving API
+module: "android.fingerprint.virt.FingerprintHalProperties"
+owner: Vendor
+
+# type of fingerprint sensor
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.type"
+ type: String
+ scope: Public
+ access: ReadWrite
+ enum_values: "default|rear|udfps|side"
+ api_name: "type"
+}
+
+# ids of call current enrollments
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.enrollments"
+ type: IntegerList
+ scope: Public
+ access: ReadWrite
+ api_name: "enrollments"
+}
+
+# authenticate and detectInteraction will succeed with this
+# enrollment id, when present, otherwise they will error
+prop {
+ prop_name: "vendor.fingerprint.virtual.enrollment_hit"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "enrollment_hit"
+}
+
+# the next enrollment in the format: "<id>:<delay>,<delay>,...:<result>"
+# for example: "2:0:true"
+# this property is reset after enroll completes
+prop {
+ prop_name: "vendor.fingerprint.virtual.next_enrollment"
+ type: String
+ scope: Public
+ access: ReadWrite
+ api_name: "next_enrollment"
+}
+
+# value for getAuthenticatorId or 0
+prop {
+ prop_name: "vendor.fingerprint.virtual.authenticator_id"
+ type: Long
+ scope: Public
+ access: ReadWrite
+ api_name: "authenticator_id"
+}
+
+# value for generateChallenge
+prop {
+ prop_name: "vendor.fingerprint.virtual.challenge"
+ type: Long
+ scope: Public
+ access: ReadWrite
+ api_name: "challenge"
+}
+
+# if locked out
+prop {
+ prop_name: "vendor.fingerprint.virtual.lockout"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "lockout"
+}
+
+# force all authenticate operations to fail
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_fails"
+}
+
+# force all detectInteraction operations to fail
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_detect_interaction_fails"
+}
+
+# force all enroll operations to fail
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_enroll_fails"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_enroll_fails"
+}
+
+# add a latency to authentication operations
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_latency"
+}
+
+# add a latency to detectInteraction operations
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_detect_interaction_latency"
+}
+
+# add a latency to enroll operations
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_enroll_latency"
+}
+
+# millisecond duration for authenticate operations
+# (waits for changes to enrollment_hit)
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_duration"
+}
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index b927770..eb810da 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -15,72 +15,35 @@
*/
#pragma once
+#include <aidl/android/hardware/biometrics/common/SensorStrength.h>
+#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
-#include <android-base/logging.h>
#include <random>
+#include <future>
+#include <vector>
+
+using namespace ::aidl::android::hardware::biometrics::common;
+
namespace aidl::android::hardware::biometrics::fingerprint {
+// A fake engine that is backed by system properties instead of hardware.
class FakeFingerprintEngine {
public:
FakeFingerprintEngine() : mRandom(std::mt19937::default_seed) {}
- void generateChallengeImpl(ISessionCallback* cb) {
- LOG(INFO) << "generateChallengeImpl";
- std::uniform_int_distribution<int64_t> dist;
- auto challenge = dist(mRandom);
- cb->onChallengeGenerated(challenge);
- }
-
- void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
- LOG(INFO) << "revokeChallengeImpl";
- cb->onChallengeRevoked(challenge);
- }
-
- void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat) {
- LOG(INFO) << "enrollImpl";
- // Do proper HAT verification in the real implementation.
- if (hat.mac.empty()) {
- cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
- return;
- }
- cb->onEnrollmentProgress(0 /* enrollmentId */, 0 /* remaining */);
- }
-
- void authenticateImpl(ISessionCallback* cb, int64_t /* operationId */) {
- LOG(INFO) << "authenticateImpl";
- cb->onAuthenticationSucceeded(0 /* enrollmentId */, {} /* hat */);
- }
-
- void detectInteractionImpl(ISessionCallback* cb) {
- LOG(INFO) << "detectInteractionImpl";
- cb->onInteractionDetected();
- }
-
- void enumerateEnrollmentsImpl(ISessionCallback* cb) {
- LOG(INFO) << "enumerateEnrollmentsImpl";
- cb->onEnrollmentsEnumerated({} /* enrollmentIds */);
- }
-
- void removeEnrollmentsImpl(ISessionCallback* cb, const std::vector<int32_t>& enrollmentIds) {
- LOG(INFO) << "removeEnrollmentsImpl";
- cb->onEnrollmentsRemoved(enrollmentIds);
- }
-
- void getAuthenticatorIdImpl(ISessionCallback* cb) {
- LOG(INFO) << "getAuthenticatorIdImpl";
- cb->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
- }
-
- void invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
- LOG(INFO) << "invalidateAuthenticatorIdImpl";
- cb->onAuthenticatorIdInvalidated(0 /* newAuthenticatorId */);
- }
-
- void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/) {
- LOG(INFO) << "resetLockoutImpl";
- cb->onLockoutCleared();
- }
+ void generateChallengeImpl(ISessionCallback* cb);
+ void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge);
+ void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ const std::future<void>& cancel);
+ void authenticateImpl(ISessionCallback* cb, int64_t operationId,
+ const std::future<void>& cancel);
+ void detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel);
+ void enumerateEnrollmentsImpl(ISessionCallback* cb);
+ void removeEnrollmentsImpl(ISessionCallback* cb, const std::vector<int32_t>& enrollmentIds);
+ void getAuthenticatorIdImpl(ISessionCallback* cb);
+ void invalidateAuthenticatorIdImpl(ISessionCallback* cb);
+ void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);
std::mt19937 mRandom;
};
diff --git a/biometrics/fingerprint/aidl/default/include/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
index 7bd3d6d..20def0c 100644
--- a/biometrics/fingerprint/aidl/default/include/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -20,7 +20,7 @@
#include "FakeFingerprintEngine.h"
#include "Session.h"
-#include "WorkerThread.h"
+#include "thread/WorkerThread.h"
namespace aidl::android::hardware::biometrics::fingerprint {
diff --git a/biometrics/fingerprint/aidl/default/include/Session.h b/biometrics/fingerprint/aidl/default/include/Session.h
index acd5def..104d819 100644
--- a/biometrics/fingerprint/aidl/default/include/Session.h
+++ b/biometrics/fingerprint/aidl/default/include/Session.h
@@ -20,7 +20,7 @@
#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
#include "FakeFingerprintEngine.h"
-#include "WorkerThread.h"
+#include "thread/WorkerThread.h"
namespace aidl::android::hardware::biometrics::fingerprint {
diff --git a/biometrics/fingerprint/aidl/default/main.cpp b/biometrics/fingerprint/aidl/default/main.cpp
index c985201..0e672b1 100644
--- a/biometrics/fingerprint/aidl/default/main.cpp
+++ b/biometrics/fingerprint/aidl/default/main.cpp
@@ -27,7 +27,7 @@
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<Fingerprint> hal = ndk::SharedRefBase::make<Fingerprint>();
- const std::string instance = std::string(Fingerprint::descriptor) + "/default";
+ const std::string instance = std::string(Fingerprint::descriptor) + "/virtual";
binder_status_t status = AServiceManager_addService(hal->asBinder().get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
new file mode 100644
index 0000000..742d933
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <android/binder_process.h>
+#include <fingerprint.sysprop.h>
+#include <gtest/gtest.h>
+
+#include <aidl/android/hardware/biometrics/fingerprint/BnSessionCallback.h>
+
+#include "FakeFingerprintEngine.h"
+
+using namespace ::android::fingerprint::virt;
+using namespace ::aidl::android::hardware::biometrics::fingerprint;
+using namespace ::aidl::android::hardware::keymaster;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class TestSessionCallback : public BnSessionCallback {
+ public:
+ ndk::ScopedAStatus onChallengeGenerated(int64_t challenge) override {
+ mLastChallenge = challenge;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onChallengeRevoked(int64_t challenge) override {
+ mLastChallengeRevoked = challenge;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onError(fingerprint::Error error, int32_t) override {
+ mError = error;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override {
+ if (remaining == 0) mLastEnrolled = enrollmentId;
+ return ndk::ScopedAStatus::ok();
+ };
+
+ ::ndk::ScopedAStatus onAuthenticationSucceeded(int32_t enrollmentId,
+ const keymaster::HardwareAuthToken&) override {
+ mLastAuthenticated = enrollmentId;
+ mAuthenticateFailed = false;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticationFailed() override {
+ mLastAuthenticated = 0;
+ mAuthenticateFailed = true;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onInteractionDetected() override {
+ mInteractionDetectedCount++;
+ return ndk::ScopedAStatus::ok();
+ };
+ ndk::ScopedAStatus onAcquired(AcquiredInfo /*info*/, int32_t /*vendorCode*/) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onEnrollmentsEnumerated(
+ const std::vector<int32_t>& enrollmentIds) override {
+ mLastEnrollmentEnumerated = enrollmentIds;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentsRemoved(const std::vector<int32_t>& enrollmentIds) override {
+ mLastEnrollmentRemoved = enrollmentIds;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t authenticatorId) override {
+ mLastAuthenticatorId = authenticatorId;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t authenticatorId) override {
+ mLastAuthenticatorId = authenticatorId;
+ mAuthenticatorIdInvalidated = true;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onLockoutPermanent() override {
+ mLockoutPermanent = true;
+ return ndk::ScopedAStatus::ok();
+ };
+ ndk::ScopedAStatus onLockoutTimed(int64_t /* timeout */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus onLockoutCleared() override { return ndk::ScopedAStatus::ok(); }
+ ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
+
+ Error mError = Error::UNKNOWN;
+ int64_t mLastChallenge = -1;
+ int64_t mLastChallengeRevoked = -1;
+ int32_t mLastEnrolled = -1;
+ int32_t mLastAuthenticated = -1;
+ int64_t mLastAuthenticatorId = -1;
+ std::vector<int32_t> mLastEnrollmentEnumerated;
+ std::vector<int32_t> mLastEnrollmentRemoved;
+ bool mAuthenticateFailed = false;
+ bool mAuthenticatorIdInvalidated = false;
+ bool mLockoutPermanent = false;
+ int mInteractionDetectedCount = 0;
+};
+
+class FakeFingerprintEngineTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ FingerprintHalProperties::operation_enroll_latency(0);
+ FingerprintHalProperties::operation_authenticate_latency(0);
+ FingerprintHalProperties::operation_detect_interaction_latency(0);
+ mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
+ }
+
+ FakeFingerprintEngine mEngine;
+ std::shared_ptr<TestSessionCallback> mCallback;
+ std::promise<void> mCancel;
+};
+
+TEST_F(FakeFingerprintEngineTest, GenerateChallenge) {
+ mEngine.generateChallengeImpl(mCallback.get());
+ ASSERT_EQ(FingerprintHalProperties::challenge().value(), mCallback->mLastChallenge);
+}
+
+TEST_F(FakeFingerprintEngineTest, RevokeChallenge) {
+ auto challenge = FingerprintHalProperties::challenge().value_or(10);
+ mEngine.revokeChallengeImpl(mCallback.get(), challenge);
+ ASSERT_FALSE(FingerprintHalProperties::challenge().has_value());
+ ASSERT_EQ(challenge, mCallback->mLastChallengeRevoked);
+}
+
+TEST_F(FakeFingerprintEngineTest, ResetLockout) {
+ FingerprintHalProperties::lockout(true);
+ mEngine.resetLockoutImpl(mCallback.get(), {});
+ ASSERT_FALSE(FingerprintHalProperties::lockout().value_or(true));
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticatorId) {
+ FingerprintHalProperties::authenticator_id(50);
+ mEngine.getAuthenticatorIdImpl(mCallback.get());
+ ASSERT_EQ(50, mCallback->mLastAuthenticatorId);
+ ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticatorIdInvalidate) {
+ FingerprintHalProperties::authenticator_id(500);
+ mEngine.invalidateAuthenticatorIdImpl(mCallback.get());
+ ASSERT_NE(500, FingerprintHalProperties::authenticator_id().value());
+ ASSERT_TRUE(mCallback->mAuthenticatorIdInvalidated);
+}
+
+TEST_F(FakeFingerprintEngineTest, Enroll) {
+ FingerprintHalProperties::enrollments({});
+ FingerprintHalProperties::next_enrollment("4:0,0:true");
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future());
+ ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value());
+ ASSERT_EQ(1, FingerprintHalProperties::enrollments().size());
+ ASSERT_EQ(4, FingerprintHalProperties::enrollments()[0].value());
+ ASSERT_EQ(4, mCallback->mLastEnrolled);
+}
+
+TEST_F(FakeFingerprintEngineTest, EnrollCancel) {
+ FingerprintHalProperties::enrollments({});
+ auto next = "4:0,0:true";
+ FingerprintHalProperties::next_enrollment(next);
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mCancel.set_value();
+ mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future());
+ ASSERT_EQ(Error::CANCELED, mCallback->mError);
+ ASSERT_EQ(-1, mCallback->mLastEnrolled);
+ ASSERT_EQ(0, FingerprintHalProperties::enrollments().size());
+ ASSERT_EQ(next, FingerprintHalProperties::next_enrollment().value_or(""));
+}
+
+TEST_F(FakeFingerprintEngineTest, EnrollFail) {
+ FingerprintHalProperties::enrollments({});
+ auto next = "2:0,0:false";
+ FingerprintHalProperties::next_enrollment(next);
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future());
+ ASSERT_EQ(Error::UNABLE_TO_PROCESS, mCallback->mError);
+ ASSERT_EQ(-1, mCallback->mLastEnrolled);
+ ASSERT_EQ(0, FingerprintHalProperties::enrollments().size());
+ ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value());
+}
+
+TEST_F(FakeFingerprintEngineTest, Authenticate) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_FALSE(mCallback->mAuthenticateFailed);
+ ASSERT_EQ(2, mCallback->mLastAuthenticated);
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticateCancel) {
+ FingerprintHalProperties::enrollments({2});
+ FingerprintHalProperties::enrollment_hit(2);
+ mCancel.set_value();
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_EQ(Error::CANCELED, mCallback->mError);
+ ASSERT_EQ(-1, mCallback->mLastAuthenticated);
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticateNotSet) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit({});
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_TRUE(mCallback->mAuthenticateFailed);
+ ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS);
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticateNotEnrolled) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(3);
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_TRUE(mCallback->mAuthenticateFailed);
+ ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS);
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticateLockout) {
+ FingerprintHalProperties::enrollments({22, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ FingerprintHalProperties::lockout(true);
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_TRUE(mCallback->mLockoutPermanent);
+ ASSERT_NE(mCallback->mError, Error::UNKNOWN);
+}
+
+TEST_F(FakeFingerprintEngineTest, InteractionDetect) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
+}
+
+TEST_F(FakeFingerprintEngineTest, InteractionDetectCancel) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ mCancel.set_value();
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(Error::CANCELED, mCallback->mError);
+ ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
+}
+
+TEST_F(FakeFingerprintEngineTest, InteractionDetectNotSet) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit({});
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
+}
+
+TEST_F(FakeFingerprintEngineTest, InteractionDetectNotEnrolled) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(25);
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
+}
+
+TEST_F(FakeFingerprintEngineTest, EnumerateEnrolled) {
+ FingerprintHalProperties::enrollments({2, 4, 8});
+ mEngine.enumerateEnrollmentsImpl(mCallback.get());
+ ASSERT_EQ(3, mCallback->mLastEnrollmentEnumerated.size());
+ for (auto id : FingerprintHalProperties::enrollments()) {
+ ASSERT_TRUE(std::find(mCallback->mLastEnrollmentEnumerated.begin(),
+ mCallback->mLastEnrollmentEnumerated.end(),
+ id) != mCallback->mLastEnrollmentEnumerated.end());
+ }
+}
+
+TEST_F(FakeFingerprintEngineTest, RemoveEnrolled) {
+ FingerprintHalProperties::enrollments({2, 4, 8, 1});
+ mEngine.removeEnrollmentsImpl(mCallback.get(), {2, 8});
+ auto enrolls = FingerprintHalProperties::enrollments();
+ ASSERT_EQ(2, mCallback->mLastEnrollmentRemoved.size());
+ for (auto id : {2, 8}) {
+ ASSERT_TRUE(std::find(mCallback->mLastEnrollmentRemoved.begin(),
+ mCallback->mLastEnrollmentRemoved.end(),
+ id) != mCallback->mLastEnrollmentRemoved.end());
+ }
+ ASSERT_EQ(2, enrolls.size());
+ for (auto id : {1, 4}) {
+ ASSERT_TRUE(std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end());
+ }
+}
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/biometrics/fingerprint/aidl/vts/Android.bp b/biometrics/fingerprint/aidl/vts/Android.bp
index 30d5624..a474f66 100644
--- a/biometrics/fingerprint/aidl/vts/Android.bp
+++ b/biometrics/fingerprint/aidl/vts/Android.bp
@@ -15,8 +15,8 @@
],
srcs: ["VtsHalBiometricsFingerprintTargetTest.cpp"],
static_libs: [
- "android.hardware.biometrics.common-V1-ndk",
- "android.hardware.biometrics.fingerprint-V1-ndk",
+ "android.hardware.biometrics.common-V2-ndk",
+ "android.hardware.biometrics.fingerprint-V2-ndk",
"android.hardware.keymaster-V3-ndk",
],
shared_libs: [
diff --git a/broadcastradio/2.0/default/TunerSession.cpp b/broadcastradio/2.0/default/TunerSession.cpp
index 2ba4d02..cc2f1a5 100644
--- a/broadcastradio/2.0/default/TunerSession.cpp
+++ b/broadcastradio/2.0/default/TunerSession.cpp
@@ -241,13 +241,13 @@
};
std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
- auto task = [this, list]() {
+ auto task = [this, filteredList]() {
lock_guard<mutex> lk(mMut);
ProgramListChunk chunk = {};
chunk.purge = true;
chunk.complete = true;
- chunk.modified = hidl_vec<ProgramInfo>(list.begin(), list.end());
+ chunk.modified = hidl_vec<ProgramInfo>(filteredList.begin(), filteredList.end());
mCallback->onProgramListUpdated(chunk);
};
diff --git a/broadcastradio/2.0/default/VirtualRadio.cpp b/broadcastradio/2.0/default/VirtualRadio.cpp
index c59fd8f..e6b1017 100644
--- a/broadcastradio/2.0/default/VirtualRadio.cpp
+++ b/broadcastradio/2.0/default/VirtualRadio.cpp
@@ -46,9 +46,9 @@
VirtualRadio gDabRadio(
"DAB radio mock",
{
- {make_selector_dab(12345, 225648), "BBC Radio 1", "Khalid", "Talk"}, // 12B
- {make_selector_dab(22345, 222064), "Classic FM", "Jean Sibelius", "Andante Festivo"}, // 11D
- {make_selector_dab(32345, 222064), "Absolute Radio", "Coldplay", "Clocks"}, // 11D
+ {make_selector_dab(0xA00001u, 0x0001u), "BBC Radio 1", "Khalid", "Talk"},
+ {make_selector_dab(0xB00001u, 0x1001u), "Classic FM", "Jean Sibelius", "Andante Festivo"},
+ {make_selector_dab(0xB00002u, 0x1001u), "Absolute Radio", "Coldplay", "Clocks"},
});
// clang-format on
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index 615fde0..5e8a5cf 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -108,6 +108,7 @@
bool openSession();
bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
std::optional<utils::ProgramInfoSet> getProgramList();
+ std::optional<utils::ProgramInfoSet> getProgramList(const ProgramFilter& filter);
sp<IBroadcastRadio> mModule;
Properties mProperties;
@@ -239,9 +240,15 @@
}
std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
+ ProgramFilter emptyFilter = {};
+ return getProgramList(emptyFilter);
+}
+
+std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList(
+ const ProgramFilter& filter) {
EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
- auto startResult = mSession->startProgramListUpdates({});
+ auto startResult = mSession->startProgramListUpdates(filter);
if (startResult == Result::NOT_SUPPORTED) {
printSkipped("Program list not supported");
return std::nullopt;
@@ -810,20 +817,98 @@
}
/**
- * Test getting program list.
+ * Test getting program list using empty program filter.
*
* Verifies that:
* - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
* - the complete list is fetched within timeout::programListScan;
* - stopProgramListUpdates does not crash.
*/
-TEST_P(BroadcastRadioHalTest, GetProgramList) {
+TEST_P(BroadcastRadioHalTest, GetProgramListFromEmptyFilter) {
ASSERT_TRUE(openSession());
getProgramList();
}
/**
+ * Test getting program list using AMFM frequency program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within timeout::programListScan;
+ * - stopProgramListUpdates does not crash;
+ * - result for startProgramListUpdates using a filter with AMFM_FREQUENCY value of the first AMFM
+ * program matches the expected result.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromAmFmFilter) {
+ ASSERT_TRUE(openSession());
+
+ auto completeList = getProgramList();
+ if (!completeList) return;
+
+ ProgramFilter amfmFilter = {};
+ int expectedResultSize = 0;
+ uint64_t expectedFreq = 0;
+ for (auto&& program : *completeList) {
+ auto amfmIds = utils::getAllIds(program.selector, IdentifierType::AMFM_FREQUENCY);
+ EXPECT_LE(amfmIds.size(), 1u);
+ if (amfmIds.size() == 0) continue;
+
+ if (expectedResultSize == 0) {
+ expectedFreq = amfmIds[0];
+ amfmFilter.identifiers = {
+ make_identifier(IdentifierType::AMFM_FREQUENCY, expectedFreq)};
+ expectedResultSize = 1;
+ } else if (amfmIds[0] == expectedFreq) {
+ expectedResultSize++;
+ }
+ }
+
+ if (expectedResultSize == 0) return;
+ auto amfmList = getProgramList(amfmFilter);
+ ASSERT_EQ(expectedResultSize, amfmList->size()) << "amfm filter result size is wrong";
+}
+
+/**
+ * Test getting program list using DAB ensemble program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within timeout::programListScan;
+ * - stopProgramListUpdates does not crash;
+ * - result for startProgramListUpdates using a filter with DAB_ENSEMBLE value of the first DAB
+ * program matches the expected result.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromDabFilter) {
+ ASSERT_TRUE(openSession());
+
+ auto completeList = getProgramList();
+ if (!completeList) return;
+
+ ProgramFilter dabFilter = {};
+ int expectedResultSize = 0;
+ uint64_t expectedEnsemble = 0;
+ for (auto&& program : *completeList) {
+ auto dabEnsembles = utils::getAllIds(program.selector, IdentifierType::DAB_ENSEMBLE);
+ EXPECT_LE(dabEnsembles.size(), 1u);
+ if (dabEnsembles.size() == 0) continue;
+
+ if (expectedResultSize == 0) {
+ expectedEnsemble = dabEnsembles[0];
+ dabFilter.identifiers = {
+ make_identifier(IdentifierType::DAB_ENSEMBLE, expectedEnsemble)};
+ expectedResultSize = 1;
+ } else if (dabEnsembles[0] == expectedEnsemble) {
+ expectedResultSize++;
+ }
+ }
+
+ if (expectedResultSize == 0) return;
+ auto dabList = getProgramList(dabFilter);
+ ASSERT_EQ(expectedResultSize, dabList->size()) << "dab filter result size is wrong";
+}
+
+/**
* Test HD_STATION_NAME correctness.
*
* Verifies that if a program on the list contains HD_STATION_NAME identifier:
diff --git a/broadcastradio/common/utils/WorkerThread.cpp b/broadcastradio/common/utils/WorkerThread.cpp
index 31f4d3f..dd87f53 100644
--- a/broadcastradio/common/utils/WorkerThread.cpp
+++ b/broadcastradio/common/utils/WorkerThread.cpp
@@ -31,7 +31,11 @@
return lhs.when > rhs.when;
}
-WorkerThread::WorkerThread() : mIsTerminating(false), mThread(&WorkerThread::threadLoop, this) {}
+WorkerThread::WorkerThread() : mIsTerminating(false) {
+ // putting mThread in constructor instead of initializer list
+ // to ensure all class members are init before mThread starts
+ mThread = std::thread(&WorkerThread::threadLoop, this);
+}
WorkerThread::~WorkerThread() {
{
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 0acb6ec..f4d819f 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -104,14 +104,6 @@
<regex-instance>.*</regex-instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.automotive.vehicle</name>
- <version>2.0</version>
- <interface>
- <name>IVehicle</name>
- <regex-instance>.*</regex-instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.biometrics.face</name>
<version>2</version>
@@ -126,6 +118,7 @@
<interface>
<name>IFingerprint</name>
<instance>default</instance>
+ <instance>virtual</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
@@ -214,18 +207,6 @@
<regex-instance>.*</regex-instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.drm</name>
- <version>1.3-4</version>
- <interface>
- <name>ICryptoFactory</name>
- <regex-instance>.*</regex-instance>
- </interface>
- <interface>
- <name>IDrmFactory</name>
- <regex-instance>.*</regex-instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.dumpstate</name>
<interface>
@@ -241,14 +222,6 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.gnss</name>
- <version>2.0-1</version>
- <interface>
- <name>IGnss</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.gnss</name>
<version>2</version>
@@ -273,21 +246,6 @@
<instance>default</instance>
</interface>
</hal>
- <!-- Either the AIDL or the HIDL allocator HAL must exist on the device.
- If the HIDL composer HAL exists, it must be at least version 2.0.
- See DeviceManifestTest.GrallocHal -->
- <hal format="hidl" optional="true">
- <name>android.hardware.graphics.allocator</name>
- <!-- New, non-Go devices should use 4.0 or the AIDL hal.
- See DeviceManifestTest.GrallocVersionCompatibility. -->
- <version>2.0</version>
- <version>3.0</version>
- <version>4.0</version>
- <interface>
- <name>IAllocator</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.graphics.allocator</name>
<version>1</version>
@@ -296,17 +254,6 @@
<instance>default</instance>
</interface>
</hal>
- <!-- Either the AIDL or the HIDL composer HAL must exist on the device.
- If the HIDL composer HAL exists, it must be at least version 2.1.
- See DeviceManifestTest.ComposerHal -->
- <hal format="hidl" optional="true">
- <name>android.hardware.graphics.composer</name>
- <version>2.1-4</version>
- <interface>
- <name>IComposer</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.graphics.composer3</name>
<version>1</version>
@@ -460,14 +407,6 @@
<regex-instance>.*</regex-instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.nfc</name>
- <version>1.2</version>
- <interface>
- <name>INfc</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.nfc</name>
<interface>
@@ -623,23 +562,6 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.sensors</name>
- <version>1.0</version>
- <version>2.0-1</version>
- <interface>
- <name>ISensors</name>
- <instance>default</instance>
- </interface>
- </hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.soundtrigger</name>
- <version>2.3</version>
- <interface>
- <name>ISoundTriggerHw</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.soundtrigger3</name>
<version>1</version>
@@ -688,14 +610,6 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.tv.tuner</name>
- <version>1.0-1</version>
- <interface>
- <name>ITuner</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.tv.tuner</name>
<version>1</version>
@@ -704,14 +618,6 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.usb</name>
- <version>1.0-3</version>
- <interface>
- <name>IUsb</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.usb</name>
<interface>
diff --git a/gnss/aidl/vts/AndroidTest.xml b/gnss/aidl/vts/AndroidTest.xml
new file mode 100644
index 0000000..d203402
--- /dev/null
+++ b/gnss/aidl/vts/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Runs VtsHalGnssTargetTest.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalGnssTargetTest->/data/local/tmp/VtsHalGnssTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalGnssTargetTest" />
+ <option name="native-test-timeout" value="2m" />
+ </test>
+</configuration>
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index fa294ff..35225d9 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -737,6 +737,39 @@
}
}
+/*
+ * Test that no two display configs are exactly the same.
+ */
+TEST_P(GraphicsComposerHidlTest, GetDisplayConfigNoRepetitions) {
+ for (const auto& display : mDisplays) {
+ std::vector<Config> configs = mComposerClient->getDisplayConfigs(display.get());
+ for (int i = 0; i < configs.size(); i++) {
+ for (int j = i + 1; j < configs.size(); j++) {
+ const int32_t width1 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[i], IComposerClient::Attribute::WIDTH);
+ const int32_t height1 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[i], IComposerClient::Attribute::HEIGHT);
+ const int32_t vsyncPeriod1 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[i], IComposerClient::Attribute::VSYNC_PERIOD);
+ const int32_t group1 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[i], IComposerClient::Attribute::CONFIG_GROUP);
+
+ const int32_t width2 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[j], IComposerClient::Attribute::WIDTH);
+ const int32_t height2 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[j], IComposerClient::Attribute::HEIGHT);
+ const int32_t vsyncPeriod2 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[j], IComposerClient::Attribute::VSYNC_PERIOD);
+ const int32_t group2 = mComposerClient->getDisplayAttribute_2_4(
+ display.get(), configs[j], IComposerClient::Attribute::CONFIG_GROUP);
+
+ ASSERT_FALSE(width1 == width2 && height1 == height2 &&
+ vsyncPeriod1 == vsyncPeriod2 && group1 == group2);
+ }
+ }
+ }
+}
+
} // namespace
} // namespace vts
} // namespace V2_4
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl
index ddd45b7..9e8c768 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.aidl
@@ -23,7 +23,6 @@
parcelable ChangedCompositionTypes {
/**
* The display which this commands refers to.
- * @see IComposer.createDisplay
*/
long display;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
index ba6fe97..ea54a89 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
@@ -23,7 +23,6 @@
parcelable ClientTargetPropertyWithBrightness {
/**
* The display which this commands refers to.
- * @see IComposer.createDisplay
*/
long display;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
index b6df147..4f69aee 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
@@ -26,7 +26,6 @@
parcelable DisplayCommand {
/**
* The display which this commands refers to.
- * @see IComposer.createDisplay
*/
long display;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl
index 27fe1e6..3a50b6e 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayRequest.aidl
@@ -34,7 +34,6 @@
/**
* The display which this commands refers to.
- * @see IComposer.createDisplay
*/
long display;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl
index 244b4e5..b757656 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentFence.aidl
@@ -20,7 +20,6 @@
parcelable PresentFence {
/**
* The display which this commands refers to.
- * @see IComposer.createDisplay
*/
long display;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl
index 5ae8940..e15dbf2 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/PresentOrValidate.aidl
@@ -20,7 +20,6 @@
parcelable PresentOrValidate {
/**
* The display which this commands refers to.
- * @see IComposer.createDisplay
*/
long display;
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl
index 459a042..58649d5 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ReleaseFences.aidl
@@ -20,7 +20,6 @@
parcelable ReleaseFences {
/**
* The display which this commands refers to.
- * @see IComposer.createDisplay
*/
long display;
@VintfStability
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 047109e..93b646f 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -2122,6 +2122,41 @@
EXPECT_TRUE(mComposerClient->setPowerMode(getPrimaryDisplayId(), PowerMode::OFF).isOk());
}
+/*
+ * Test that no two display configs are exactly the same.
+ */
+TEST_P(GraphicsComposerAidlTest, GetDisplayConfigNoRepetitions) {
+ for (const auto& display : mDisplays) {
+ const auto& [status, configs] = mComposerClient->getDisplayConfigs(display.getDisplayId());
+ for (std::vector<int>::size_type i = 0; i < configs.size(); i++) {
+ for (std::vector<int>::size_type j = i + 1; j < configs.size(); j++) {
+ const auto& [widthStatus1, width1] = mComposerClient->getDisplayAttribute(
+ display.getDisplayId(), configs[i], DisplayAttribute::WIDTH);
+ const auto& [heightStatus1, height1] = mComposerClient->getDisplayAttribute(
+ display.getDisplayId(), configs[i], DisplayAttribute::HEIGHT);
+ const auto& [vsyncPeriodStatus1, vsyncPeriod1] =
+ mComposerClient->getDisplayAttribute(display.getDisplayId(), configs[i],
+ DisplayAttribute::VSYNC_PERIOD);
+ const auto& [groupStatus1, group1] = mComposerClient->getDisplayAttribute(
+ display.getDisplayId(), configs[i], DisplayAttribute::CONFIG_GROUP);
+
+ const auto& [widthStatus2, width2] = mComposerClient->getDisplayAttribute(
+ display.getDisplayId(), configs[j], DisplayAttribute::WIDTH);
+ const auto& [heightStatus2, height2] = mComposerClient->getDisplayAttribute(
+ display.getDisplayId(), configs[j], DisplayAttribute::HEIGHT);
+ const auto& [vsyncPeriodStatus2, vsyncPeriod2] =
+ mComposerClient->getDisplayAttribute(display.getDisplayId(), configs[j],
+ DisplayAttribute::VSYNC_PERIOD);
+ const auto& [groupStatus2, group2] = mComposerClient->getDisplayAttribute(
+ display.getDisplayId(), configs[j], DisplayAttribute::CONFIG_GROUP);
+
+ ASSERT_FALSE(width1 == width2 && height1 == height2 &&
+ vsyncPeriod1 == vsyncPeriod2 && group1 == group2);
+ }
+ }
+ }
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerAidlCommandTest);
INSTANTIATE_TEST_SUITE_P(
PerInstance, GraphicsComposerAidlCommandTest,
diff --git a/radio/aidl/Android.bp b/radio/aidl/Android.bp
index 98eafc1..c0609d8 100644
--- a/radio/aidl/Android.bp
+++ b/radio/aidl/Android.bp
@@ -15,7 +15,7 @@
stability: "vintf",
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
@@ -39,7 +39,7 @@
imports: ["android.hardware.radio"],
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
@@ -63,7 +63,7 @@
imports: ["android.hardware.radio"],
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
@@ -87,7 +87,7 @@
imports: ["android.hardware.radio"],
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
@@ -111,7 +111,7 @@
imports: ["android.hardware.radio"],
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
@@ -135,7 +135,7 @@
imports: ["android.hardware.radio"],
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
@@ -162,7 +162,7 @@
],
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
@@ -189,7 +189,7 @@
imports: ["android.hardware.radio"],
backend: {
cpp: {
- enabled: false,
+ enabled: true,
},
java: {
sdk_version: "module_current",
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyMode.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyMode.aidl
new file mode 100644
index 0000000..071e6b5
--- /dev/null
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyMode.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+@Backing(type="int") @JavaDerive(toString=true) @VintfStability
+enum EmergencyMode {
+ EMERGENCY_WWAN = 1,
+ EMERGENCY_WLAN = 2,
+ EMERGENCY_CALLBACK = 3,
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyNetworkScanTrigger.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyNetworkScanTrigger.aidl
new file mode 100644
index 0000000..2797aff
--- /dev/null
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyNetworkScanTrigger.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+@JavaDerive(toString=true) @VintfStability
+parcelable EmergencyNetworkScanTrigger {
+ android.hardware.radio.AccessNetwork[] accessNetwork;
+ android.hardware.radio.network.EmergencyScanType scanType;
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
new file mode 100644
index 0000000..cb598f3
--- /dev/null
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+@JavaDerive(toString=true) @VintfStability
+parcelable EmergencyRegResult {
+ android.hardware.radio.AccessNetwork accessNetwork;
+ android.hardware.radio.network.RegState regState;
+ android.hardware.radio.network.Domain emcDomain;
+ boolean isEmcBearerSupported;
+ byte nwProvidedEmc;
+ byte nwProvidedEmf;
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyScanType.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyScanType.aidl
new file mode 100644
index 0000000..5e86c76
--- /dev/null
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyScanType.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+@Backing(type="int") @JavaDerive(toString=true) @VintfStability
+enum EmergencyScanType {
+ NO_PREFERENCE = 0,
+ LIMITED_SERVICE = 1,
+ FULL_SERVICE = 2,
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
index 2b70e45..832738f 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
@@ -70,4 +70,8 @@
oneway void supplyNetworkDepersonalization(in int serial, in String netPin);
oneway void setUsageSetting(in int serial, in android.hardware.radio.network.UsageSetting usageSetting);
oneway void getUsageSetting(in int serial);
+ oneway void setEmergencyMode(int serial, in android.hardware.radio.network.EmergencyMode emcModeType);
+ oneway void triggerEmergencyNetworkScan(int serial, in android.hardware.radio.network.EmergencyNetworkScanTrigger request);
+ oneway void cancelEmergencyNetworkScan(in int serial);
+ oneway void exitEmergencyMode(in int serial);
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
index bd03c51..0f017ea 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -48,4 +48,5 @@
oneway void restrictedStateChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.PhoneRestrictedState state);
oneway void suppSvcNotify(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.SuppSvcNotification suppSvc);
oneway void voiceRadioTechChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.RadioTechnology rat);
+ oneway void emergencyNetworkScanResult(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.EmergencyRegResult result);
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
index 5f6c736..24d587e 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
@@ -69,4 +69,8 @@
oneway void supplyNetworkDepersonalizationResponse(in android.hardware.radio.RadioResponseInfo info, in int remainingRetries);
oneway void setUsageSettingResponse(in android.hardware.radio.RadioResponseInfo info);
oneway void getUsageSettingResponse(in android.hardware.radio.RadioResponseInfo info, in android.hardware.radio.network.UsageSetting usageSetting);
+ oneway void setEmergencyModeResponse(in android.hardware.radio.RadioResponseInfo info, in android.hardware.radio.network.EmergencyRegResult regState);
+ oneway void triggerEmergencyNetworkScanResponse(in android.hardware.radio.RadioResponseInfo info);
+ oneway void exitEmergencyModeResponse(in android.hardware.radio.RadioResponseInfo info);
+ oneway void cancelEmergencyNetworkScanResponse(in android.hardware.radio.RadioResponseInfo info);
}
diff --git a/radio/aidl/android/hardware/radio/messaging/GsmSmsMessage.aidl b/radio/aidl/android/hardware/radio/messaging/GsmSmsMessage.aidl
index ee62d95..b256c9a 100644
--- a/radio/aidl/android/hardware/radio/messaging/GsmSmsMessage.aidl
+++ b/radio/aidl/android/hardware/radio/messaging/GsmSmsMessage.aidl
@@ -27,6 +27,7 @@
/**
* SMS in PDU format as an ASCII hex string less the SMSC address.
* TP-Layer-Length is be "strlen(pdu)/2
+ * TP - MessageRef field of pdu must not be modified by modem
*/
String pdu;
}
diff --git a/radio/aidl/android/hardware/radio/network/EmergencyMode.aidl b/radio/aidl/android/hardware/radio/network/EmergencyMode.aidl
new file mode 100644
index 0000000..25031a9
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/EmergencyMode.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+
+@VintfStability
+@Backing(type="int")
+@JavaDerive(toString=true)
+enum EmergencyMode {
+ /**
+ * Mode Type Emergency WWAN, indicates that the current domain selected for the Emergency call
+ * is cellular.
+ */
+ EMERGENCY_WWAN = 1,
+
+ /**
+ * Mode Type Emergency WLAN, indicates that the current domain selected for the Emergency call
+ * is WLAN/WIFI.
+ */
+ EMERGENCY_WLAN = 2,
+
+ /**
+ * Mode Type Emergency Callback, indicates that the current mode set request is for Emergency
+ * callback.
+ */
+ EMERGENCY_CALLBACK = 3,
+}
diff --git a/radio/aidl/android/hardware/radio/network/EmergencyNetworkScanTrigger.aidl b/radio/aidl/android/hardware/radio/network/EmergencyNetworkScanTrigger.aidl
new file mode 100644
index 0000000..0a22e4c
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/EmergencyNetworkScanTrigger.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+import android.hardware.radio.AccessNetwork;
+import android.hardware.radio.network.EmergencyScanType;
+
+@VintfStability
+@JavaDerive(toString=true)
+parcelable EmergencyNetworkScanTrigger{
+ /**
+ * Access network to be prioritized during emergency scan. The 1st entry has the highest
+ * priority.
+ */
+ AccessNetwork[] accessNetwork;
+
+ /**
+ * Scan type indicates the type of scans to be performed i.e. limited scan, full service scan or
+ * any scan.
+ */
+ EmergencyScanType scanType;
+}
diff --git a/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl b/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
new file mode 100644
index 0000000..cf5caa4
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+import android.hardware.radio.AccessNetwork;
+import android.hardware.radio.network.RegState;
+import android.hardware.radio.network.Domain;
+
+@VintfStability
+@JavaDerive(toString=true)
+parcelable EmergencyRegResult {
+ /**
+ * Indicates the cellular access network of the current emergency capable system.
+ */
+ AccessNetwork accessNetwork;
+
+ /**
+ * Registration state of the current emergency capable system.
+ */
+ RegState regState;
+
+ /**
+ * EMC domain indicates the current domain of the acquired system.
+ */
+ Domain emcDomain;
+
+ /**
+ * This indicates if camped network support VoLTE emergency bearers.
+ * This should only be set if the UE is in LTE mode.
+ */
+ boolean isEmcBearerSupported;
+
+ /**
+ * The value of the network provided EMC 5G Registration ACCEPT.
+ * This should be set only if the UE is in 5G mode.
+ */
+ byte nwProvidedEmc;
+
+ /**
+ * The value of the network provided EMF ( EPS Fallback) in 5G Registration ACCEPT.
+ * This should not be set if UE is not in 5G mode.
+ */
+ byte nwProvidedEmf;
+}
diff --git a/radio/aidl/android/hardware/radio/network/EmergencyScanType.aidl b/radio/aidl/android/hardware/radio/network/EmergencyScanType.aidl
new file mode 100644
index 0000000..72c5490
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/EmergencyScanType.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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.radio.network;
+
+@VintfStability
+@Backing(type="int")
+@JavaDerive(toString=true)
+enum EmergencyScanType {
+ /**
+ * Scan Type No Preference, indicates that the modem can scan for emergency
+ * service as per modem’s implementation.
+ */
+ NO_PREFERENCE = 0,
+
+ /**
+ * Scan Type limited, indicates that the modem will scan for
+ * emergency service in limited service mode.
+ */
+ LIMITED_SERVICE = 1,
+
+ /**
+ * Scan Type Full Service, indicates that the modem will scan for
+ * emergency service in Full service mode.
+ */
+ FULL_SERVICE = 2,
+}
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
index cce52ff..0ac8b0e 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
@@ -27,6 +27,8 @@
import android.hardware.radio.network.RadioBandMode;
import android.hardware.radio.network.SignalThresholdInfo;
import android.hardware.radio.network.UsageSetting;
+import android.hardware.radio.network.EmergencyNetworkScanTrigger;
+import android.hardware.radio.network.EmergencyMode;
/**
* This interface is used by telephony and telecom to talk to cellular radio for network APIs.
@@ -437,4 +439,44 @@
* @param serial Serial number of request.
*/
oneway void getUsageSetting(in int serial);
+
+ /**
+ * Set the Emergency Mode
+ *
+ * @param serial Serial number of the request.
+ * @param emcModeType Defines the radio emergency mode type/radio network required/
+ * type of service to be scanned.
+ *
+ * Response function is IRadioEmergencyResponse.setEmergencyModeResponse()
+ */
+ void setEmergencyMode(int serial, in EmergencyMode emcModeType );
+
+ /**
+ * Triggers an Emergency network scan.
+ *
+ * @param serial Serial number of the request.
+ * @param request Defines the radio target networks/preferred network/
+ * Max Scan Time and type of service to be scanned.
+ *
+ * Response function is IRadioEmergencyResponse.triggerEmergencyNetworkScanResponse()
+ */
+ void triggerEmergencyNetworkScan( int serial, in EmergencyNetworkScanTrigger request);
+
+ /**
+ * Cancels ongoing Emergency network scan
+ *
+ * @param serial Serial number of the request.
+ *
+ * Response function is IRadioEmergencyResponse.cancelEmergencyNetworkScan()
+ */
+ void cancelEmergencyNetworkScan(in int serial);
+
+ /**
+ * Exits ongoing Emergency Mode
+ *
+ * @param serial Serial number of the request.
+ *
+ * Response function is IRadioEmergencyResponse.exitEmergencyModeResponse()
+ */
+ void exitEmergencyMode(in int serial);
}
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
index f471433..47d932d 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -27,6 +27,7 @@
import android.hardware.radio.network.PhysicalChannelConfig;
import android.hardware.radio.network.SignalStrength;
import android.hardware.radio.network.SuppSvcNotification;
+import android.hardware.radio.network.EmergencyRegResult;
/**
* Interface declaring unsolicited radio indications for network APIs.
@@ -190,4 +191,12 @@
* @param rat Current new voice rat
*/
void voiceRadioTechChanged(in RadioIndicationType type, in RadioTechnology rat);
+
+ /**
+ * Emergency Scan Results.
+ *
+ * @param type Type of radio indication
+ * @param result the result of the Emergency Network Scan
+ */
+ void emergencyNetworkScanResult(in RadioIndicationType type, in EmergencyRegResult result);
}
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
index dcf0004..d98a31b 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
@@ -29,6 +29,7 @@
import android.hardware.radio.network.RegStateResult;
import android.hardware.radio.network.SignalStrength;
import android.hardware.radio.network.UsageSetting;
+import android.hardware.radio.network.EmergencyRegResult;
/**
* Interface declaring response functions to solicited radio requests for network APIs.
@@ -572,4 +573,51 @@
* RadioError:SIM_ABSENT
*/
oneway void getUsageSettingResponse(in RadioResponseInfo info, in UsageSetting usageSetting);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ * @param regState the current registration state of the modem.
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:MODEM_ERR
+ * RadioError:INVALID_ARGUMENTS
+ */
+ void setEmergencyModeResponse(in RadioResponseInfo info, in EmergencyRegResult regState);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:MODEM_ERR
+ * RadioError:INVALID_ARGUMENTS
+ */
+ void triggerEmergencyNetworkScanResponse(in RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:MODEM_ERR
+ */
+ void exitEmergencyModeResponse(in RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:MODEM_ERR
+ */
+ void cancelEmergencyNetworkScanResponse(in RadioResponseInfo info);
}
diff --git a/sensors/2.1/default/apex/apex_manifest.json b/sensors/2.1/default/apex/apex_manifest.json
deleted file mode 100644
index 47e45ee..0000000
--- a/sensors/2.1/default/apex/apex_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.hardware.sensors",
- "version": 1
-}
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.rc b/sensors/2.1/default/apex/com.android.hardware.sensors.rc
deleted file mode 100644
index bd245b4..0000000
--- a/sensors/2.1/default/apex/com.android.hardware.sensors.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service vendor.sensors-hal-2-1-mock /apex/com.android.hardware.sensors/bin/hw/android.hardware.sensors@2.1-service.mock
- interface android.hardware.sensors@2.0::ISensors default
- interface android.hardware.sensors@2.1::ISensors default
- class hal
- user system
- group system
- rlimit rtprio 10 10
diff --git a/sensors/2.1/default/apex/file_contexts b/sensors/2.1/default/apex/file_contexts
deleted file mode 100644
index d0095c0..0000000
--- a/sensors/2.1/default/apex/file_contexts
+++ /dev/null
@@ -1,5 +0,0 @@
-(/.*)? u:object_r:vendor_file:s0
-# Permission XMLs
-/etc/permissions(/.*)? u:object_r:vendor_configs_file:s0
-# Service binary
-/bin/hw/android\.hardware\.sensors@2\.1-service\.mock u:object_r:hal_sensors_default_exec:s0
diff --git a/sensors/aidl/default/Android.bp b/sensors/aidl/default/Android.bp
index 49841a4..3c66744 100644
--- a/sensors/aidl/default/Android.bp
+++ b/sensors/aidl/default/Android.bp
@@ -23,6 +23,16 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
+filegroup {
+ name: "sensors-default.rc",
+ srcs: ["sensors-default.rc"],
+}
+
+filegroup {
+ name: "sensors-default.xml",
+ srcs: ["sensors-default.xml"],
+}
+
cc_library_static {
name: "libsensorsexampleimpl",
vendor: true,
@@ -47,8 +57,8 @@
cc_binary {
name: "android.hardware.sensors-service.example",
relative_install_path: "hw",
- init_rc: ["sensors-default.rc"],
- vintf_fragments: ["sensors-default.xml"],
+ init_rc: [":sensors-default.rc"],
+ vintf_fragments: [":sensors-default.xml"],
vendor: true,
shared_libs: [
"libbase",
diff --git a/sensors/2.1/default/apex/Android.bp b/sensors/aidl/default/apex/Android.bp
similarity index 78%
rename from sensors/2.1/default/apex/Android.bp
rename to sensors/aidl/default/apex/Android.bp
index 3345b92..ceb428b 100644
--- a/sensors/2.1/default/apex/Android.bp
+++ b/sensors/aidl/default/apex/Android.bp
@@ -13,9 +13,16 @@
certificate: "com.android.hardware.sensors",
}
+genrule {
+ name: "com.android.hardware.sensors.rc-gen",
+ srcs: [":sensors-default.rc"],
+ out: ["com.android.hardware.sensors.rc"],
+ cmd: "sed -E 's/\\/vendor/\\/apex\\/com.android.hardware.sensors/' $(in) > $(out)",
+}
+
prebuilt_etc {
name: "com.android.hardware.sensors.rc",
- src: "com.android.hardware.sensors.rc",
+ src: ":com.android.hardware.sensors.rc-gen",
installable: false,
}
@@ -31,7 +38,7 @@
updatable: false,
// Install the apex in /vendor/apex
soc_specific: true,
- binaries: ["android.hardware.sensors@2.1-service.mock"],
+ binaries: ["android.hardware.sensors-service.example"],
prebuilts: [
"com.android.hardware.sensors.rc",
"android.hardware.sensor.ambient_temperature.prebuilt.xml",
@@ -42,5 +49,5 @@
"android.hardware.sensor.proximity.prebuilt.xml",
"android.hardware.sensor.relative_humidity.prebuilt.xml",
],
- vintf_fragments: [":android.hardware.sensors@2.1.xml"],
+ vintf_fragments: [":sensors-default.xml"],
}
diff --git a/sensors/aidl/default/apex/apex_manifest.json b/sensors/aidl/default/apex/apex_manifest.json
new file mode 100644
index 0000000..659e739
--- /dev/null
+++ b/sensors/aidl/default/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.hardware.sensors",
+ "version": 1
+}
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.avbpubkey b/sensors/aidl/default/apex/com.android.hardware.sensors.avbpubkey
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.avbpubkey
rename to sensors/aidl/default/apex/com.android.hardware.sensors.avbpubkey
Binary files differ
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.pem b/sensors/aidl/default/apex/com.android.hardware.sensors.pem
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.pem
rename to sensors/aidl/default/apex/com.android.hardware.sensors.pem
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.pk8 b/sensors/aidl/default/apex/com.android.hardware.sensors.pk8
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.pk8
rename to sensors/aidl/default/apex/com.android.hardware.sensors.pk8
Binary files differ
diff --git a/sensors/2.1/default/apex/com.android.hardware.sensors.x509.pem b/sensors/aidl/default/apex/com.android.hardware.sensors.x509.pem
similarity index 100%
rename from sensors/2.1/default/apex/com.android.hardware.sensors.x509.pem
rename to sensors/aidl/default/apex/com.android.hardware.sensors.x509.pem
diff --git a/sensors/aidl/default/apex/file_contexts b/sensors/aidl/default/apex/file_contexts
new file mode 100644
index 0000000..27be16b
--- /dev/null
+++ b/sensors/aidl/default/apex/file_contexts
@@ -0,0 +1,5 @@
+(/.*)? u:object_r:vendor_file:s0
+# Permission XMLs
+/etc/permissions(/.*)? u:object_r:vendor_configs_file:s0
+# Service binary
+/bin/hw/android\.hardware\.sensors-service\.example u:object_r:hal_sensors_default_exec:s0
\ No newline at end of file
diff --git a/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc b/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc
index 3f91a0a..1edfbec 100644
--- a/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc
+++ b/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc
@@ -1,7 +1,7 @@
service vendor.sensors-hal-multihal /vendor/bin/hw/android.hardware.sensors-service.multihal
class hal
user system
- group system wakelock context_hub
+ group system wakelock context_hub input
task_profiles ServiceCapacityLow
capabilities BLOCK_SUSPEND
rlimit rtprio 10 10
\ No newline at end of file
diff --git a/tv/tuner/aidl/default/Dvr.cpp b/tv/tuner/aidl/default/Dvr.cpp
index c591d07..9928a59 100644
--- a/tv/tuner/aidl/default/Dvr.cpp
+++ b/tv/tuner/aidl/default/Dvr.cpp
@@ -427,7 +427,7 @@
map<int64_t, std::shared_ptr<IFilter>>::iterator it;
// Handle the output data per filter type
for (it = mFilters.begin(); it != mFilters.end(); it++) {
- if (mDemux->startFilterHandler(it->first).isOk()) {
+ if (!mDemux->startFilterHandler(it->first).isOk()) {
return false;
}
}
diff --git a/tv/tuner/aidl/default/Filter.cpp b/tv/tuner/aidl/default/Filter.cpp
index 769ebe2..ade265c 100644
--- a/tv/tuner/aidl/default/Filter.cpp
+++ b/tv/tuner/aidl/default/Filter.cpp
@@ -432,12 +432,12 @@
if (mSharedAvMemHandle != nullptr) {
*out_avMemory = ::android::dupToAidl(mSharedAvMemHandle);
- *_aidl_return = BUFFER_SIZE_16M;
+ *_aidl_return = BUFFER_SIZE;
mUsingSharedAvMem = true;
return ::ndk::ScopedAStatus::ok();
}
- int av_fd = createAvIonFd(BUFFER_SIZE_16M);
+ int av_fd = createAvIonFd(BUFFER_SIZE);
if (av_fd < 0) {
return ::ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(Result::OUT_OF_MEMORY));
@@ -454,7 +454,7 @@
mUsingSharedAvMem = true;
*out_avMemory = ::android::dupToAidl(mSharedAvMemHandle);
- *_aidl_return = BUFFER_SIZE_16M;
+ *_aidl_return = BUFFER_SIZE;
return ::ndk::ScopedAStatus::ok();
}
@@ -793,7 +793,8 @@
}
if (prefix == 0x000001) {
// TODO handle mulptiple Pes filters
- mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9];
+ mPesSizeLeft = (static_cast<uint8_t>(mFilterOutput[i + 8]) << 8) |
+ static_cast<uint8_t>(mFilterOutput[i + 9]);
mPesSizeLeft += 6;
if (DEBUG_FILTER) {
ALOGD("[Filter] pes data length %d", mPesSizeLeft);
@@ -803,7 +804,7 @@
}
}
- int endPoint = min(184, mPesSizeLeft);
+ uint32_t endPoint = min(184u, mPesSizeLeft);
// append data and check size
vector<int8_t>::const_iterator first = mFilterOutput.begin() + i + 4;
vector<int8_t>::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint;
@@ -852,11 +853,17 @@
return ::ndk::ScopedAStatus::ok();
}
+// Read PES (Packetized Elementary Stream) Packets from TransportStreams
+// as defined in ISO/IEC 13818-1 Section 2.4.3.6. Create MediaEvents
+// containing only their data without TS or PES headers.
::ndk::ScopedAStatus Filter::startMediaFilterHandler() {
if (mFilterOutput.empty()) {
return ::ndk::ScopedAStatus::ok();
}
+ // mPts being set before our MediaFilterHandler begins indicates that all
+ // metadata has already been handled. We can therefore create an event
+ // with the existing data. This method is used when processing ES files.
::ndk::ScopedAStatus result;
if (mPts) {
result = createMediaFilterEventWithIon(mFilterOutput);
@@ -867,16 +874,38 @@
}
for (int i = 0; i < mFilterOutput.size(); i += 188) {
+ // Every packet has a 4 Byte TS Header preceding it
+ uint32_t headerSize = 4;
+
if (mPesSizeLeft == 0) {
- uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
- mFilterOutput[i + 6];
+ // Packet Start Code Prefix is defined as the first 3 bytes of
+ // the PES Header and should always have the value 0x000001
+ uint32_t prefix = (static_cast<uint8_t>(mFilterOutput[i + 4]) << 16) |
+ (static_cast<uint8_t>(mFilterOutput[i + 5]) << 8) |
+ static_cast<uint8_t>(mFilterOutput[i + 6]);
if (DEBUG_FILTER) {
ALOGD("[Filter] prefix %d", prefix);
}
if (prefix == 0x000001) {
- // TODO handle mulptiple Pes filters
- mPesSizeLeft = (mFilterOutput[i + 8] << 8) | mFilterOutput[i + 9];
- mPesSizeLeft += 6;
+ // TODO handle multiple Pes filters
+ // Location of PES fields from ISO/IEC 13818-1 Section 2.4.3.6
+ mPesSizeLeft = (static_cast<uint8_t>(mFilterOutput[i + 8]) << 8) |
+ static_cast<uint8_t>(mFilterOutput[i + 9]);
+ bool hasPts = static_cast<uint8_t>(mFilterOutput[i + 11]) & 0x80;
+ uint8_t optionalFieldsLength = static_cast<uint8_t>(mFilterOutput[i + 12]);
+ headerSize += 9 + optionalFieldsLength;
+
+ if (hasPts) {
+ // Pts is a 33-bit field which is stored across 5 bytes, with
+ // bits in between as reserved fields which must be ignored
+ mPts = 0;
+ mPts |= (static_cast<uint8_t>(mFilterOutput[i + 13]) & 0x0e) << 29;
+ mPts |= (static_cast<uint8_t>(mFilterOutput[i + 14]) & 0xff) << 22;
+ mPts |= (static_cast<uint8_t>(mFilterOutput[i + 15]) & 0xfe) << 14;
+ mPts |= (static_cast<uint8_t>(mFilterOutput[i + 16]) & 0xff) << 7;
+ mPts |= (static_cast<uint8_t>(mFilterOutput[i + 17]) & 0xfe) >> 1;
+ }
+
if (DEBUG_FILTER) {
ALOGD("[Filter] pes data length %d", mPesSizeLeft);
}
@@ -885,10 +914,10 @@
}
}
- int endPoint = min(184, mPesSizeLeft);
+ uint32_t endPoint = min(188u - headerSize, mPesSizeLeft);
// append data and check size
- vector<int8_t>::const_iterator first = mFilterOutput.begin() + i + 4;
- vector<int8_t>::const_iterator last = mFilterOutput.begin() + i + 4 + endPoint;
+ vector<int8_t>::const_iterator first = mFilterOutput.begin() + i + headerSize;
+ vector<int8_t>::const_iterator last = mFilterOutput.begin() + i + headerSize + endPoint;
mPesOutput.insert(mPesOutput.end(), first, last);
// size does not match then continue
mPesSizeLeft -= endPoint;
@@ -900,7 +929,8 @@
}
result = createMediaFilterEventWithIon(mPesOutput);
- if (result.isOk()) {
+ if (!result.isOk()) {
+ mFilterOutput.clear();
return result;
}
}
@@ -961,24 +991,65 @@
return ::ndk::ScopedAStatus::ok();
}
+// Read PSI (Program Specific Information) Sections from TransportStreams
+// as defined in ISO/IEC 13818-1 Section 2.4.4
bool Filter::writeSectionsAndCreateEvent(vector<int8_t>& data) {
// TODO check how many sections has been read
ALOGD("[Filter] section handler");
- if (!writeDataToFilterMQ(data)) {
- return false;
- }
- DemuxFilterSectionEvent secEvent;
- secEvent = {
- // temp dump meta data
- .tableId = 0,
- .version = 1,
- .sectionNum = 1,
- .dataLength = static_cast<int32_t>(data.size()),
- };
- {
- std::lock_guard<std::mutex> lock(mFilterEventsLock);
- mFilterEvents.push_back(DemuxFilterEvent::make<DemuxFilterEvent::Tag::section>(secEvent));
+ // Transport Stream Packets are 188 bytes long, as defined in the
+ // Introduction of ISO/IEC 13818-1
+ for (int i = 0; i < data.size(); i += 188) {
+ if (mSectionSizeLeft == 0) {
+ // Location for sectionSize as defined by Section 2.4.4
+ // Note that the first 4 bytes skipped are the TsHeader
+ mSectionSizeLeft = ((static_cast<uint8_t>(data[i + 5]) & 0x0f) << 8) |
+ static_cast<uint8_t>(data[i + 6]);
+ mSectionSizeLeft += 3;
+ if (DEBUG_FILTER) {
+ ALOGD("[Filter] section data length %d", mSectionSizeLeft);
+ }
+ }
+
+ // 184 bytes per packet is derived by subtracting the 4 byte length of
+ // the TsHeader from its 188 byte packet size
+ uint32_t endPoint = min(184u, mSectionSizeLeft);
+ // append data and check size
+ vector<int8_t>::const_iterator first = data.begin() + i + 4;
+ vector<int8_t>::const_iterator last = data.begin() + i + 4 + endPoint;
+ mSectionOutput.insert(mSectionOutput.end(), first, last);
+ // size does not match then continue
+ mSectionSizeLeft -= endPoint;
+ if (DEBUG_FILTER) {
+ ALOGD("[Filter] section data left %d", mSectionSizeLeft);
+ }
+ if (mSectionSizeLeft > 0) {
+ continue;
+ }
+
+ if (!writeDataToFilterMQ(mSectionOutput)) {
+ mSectionOutput.clear();
+ return false;
+ }
+
+ DemuxFilterSectionEvent secEvent;
+ secEvent = {
+ // temp dump meta data
+ .tableId = 0,
+ .version = 1,
+ .sectionNum = 1,
+ .dataLength = static_cast<int32_t>(mSectionOutput.size()),
+ };
+ if (DEBUG_FILTER) {
+ ALOGD("[Filter] assembled section data length %" PRIu64, secEvent.dataLength);
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mFilterEventsLock);
+ mFilterEvents.push_back(
+ DemuxFilterEvent::make<DemuxFilterEvent::Tag::section>(secEvent));
+ }
+ mSectionOutput.clear();
}
return true;
@@ -1168,7 +1239,7 @@
mediaEvent.isPesPrivateData = true;
mediaEvent.extraMetaData.set<DemuxFilterMediaEventExtraMetaData::Tag::audio>(audio);
- int av_fd = createAvIonFd(BUFFER_SIZE_16M);
+ int av_fd = createAvIonFd(BUFFER_SIZE);
if (av_fd == -1) {
return;
}
diff --git a/tv/tuner/aidl/default/Filter.h b/tv/tuner/aidl/default/Filter.h
index e301249..b638f0c 100644
--- a/tv/tuner/aidl/default/Filter.h
+++ b/tv/tuner/aidl/default/Filter.h
@@ -50,7 +50,9 @@
using ::android::hardware::EventFlag;
using FilterMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
-const uint32_t BUFFER_SIZE_16M = 0x1000000;
+// Large buffer size can lead to sudden crashes due to being de-allocated
+// by the memory management system. Change the buffer size when needed.
+const uint32_t BUFFER_SIZE = 0x800000; // 8 MB
class Demux;
class Dvr;
@@ -256,9 +258,13 @@
std::mutex mFilterOutputLock;
std::mutex mRecordFilterOutputLock;
+ // handle single Section filter
+ uint32_t mSectionSizeLeft = 0;
+ vector<int8_t> mSectionOutput;
+
// temp handle single PES filter
// TODO handle mulptiple Pes filters
- int mPesSizeLeft = 0;
+ uint32_t mPesSizeLeft = 0;
vector<int8_t> mPesOutput;
// A map from data id to ion handle
diff --git a/tv/tuner/aidl/default/tuner-default.rc b/tv/tuner/aidl/default/tuner-default.rc
index d0248c2..661c219 100644
--- a/tv/tuner/aidl/default/tuner-default.rc
+++ b/tv/tuner/aidl/default/tuner-default.rc
@@ -3,5 +3,5 @@
user media
group mediadrm drmrpc
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
+ task_profiles ProcessCapacityHigh
onrestart restart media.tuner
diff --git a/tv/tuner/aidl/vts/functional/FrontendTests.cpp b/tv/tuner/aidl/vts/functional/FrontendTests.cpp
index a1f51df..2ff0c3d 100644
--- a/tv/tuner/aidl/vts/functional/FrontendTests.cpp
+++ b/tv/tuner/aidl/vts/functional/FrontendTests.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include <aidl/android/hardware/tv/tuner/Result.h>
-
#include "FrontendTests.h"
+#include <aidl/android/hardware/tv/tuner/Result.h>
+
ndk::ScopedAStatus FrontendCallback::onEvent(FrontendEventType frontendEventType) {
android::Mutex::Autolock autoLock(mMsgLock);
ALOGD("[vts] frontend event received. Type: %d", frontendEventType);
@@ -323,7 +323,10 @@
FrontendStatusType type = statusTypes[i];
switch (type) {
case FrontendStatusType::MODULATIONS: {
- // TODO: verify modulations
+ ASSERT_TRUE(std::equal(
+ realStatuses[i].get<FrontendStatus::Tag::modulations>().begin(),
+ realStatuses[i].get<FrontendStatus::Tag::modulations>().end(),
+ expectStatuses[i].get<FrontendStatus::Tag::modulations>().begin()));
break;
}
case FrontendStatusType::BERS: {
@@ -340,11 +343,13 @@
break;
}
case FrontendStatusType::GUARD_INTERVAL: {
- // TODO: verify interval
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::interval>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::interval>());
break;
}
case FrontendStatusType::TRANSMISSION_MODE: {
- // TODO: verify tranmission mode
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::transmissionMode>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::transmissionMode>());
break;
}
case FrontendStatusType::UEC: {
@@ -379,7 +384,8 @@
break;
}
case FrontendStatusType::ROLL_OFF: {
- // TODO: verify roll off
+ ASSERT_TRUE(realStatuses[i].get<FrontendStatus::Tag::rollOff>() ==
+ expectStatuses[i].get<FrontendStatus::Tag::rollOff>());
break;
}
case FrontendStatusType::IS_MISO: {
@@ -599,9 +605,10 @@
ASSERT_TRUE(tuneFrontend(frontendConf, false /*testWithDemux*/));
// TODO: find a better way to push all frontend status types
- for (int32_t i = 0; i < static_cast<int32_t>(FrontendStatusType::ATSC3_ALL_PLP_INFO); i++) {
+ for (int32_t i = 0; i <= static_cast<int32_t>(FrontendStatusType::ATSC3_ALL_PLP_INFO); i++) {
allTypes.push_back(static_cast<FrontendStatusType>(i));
}
+
ndk::ScopedAStatus status = mFrontend->getFrontendStatusReadiness(allTypes, &readiness);
ASSERT_TRUE(status.isOk());
ASSERT_TRUE(readiness.size() == allTypes.size());
diff --git a/tv/tuner/aidl/vts/functional/LnbTests.h b/tv/tuner/aidl/vts/functional/LnbTests.h
index d6b5a25..a21e68d 100644
--- a/tv/tuner/aidl/vts/functional/LnbTests.h
+++ b/tv/tuner/aidl/vts/functional/LnbTests.h
@@ -25,6 +25,8 @@
#include <utils/Mutex.h>
#include <map>
+#define INVALID_LNB_ID -1
+
using android::Condition;
using android::Mutex;
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index 07e3e3c..9a17d05 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -129,8 +129,8 @@
mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
- if (mLnbId) {
- ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
+ if (mLnbId != INVALID_LNB_ID) {
+ ASSERT_TRUE(mFrontendTests.setLnb(mLnbId));
}
if (frontendConf.isSoftwareFe) {
mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
@@ -162,18 +162,19 @@
ASSERT_TRUE(mLnbTests.getLnbIds(ids));
ASSERT_TRUE(ids.size() > 0);
ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
- mLnbId = &ids[0];
+ mLnbId = ids[0];
} else {
- mLnbId = (int32_t*)malloc(sizeof(int32_t));
- ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, mLnbId));
}
ASSERT_TRUE(mLnbTests.setLnbCallback());
ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
- broadcastSingleFilterTest(filterConf, frontendConf);
+ if (!frontendConf.isSoftwareFe) {
+ broadcastSingleFilterTest(filterConf, frontendConf);
+ }
ASSERT_TRUE(mLnbTests.closeLnb());
- mLnbId = nullptr;
+ mLnbId = INVALID_LNB_ID;
}
void TunerBroadcastAidlTest::mediaFilterUsingSharedMemoryTest(FilterConfig filterConf,
@@ -248,10 +249,9 @@
ASSERT_TRUE(mLnbTests.getLnbIds(ids));
ASSERT_TRUE(ids.size() > 0);
ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
- mLnbId = &ids[0];
+ mLnbId = ids[0];
} else {
- mLnbId = (int32_t*)malloc(sizeof(int32_t));
- ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, mLnbId));
}
ASSERT_TRUE(mLnbTests.setLnbCallback());
ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
@@ -260,9 +260,11 @@
for (auto msgName : lnbRecord.diseqcMsgs) {
ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
}
- recordSingleFilterTest(filterConf, frontendConf, dvrConf);
+ if (!frontendConf.isSoftwareFe) {
+ recordSingleFilterTest(filterConf, frontendConf, dvrConf, Dataflow_Context::LNBRECORD);
+ }
ASSERT_TRUE(mLnbTests.closeLnb());
- mLnbId = nullptr;
+ mLnbId = INVALID_LNB_ID;
}
void TunerRecordAidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
@@ -318,29 +320,47 @@
}
void TunerRecordAidlTest::recordSingleFilterTest(FilterConfig filterConf,
- FrontendConfig frontendConf, DvrConfig dvrConf) {
+ FrontendConfig frontendConf, DvrConfig dvrConf,
+ Dataflow_Context context) {
int32_t demuxId;
std::shared_ptr<IDemux> demux;
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
mDvrTests.setDemux(demux);
DvrConfig dvrSourceConfig;
- if (record.hasFrontendConnection) {
+ if (context == Dataflow_Context::RECORD) {
+ if (record.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDvrTests(&mDvrTests);
+ } else {
+ dvrSourceConfig = dvrMap[record.dvrSourceId];
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+ } else if (context == Dataflow_Context::LNBRECORD) {
+ // If function arrives here, frontend should not be software, so no need to configure a dvr
+ // source or dvr fe connection that might be used for recording without an Lnb
int32_t feId;
mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
- if (frontendConf.isSoftwareFe) {
- mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+ if (mLnbId != INVALID_LNB_ID) {
+ ASSERT_TRUE(mFrontendTests.setLnb(mLnbId));
+ } else {
+ FAIL();
}
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFrontendTests.setDvrTests(&mDvrTests);
- } else {
- dvrSourceConfig = dvrMap[record.dvrSourceId];
- ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
- ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
- ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
}
int64_t filterId;
@@ -360,24 +380,31 @@
ASSERT_TRUE(mDvrTests.startDvrRecord());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
- if (record.hasFrontendConnection) {
+ if (context == Dataflow_Context::RECORD) {
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ } else {
+ // Start DVR Source
+ mDvrTests.startPlaybackInputThread(
+ dvrSourceConfig.playbackInputFile,
+ dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ }
+ } else if (context == Dataflow_Context::LNBRECORD) {
ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
- } else {
- // Start DVR Source
- mDvrTests.startPlaybackInputThread(
- dvrSourceConfig.playbackInputFile,
- dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
- ASSERT_TRUE(mDvrTests.startDvrPlayback());
}
-
mDvrTests.testRecordOutput();
mDvrTests.stopRecordThread();
- if (record.hasFrontendConnection) {
+ if (context == Dataflow_Context::RECORD) {
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ } else {
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ }
+ } else if (context == Dataflow_Context::LNBRECORD) {
ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
- } else {
- mDvrTests.stopPlaybackThread();
- ASSERT_TRUE(mDvrTests.stopDvrPlayback());
}
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
@@ -386,10 +413,14 @@
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
mDvrTests.closeDvrRecord();
- if (record.hasFrontendConnection) {
+ if (context == Dataflow_Context::RECORD) {
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ } else {
+ mDvrTests.closeDvrPlayback();
+ }
+ } else if (context == Dataflow_Context::LNBRECORD) {
ASSERT_TRUE(mFrontendTests.closeFrontend());
- } else {
- mDvrTests.closeDvrPlayback();
}
ASSERT_TRUE(mDemuxTests.closeDemux());
@@ -397,28 +428,45 @@
void TunerDescramblerAidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
FrontendConfig frontendConf,
- DescramblerConfig descConfig) {
+ DescramblerConfig descConfig,
+ Dataflow_Context context) {
int32_t demuxId;
std::shared_ptr<IDemux> demux;
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
DvrConfig dvrSourceConfig;
- if (descrambling.hasFrontendConnection) {
+ if (context == Dataflow_Context::DESCRAMBLING) {
+ if (descrambling.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+ }
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDemux(demux);
+ } else {
+ dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
+ mDvrTests.setDemux(demux);
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+ } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
int32_t feId;
mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
- if (frontendConf.isSoftwareFe) {
- mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+ if (mLnbId != INVALID_LNB_ID) {
+ ASSERT_TRUE(mFrontendTests.setLnb(mLnbId));
+ } else {
+ // If, for some reason, the test got here without failing. We fail it here.
+ ALOGD("mLnbId is null. Something went wrong. Exiting ScrambledBroadcastWithLnbId.");
+ FAIL();
}
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFrontendTests.setDemux(demux);
- } else {
- dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
- mDvrTests.setDemux(demux);
- ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
- ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
- ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
}
set<int64_t> filterIds;
@@ -449,24 +497,32 @@
ASSERT_TRUE(mFilterTests.startFilter(*id));
}
- if (descrambling.hasFrontendConnection) {
- // tune test
+ if (context == Dataflow_Context::DESCRAMBLING) {
+ if (descrambling.hasFrontendConnection) {
+ // tune test
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+ } else {
+ // Start DVR Source
+ mDvrTests.startPlaybackInputThread(
+ dvrSourceConfig.playbackInputFile,
+ dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ }
+ } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
- } else {
- // Start DVR Source
- mDvrTests.startPlaybackInputThread(
- dvrSourceConfig.playbackInputFile,
- dvrSourceConfig.settings.get<DvrSettings::Tag::playback>());
- ASSERT_TRUE(mDvrTests.startDvrPlayback());
}
ASSERT_TRUE(filterDataOutputTest());
- if (descrambling.hasFrontendConnection) {
+ if (context == Dataflow_Context::DESCRAMBLING) {
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+ } else {
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ }
+ } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
- } else {
- mDvrTests.stopPlaybackThread();
- ASSERT_TRUE(mDvrTests.stopDvrPlayback());
}
for (id = filterIds.begin(); id != filterIds.end(); id++) {
@@ -480,37 +536,80 @@
ASSERT_TRUE(mFilterTests.closeFilter(*id));
}
- if (descrambling.hasFrontendConnection) {
+ if (context == Dataflow_Context::DESCRAMBLING) {
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ } else {
+ mDvrTests.closeDvrPlayback();
+ }
+ } else if (context == Dataflow_Context::LNBDESCRAMBLING) {
ASSERT_TRUE(mFrontendTests.closeFrontend());
- } else {
- mDvrTests.closeDvrPlayback();
}
ASSERT_TRUE(mDemuxTests.closeDemux());
}
+void TunerDescramblerAidlTest::scrambledBroadcastTestWithLnb(
+ set<struct FilterConfig>& mediaFilterConfs, FrontendConfig& frontendConf,
+ DescramblerConfig& descConfig, LnbConfig& lnbConfig) {
+ // We can test the Lnb individually and make sure it functions properly. If the frontend is
+ // software, we cannot test the whole dataflow. If the frontend is hardware, we can
+ if (lnbConfig.name.compare(emptyHardwareId) == 0) {
+ vector<int32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ mLnbId = ids[0];
+ } else {
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbConfig.name, mLnbId));
+ }
+ // Once Lnb is opened, test some of its basic functionality
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbConfig.voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbConfig.tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConfig.position));
+ if (!frontendConf.isSoftwareFe) {
+ ALOGD("Frontend is not software, testing entire dataflow.");
+ scrambledBroadcastTest(mediaFilterConfs, frontendConf, descConfig,
+ Dataflow_Context::LNBDESCRAMBLING);
+ } else {
+ ALOGD("Frontend is software, did not test the entire dataflow, but tested the Lnb "
+ "individually.");
+ }
+ ASSERT_TRUE(mLnbTests.closeLnb());
+ mLnbId = INVALID_LNB_ID;
+}
+
TEST_P(TunerLnbAidlTest, SendDiseqcMessageToLnb) {
description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
if (!lnbLive.support) {
return;
}
- if (lnbMap[lnbLive.lnbId].name.compare(emptyHardwareId) == 0) {
- vector<int32_t> ids;
- ASSERT_TRUE(mLnbTests.getLnbIds(ids));
- ASSERT_TRUE(ids.size() > 0);
- ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
- } else {
- int32_t id;
- ASSERT_TRUE(mLnbTests.openLnbByName(lnbMap[lnbLive.lnbId].name, id));
+ vector<LnbLiveHardwareConnections> lnbLive_configs = generateLnbLiveConfigurations();
+ if (lnbLive_configs.empty()) {
+ ALOGD("No frontends that support satellites.");
+ return;
}
- ASSERT_TRUE(mLnbTests.setLnbCallback());
- ASSERT_TRUE(mLnbTests.setVoltage(lnbMap[lnbLive.lnbId].voltage));
- ASSERT_TRUE(mLnbTests.setTone(lnbMap[lnbLive.lnbId].tone));
- ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbMap[lnbLive.lnbId].position));
- for (auto msgName : lnbLive.diseqcMsgs) {
- ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+ for (auto& combination : lnbLive_configs) {
+ lnbLive = combination;
+ if (lnbMap[lnbLive.lnbId].name.compare(emptyHardwareId) == 0) {
+ vector<int32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ } else {
+ int32_t id;
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbMap[lnbLive.lnbId].name, id));
+ }
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbMap[lnbLive.lnbId].voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbMap[lnbLive.lnbId].tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbMap[lnbLive.lnbId].position));
+ for (auto msgName : lnbLive.diseqcMsgs) {
+ ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+ }
+ ASSERT_TRUE(mLnbTests.closeLnb());
}
- ASSERT_TRUE(mLnbTests.closeLnb());
}
TEST_P(TunerDemuxAidlTest, openDemux) {
@@ -518,17 +617,21 @@
if (!live.hasFrontendConnection) {
return;
}
- int32_t feId;
- int32_t demuxId;
- std::shared_ptr<IDemux> demux;
- mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
- ASSERT_TRUE(feId != INVALID_ID);
- ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
- ASSERT_TRUE(mFrontendTests.setFrontendCallback());
- ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
- ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
- ASSERT_TRUE(mDemuxTests.closeDemux());
- ASSERT_TRUE(mFrontendTests.closeFrontend());
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
}
TEST_P(TunerDemuxAidlTest, getAvSyncTime) {
@@ -536,40 +639,45 @@
if (!live.hasFrontendConnection) {
return;
}
- if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
- return;
- }
- int32_t feId;
- int32_t demuxId;
- std::shared_ptr<IDemux> demux;
- int64_t mediaFilterId;
- int64_t pcrFilterId;
- int32_t avSyncHwId;
- std::shared_ptr<IFilter> mediaFilter;
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
+ continue;
+ }
+ int32_t feId;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ int64_t mediaFilterId;
+ int64_t pcrFilterId;
+ int32_t avSyncHwId;
+ std::shared_ptr<IFilter> mediaFilter;
- mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
- ASSERT_TRUE(feId != INVALID_ID);
- ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
- ASSERT_TRUE(mFrontendTests.setFrontendCallback());
- ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
- ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
- mFilterTests.setDemux(demux);
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.videoFilterId].type,
- filterMap[live.videoFilterId].bufferSize));
- ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(mediaFilterId));
- ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.videoFilterId].settings, mediaFilterId));
- mediaFilter = mFilterTests.getFilterById(mediaFilterId);
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.pcrFilterId].type,
- filterMap[live.pcrFilterId].bufferSize));
- ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(pcrFilterId));
- ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.pcrFilterId].settings, pcrFilterId));
- ASSERT_TRUE(mDemuxTests.getAvSyncId(mediaFilter, avSyncHwId));
- ASSERT_TRUE(pcrFilterId == avSyncHwId);
- ASSERT_TRUE(mDemuxTests.getAvSyncTime(pcrFilterId));
- ASSERT_TRUE(mFilterTests.closeFilter(pcrFilterId));
- ASSERT_TRUE(mFilterTests.closeFilter(mediaFilterId));
- ASSERT_TRUE(mDemuxTests.closeDemux());
- ASSERT_TRUE(mFrontendTests.closeFrontend());
+ mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.videoFilterId].type,
+ filterMap[live.videoFilterId].bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(mediaFilterId));
+ ASSERT_TRUE(
+ mFilterTests.configFilter(filterMap[live.videoFilterId].settings, mediaFilterId));
+ mediaFilter = mFilterTests.getFilterById(mediaFilterId);
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.pcrFilterId].type,
+ filterMap[live.pcrFilterId].bufferSize));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(pcrFilterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.pcrFilterId].settings, pcrFilterId));
+ ASSERT_TRUE(mDemuxTests.getAvSyncId(mediaFilter, avSyncHwId));
+ ASSERT_TRUE(pcrFilterId == avSyncHwId);
+ ASSERT_TRUE(mDemuxTests.getAvSyncTime(pcrFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(pcrFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(mediaFilterId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
}
TEST_P(TunerFilterAidlTest, StartFilterInDemux) {
@@ -578,7 +686,11 @@
return;
}
// TODO use parameterized tests
- configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerFilterAidlTest, ConfigIpFilterInDemuxWithCid) {
@@ -587,10 +699,14 @@
if (!live.hasFrontendConnection) {
return;
}
- if (live.ipFilterId.compare(emptyHardwareId) == 0) {
- return;
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ if (live.ipFilterId.compare(emptyHardwareId) == 0) {
+ continue;
+ }
+ configSingleFilterInDemuxTest(filterMap[live.ipFilterId], frontendMap[live.frontendId]);
}
- configSingleFilterInDemuxTest(filterMap[live.ipFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerFilterAidlTest, ReconfigFilterToReceiveStartId) {
@@ -599,8 +715,13 @@
return;
}
// TODO use parameterized tests
- reconfigSingleFilterInDemuxTest(filterMap[live.videoFilterId], filterMap[live.videoFilterId],
- frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ reconfigSingleFilterInDemuxTest(filterMap[live.videoFilterId],
+ filterMap[live.videoFilterId],
+ frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerFilterAidlTest, SetFilterLinkage) {
@@ -637,7 +758,11 @@
return;
}
// TODO use parameterized tests
- testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
+ auto timeFilter_configs = generateTimeFilterConfigurations();
+ for (auto& configuration : timeFilter_configs) {
+ timeFilter = configuration;
+ testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
+ }
}
static bool isEventProducingFilter(const FilterConfig& filterConfig) {
@@ -839,10 +964,16 @@
TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
description("Feed ts data from playback and configure Ts section filter to get output");
- if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) {
+ if (!playback.support) {
return;
}
- playbackSingleFilterTest(filterMap[playback.sectionFilterId], dvrMap[playback.dvrId]);
+ vector<DvrPlaybackHardwareConnections> playback_configs = generatePlaybackConfigs();
+ for (auto& configuration : playback_configs) {
+ if (configuration.sectionFilterId.compare(emptyHardwareId) != 0) {
+ playback = configuration;
+ playbackSingleFilterTest(filterMap[playback.sectionFilterId], dvrMap[playback.dvrId]);
+ }
+ }
}
TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsAudioFilterTest) {
@@ -850,7 +981,11 @@
if (!playback.support) {
return;
}
- playbackSingleFilterTest(filterMap[playback.audioFilterId], dvrMap[playback.dvrId]);
+ vector<DvrPlaybackHardwareConnections> playback_configs = generatePlaybackConfigs();
+ for (auto& configuration : playback_configs) {
+ playback = configuration;
+ playbackSingleFilterTest(filterMap[playback.audioFilterId], dvrMap[playback.dvrId]);
+ }
}
TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsVideoFilterTest) {
@@ -858,7 +993,11 @@
if (!playback.support) {
return;
}
- playbackSingleFilterTest(filterMap[playback.videoFilterId], dvrMap[playback.dvrId]);
+ vector<DvrPlaybackHardwareConnections> playback_configs = generatePlaybackConfigs();
+ for (auto& configuration : playback_configs) {
+ playback = configuration;
+ playbackSingleFilterTest(filterMap[playback.videoFilterId], dvrMap[playback.dvrId]);
+ }
}
TEST_P(TunerRecordAidlTest, RecordDataFlowWithTsRecordFilterTest) {
@@ -866,8 +1005,12 @@
if (!record.support) {
return;
}
- recordSingleFilterTest(filterMap[record.recordFilterId], frontendMap[record.frontendId],
- dvrMap[record.dvrRecordId]);
+ auto record_configs = generateRecordConfigurations();
+ for (auto& configuration : record_configs) {
+ record = configuration;
+ recordSingleFilterTest(filterMap[record.recordFilterId], frontendMap[record.frontendId],
+ dvrMap[record.dvrRecordId], Dataflow_Context::RECORD);
+ }
}
TEST_P(TunerRecordAidlTest, AttachFiltersToRecordTest) {
@@ -876,8 +1019,13 @@
if (!record.support) {
return;
}
- attachSingleFilterToRecordDvrTest(filterMap[record.recordFilterId],
- frontendMap[record.frontendId], dvrMap[record.dvrRecordId]);
+ auto record_configs = generateRecordConfigurations();
+ for (auto& configuration : record_configs) {
+ record = configuration;
+ attachSingleFilterToRecordDvrTest(filterMap[record.recordFilterId],
+ frontendMap[record.frontendId],
+ dvrMap[record.dvrRecordId]);
+ }
}
TEST_P(TunerRecordAidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
@@ -885,9 +1033,17 @@
if (!lnbRecord.support) {
return;
}
- recordSingleFilterTestWithLnb(filterMap[lnbRecord.recordFilterId],
- frontendMap[lnbRecord.frontendId], dvrMap[lnbRecord.dvrRecordId],
- lnbMap[lnbRecord.lnbId]);
+ vector<LnbRecordHardwareConnections> lnbRecord_configs = generateLnbRecordConfigurations();
+ if (lnbRecord_configs.empty()) {
+ ALOGD("No frontends that support satellites.");
+ return;
+ }
+ for (auto& configuration : lnbRecord_configs) {
+ lnbRecord = configuration;
+ recordSingleFilterTestWithLnb(filterMap[lnbRecord.recordFilterId],
+ frontendMap[lnbRecord.frontendId],
+ dvrMap[lnbRecord.dvrRecordId], lnbMap[lnbRecord.lnbId]);
+ }
}
TEST_P(TunerFrontendAidlTest, TuneFrontend) {
@@ -895,7 +1051,11 @@
if (!live.hasFrontendConnection) {
return;
}
- mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerFrontendAidlTest, AutoScanFrontend) {
@@ -903,7 +1063,11 @@
if (!scan.hasFrontendConnection) {
return;
}
- mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_AUTO);
+ vector<ScanHardwareConnections> scan_configs = generateScanConfigurations();
+ for (auto& configuration : scan_configs) {
+ scan = configuration;
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_AUTO);
+ }
}
TEST_P(TunerFrontendAidlTest, BlindScanFrontend) {
@@ -911,7 +1075,11 @@
if (!scan.hasFrontendConnection) {
return;
}
- mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
+ vector<ScanHardwareConnections> scan_configs = generateScanConfigurations();
+ for (auto& configuration : scan_configs) {
+ scan = configuration;
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
+ }
}
TEST_P(TunerFrontendAidlTest, TuneFrontendWithFrontendSettings) {
@@ -919,7 +1087,11 @@
if (!live.hasFrontendConnection) {
return;
}
- mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ mFrontendTests.tuneTest(frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerFrontendAidlTest, BlindScanFrontendWithEndFrequency) {
@@ -927,7 +1099,11 @@
if (!scan.hasFrontendConnection) {
return;
}
- mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
+ vector<ScanHardwareConnections> scan_configs = generateScanConfigurations();
+ for (auto& configuration : scan_configs) {
+ scan = configuration;
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
+ }
}
TEST_P(TunerFrontendAidlTest, LinkToCiCam) {
@@ -935,10 +1111,14 @@
if (!live.hasFrontendConnection) {
return;
}
- if (!frontendMap[live.frontendId].canConnectToCiCam) {
- return;
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ if (!frontendMap[live.frontendId].canConnectToCiCam) {
+ continue;
+ }
+ mFrontendTests.tuneTest(frontendMap[live.frontendId]);
}
- mFrontendTests.tuneTest(frontendMap[live.frontendId]);
}
TEST_P(TunerFrontendAidlTest, getHardwareInfo) {
@@ -946,7 +1126,11 @@
if (!live.hasFrontendConnection) {
return;
}
- mFrontendTests.debugInfoTest(frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ mFrontendTests.debugInfoTest(frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerFrontendAidlTest, maxNumberOfFrontends) {
@@ -962,7 +1146,11 @@
if (!live.hasFrontendConnection) {
return;
}
- mFrontendTests.statusReadinessTest(frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ mFrontendTests.statusReadinessTest(frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowVideoFilterTest) {
@@ -970,7 +1158,11 @@
if (!live.hasFrontendConnection) {
return;
}
- broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowAudioFilterTest) {
@@ -978,7 +1170,11 @@
if (!live.hasFrontendConnection) {
return;
}
- broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerBroadcastAidlTest, BroadcastDataFlowSectionFilterTest) {
@@ -986,10 +1182,14 @@
if (!live.hasFrontendConnection) {
return;
}
- if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
- return;
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
+ continue;
+ }
+ broadcastSingleFilterTest(filterMap[live.sectionFilterId], frontendMap[live.frontendId]);
}
- broadcastSingleFilterTest(filterMap[live.sectionFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerBroadcastAidlTest, IonBufferTest) {
@@ -997,7 +1197,11 @@
if (!live.hasFrontendConnection) {
return;
}
- broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerBroadcastAidlTest, LnbBroadcastDataFlowVideoFilterTest) {
@@ -1005,8 +1209,16 @@
if (!lnbLive.support) {
return;
}
- broadcastSingleFilterTestWithLnb(filterMap[lnbLive.videoFilterId],
- frontendMap[lnbLive.frontendId], lnbMap[lnbLive.lnbId]);
+ vector<LnbLiveHardwareConnections> lnbLive_configs = generateLnbLiveConfigurations();
+ if (lnbLive_configs.empty()) {
+ ALOGD("No frontends that support satellites.");
+ return;
+ }
+ for (auto& combination : lnbLive_configs) {
+ lnbLive = combination;
+ broadcastSingleFilterTestWithLnb(filterMap[lnbLive.videoFilterId],
+ frontendMap[lnbLive.frontendId], lnbMap[lnbLive.lnbId]);
+ }
}
TEST_P(TunerBroadcastAidlTest, MediaFilterWithSharedMemoryHandle) {
@@ -1014,7 +1226,12 @@
if (!live.hasFrontendConnection) {
return;
}
- mediaFilterUsingSharedMemoryTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
+ auto live_configs = generateLiveConfigurations();
+ for (auto& configuration : live_configs) {
+ live = configuration;
+ mediaFilterUsingSharedMemoryTest(filterMap[live.videoFilterId],
+ frontendMap[live.frontendId]);
+ }
}
TEST_P(TunerDescramblerAidlTest, CreateDescrambler) {
@@ -1022,25 +1239,34 @@
if (!descrambling.support) {
return;
}
- int32_t demuxId;
- std::shared_ptr<IDemux> demux;
- ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-
- if (descrambling.hasFrontendConnection) {
- int32_t feId;
- mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
- ASSERT_TRUE(feId != INVALID_ID);
- ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
- ASSERT_TRUE(mFrontendTests.setFrontendCallback());
- ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ vector<DescramblingHardwareConnections> descrambling_configs =
+ generateDescramblingConfigurations();
+ if (descrambling_configs.empty()) {
+ ALOGD("No valid descrambling combinations in the configuration file.");
+ return;
}
+ for (auto& combination : descrambling_configs) {
+ descrambling = combination;
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
- ASSERT_TRUE(mDescramblerTests.openDescrambler(demuxId));
- ASSERT_TRUE(mDescramblerTests.closeDescrambler());
- ASSERT_TRUE(mDemuxTests.closeDemux());
+ if (descrambling.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ }
- if (descrambling.hasFrontendConnection) {
- ASSERT_TRUE(mFrontendTests.closeFrontend());
+ ASSERT_TRUE(mDescramblerTests.openDescrambler(demuxId));
+ ASSERT_TRUE(mDescramblerTests.closeDescrambler());
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+
+ if (descrambling.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
}
}
@@ -1049,11 +1275,42 @@
if (!descrambling.support) {
return;
}
- set<FilterConfig> filterConfs;
- filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.audioFilterId]));
- filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.videoFilterId]));
- scrambledBroadcastTest(filterConfs, frontendMap[descrambling.frontendId],
- descramblerMap[descrambling.descramblerId]);
+ vector<DescramblingHardwareConnections> descrambling_configs =
+ generateDescramblingConfigurations();
+ if (descrambling_configs.empty()) {
+ ALOGD("No valid descrambling combinations in the configuration file.");
+ return;
+ }
+ for (auto& combination : descrambling_configs) {
+ descrambling = combination;
+ set<FilterConfig> filterConfs;
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.audioFilterId]));
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.videoFilterId]));
+ scrambledBroadcastTest(filterConfs, frontendMap[descrambling.frontendId],
+ descramblerMap[descrambling.descramblerId],
+ Dataflow_Context::DESCRAMBLING);
+ }
+}
+
+TEST_P(TunerDescramblerAidlTest, ScrambledBroadcastDataFlowMediaFiltersTestWithLnb) {
+ description("Test media filters in scrambled broadcast use case with Lnb");
+ if (!lnbDescrambling.support) {
+ return;
+ }
+ auto lnbDescrambling_configs = generateLnbDescramblingConfigurations();
+ if (lnbDescrambling_configs.empty()) {
+ ALOGD("No frontends that support satellites.");
+ return;
+ }
+ for (auto& configuration : lnbDescrambling_configs) {
+ lnbDescrambling = configuration;
+ set<FilterConfig> filterConfs;
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[lnbDescrambling.audioFilterId]));
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[lnbDescrambling.videoFilterId]));
+ scrambledBroadcastTestWithLnb(filterConfs, frontendMap[lnbDescrambling.frontendId],
+ descramblerMap[lnbDescrambling.descramblerId],
+ lnbMap[lnbDescrambling.lnbId]);
+ }
}
INSTANTIATE_TEST_SUITE_P(PerInstance, TunerBroadcastAidlTest,
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
index 7f80d90..f68e1ec 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
@@ -36,11 +36,17 @@
initFrontendConfig();
initFilterConfig();
initDvrConfig();
+ initTimeFilterConfig();
+ initDescramblerConfig();
+ initLnbConfig();
+ initDiseqcMsgsConfig();
connectHardwaresToTestCases();
if (!validateConnections()) {
ALOGW("[vts] failed to validate connections.");
return false;
}
+ determineDataFlows();
+
return true;
}
@@ -58,6 +64,24 @@
return success();
}
+void clearIds() {
+ lnbIds.clear();
+ diseqcMsgs.clear();
+ frontendIds.clear();
+ ipFilterIds.clear();
+ pcrFilterIds.clear();
+ recordDvrIds.clear();
+ timeFilterIds.clear();
+ descramblerIds.clear();
+ audioFilterIds.clear();
+ videoFilterIds.clear();
+ playbackDvrIds.clear();
+ recordFilterIds.clear();
+ sectionFilterIds.clear();
+}
+
+enum class Dataflow_Context { LNBRECORD, RECORD, DESCRAMBLING, LNBDESCRAMBLING };
+
class TunerLnbAidlTest : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
@@ -73,6 +97,11 @@
mLnbTests.setService(mService);
}
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
+ }
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -101,6 +130,11 @@
mFilterTests.setService(mService);
}
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
+ }
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -124,13 +158,18 @@
mService = nullptr;
}
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
mFilterTests.setService(mService);
}
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
+ }
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -197,6 +236,11 @@
mDvrTests.setService(mService);
}
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
+ }
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -225,7 +269,7 @@
mService = nullptr;
}
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -234,6 +278,11 @@
mLnbTests.setService(mService);
}
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
+ }
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -244,7 +293,7 @@
void recordSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
DvrConfig dvrConf, LnbConfig lnbConf);
void recordSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf,
- DvrConfig dvrConf);
+ DvrConfig dvrConf, Dataflow_Context context);
std::shared_ptr<ITuner> mService;
FrontendTests mFrontendTests;
@@ -254,7 +303,7 @@
LnbTests mLnbTests;
private:
- int32_t* mLnbId = nullptr;
+ int32_t mLnbId = INVALID_LNB_ID;
};
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerRecordAidlTest);
@@ -269,11 +318,16 @@
mService = nullptr;
}
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
}
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
+ }
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -295,7 +349,7 @@
mService = nullptr;
}
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -304,6 +358,11 @@
mDvrTests.setService(mService);
}
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
+ }
+
protected:
static void description(const std::string& description) {
RecordProperty("description", description);
@@ -324,7 +383,7 @@
void mediaFilterUsingSharedMemoryTest(FilterConfig filterConf, FrontendConfig frontendConf);
private:
- int32_t* mLnbId = nullptr;
+ int32_t mLnbId = INVALID_LNB_ID;
};
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerBroadcastAidlTest);
@@ -348,6 +407,12 @@
mDvrTests.setService(mService);
mDescramblerTests.setService(mService);
mDescramblerTests.setCasService(mCasService);
+ mLnbTests.setService(mService);
+ }
+
+ virtual void TearDown() override {
+ clearIds();
+ mService = nullptr;
}
protected:
@@ -356,7 +421,11 @@
}
void scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
- FrontendConfig frontendConf, DescramblerConfig descConfig);
+ FrontendConfig frontendConf, DescramblerConfig descConfig,
+ Dataflow_Context context);
+ void scrambledBroadcastTestWithLnb(set<struct FilterConfig>& mediaFilterConfs,
+ FrontendConfig& frontendConf, DescramblerConfig& descConfig,
+ LnbConfig& lnbConfig);
AssertionResult filterDataOutputTest();
std::shared_ptr<ITuner> mService;
@@ -366,6 +435,10 @@
FilterTests mFilterTests;
DescramblerTests mDescramblerTests;
DvrTests mDvrTests;
+ LnbTests mLnbTests;
+
+ private:
+ int32_t mLnbId = INVALID_LNB_ID;
};
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerDescramblerAidlTest);
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
index 5f1f9c5..fea5f83 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
@@ -74,6 +74,512 @@
static LnbLiveHardwareConnections lnbLive;
static LnbRecordHardwareConnections lnbRecord;
static TimeFilterHardwareConnections timeFilter;
+static LnbDescramblingHardwareConnections lnbDescrambling;
+
+/*
+ * This function takes in a 2d vector of device Id's
+ * The n vectors correlate to the ids for n different devices (eg frontends, filters)
+ * The resultant 2d vector is every combination of id's with 1 id from each vector
+ */
+inline vector<vector<string>> generateIdCombinations(vector<vector<string>>& ids) {
+ vector<vector<string>> combinations;
+
+ // The index of each vector in ids that will be used in the next combination
+ // EG {0, 2} means combo {ids[0][0] ids[1][2]} will be next
+ const int size = static_cast<int>(ids.size());
+ vector<int> indexes_used_in_combination(size, 0);
+
+ // The vector number from ids whose elements we will cycle through to make combinations.
+ // First, start at the right most vector
+ int cycled_vector = size - 1;
+
+ while (cycled_vector >= 0) {
+ // Make a combination (one at a time)
+ vector<string> combo;
+ for (size_t i = 0; i < indexes_used_in_combination.size(); ++i) {
+ const int combo_index = indexes_used_in_combination[i];
+ combo.push_back(ids[i][combo_index]);
+ }
+ combinations.push_back(combo);
+
+ // Find the right most vector that still has space [elements left] to cycle through and
+ // create a combination
+ while (cycled_vector >= 0 &&
+ indexes_used_in_combination[cycled_vector] == ids[cycled_vector].size() - 1) {
+ cycled_vector--;
+ }
+
+ // Use this check to avoid segmentation faults
+ if (cycled_vector >= 0) {
+ // Once found, we have a vector we can cycle through, so increase to its next element
+ indexes_used_in_combination[cycled_vector]++;
+
+ // Reset the other vectors to the right to their first element so we can cycle through
+ // them again with the new element from cycled vector
+ for (size_t i = cycled_vector + 1; i < indexes_used_in_combination.size(); ++i) {
+ indexes_used_in_combination[i] = 0;
+ }
+
+ // all the vectors to the right were reset, so we can cycle through them again
+ // Start at the furthest right vector
+ cycled_vector = size - 1;
+ }
+ }
+
+ return combinations;
+}
+
+/*
+ * index 0 - playback dvr
+ * index 1 - audio filters
+ * index 2 - optional section filters
+ */
+static inline vector<DvrPlaybackHardwareConnections> generatePlaybackCombinations() {
+ vector<DvrPlaybackHardwareConnections> combinations;
+ vector<string> sectionFilterIds_optional = sectionFilterIds;
+ sectionFilterIds_optional.push_back(emptyHardwareId);
+ vector<vector<string>> deviceIds{playbackDvrIds, audioFilterIds, sectionFilterIds_optional};
+
+ const int dvrIndex = 0;
+ const int audioFilterIndex = 1;
+ const int sectionFilterIndex = 2;
+
+ auto idCombinations = generateIdCombinations(deviceIds);
+ for (auto& combo : idCombinations) {
+ DvrPlaybackHardwareConnections mPlayback;
+ mPlayback.dvrId = combo[dvrIndex];
+ mPlayback.audioFilterId = combo[audioFilterIndex];
+ mPlayback.sectionFilterId = combo[sectionFilterIndex];
+ const int videoFilterIndex =
+ find(audioFilterIds.begin(), audioFilterIds.end(), mPlayback.audioFilterId) -
+ audioFilterIds.begin();
+ mPlayback.videoFilterId = videoFilterIds[videoFilterIndex];
+ combinations.push_back(mPlayback);
+ }
+
+ return combinations;
+}
+
+static inline vector<DvrPlaybackHardwareConnections> generatePlaybackConfigs() {
+ vector<DvrPlaybackHardwareConnections> playback_configs;
+ if (configuredPlayback) {
+ ALOGD("Using DVR playback configuration provided.");
+ playback_configs = {playback};
+ } else {
+ ALOGD("Dvr playback not provided. Generating possible combinations. Consider adding it to "
+ "the configuration file.");
+ playback_configs = generatePlaybackCombinations();
+ }
+
+ return playback_configs;
+}
+
+/*
+ * index 0 - frontends
+ * index 1 - audio filters
+ * index 2 - lnbs
+ */
+static inline vector<LnbLiveHardwareConnections> generateLnbLiveCombinations() {
+ vector<LnbLiveHardwareConnections> combinations;
+ vector<vector<string>> deviceIds{frontendIds, audioFilterIds, lnbIds};
+
+ const int frontendIndex = 0;
+ const int audioFilterIndex = 1;
+ const int lnbIndex = 2;
+
+ // TODO: Find a better way to vary diseqcMsgs, if at all
+ auto idCombinations = generateIdCombinations(deviceIds);
+ for (auto& combo : idCombinations) {
+ const string feId = combo[frontendIndex];
+ auto type = frontendMap[feId].type;
+ if (type == FrontendType::DVBS || type == FrontendType::ISDBS ||
+ type == FrontendType::ISDBS3) {
+ LnbLiveHardwareConnections mLnbLive;
+ mLnbLive.frontendId = feId;
+ mLnbLive.audioFilterId = combo[audioFilterIndex];
+ const int videoFilterIndex =
+ find(audioFilterIds.begin(), audioFilterIds.end(), mLnbLive.audioFilterId) -
+ audioFilterIds.begin();
+ mLnbLive.videoFilterId = videoFilterIds[videoFilterIndex];
+ mLnbLive.lnbId = combo[lnbIndex];
+ mLnbLive.diseqcMsgs = diseqcMsgs;
+ combinations.push_back(mLnbLive);
+ }
+ }
+
+ return combinations;
+}
+
+static inline vector<LnbLiveHardwareConnections> generateLnbLiveConfigurations() {
+ vector<LnbLiveHardwareConnections> lnbLive_configs;
+ if (configuredLnbLive) {
+ ALOGD("Using LnbLive configuration provided.");
+ lnbLive_configs = {lnbLive};
+ } else {
+ ALOGD("LnbLive not provided. Generating possible combinations. Consider adding it to the "
+ "configuration file.");
+ lnbLive_configs = generateLnbLiveCombinations();
+ }
+
+ return lnbLive_configs;
+}
+
+static inline vector<ScanHardwareConnections> generateScanCombinations() {
+ vector<ScanHardwareConnections> combinations;
+
+ for (auto& id : frontendIds) {
+ ScanHardwareConnections mScan;
+ mScan.frontendId = id;
+ combinations.push_back(mScan);
+ }
+
+ return combinations;
+}
+
+static inline vector<ScanHardwareConnections> generateScanConfigurations() {
+ vector<ScanHardwareConnections> scan_configs;
+ if (configuredScan) {
+ ALOGD("Using scan configuration provided.");
+ scan_configs = {scan};
+ } else {
+ ALOGD("Scan not provided. Generating possible combinations. Consider adding it to "
+ "the configuration file.");
+ scan_configs = generateScanCombinations();
+ }
+
+ return scan_configs;
+}
+
+/*
+ * index 0 - frontends
+ * index 1 - record filter
+ * index 2 - Record Dvr
+ * index 3 - Lnb
+ */
+static inline vector<LnbRecordHardwareConnections> generateLnbRecordCombinations() {
+ vector<LnbRecordHardwareConnections> combinations;
+ vector<vector<string>> deviceIds{frontendIds, recordFilterIds, recordDvrIds, lnbIds};
+
+ const int frontendIndex = 0;
+ const int recordFilterIndex = 1;
+ const int dvrIndex = 2;
+ const int lnbIndex = 3;
+
+ auto idCombinations = generateIdCombinations(deviceIds);
+ // TODO : Find a better way to vary diseqcMsgs, if at all
+ for (auto& combo : idCombinations) {
+ const string feId = combo[frontendIndex];
+ auto type = frontendMap[feId].type;
+ if (type == FrontendType::DVBS || type == FrontendType::ISDBS ||
+ type == FrontendType::ISDBS3) {
+ LnbRecordHardwareConnections mLnbRecord;
+ mLnbRecord.frontendId = feId;
+ mLnbRecord.recordFilterId = combo[recordFilterIndex];
+ mLnbRecord.dvrRecordId = combo[dvrIndex];
+ mLnbRecord.lnbId = combo[lnbIndex];
+ mLnbRecord.diseqcMsgs = diseqcMsgs;
+ combinations.push_back(mLnbRecord);
+ }
+ }
+
+ return combinations;
+}
+
+static inline vector<LnbRecordHardwareConnections> generateLnbRecordConfigurations() {
+ vector<LnbRecordHardwareConnections> lnbRecord_configs;
+ if (configuredLnbRecord) {
+ ALOGD("Using LnbRecord configuration provided.");
+ lnbRecord_configs = {lnbRecord};
+ } else {
+ ALOGD("LnbRecord not provided. Generating possible combinations. Consider adding it to "
+ "the configuration file.");
+ lnbRecord_configs = generateLnbRecordCombinations();
+ }
+
+ return lnbRecord_configs;
+}
+
+/*
+ * index 0 - decramblers
+ * index 1 - frontends
+ * index 2 - audio filters
+ * index 3 - Dvr SW Fe Connections
+ * index 4 - DVR Source Connections
+ */
+static inline vector<DescramblingHardwareConnections> generateDescramblingCombinations() {
+ vector<DescramblingHardwareConnections> combinations;
+ vector<string> mfrontendIds = frontendIds;
+ vector<string> mDvrFeConnectionIds = playbackDvrIds;
+ vector<string> mDvrSourceConnectionIds = playbackDvrIds;
+
+ // Add the empty hardware id to each vector to include combinations where these 3 fields might
+ // be optional
+ mfrontendIds.push_back(emptyHardwareId);
+ mDvrFeConnectionIds.push_back(emptyHardwareId);
+ mDvrSourceConnectionIds.push_back(emptyHardwareId);
+
+ const int descramblerIndex = 0;
+ const int frontendIndex = 1;
+ const int audioFilterIndex = 2;
+ const int dvrFeIdIndex = 3;
+ const int dvrSourceIdIndex = 4;
+
+ vector<vector<string>> deviceIds{descramblerIds, mfrontendIds, audioFilterIds,
+ mDvrFeConnectionIds, mDvrSourceConnectionIds};
+ auto idCombinations = generateIdCombinations(deviceIds);
+ for (auto& combo : idCombinations) {
+ DescramblingHardwareConnections mDescrambling;
+ const string feId = combo[frontendIndex];
+ const string dvrSwFeId = combo[dvrFeIdIndex];
+ const string dvrSourceId = combo[dvrSourceIdIndex];
+ mDescrambling.hasFrontendConnection = feId.compare(emptyHardwareId) == 0 ? false : true;
+ if (!mDescrambling.hasFrontendConnection) {
+ if (dvrSourceId.compare(emptyHardwareId) == 0) {
+ // If combination does not have a frontend or dvr source connection, do not include
+ // it
+ continue;
+ }
+ } else {
+ if (frontendMap[feId].isSoftwareFe && dvrSwFeId.compare(emptyHardwareId) == 0) {
+ // If combination has a software frontend and no dvr->software frontend connection,
+ // do not include it
+ continue;
+ }
+ }
+ if (dvrSwFeId.compare(dvrSourceId) == 0) {
+ // If dvr->software frontend connection is the same as dvr source input to tuner, do not
+ // include it.
+ continue;
+ }
+ mDescrambling.frontendId = feId;
+ mDescrambling.audioFilterId = combo[audioFilterIndex];
+ const int videoFilterIndex =
+ find(audioFilterIds.begin(), audioFilterIds.end(), mDescrambling.audioFilterId) -
+ audioFilterIds.begin();
+ mDescrambling.videoFilterId = videoFilterIds[videoFilterIndex];
+ mDescrambling.dvrSoftwareFeId = dvrSwFeId;
+ mDescrambling.dvrSourceId = dvrSourceId;
+ mDescrambling.descramblerId = combo[descramblerIndex];
+ combinations.push_back(mDescrambling);
+ }
+
+ return combinations;
+}
+
+static inline vector<DescramblingHardwareConnections> generateDescramblingConfigurations() {
+ vector<DescramblingHardwareConnections> descrambling_configs;
+ if (configuredDescrambling) {
+ ALOGD("Using Descrambling configuration provided.");
+ descrambling_configs = {descrambling};
+ } else {
+ ALOGD("Descrambling not provided. Generating possible combinations. Consider adding it to "
+ "the "
+ "configuration file.");
+ descrambling_configs = generateDescramblingCombinations();
+ }
+
+ return descrambling_configs;
+}
+
+static inline vector<TimeFilterHardwareConnections> generateTimeFilterCombinations() {
+ vector<TimeFilterHardwareConnections> combinations;
+
+ for (auto& id : timeFilterIds) {
+ TimeFilterHardwareConnections mTimeFilter;
+ mTimeFilter.timeFilterId = id;
+ combinations.push_back(mTimeFilter);
+ }
+
+ return combinations;
+}
+
+static inline vector<TimeFilterHardwareConnections> generateTimeFilterConfigurations() {
+ vector<TimeFilterHardwareConnections> timeFilter_configs;
+ if (configuredTimeFilter) {
+ ALOGD("Using TimeFilter configuration provided.");
+ timeFilter_configs = {timeFilter};
+ } else {
+ ALOGD("TimeFilter not provided. Generating possible combinations. Consider adding it to "
+ "the "
+ "configuration file.");
+ timeFilter_configs = generateTimeFilterCombinations();
+ }
+
+ return timeFilter_configs;
+}
+
+/*
+ * index 0 - frontends
+ * index 1 - record dvrs
+ * index 2 - record filters
+ */
+static inline vector<DvrRecordHardwareConnections> generateRecordCombinations() {
+ vector<DvrRecordHardwareConnections> combinations;
+
+ const int frontendIdIndex = 0;
+ const int recordDvrIndex = 1;
+ const int recordFilterIndex = 2;
+
+ vector<vector<string>> deviceIds{frontendIds, recordDvrIds, recordFilterIds};
+
+ auto idCombinations = generateIdCombinations(deviceIds);
+ for (auto& combo : idCombinations) {
+ DvrRecordHardwareConnections mRecord;
+ const string feId = combo[frontendIdIndex];
+ mRecord.hasFrontendConnection = true;
+ if (frontendMap[feId].isSoftwareFe) {
+ // If we have a software frontend, do not include configuration for testing.
+ continue;
+ }
+ mRecord.frontendId = feId;
+ mRecord.support = true;
+ mRecord.dvrSourceId = emptyHardwareId;
+ mRecord.dvrSoftwareFeId = emptyHardwareId;
+ mRecord.recordFilterId = combo[recordFilterIndex];
+ mRecord.dvrRecordId = combo[recordDvrIndex];
+ combinations.push_back(mRecord);
+ }
+
+ return combinations;
+}
+
+static inline vector<DvrRecordHardwareConnections> generateRecordConfigurations() {
+ vector<DvrRecordHardwareConnections> record_configs;
+ if (configuredRecord) {
+ ALOGD("Using Record configuration provided.");
+ record_configs = {record};
+ } else {
+ ALOGD("Record not provided. Generating possible combinations. Consider adding it to "
+ "the "
+ "configuration file.");
+ record_configs = generateRecordCombinations();
+ }
+
+ return record_configs;
+}
+
+/*
+ * index 0 - frontends
+ * index 1 - audio filters
+ * index 2 - playback dvrs
+ * index 3 - section Filters
+ */
+static inline vector<LiveBroadcastHardwareConnections> generateLiveCombinations() {
+ vector<LiveBroadcastHardwareConnections> combinations;
+ vector<string> mSectionFilterIds = sectionFilterIds;
+ vector<string> mDvrSwConnectionIds = playbackDvrIds;
+
+ // Adding the empty hardware id to cover cases where fields are optional
+ mSectionFilterIds.push_back(emptyHardwareId);
+ mDvrSwConnectionIds.push_back(emptyHardwareId);
+
+ const int frontendIdIndex = 0;
+ const int audioFilterIdIndex = 1;
+ const int dvrSwConnectionIdIndex = 2;
+ const int sectionFilterIdIndex = 3;
+
+ vector<vector<string>> deviceIds{frontendIds, audioFilterIds, mDvrSwConnectionIds,
+ mSectionFilterIds};
+
+ auto idCombinations = generateIdCombinations(deviceIds);
+ for (auto& combo : idCombinations) {
+ LiveBroadcastHardwareConnections mLive;
+ const string feId = combo[frontendIdIndex];
+ const string dvrSwConnectionId = combo[dvrSwConnectionIdIndex];
+ mLive.hasFrontendConnection = true;
+
+ if (frontendMap[feId].isSoftwareFe && dvrSwConnectionId.compare(emptyHardwareId) == 0) {
+ // If the frontend is a software frontend and there is no dvr playback connected, do not
+ // include configuration
+ continue;
+ }
+ mLive.frontendId = feId;
+ mLive.dvrSoftwareFeId = dvrSwConnectionId;
+ mLive.audioFilterId = combo[audioFilterIdIndex];
+ const int videoFilterIdIndex =
+ find(audioFilterIds.begin(), audioFilterIds.end(), mLive.audioFilterId) -
+ audioFilterIds.begin();
+ mLive.videoFilterId = videoFilterIds[videoFilterIdIndex];
+ mLive.sectionFilterId = combo[sectionFilterIdIndex];
+
+ if (pcrFilterIds.empty()) {
+ // If pcr Filters have not been provided, set it to empty hardware id
+ mLive.pcrFilterId = emptyHardwareId;
+ } else {
+ // If pcr Filters have been provided, use the first index if there is only 1, or choose
+ // the filter that corresponds to the correct audio and video filter pair
+ const int pcrFilterIdIndex = pcrFilterIds.size() == 1 ? 0 : videoFilterIdIndex;
+ mLive.pcrFilterId = pcrFilterIds[pcrFilterIdIndex];
+ }
+
+ combinations.push_back(mLive);
+ }
+
+ return combinations;
+}
+
+static inline vector<LiveBroadcastHardwareConnections> generateLiveConfigurations() {
+ vector<LiveBroadcastHardwareConnections> live_configs;
+ if (configuredLive) {
+ ALOGD("Using Live configuration provided.");
+ live_configs = {live};
+ } else {
+ ALOGD("Live not provided. Generating possible combinations. Consider adding it to "
+ "the "
+ "configuration file.");
+ live_configs = generateLiveCombinations();
+ }
+
+ return live_configs;
+}
+
+static inline vector<LnbDescramblingHardwareConnections> generateLnbDescramblingCombinations() {
+ vector<LnbDescramblingHardwareConnections> combinations;
+ vector<vector<string>> deviceIds{frontendIds, audioFilterIds, lnbIds, descramblerIds};
+
+ const int frontendIdIndex = 0;
+ const int audioFilterIdIndex = 1;
+ const int lnbIdIndex = 2;
+ const int descramblerIdIndex = 3;
+
+ auto idCombinations = generateIdCombinations(deviceIds);
+ // TODO : Find a better way to vary diseqcMsgs, if at all
+ for (auto& combo : idCombinations) {
+ const string feId = combo[frontendIdIndex];
+ auto type = frontendMap[feId].type;
+ if (type == FrontendType::DVBS || type == FrontendType::ISDBS ||
+ type == FrontendType::ISDBS3) {
+ LnbDescramblingHardwareConnections mLnbDescrambling;
+ mLnbDescrambling.support = true;
+ mLnbDescrambling.frontendId = feId;
+ mLnbDescrambling.audioFilterId = combo[audioFilterIdIndex];
+ const int videoFilterIdIndex = find(audioFilterIds.begin(), audioFilterIds.end(),
+ mLnbDescrambling.audioFilterId) -
+ audioFilterIds.begin();
+ mLnbDescrambling.videoFilterId = videoFilterIds[videoFilterIdIndex];
+ mLnbDescrambling.lnbId = combo[lnbIdIndex];
+ mLnbDescrambling.descramblerId = combo[descramblerIdIndex];
+ mLnbDescrambling.diseqcMsgs = diseqcMsgs;
+ combinations.push_back(mLnbDescrambling);
+ }
+ }
+
+ return combinations;
+}
+
+static inline vector<LnbDescramblingHardwareConnections> generateLnbDescramblingConfigurations() {
+ vector<LnbDescramblingHardwareConnections> lnbDescrambling_configs;
+ if (configuredLnbDescrambling) {
+ ALOGD("Using LnbDescrambling configuration provided");
+ lnbDescrambling_configs = {lnbDescrambling};
+ } else {
+ ALOGD("LnbDescrambling not provided. Generating possible combinations. Consider adding it "
+ "to the configuration file.");
+ lnbDescrambling_configs = generateLnbDescramblingCombinations();
+ }
+
+ return lnbDescrambling_configs;
+}
/** Config all the frontends that would be used in the tests */
inline void initFrontendConfig() {
@@ -162,13 +668,141 @@
TunerTestingConfigAidlReader1_0::readDvrConfig1_0(dvrMap);
};
+inline void initTimeFilterConfig() {
+ // Read customized config
+ TunerTestingConfigAidlReader1_0::readTimeFilterConfig1_0(timeFilterMap);
+};
+
+inline void initDescramblerConfig() {
+ // Read customized config
+ TunerTestingConfigAidlReader1_0::readDescramblerConfig1_0(descramblerMap);
+}
+
+inline void initLnbConfig() {
+ // Read customized config
+ TunerTestingConfigAidlReader1_0::readLnbConfig1_0(lnbMap);
+};
+
+inline void initDiseqcMsgsConfig() {
+ // Read customized config
+ TunerTestingConfigAidlReader1_0::readDiseqcMessages(diseqcMsgMap);
+};
+
+inline void determineScan() {
+ if (!frontendMap.empty()) {
+ scan.hasFrontendConnection = true;
+ ALOGD("Can support scan");
+ }
+}
+
+inline void determineTimeFilter() {
+ if (!timeFilterMap.empty()) {
+ timeFilter.support = true;
+ ALOGD("Can support time filter");
+ }
+}
+
+inline void determineDvrPlayback() {
+ if (!playbackDvrIds.empty() && !audioFilterIds.empty() && !videoFilterIds.empty()) {
+ playback.support = true;
+ ALOGD("Can support dvr playback");
+ }
+}
+
+inline void determineLnbLive() {
+ if (!audioFilterIds.empty() && !videoFilterIds.empty() && !frontendMap.empty() &&
+ !lnbMap.empty()) {
+ lnbLive.support = true;
+ ALOGD("Can support lnb live");
+ }
+}
+
+inline void determineLnbRecord() {
+ if (!frontendMap.empty() && !recordFilterIds.empty() && !recordDvrIds.empty() &&
+ !lnbMap.empty()) {
+ lnbRecord.support = true;
+ ALOGD("Can support lnb record");
+ }
+}
+
+inline void determineLive() {
+ if (videoFilterIds.empty() || audioFilterIds.empty() || frontendMap.empty()) {
+ return;
+ }
+ if (hasSwFe && !hasHwFe && dvrMap.empty()) {
+ ALOGD("Cannot configure Live. Only software frontends and no dvr connections");
+ return;
+ }
+ ALOGD("Can support live");
+ live.hasFrontendConnection = true;
+}
+
+inline void determineDescrambling() {
+ if (descramblerMap.empty() || audioFilterIds.empty() || videoFilterIds.empty()) {
+ return;
+ }
+ if (frontendMap.empty() && playbackDvrIds.empty()) {
+ ALOGD("Cannot configure descrambling. No frontends or playback dvr's");
+ return;
+ }
+ if (hasSwFe && !hasHwFe && playbackDvrIds.empty()) {
+ ALOGD("cannot configure descrambling. Only SW frontends and no playback dvr's");
+ return;
+ }
+ ALOGD("Can support descrambling");
+ descrambling.support = true;
+}
+
+inline void determineDvrRecord() {
+ if (recordDvrIds.empty() || recordFilterIds.empty()) {
+ return;
+ }
+ if (frontendMap.empty() && playbackDvrIds.empty()) {
+ ALOGD("Cannot support dvr record. No frontends and no playback dvr's");
+ return;
+ }
+ if (hasSwFe && !hasHwFe && playbackDvrIds.empty()) {
+ ALOGD("Cannot support dvr record. Only SW frontends and no playback dvr's");
+ return;
+ }
+ ALOGD("Can support dvr record.");
+ record.support = true;
+}
+
+inline void determineLnbDescrambling() {
+ if (frontendIds.empty() || audioFilterIds.empty() || videoFilterIds.empty() || lnbIds.empty() ||
+ descramblerIds.empty()) {
+ return;
+ }
+ ALOGD("Can support LnbDescrambling.");
+ lnbDescrambling.support = true;
+}
+
/** Read the vendor configurations of which hardware to use for each test cases/data flows */
inline void connectHardwaresToTestCases() {
TunerTestingConfigAidlReader1_0::connectLiveBroadcast(live);
TunerTestingConfigAidlReader1_0::connectScan(scan);
TunerTestingConfigAidlReader1_0::connectDvrRecord(record);
+ TunerTestingConfigAidlReader1_0::connectTimeFilter(timeFilter);
+ TunerTestingConfigAidlReader1_0::connectDescrambling(descrambling);
+ TunerTestingConfigAidlReader1_0::connectLnbLive(lnbLive);
+ TunerTestingConfigAidlReader1_0::connectLnbRecord(lnbRecord);
+ TunerTestingConfigAidlReader1_0::connectDvrPlayback(playback);
+ TunerTestingConfigAidlReader1_0::connectLnbDescrambling(lnbDescrambling);
};
+inline void determineDataFlows() {
+ determineScan();
+ determineTimeFilter();
+ determineDvrPlayback();
+ determineLnbLive();
+ determineLnbRecord();
+ determineLive();
+ determineDescrambling();
+ determineDvrRecord();
+ determineLnbDescrambling();
+}
+
inline bool validateConnections() {
if (record.support && !record.hasFrontendConnection &&
record.dvrSourceId.compare(emptyHardwareId) == 0) {
@@ -183,6 +817,18 @@
feIsValid &= record.support && record.hasFrontendConnection
? frontendMap.find(record.frontendId) != frontendMap.end()
: true;
+ feIsValid &= descrambling.support && descrambling.hasFrontendConnection
+ ? frontendMap.find(descrambling.frontendId) != frontendMap.end()
+ : true;
+
+ feIsValid &= lnbLive.support ? frontendMap.find(lnbLive.frontendId) != frontendMap.end() : true;
+
+ feIsValid &=
+ lnbRecord.support ? frontendMap.find(lnbRecord.frontendId) != frontendMap.end() : true;
+
+ feIsValid &= lnbDescrambling.support
+ ? frontendMap.find(lnbDescrambling.frontendId) != frontendMap.end()
+ : true;
if (!feIsValid) {
ALOGW("[vts config] dynamic config fe connection is invalid.");
@@ -204,6 +850,20 @@
dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
}
+ if (descrambling.support) {
+ if (descrambling.hasFrontendConnection) {
+ if (frontendMap[descrambling.frontendId].isSoftwareFe) {
+ dvrIsValid &= dvrMap.find(descrambling.dvrSoftwareFeId) != dvrMap.end();
+ }
+ } else {
+ dvrIsValid &= dvrMap.find(descrambling.dvrSourceId) != dvrMap.end();
+ }
+ }
+
+ dvrIsValid &= lnbRecord.support ? dvrMap.find(lnbRecord.dvrRecordId) != dvrMap.end() : true;
+
+ dvrIsValid &= playback.support ? dvrMap.find(playback.dvrId) != dvrMap.end() : true;
+
if (!dvrIsValid) {
ALOGW("[vts config] dynamic config dvr connection is invalid.");
return false;
@@ -216,10 +876,120 @@
filterIsValid &=
record.support ? filterMap.find(record.recordFilterId) != filterMap.end() : true;
+ filterIsValid &= descrambling.support
+ ? filterMap.find(descrambling.videoFilterId) != filterMap.end() &&
+ filterMap.find(descrambling.audioFilterId) != filterMap.end()
+ : true;
+
+ for (auto& filterId : descrambling.extraFilters) {
+ filterIsValid &= filterMap.find(filterId) != filterMap.end();
+ }
+
+ filterIsValid &= lnbLive.support
+ ? filterMap.find(lnbLive.audioFilterId) != filterMap.end() &&
+ filterMap.find(lnbLive.videoFilterId) != filterMap.end()
+ : true;
+
+ filterIsValid &=
+ lnbRecord.support ? filterMap.find(lnbRecord.recordFilterId) != filterMap.end() : true;
+
+ for (auto& filterId : lnbRecord.extraFilters) {
+ filterIsValid &= filterMap.find(filterId) != filterMap.end();
+ }
+
+ for (auto& filterId : lnbLive.extraFilters) {
+ filterIsValid &= filterMap.find(filterId) != filterMap.end();
+ }
+
+ filterIsValid &= playback.support
+ ? filterMap.find(playback.audioFilterId) != filterMap.end() &&
+ filterMap.find(playback.videoFilterId) != filterMap.end()
+ : true;
+ filterIsValid &= playback.sectionFilterId.compare(emptyHardwareId) == 0
+ ? true
+ : filterMap.find(playback.sectionFilterId) != filterMap.end();
+
+ for (auto& filterId : playback.extraFilters) {
+ filterIsValid &=
+ playback.hasExtraFilters ? filterMap.find(filterId) != filterMap.end() : true;
+ }
+
+ filterIsValid &=
+ lnbDescrambling.support
+ ? filterMap.find(lnbDescrambling.audioFilterId) != filterMap.end() &&
+ filterMap.find(lnbDescrambling.videoFilterId) != filterMap.end()
+ : true;
+
if (!filterIsValid) {
ALOGW("[vts config] dynamic config filter connection is invalid.");
return false;
}
+ if (audioFilterIds.size() != videoFilterIds.size()) {
+ ALOGW("[vts config] the number of audio and video filters should be equal");
+ return false;
+ }
+
+ if (!pcrFilterIds.empty() && pcrFilterIds.size() != 1 &&
+ pcrFilterIds.size() != audioFilterIds.size()) {
+ ALOGW("[vts config] When more than 1 pcr filter is configured, the number of pcr filters "
+ "must equal the number of audio and video filters.");
+ return false;
+ }
+
+ bool timeFilterIsValid =
+ timeFilter.support ? timeFilterMap.find(timeFilter.timeFilterId) != timeFilterMap.end()
+ : true;
+
+ if (!timeFilterIsValid) {
+ ALOGW("[vts config] dynamic config time filter connection is invalid.");
+ }
+
+ bool descramblerIsValid =
+ descrambling.support
+ ? descramblerMap.find(descrambling.descramblerId) != descramblerMap.end()
+ : true;
+
+ descramblerIsValid &=
+ lnbDescrambling.support
+ ? descramblerMap.find(lnbDescrambling.descramblerId) != descramblerMap.end()
+ : true;
+
+ if (!descramblerIsValid) {
+ ALOGW("[vts config] dynamic config descrambler connection is invalid.");
+ return false;
+ }
+
+ bool lnbIsValid = lnbLive.support ? lnbMap.find(lnbLive.lnbId) != lnbMap.end() : true;
+
+ lnbIsValid &= lnbRecord.support ? lnbMap.find(lnbRecord.lnbId) != lnbMap.end() : true;
+
+ lnbIsValid &=
+ lnbDescrambling.support ? lnbMap.find(lnbDescrambling.lnbId) != lnbMap.end() : true;
+
+ if (!lnbIsValid) {
+ ALOGW("[vts config] dynamic config lnb connection is invalid.");
+ return false;
+ }
+
+ bool diseqcMsgsIsValid = true;
+
+ for (auto& msg : lnbRecord.diseqcMsgs) {
+ diseqcMsgsIsValid &= diseqcMsgMap.find(msg) != diseqcMsgMap.end();
+ }
+
+ for (auto& msg : lnbLive.diseqcMsgs) {
+ diseqcMsgsIsValid &= diseqcMsgMap.find(msg) != diseqcMsgMap.end();
+ }
+
+ for (auto& msg : lnbDescrambling.diseqcMsgs) {
+ diseqcMsgsIsValid &= diseqcMsgMap.find(msg) != diseqcMsgMap.end();
+ }
+
+ if (!diseqcMsgsIsValid) {
+ ALOGW("[vts config] dynamic config diseqcMsg is invalid.");
+ return false;
+ }
+
return true;
}
diff --git a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
index 189f5fd..9517520 100644
--- a/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
+++ b/tv/tuner/config/TunerTestingConfigAidlReaderV1_0.h
@@ -62,10 +62,37 @@
using namespace aidl::android::hardware::tv::tuner;
using namespace android::media::tuner::testing::configuration::V1_0;
+static bool hasHwFe = false;
+static bool hasSwFe = false;
+static bool configFileRead = false;
+static bool configuredLive = false;
+static bool configuredScan = false;
+static bool configuredRecord = false;
+static bool configuredLnbLive = false;
+static bool configuredPlayback = false;
+static bool configuredLnbRecord = false;
+static bool configuredTimeFilter = false;
+static bool configuredDescrambling = false;
+static bool configuredLnbDescrambling = false;
+
const string emptyHardwareId = "";
static string mConfigFilePath;
+static vector<string> playbackDvrIds;
+static vector<string> ipFilterIds;
+static vector<string> recordDvrIds;
+static vector<string> pcrFilterIds;
+static vector<string> timeFilterIds;
+static vector<string> audioFilterIds;
+static vector<string> videoFilterIds;
+static vector<string> recordFilterIds;
+static vector<string> sectionFilterIds;
+static vector<string> frontendIds;
+static vector<string> lnbIds;
+static vector<string> diseqcMsgs;
+static vector<string> descramblerIds;
+
#define PROVISION_STR \
"{ " \
" \"id\": 21140844, " \
@@ -137,6 +164,7 @@
string ipFilterId;
string pcrFilterId;
/* list string of extra filters; */
+ vector<string> extraFilters;
};
struct ScanHardwareConnections {
@@ -146,12 +174,13 @@
struct DvrPlaybackHardwareConnections {
bool support;
+ bool hasExtraFilters = false;
string frontendId;
string dvrId;
string audioFilterId;
string videoFilterId;
string sectionFilterId;
- /* list string of extra filters; */
+ vector<string> extraFilters;
};
struct DvrRecordHardwareConnections {
@@ -173,7 +202,7 @@
string videoFilterId;
string descramblerId;
string dvrSourceId;
- /* list string of extra filters; */
+ vector<string> extraFilters;
};
struct LnbLiveHardwareConnections {
@@ -183,7 +212,7 @@
string videoFilterId;
string lnbId;
vector<string> diseqcMsgs;
- /* list string of extra filters; */
+ vector<string> extraFilters;
};
struct LnbRecordHardwareConnections {
@@ -193,7 +222,7 @@
string recordFilterId;
string lnbId;
vector<string> diseqcMsgs;
- /* list string of extra filters; */
+ vector<string> extraFilters;
};
struct TimeFilterHardwareConnections {
@@ -201,6 +230,16 @@
string timeFilterId;
};
+struct LnbDescramblingHardwareConnections {
+ bool support;
+ string frontendId;
+ string audioFilterId;
+ string videoFilterId;
+ string lnbId;
+ string descramblerId;
+ vector<string> diseqcMsgs;
+};
+
struct TunerTestingConfigAidlReader1_0 {
public:
static void setConfigFilePath(string path) { mConfigFilePath = path; }
@@ -248,6 +287,7 @@
auto frontends = *hardwareConfig.getFirstFrontends();
for (auto feConfig : frontends.getFrontend()) {
string id = feConfig.getId();
+ frontendIds.push_back(id);
if (id.compare(string("FE_DEFAULT")) == 0) {
// overrid default
frontendMap.erase(string("FE_DEFAULT"));
@@ -263,6 +303,9 @@
break;
case FrontendTypeEnum::ATSC:
type = FrontendType::ATSC;
+ frontendMap[id].settings.set<
+ FrontendSettings::Tag::atsc>(
+ readAtscFrontendSettings(feConfig));
break;
case FrontendTypeEnum::ATSC3:
type = FrontendType::ATSC3;
@@ -283,12 +326,16 @@
}
case FrontendTypeEnum::ISDBS:
type = FrontendType::ISDBS;
+ frontendMap[id].settings.set<FrontendSettings::Tag::isdbs>(
+ readIsdbsFrontendSettings(feConfig));
break;
case FrontendTypeEnum::ISDBS3:
type = FrontendType::ISDBS3;
break;
case FrontendTypeEnum::ISDBT:
type = FrontendType::ISDBT;
+ frontendMap[id].settings.set<FrontendSettings::Tag::isdbt>(
+ readIsdbtFrontendSettings(feConfig));
break;
case FrontendTypeEnum::DTMB:
type = FrontendType::DTMB;
@@ -302,6 +349,11 @@
}
frontendMap[id].type = type;
frontendMap[id].isSoftwareFe = feConfig.getIsSoftwareFrontend();
+ if (frontendMap[id].isSoftwareFe) {
+ hasSwFe = true;
+ } else {
+ hasHwFe = true;
+ }
// TODO: b/182519645 complete the tune status config
frontendMap[id].tuneStatusTypes = types;
frontendMap[id].expectTuneStatuses = statuses;
@@ -376,11 +428,13 @@
DvrType type;
switch (dvrConfig.getType()) {
case DvrTypeEnum::PLAYBACK:
+ playbackDvrIds.push_back(id);
type = DvrType::PLAYBACK;
dvrMap[id].settings.set<DvrSettings::Tag::playback>(
readPlaybackSettings(dvrConfig));
break;
case DvrTypeEnum::RECORD:
+ recordDvrIds.push_back(id);
type = DvrType::RECORD;
dvrMap[id].settings.set<DvrSettings::Tag::record>(
readRecordSettings(dvrConfig));
@@ -404,6 +458,7 @@
auto lnbs = *hardwareConfig.getFirstLnbs();
for (auto lnbConfig : lnbs.getLnb()) {
string id = lnbConfig.getId();
+ lnbIds.push_back(id);
if (lnbConfig.hasName()) {
lnbMap[id].name = lnbConfig.getName();
} else {
@@ -422,6 +477,7 @@
auto descramblers = *hardwareConfig.getFirstDescramblers();
for (auto descramblerConfig : descramblers.getDescrambler()) {
string id = descramblerConfig.getId();
+ descramblerIds.push_back(id);
descramblerMap[id].casSystemId =
static_cast<int32_t>(descramblerConfig.getCasSystemId());
if (descramblerConfig.hasProvisionStr()) {
@@ -447,6 +503,7 @@
auto msgs = *hardwareConfig.getFirstDiseqcMessages();
for (auto msgConfig : msgs.getDiseqcMessage()) {
string name = msgConfig.getMsgName();
+ diseqcMsgs.push_back(name);
for (uint8_t atom : msgConfig.getMsgBody()) {
diseqcMsgMap[name].push_back(atom);
}
@@ -460,6 +517,7 @@
auto timeFilters = *hardwareConfig.getFirstTimeFilters();
for (auto timeFilterConfig : timeFilters.getTimeFilter()) {
string id = timeFilterConfig.getId();
+ timeFilterIds.push_back(id);
timeFilterMap[id].timeStamp = static_cast<int64_t>(timeFilterConfig.getTimeStamp());
}
}
@@ -469,6 +527,7 @@
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasClearLiveBroadcast()) {
live.hasFrontendConnection = true;
+ configuredLive = true;
} else {
live.hasFrontendConnection = false;
return;
@@ -496,12 +555,17 @@
} else {
live.ipFilterId = emptyHardwareId;
}
+ if (liveConfig.hasOptionalFilters()) {
+ auto optionalFilters = liveConfig.getOptionalFilters();
+ live.extraFilters = optionalFilters;
+ }
}
static void connectScan(ScanHardwareConnections& scan) {
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasScan()) {
scan.hasFrontendConnection = true;
+ configuredScan = true;
} else {
scan.hasFrontendConnection = false;
return;
@@ -514,6 +578,7 @@
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasDvrPlayback()) {
playback.support = true;
+ configuredPlayback = true;
} else {
playback.support = false;
return;
@@ -527,12 +592,17 @@
} else {
playback.sectionFilterId = emptyHardwareId;
}
+ if (playbackConfig.hasOptionalFilters()) {
+ auto optionalFilters = playbackConfig.getOptionalFilters();
+ playback.extraFilters = optionalFilters;
+ }
}
static void connectDvrRecord(DvrRecordHardwareConnections& record) {
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasDvrRecord()) {
record.support = true;
+ configuredRecord = true;
} else {
record.support = false;
return;
@@ -557,6 +627,7 @@
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasDescrambling()) {
descrambling.support = true;
+ configuredDescrambling = true;
} else {
descrambling.support = false;
return;
@@ -576,12 +647,17 @@
descrambling.hasFrontendConnection = false;
descrambling.dvrSourceId = descConfig.getDvrSourceConnection();
}
+ if (descConfig.hasOptionalFilters()) {
+ auto optionalFilters = descConfig.getOptionalFilters();
+ descrambling.extraFilters = optionalFilters;
+ }
}
static void connectLnbLive(LnbLiveHardwareConnections& lnbLive) {
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasLnbLive()) {
lnbLive.support = true;
+ configuredLnbLive = true;
} else {
lnbLive.support = false;
return;
@@ -596,12 +672,17 @@
lnbLive.diseqcMsgs.push_back(msgName);
}
}
+ if (lnbLiveConfig.hasOptionalFilters()) {
+ auto optionalFilters = lnbLiveConfig.getOptionalFilters();
+ lnbLive.extraFilters = optionalFilters;
+ }
}
static void connectLnbRecord(LnbRecordHardwareConnections& lnbRecord) {
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasLnbRecord()) {
lnbRecord.support = true;
+ configuredLnbRecord = true;
} else {
lnbRecord.support = false;
return;
@@ -616,12 +697,17 @@
lnbRecord.diseqcMsgs.push_back(msgName);
}
}
+ if (lnbRecordConfig.hasOptionalFilters()) {
+ auto optionalFilters = lnbRecordConfig.getOptionalFilters();
+ lnbRecord.extraFilters = optionalFilters;
+ }
}
static void connectTimeFilter(TimeFilterHardwareConnections& timeFilter) {
auto dataFlow = getDataFlowConfiguration();
if (dataFlow.hasTimeFilter()) {
timeFilter.support = true;
+ configuredTimeFilter = true;
} else {
timeFilter.support = false;
return;
@@ -630,6 +716,28 @@
timeFilter.timeFilterId = timeFilterConfig.getTimeFilterConnection();
}
+ static void connectLnbDescrambling(LnbDescramblingHardwareConnections& lnbDescrambling) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasLnbDescrambling()) {
+ lnbDescrambling.support = true;
+ configuredLnbDescrambling = true;
+ } else {
+ lnbDescrambling.support = false;
+ return;
+ }
+ auto lnbDescramblingConfig = *dataFlow.getFirstLnbDescrambling();
+ lnbDescrambling.frontendId = lnbDescramblingConfig.getFrontendConnection();
+ lnbDescrambling.audioFilterId = lnbDescramblingConfig.getAudioFilterConnection();
+ lnbDescrambling.videoFilterId = lnbDescramblingConfig.getVideoFilterConnection();
+ lnbDescrambling.lnbId = lnbDescramblingConfig.getLnbConnection();
+ lnbDescrambling.descramblerId = lnbDescramblingConfig.getDescramblerConnection();
+ if (lnbDescramblingConfig.hasDiseqcMsgSender()) {
+ for (auto& msgName : lnbDescramblingConfig.getDiseqcMsgSender()) {
+ lnbDescrambling.diseqcMsgs.push_back(msgName);
+ }
+ }
+ }
+
private:
static FrontendDvbtSettings readDvbtFrontendSettings(Frontend feConfig) {
ALOGW("[ConfigReader] fe type is dvbt");
@@ -677,25 +785,115 @@
ALOGW("[ConfigReader] no more dvbs settings");
return dvbsSettings;
}
- dvbsSettings.symbolRate = static_cast<int32_t>(
- feConfig.getFirstDvbsFrontendSettings_optional()->getSymbolRate());
- dvbsSettings.inputStreamId = static_cast<int32_t>(
- feConfig.getFirstDvbsFrontendSettings_optional()->getInputStreamId());
auto dvbs = feConfig.getFirstDvbsFrontendSettings_optional();
- if (dvbs->hasScanType()) {
- dvbsSettings.scanType = static_cast<FrontendDvbsScanType>(dvbs->getScanType());
- }
- if (dvbs->hasIsDiseqcRxMessage()) {
- dvbsSettings.isDiseqcRxMessage = dvbs->getIsDiseqcRxMessage();
- }
+ dvbsSettings.symbolRate = static_cast<int32_t>(dvbs->getSymbolRate());
+ dvbsSettings.inputStreamId = static_cast<int32_t>(dvbs->getInputStreamId());
+ dvbsSettings.scanType = static_cast<FrontendDvbsScanType>(dvbs->getScanType());
+ dvbsSettings.isDiseqcRxMessage = dvbs->getIsDiseqcRxMessage();
+ dvbsSettings.inversion = static_cast<FrontendSpectralInversion>(dvbs->getInversion());
+ dvbsSettings.modulation = static_cast<FrontendDvbsModulation>(dvbs->getModulation());
+ dvbsSettings.rolloff = static_cast<FrontendDvbsRolloff>(dvbs->getRolloff());
+ dvbsSettings.pilot = static_cast<FrontendDvbsPilot>(dvbs->getPilot());
+ dvbsSettings.standard = static_cast<FrontendDvbsStandard>(dvbs->getStandard());
+ dvbsSettings.vcmMode = static_cast<FrontendDvbsVcmMode>(dvbs->getVcmMode());
return dvbsSettings;
}
+ static FrontendAtscSettings readAtscFrontendSettings(Frontend& feConfig) {
+ ALOGW("[ConfigReader] fe type is atsc");
+ FrontendAtscSettings atscSettings{
+ .frequency = (int64_t)feConfig.getFrequency(),
+ };
+ if (feConfig.hasEndFrequency()) {
+ atscSettings.endFrequency = (int64_t)feConfig.getEndFrequency();
+ }
+ if (!feConfig.hasAtscFrontendSettings_optional()) {
+ ALOGW("[ConfigReader] no more atsc settings");
+ return atscSettings;
+ }
+ auto atsc = feConfig.getFirstAtscFrontendSettings_optional();
+ atscSettings.inversion = static_cast<FrontendSpectralInversion>(atsc->getInversion());
+ atscSettings.modulation = static_cast<FrontendAtscModulation>(atsc->getModulation());
+ return atscSettings;
+ }
+
+ static FrontendIsdbsSettings readIsdbsFrontendSettings(Frontend& feConfig) {
+ ALOGW("[ConfigReader] fe type is isdbs");
+ FrontendIsdbsSettings isdbsSettings{.frequency = (int64_t)feConfig.getFrequency()};
+ if (feConfig.hasEndFrequency()) {
+ isdbsSettings.endFrequency = (int64_t)feConfig.getEndFrequency();
+ }
+ if (!feConfig.hasIsdbsFrontendSettings_optional()) {
+ ALOGW("[ConfigReader] no more isdbs settings");
+ return isdbsSettings;
+ }
+ auto isdbs = feConfig.getFirstIsdbsFrontendSettings_optional();
+ isdbsSettings.streamId = (int32_t)isdbs->getStreamId();
+ isdbsSettings.symbolRate = (int32_t)isdbs->getSymbolRate();
+ isdbsSettings.modulation = static_cast<FrontendIsdbsModulation>(isdbs->getModulation());
+ isdbsSettings.coderate = static_cast<FrontendIsdbsCoderate>(isdbs->getCoderate());
+ isdbsSettings.rolloff = static_cast<FrontendIsdbsRolloff>(isdbs->getRolloff());
+ isdbsSettings.streamIdType =
+ static_cast<FrontendIsdbsStreamIdType>(isdbs->getStreamIdType());
+ return isdbsSettings;
+ }
+
+ static FrontendIsdbtSettings readIsdbtFrontendSettings(Frontend& feConfig) {
+ ALOGW("[ConfigReader] fe type is isdbt");
+ FrontendIsdbtSettings isdbtSettings{
+ .frequency = (int64_t)feConfig.getFrequency(),
+ };
+ if (feConfig.hasEndFrequency()) {
+ isdbtSettings.endFrequency = (int64_t)feConfig.getEndFrequency();
+ }
+ if (!feConfig.hasIsdbtFrontendSettings_optional()) {
+ ALOGW("[ConfigReader] no more isdbt settings");
+ return isdbtSettings;
+ }
+ auto isdbt = feConfig.getFirstIsdbtFrontendSettings_optional();
+ isdbtSettings.inversion = static_cast<FrontendSpectralInversion>(isdbt->getInversion());
+ isdbtSettings.bandwidth = static_cast<FrontendIsdbtBandwidth>(isdbt->getBandwidth());
+ isdbtSettings.mode = static_cast<FrontendIsdbtMode>(isdbt->getMode());
+ isdbtSettings.guardInterval =
+ static_cast<FrontendIsdbtGuardInterval>(isdbt->getGuardInterval());
+ isdbtSettings.serviceAreaId = (int32_t)isdbt->getServiceAreaId();
+ isdbtSettings.partialReceptionFlag =
+ static_cast<FrontendIsdbtPartialReceptionFlag>(isdbt->getPartialReceptionFlag());
+ if (!isdbt->hasFrontendIsdbtLayerSettings()) {
+ ALOGW("[ConfigReader] no isdbt layer settings");
+ return isdbtSettings;
+ }
+ auto layerSettings = isdbt->getFirstFrontendIsdbtLayerSettings();
+ ::aidl::android::hardware::tv::tuner::FrontendIsdbtLayerSettings mLayerSettings;
+ mLayerSettings.modulation =
+ static_cast<FrontendIsdbtModulation>(layerSettings->getModulation());
+ mLayerSettings.coderate = static_cast<FrontendIsdbtCoderate>(layerSettings->getCoderate());
+ mLayerSettings.timeInterleave =
+ static_cast<FrontendIsdbtTimeInterleaveMode>(layerSettings->getTimeInterleave());
+ mLayerSettings.numOfSegment = (int32_t)layerSettings->getNumOfSegment();
+ isdbtSettings.layerSettings.push_back(mLayerSettings);
+ return isdbtSettings;
+ }
+
static bool readFilterTypeAndSettings(Filter filterConfig, DemuxFilterType& type,
DemuxFilterSettings& settings) {
auto mainType = filterConfig.getMainType();
auto subType = filterConfig.getSubType();
+ if (subType == FilterSubTypeEnum::AUDIO) {
+ audioFilterIds.push_back(filterConfig.getId());
+ } else if (subType == FilterSubTypeEnum::VIDEO) {
+ videoFilterIds.push_back(filterConfig.getId());
+ } else if (subType == FilterSubTypeEnum::RECORD) {
+ recordFilterIds.push_back(filterConfig.getId());
+ } else if (subType == FilterSubTypeEnum::SECTION) {
+ sectionFilterIds.push_back(filterConfig.getId());
+ } else if (subType == FilterSubTypeEnum::PCR) {
+ pcrFilterIds.push_back(filterConfig.getId());
+ } else if (subType == FilterSubTypeEnum::IP) {
+ ipFilterIds.push_back(filterConfig.getId());
+ }
+
switch (mainType) {
case FilterMainTypeEnum::TS: {
ALOGW("[ConfigReader] filter main type is ts");
diff --git a/tv/tuner/config/api/current.txt b/tv/tuner/config/api/current.txt
index 383d49f..dbd3486 100644
--- a/tv/tuner/config/api/current.txt
+++ b/tv/tuner/config/api/current.txt
@@ -1,6 +1,14 @@
// Signature format: 2.0
package android.media.tuner.testing.configuration.V1_0 {
+ public class AtscFrontendSettings {
+ ctor public AtscFrontendSettings();
+ method @Nullable public java.math.BigInteger getInversion();
+ method @Nullable public java.math.BigInteger getModulation();
+ method public void setInversion(@Nullable java.math.BigInteger);
+ method public void setModulation(@Nullable java.math.BigInteger);
+ }
+
public class AvFilterSettings {
ctor public AvFilterSettings();
method @Nullable public short getAudioStreamType_optional();
@@ -19,6 +27,7 @@
method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling getDescrambling();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback getDvrPlayback();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord getDvrRecord();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbDescrambling getLnbDescrambling();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive getLnbLive();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord getLnbRecord();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan getScan();
@@ -27,6 +36,7 @@
method public void setDescrambling(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling);
method public void setDvrPlayback(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback);
method public void setDvrRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord);
+ method public void setLnbDescrambling(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbDescrambling);
method public void setLnbLive(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive);
method public void setLnbRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord);
method public void setScan(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan);
@@ -39,6 +49,7 @@
method @Nullable public String getDvrSoftwareFeConnection();
method @Nullable public String getFrontendConnection();
method @Nullable public String getIpFilterConnection();
+ method @Nullable public java.util.List<java.lang.String> getOptionalFilters();
method @Nullable public String getPcrFilterConnection();
method @Nullable public String getSectionFilterConnection();
method @Nullable public String getVideoFilterConnection();
@@ -46,6 +57,7 @@
method public void setDvrSoftwareFeConnection(@Nullable String);
method public void setFrontendConnection(@Nullable String);
method public void setIpFilterConnection(@Nullable String);
+ method public void setOptionalFilters(@Nullable java.util.List<java.lang.String>);
method public void setPcrFilterConnection(@Nullable String);
method public void setSectionFilterConnection(@Nullable String);
method public void setVideoFilterConnection(@Nullable String);
@@ -59,6 +71,7 @@
method @Nullable public String getDvrSourceConnection();
method @Nullable public String getFrontendConnection();
method @Nullable public boolean getHasFrontendConnection();
+ method @Nullable public java.util.List<java.lang.String> getOptionalFilters();
method @Nullable public String getVideoFilterConnection();
method public void setAudioFilterConnection(@Nullable String);
method public void setDescramblerConnection(@Nullable String);
@@ -66,6 +79,7 @@
method public void setDvrSourceConnection(@Nullable String);
method public void setFrontendConnection(@Nullable String);
method public void setHasFrontendConnection(@Nullable boolean);
+ method public void setOptionalFilters(@Nullable java.util.List<java.lang.String>);
method public void setVideoFilterConnection(@Nullable String);
}
@@ -73,10 +87,12 @@
ctor public DataFlowConfiguration.DvrPlayback();
method @Nullable public String getAudioFilterConnection();
method @Nullable public String getDvrConnection();
+ method @Nullable public java.util.List<java.lang.String> getOptionalFilters();
method @Nullable public String getSectionFilterConnection();
method @Nullable public String getVideoFilterConnection();
method public void setAudioFilterConnection(@Nullable String);
method public void setDvrConnection(@Nullable String);
+ method public void setOptionalFilters(@Nullable java.util.List<java.lang.String>);
method public void setSectionFilterConnection(@Nullable String);
method public void setVideoFilterConnection(@Nullable String);
}
@@ -97,17 +113,35 @@
method public void setRecordFilterConnection(@Nullable String);
}
+ public static class DataFlowConfiguration.LnbDescrambling {
+ ctor public DataFlowConfiguration.LnbDescrambling();
+ method @Nullable public String getAudioFilterConnection();
+ method @Nullable public String getDescramblerConnection();
+ method @Nullable public java.util.List<java.lang.String> getDiseqcMsgSender();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public String getLnbConnection();
+ method @Nullable public String getVideoFilterConnection();
+ method public void setAudioFilterConnection(@Nullable String);
+ method public void setDescramblerConnection(@Nullable String);
+ method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setLnbConnection(@Nullable String);
+ method public void setVideoFilterConnection(@Nullable String);
+ }
+
public static class DataFlowConfiguration.LnbLive {
ctor public DataFlowConfiguration.LnbLive();
method @Nullable public String getAudioFilterConnection();
method @Nullable public java.util.List<java.lang.String> getDiseqcMsgSender();
method @Nullable public String getFrontendConnection();
method @Nullable public String getLnbConnection();
+ method @Nullable public java.util.List<java.lang.String> getOptionalFilters();
method @Nullable public String getVideoFilterConnection();
method public void setAudioFilterConnection(@Nullable String);
method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
method public void setFrontendConnection(@Nullable String);
method public void setLnbConnection(@Nullable String);
+ method public void setOptionalFilters(@Nullable java.util.List<java.lang.String>);
method public void setVideoFilterConnection(@Nullable String);
}
@@ -117,11 +151,13 @@
method @Nullable public String getDvrRecordConnection();
method @Nullable public String getFrontendConnection();
method @Nullable public String getLnbConnection();
+ method @Nullable public java.util.List<java.lang.String> getOptionalFilters();
method @Nullable public String getRecordFilterConnection();
method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
method public void setDvrRecordConnection(@Nullable String);
method public void setFrontendConnection(@Nullable String);
method public void setLnbConnection(@Nullable String);
+ method public void setOptionalFilters(@Nullable java.util.List<java.lang.String>);
method public void setRecordFilterConnection(@Nullable String);
}
@@ -160,13 +196,25 @@
public class DvbsFrontendSettings {
ctor public DvbsFrontendSettings();
method @Nullable public java.math.BigInteger getInputStreamId();
+ method @Nullable public java.math.BigInteger getInversion();
method @Nullable public boolean getIsDiseqcRxMessage();
+ method @Nullable public java.math.BigInteger getModulation();
+ method @Nullable public java.math.BigInteger getPilot();
+ method @Nullable public java.math.BigInteger getRolloff();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbsScanType getScanType();
+ method @Nullable public java.math.BigInteger getStandard();
method @Nullable public java.math.BigInteger getSymbolRate();
+ method @Nullable public java.math.BigInteger getVcmMode();
method public void setInputStreamId(@Nullable java.math.BigInteger);
+ method public void setInversion(@Nullable java.math.BigInteger);
method public void setIsDiseqcRxMessage(@Nullable boolean);
+ method public void setModulation(@Nullable java.math.BigInteger);
+ method public void setPilot(@Nullable java.math.BigInteger);
+ method public void setRolloff(@Nullable java.math.BigInteger);
method public void setScanType(@Nullable android.media.tuner.testing.configuration.V1_0.DvbsScanType);
+ method public void setStandard(@Nullable java.math.BigInteger);
method public void setSymbolRate(@Nullable java.math.BigInteger);
+ method public void setVcmMode(@Nullable java.math.BigInteger);
}
public enum DvbsScanType {
@@ -310,6 +358,7 @@
public class Frontend {
ctor public Frontend();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.AtscFrontendSettings getAtscFrontendSettings_optional();
method @Nullable public java.math.BigInteger getConnectToCicamId();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings getDvbsFrontendSettings_optional();
method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings getDvbtFrontendSettings_optional();
@@ -317,8 +366,11 @@
method @Nullable public java.math.BigInteger getFrequency();
method @Nullable public String getId();
method @Nullable public boolean getIsSoftwareFrontend();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.IsdbsFrontendSettings getIsdbsFrontendSettings_optional();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.IsdbtFrontendSettings getIsdbtFrontendSettings_optional();
method @Nullable public java.math.BigInteger getRemoveOutputPid();
method @Nullable public android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum getType();
+ method public void setAtscFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.AtscFrontendSettings);
method public void setConnectToCicamId(@Nullable java.math.BigInteger);
method public void setDvbsFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings);
method public void setDvbtFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings);
@@ -326,10 +378,24 @@
method public void setFrequency(@Nullable java.math.BigInteger);
method public void setId(@Nullable String);
method public void setIsSoftwareFrontend(@Nullable boolean);
+ method public void setIsdbsFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.IsdbsFrontendSettings);
+ method public void setIsdbtFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.IsdbtFrontendSettings);
method public void setRemoveOutputPid(@Nullable java.math.BigInteger);
method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum);
}
+ public class FrontendIsdbtLayerSettings {
+ ctor public FrontendIsdbtLayerSettings();
+ method @Nullable public java.math.BigInteger getCoderate();
+ method @Nullable public java.math.BigInteger getModulation();
+ method @Nullable public java.math.BigInteger getNumOfSegment();
+ method @Nullable public java.math.BigInteger getTimeInterleave();
+ method public void setCoderate(@Nullable java.math.BigInteger);
+ method public void setModulation(@Nullable java.math.BigInteger);
+ method public void setNumOfSegment(@Nullable java.math.BigInteger);
+ method public void setTimeInterleave(@Nullable java.math.BigInteger);
+ }
+
public enum FrontendTypeEnum {
method @NonNull public String getRawName();
enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ANALOG;
@@ -422,6 +488,40 @@
method public void setSrcPort(@Nullable long);
}
+ public class IsdbsFrontendSettings {
+ ctor public IsdbsFrontendSettings();
+ method @Nullable public java.math.BigInteger getCoderate();
+ method @Nullable public java.math.BigInteger getModulation();
+ method @Nullable public java.math.BigInteger getRolloff();
+ method @Nullable public java.math.BigInteger getStreamId();
+ method @Nullable public java.math.BigInteger getStreamIdType();
+ method @Nullable public java.math.BigInteger getSymbolRate();
+ method public void setCoderate(@Nullable java.math.BigInteger);
+ method public void setModulation(@Nullable java.math.BigInteger);
+ method public void setRolloff(@Nullable java.math.BigInteger);
+ method public void setStreamId(@Nullable java.math.BigInteger);
+ method public void setStreamIdType(@Nullable java.math.BigInteger);
+ method public void setSymbolRate(@Nullable java.math.BigInteger);
+ }
+
+ public class IsdbtFrontendSettings {
+ ctor public IsdbtFrontendSettings();
+ method @Nullable public java.math.BigInteger getBandwidth();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.FrontendIsdbtLayerSettings getFrontendIsdbtLayerSettings();
+ method @Nullable public java.math.BigInteger getGuardInterval();
+ method @Nullable public java.math.BigInteger getInversion();
+ method @Nullable public java.math.BigInteger getMode();
+ method @Nullable public java.math.BigInteger getPartialReceptionFlag();
+ method @Nullable public java.math.BigInteger getServiceAreaId();
+ method public void setBandwidth(@Nullable java.math.BigInteger);
+ method public void setFrontendIsdbtLayerSettings(@Nullable android.media.tuner.testing.configuration.V1_0.FrontendIsdbtLayerSettings);
+ method public void setGuardInterval(@Nullable java.math.BigInteger);
+ method public void setInversion(@Nullable java.math.BigInteger);
+ method public void setMode(@Nullable java.math.BigInteger);
+ method public void setPartialReceptionFlag(@Nullable java.math.BigInteger);
+ method public void setServiceAreaId(@Nullable java.math.BigInteger);
+ }
+
public class Lnb {
ctor public Lnb();
method @Nullable public String getId();
@@ -462,6 +562,12 @@
enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_5V;
}
+ public class OptionalFilter {
+ ctor public OptionalFilter();
+ method @Nullable public String getFilterId();
+ method public void setFilterId(@Nullable String);
+ }
+
public class RecordFilterSettings {
ctor public RecordFilterSettings();
method @Nullable public android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum getScIndexType();
diff --git a/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml b/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml
index fefe86e..f74af7e 100644
--- a/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml
+++ b/tv/tuner/config/sample_tuner_vts_config_aidl_V1.xml
@@ -1,19 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (C) 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.
-->
-
<!-- The Sample Tuner Testing Configuration.
Name the customized xml with "tuner_vts_config.xml" and push into the device
"/vendor/etc" path. Please use "tuner_testing_dynamic_configuration.xsd" to verify the xml.
@@ -32,7 +28,6 @@
- The default settings can be found in the sample_tuner_vts_configurations.xml.
- The users can also override the default frontend settings using id="FE_DEFAULT".
- The users can configure 1 or more frontend elements in the frontends sections.
-
Each frontend element contain the following attributes:
"id": unique id of the frontend that could be used to connect to the test the
"dataFlowConfiguration"
@@ -46,7 +41,6 @@
output. Supported in Tuner 2.0 or higher.
"frequency": the frequency used to configure tune and scan.
"endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher.
-
Each frontend element also contains one and only one type-related "frontendSettings".
- The settings type should match the frontend "type" attribute.
- For example, when frontend type="DVBT", dvbtFrontendSettings can be configured.
@@ -54,14 +48,36 @@
config only to the hal.
-->
<frontends>
- <frontend id="FE_DEFAULT" type="DVBT" isSoftwareFrontend="true"
+ <frontend id="FE_DEFAULT" type="ISDBS" isSoftwareFrontend="true"
connectToCicamId="0" removeOutputPid="10" frequency="578000000"
endFrequency="800000000">
- <dvbtFrontendSettings bandwidth="8" transmissionMode="128" isHighPriority="1"/>
+ <isdbsFrontendSettings streamId="0" symbolRate="0" streamIdType="0" modulation="0" coderate="0" rolloff="0"/>
+ </frontend>
+ <frontend id="FE_ATSC_0" type="ATSC" isSoftwareFrontend="true"
+ connectToCicamId="0" removeOutputPid="10" frequency="578000000"
+ endFrequency="800000000">
+ <atscFrontendSettings inversion="0" modulation="0"/>
+ </frontend>
+ <frontend id="FE_ISDBT_0" type="ISDBT" isSoftwareFrontend="true"
+ connectToCicamId="0" removeOutputPid="10" frequency="578000000"
+ endFrequency="800000000">
+ <isdbtFrontendSettings serviceAreaId="0" inversion="0" bandwidth="0" mode="0" guardInterval="0" partialReceptionFlag="0">
+ <FrontendIsdbtLayerSettings modulation="0" coderate="0" timeInterleave="0" numOfSegment="0"/>
+ </isdbtFrontendSettings>
</frontend>
<frontend id="FE_DVBS_0" type="DVBS" isSoftwareFrontend="true"
connectToCicamId="0" removeOutputPid="10" frequency="578000000"
endFrequency="800000000">
+ <dvbsFrontendSettings inputStreamId="0" symbolRate="0" inversion="0" modulation="0" coderate="0"
+ rolloff="0" pilot="0" standard="0" vcmMode="0" scanType="0" isDiseqcRxMessage="true"/>
+ </frontend>
+ <frontend id="FE_DVBT_0" type="DVBT" isSoftwareFrontend="true"
+ connectToCicamId="0" removeOutputPid="10" frequency="578000000"
+ endFrequency="800000000">
+ <dvbtFrontendSettings bandwidth="8" transmissionMode="1" isHighPriority="1"
+ constellation="1" hierarchy="1" hpCoderate="1" lpCoderate="1"
+ guardInterval="1" standard="1" isMiso="0" plpMode="1"
+ plpId="0" plpGroupId="0"/>
</frontend>
</frontends>
<!-- Filter section:
@@ -71,7 +87,6 @@
- The users can also override the default filter settings using
- id="FILTER_AUDIO_DEFAULT" or "FILTER_VIDEO_DEFAULT".
- The users can configure 1 or more filter elements in the filters sections.
-
Each filter element contain the following attributes:
"id": unique id of the filter that could be used to connect to the test the
"dataFlowConfiguration"
@@ -80,7 +95,6 @@
"bufferSize": the buffer size of the filter in hex.
"pid": the pid that would be used to configure the filter.
"useFMQ": if the filter uses FMQ.
-
Each filter element also contains at most one type-related "filterSettings".
- The settings type should match the filter "subType" attribute.
- For example, when filter subType is audio or video, the avFilterSettings can be
@@ -89,6 +103,9 @@
only to the hal.
-->
<filters>
+ <filter id="FILTER_SECTION_DEFAULT" mainType="TS" subType="SECTION"
+ bufferSize="16777216" pid="257" useFMQ="false" monitorEventTypes="3">
+ </filter>
<filter id="FILTER_AUDIO_DEFAULT" mainType="TS" subType="AUDIO"
bufferSize="16777216" pid="257" useFMQ="false" monitorEventTypes="3">
<avFilterSettings isPassthrough="false" isSecureMemory="false">
@@ -116,7 +133,6 @@
This section contains configurations of all the dvrs that would be used in the tests.
- This section is optional and can be skipped if DVR is not supported.
- The users can configure 1 or more dvr elements in the dvrs sections.
-
Each dvr element contain the following attributes:
"id": unique id of the dvr that could be used to connect to the test the
"dataFlowConfiguration"
@@ -140,8 +156,21 @@
statusMask="15" lowThreshold="4096" highThreshold="32767"
dataFormat="ES" packetSize="188" inputFilePath="/data/local/tmp/test.es"/>
</dvrs>
+ <descramblers>
+ <descrambler id="DESCRAMBLER_0" casSystemId="63192"/>
+ </descramblers>
+ <timeFilters>
+ <timeFilter id="TIME_FILTER_0" timeStamp="1"/>
+ </timeFilters>
+ <diseqcMessages>
+ <diseqcMessage msgName="DISEQC_POWER_ON" msgBody="14 0 0 0 0 3"/>
+ </diseqcMessages>
+ <lnbs>
+ <lnb id="LNB_0" voltage="VOLTAGE_12V" tone="NONE" position="UNDEFINED"/>
+ <lnb id="LNB_1" name="default_lnb_external" voltage="VOLTAGE_5V"
+ tone="NONE" position="UNDEFINED"/>
+ </lnbs>
</hardwareConfiguration>
-
<!-- Data flow configuration section connects each data flow under test to the ids of the
hardwares that would be used during the tests. -->
<dataFlowConfiguration>
@@ -151,13 +180,39 @@
ipFilterConnection="FILTER_IP_IP_0"
dvrSoftwareFeConnection="DVR_PLAYBACK_1"/>
<scan frontendConnection="FE_DEFAULT"/>
- <dvrPlayback dvrConnection="DVR_PLAYBACK_1"
- audioFilterConnection="FILTER_AUDIO_DEFAULT"
- videoFilterConnection="FILTER_VIDEO_DEFAULT"/>
<dvrRecord hasFrontendConnection="true"
frontendConnection="FE_DEFAULT"
recordFilterConnection="FILTER_TS_RECORD_0"
dvrRecordConnection="DVR_RECORD_0"
dvrSoftwareFeConnection="DVR_PLAYBACK_1"/>
+ <dvrPlayback dvrConnection="DVR_PLAYBACK_1"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ sectionFilterConnection="FILTER_SECTION_DEFAULT"/>
+ <descrambling hasFrontendConnection="true"
+ frontendConnection="FE_DEFAULT"
+ descramblerConnection="DESCRAMBLER_0"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ dvrSoftwareFeConnection="DVR_PLAYBACK_0"
+ dvrSourceConnection="DVR_PLAYBACK_1">
+ </descrambling>
+ <timeFilter timeFilterConnection="TIME_FILTER_0"/>
+ <lnbLive frontendConnection="FE_DVBS_0"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ lnbConnection="LNB_1"
+ diseqcMsgSender="DISEQC_POWER_ON"/>
+ <lnbRecord frontendConnection="FE_DVBS_0"
+ recordFilterConnection="FILTER_TS_RECORD_0"
+ dvrRecordConnection="DVR_RECORD_0"
+ lnbConnection="LNB_0"
+ diseqcMsgSender="DISEQC_POWER_ON"/>
+ <lnbDescrambling frontendConnection="FE_DVBS_0"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ lnbConnection="LNB_1"
+ descramblerConnection="DESCRAMBLER_0"/>
+
</dataFlowConfiguration>
</TunerConfiguration>
diff --git a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
index 59abd9a..c51ac51 100644
--- a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -77,8 +77,41 @@
<xs:complexType name="dvbsFrontendSettings">
<xs:attribute name="inputStreamId" type="xs:nonNegativeInteger" use="required"/>
<xs:attribute name="symbolRate" type="xs:nonNegativeInteger" use="required"/>
- <xs:attribute name="scanType" type="dvbsScanType" use="optional"/>
- <xs:attribute name="isDiseqcRxMessage" type="xs:boolean" use="optional"/>
+ <xs:attribute name="scanType" type="dvbsScanType" use="required"/>
+ <xs:attribute name="isDiseqcRxMessage" type="xs:boolean" use="required"/>
+ <xs:attribute name="inversion" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="modulation" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="pilot" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="standard" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="vcmMode" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="rolloff" type="xs:nonNegativeInteger" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="atscFrontendSettings">
+ <xs:attribute name="inversion" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="modulation" type="xs:nonNegativeInteger" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="isdbsFrontendSettings">
+ <xs:attribute name="streamId" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="symbolRate" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="streamIdType" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="modulation" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="coderate" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="rolloff" type="xs:nonNegativeInteger" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="isdbtFrontendSettings">
+ <xs:attribute name="serviceAreaId" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="inversion" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="bandwidth" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="mode" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="guardInterval" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="partialReceptionFlag" type="xs:nonNegativeInteger" use="required"/>
+ <xs:element name="FrontendIsdbtLayerSettings" type="FrontendIsdbtLayerSettings"/>
+ </xs:complexType>
+ <xs:complexType name="FrontendIsdbtLayerSettings">
+ <xs:attribute name="modulation" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="coderate" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="timeInterleave" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="numOfSegment" type="xs:nonNegativeInteger" use="required"/>
</xs:complexType>
<xs:complexType name="frontend">
@@ -109,16 +142,16 @@
</xs:annotation>
<xs:choice minOccurs="0" maxOccurs="1">
<!-- TODO: b/182519645 finish all the frontend settings structures. -->
- <!--xs:element name="analog" type="analogSettings"/>
- <xs:element name="atsc" type="atscSettings"/>
- <xs:element name="atsc3" type="atsc3Settings"/>
+ <!--xs:element name="analog" type="analogSettings"/-->
+ <xs:element name="atscFrontendSettings" type="atscFrontendSettings"/>
+ <!--xs:element name="atsc3" type="atsc3Settings"/>
<xs:element name="dvbc" type="dvbcSettings"/-->
<xs:element name="dvbsFrontendSettings" type="dvbsFrontendSettings"/>
<xs:element name="dvbtFrontendSettings" type="dvbtFrontendSettings"/>
- <!--xs:element name="isdbs" type="isdbsSettings"/>
- <xs:element name="isdbs3" type="isdbs3Settings"/>
- <xs:element name="isdbt" type="isdbtSettings"/>
- <xs:element name="dtmb" type="dtmbSettings"/-->
+ <xs:element name="isdbsFrontendSettings" type="isdbsFrontendSettings"/>
+ <!--xs:element name="isdbs3" type="isdbs3Settings"/-->
+ <xs:element name="isdbtFrontendSettings" type="isdbtFrontendSettings"/>
+ <!--xs:element name="dtmb" type="dtmbSettings"/-->
</xs:choice>
<xs:attribute name="id" type="frontendId" use="required"/>
<xs:attribute name="type" type="frontendTypeEnum" use="required"/>
@@ -617,7 +650,7 @@
<xs:attribute name="pcrFilterConnection" type="filterId" use="optional"/>
<xs:attribute name="sectionFilterConnection" type="filterId" use="optional"/>
<xs:attribute name="ipFilterConnection" type="filterId" use="optional"/>
- <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ <xs:element name="optionalFilters" type="filterConnections" minOccurs="0" maxOccurs="1"/>
<!-- DVR is only required when the frontend is using the software input -->
<xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
</xs:complexType>
@@ -638,7 +671,7 @@
<xs:attribute name="descramblerConnection" type="descramblerId" use="required"/>
<xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
<xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
- <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ <xs:element name="optionalFilters" type="filterConnections" minOccurs="0" maxOccurs="1"/>
<!-- This DVR is only required when the frontend is using the software input -->
<xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
<!-- This Dvr is only required when there's no frontend(sw or hw) connection -->
@@ -651,7 +684,7 @@
<xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
<xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
<xs:attribute name="sectionFilterConnection" type="filterId" use="optional"/>
- <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ <xs:element name="optionalFilters" type="filterConnections" minOccurs="0" maxOccurs="1"/>
</xs:complexType>
</xs:element>
<xs:element name="dvrRecord" minOccurs="0" maxOccurs="1">
@@ -675,7 +708,7 @@
<xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
<xs:attribute name="lnbConnection" type="lnbId" use="required"/>
<xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
- <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ <xs:element name="optionalFilters" type="filterConnections" minOccurs="0" maxOccurs="1"/>
</xs:complexType>
</xs:element>
<xs:element name="lnbRecord" minOccurs="0" maxOccurs="1">
@@ -685,6 +718,7 @@
<xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
<xs:attribute name="lnbConnection" type="lnbId" use="required"/>
<xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
+ <xs:element name="optionalFilters" type="filterConnections" minOccurs="0" maxOccurs="1"/>
</xs:complexType>
</xs:element>
<xs:element name="timeFilter" minOccurs="0" maxOccurs="1">
@@ -692,6 +726,16 @@
<xs:attribute name="timeFilterConnection" type="timeFilterId" use="required"/>
</xs:complexType>
</xs:element>
+ <xs:element name="lnbDescrambling" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+ <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="lnbConnection" type="lnbId" use="required"/>
+ <xs:attribute name="descramblerConnection" type="descramblerId" use="required"/>
+ <xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
+ </xs:complexType>
+ </xs:element>
</xs:sequence>
</xs:complexType>
@@ -729,4 +773,7 @@
<xs:field xpath="@id"/>
</xs:key>
</xs:element>
+ <xs:complexType name="optionalFilter">
+ <xs:attribute name="filterId" type="filterId" use="required"/>
+ </xs:complexType>
</xs:schema>
diff --git a/uwb/aidl/Android.bp b/uwb/aidl/Android.bp
index 52f0605..7dc2b7f 100755
--- a/uwb/aidl/Android.bp
+++ b/uwb/aidl/Android.bp
@@ -47,6 +47,7 @@
aidl_interface {
name: "android.hardware.uwb.fira_android",
+ owner: "google",
vendor_available: true,
srcs: ["android/hardware/uwb/fira_android/*.aidl"],
stability: "vintf",
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index cbe2068..39bb5d9 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -44,4 +44,8 @@
CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 166,
CCC_SUPPORTED_RAN_MULTIPLIER = 167,
SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 227,
+ SUPPORTED_MIN_RANGING_INTERVAL_MS = 228,
+ SUPPORTED_RANGE_DATA_NTF_CONFIG = 229,
+ SUPPORTED_RSSI_REPORTING = 230,
+ SUPPORTED_DIAGNOSTICS = 231,
}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index cd2e122..fbcfbff 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -36,4 +36,5 @@
enum UwbVendorGidAndroidOids {
ANDROID_GET_POWER_STATS = 0,
ANDROID_SET_COUNTRY_CODE = 1,
+ ANDROID_RANGE_DIAGNOSTICS = 2,
}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index d35728f..8413f06 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -43,4 +43,6 @@
NB_OF_RANGE_MEASUREMENTS = 227,
NB_OF_AZIMUTH_MEASUREMENTS = 228,
NB_OF_ELEVATION_MEASUREMENTS = 229,
+ ENABLE_DIAGNOSTICS = 232,
+ DIAGRAMS_FRAME_REPORTS_FIELDS = 233,
}
diff --git a/uwb/aidl/android/hardware/uwb/IUwbChip.aidl b/uwb/aidl/android/hardware/uwb/IUwbChip.aidl
index 00cb8e0..6ee5799 100644
--- a/uwb/aidl/android/hardware/uwb/IUwbChip.aidl
+++ b/uwb/aidl/android/hardware/uwb/IUwbChip.aidl
@@ -71,8 +71,8 @@
* The UCI message format is as per UCI protocol and it is
* defined in "FiRa Consortium - UCI Generic Specification_v1.0" specification at FiRa
* consortium.
- * WIP doc link: https://groups.firaconsortium.org/wg/Technical/document/folder/127.
- * TODO(b/196004116): Link to the published specification.
+ *
+ * UCI 1.1 specification: https://groups.firaconsortium.org/wg/members/document/1949.
*
* This method may queue writes and return immediately, or it may block until data is written.
* Implementation must guarantee that writes are executed in order.
diff --git a/uwb/aidl/android/hardware/uwb/IUwbClientCallback.aidl b/uwb/aidl/android/hardware/uwb/IUwbClientCallback.aidl
index 75853cd..f31aeba 100755
--- a/uwb/aidl/android/hardware/uwb/IUwbClientCallback.aidl
+++ b/uwb/aidl/android/hardware/uwb/IUwbClientCallback.aidl
@@ -28,8 +28,7 @@
* can use to pass incoming data to the stack. These include UCI
* responses and notifications from the UWB subsystem.
*
- * WIP doc link: https://groups.firaconsortium.org/wg/Technical/document/folder/127.
- * TODO(b/196004116): Link to the published specification.
+ * UCI 1.1 specification: https://groups.firaconsortium.org/wg/members/document/1949.
*
* @param data UCI packet sent.
*/
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index 0140fdd..86479fb 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -150,4 +150,33 @@
* 0 - Feature not supported.
*/
SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xE3,
+
+ /**
+ * 4 byte value to indicate supported min ranging interval in ms.
+ */
+ SUPPORTED_MIN_RANGING_INTERVAL_MS = 0xE4,
+
+ /**
+ * 4 byte bitmask to indicate the supported RANGE_DATA_NTF_CONFIG values
+ *
+ * Bitmask where each bit corresponds to values used in
+ * RANGE_DATA_NTF_CONFIG in SET_APP_CFG_CMD
+ */
+ SUPPORTED_RANGE_DATA_NTF_CONFIG = 0xE5,
+
+ /**
+ * 1 byte bitmask to indicate the supported RSSI_REPORTING values
+ * Values:
+ * 1 - Feature supported.
+ * 0 - Feature not supported.
+ */
+ SUPPORTED_RSSI_REPORTING = 0xE6,
+
+ /**
+ * 1 byte value to indicate support for diagnostics feature.
+ * Values:
+ * 1 - Feature supported.
+ * 0 - Feature not supported.
+ */
+ SUPPORTED_DIAGNOSTICS = 0xE7,
}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
index e389a2d..42d52f1 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorGidAndroidOids.aidl
@@ -31,4 +31,8 @@
// Used to set the current regulatory country code (determined usinag
// SIM or hardcoded by OEM).
ANDROID_SET_COUNTRY_CODE = 0x1,
+ // Used by the notification to get UWB ranging diagnostics stats.
+ // Supported only if the UwbVendorCapabilityTlvTypes.SUPPORTED_DIAGNOSTICS set
+ // to 1.
+ ANDROID_RANGE_DIAGNOSTICS = 0x2,
}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
index f43b249..f303ed9 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorSessionAppConfigTlvTypes.aidl
@@ -66,4 +66,23 @@
NB_OF_AZIMUTH_MEASUREMENTS = 0xE4,
/** 1 byte data */
NB_OF_ELEVATION_MEASUREMENTS = 0xE5,
+
+ /**
+ * Supported only if the UwbVendorCapabilityTlvTypes
+ * .SUPPORTED_DIAGNOSTICS set to 1.
+ */
+ /**
+ * 1 byte data
+ * 1 - Enable,
+ * 0 - Disable
+ */
+ ENABLE_DIAGNOSTICS = 0xE8,
+ /**
+ * 1 byte bitmask
+ * b0: Activate RSSIs field,
+ * b1: Activate AoAs field,
+ * b2: Activate CIRs field,
+ * b3 - b7: RFU
+ */
+ DIAGRAMS_FRAME_REPORTS_FIELDS = 0xE9,
}
diff --git a/wifi/netlinkinterceptor/libnlinterceptor/include/libnlinterceptor/libnlinterceptor.h b/wifi/netlinkinterceptor/libnlinterceptor/include/libnlinterceptor/libnlinterceptor.h
index ac8653e..32e5a6e 100644
--- a/wifi/netlinkinterceptor/libnlinterceptor/include/libnlinterceptor/libnlinterceptor.h
+++ b/wifi/netlinkinterceptor/libnlinterceptor/include/libnlinterceptor/libnlinterceptor.h
@@ -117,14 +117,13 @@
int clientSocketFd, const char* clientName,
struct android_nlinterceptor_InterceptedSocket* interceptedSocket);
-void android_nlinterceptor_closeSocket(
- const struct android_nlinterceptor_InterceptedSocket* sock);
+void android_nlinterceptor_closeSocket(struct android_nlinterceptor_InterceptedSocket sock);
-bool android_nlinterceptor_subscribe(
- const struct android_nlinterceptor_InterceptedSocket* sock, uint32_t group);
+bool android_nlinterceptor_subscribe(struct android_nlinterceptor_InterceptedSocket sock,
+ uint32_t group);
-bool android_nlinterceptor_unsubscribe(
- const struct android_nlinterceptor_InterceptedSocket* sock, uint32_t group);
+bool android_nlinterceptor_unsubscribe(struct android_nlinterceptor_InterceptedSocket sock,
+ uint32_t group);
#ifdef __cplusplus
}
diff --git a/wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp b/wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp
index 575f900..aae7a3a 100644
--- a/wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp
+++ b/wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp
@@ -150,25 +150,18 @@
return true;
}
-extern "C" void android_nlinterceptor_closeSocket(
- const android_nlinterceptor_InterceptedSocket* sock) {
- if (!sock) {
- LOG(ERROR) << "Can't close socket identified by a null pointer!";
- return;
- }
- closeSocket({sock->nlFamily, sock->portId});
+extern "C" void android_nlinterceptor_closeSocket(android_nlinterceptor_InterceptedSocket sock) {
+ closeSocket({sock.nlFamily, sock.portId});
}
-extern "C" bool android_nlinterceptor_subscribe(
- const android_nlinterceptor_InterceptedSocket* sock, uint32_t group) {
- if (!sock) return false;
- return subscribe({sock->nlFamily, sock->portId}, group);
+extern "C" bool android_nlinterceptor_subscribe(android_nlinterceptor_InterceptedSocket sock,
+ uint32_t group) {
+ return subscribe({sock.nlFamily, sock.portId}, group);
}
-extern "C" bool android_nlinterceptor_unsubscribe(
- const android_nlinterceptor_InterceptedSocket* sock, uint32_t group) {
- if (!sock) return false;
- return unsubscribe({sock->nlFamily, sock->portId}, group);
+extern "C" bool android_nlinterceptor_unsubscribe(android_nlinterceptor_InterceptedSocket sock,
+ uint32_t group) {
+ return unsubscribe({sock.nlFamily, sock.portId}, group);
}
} // namespace android::nlinterceptor
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
index d95bd03..b7984fa 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_p2p_iface_aidl_test.cpp
@@ -19,6 +19,7 @@
#include <aidl/Vintf.h>
#include <aidl/android/hardware/wifi/supplicant/BnSupplicant.h>
#include <aidl/android/hardware/wifi/supplicant/BnSupplicantP2pIfaceCallback.h>
+#include <aidl/android/hardware/wifi/supplicant/SupplicantStatusCode.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
#include <binder/IServiceManager.h>
@@ -38,6 +39,7 @@
using aidl::android::hardware::wifi::supplicant::P2pGroupCapabilityMask;
using aidl::android::hardware::wifi::supplicant::P2pProvDiscStatusCode;
using aidl::android::hardware::wifi::supplicant::P2pStatusCode;
+using aidl::android::hardware::wifi::supplicant::SupplicantStatusCode;
using aidl::android::hardware::wifi::supplicant::WpsConfigMethods;
using aidl::android::hardware::wifi::supplicant::WpsDevPasswordId;
using aidl::android::hardware::wifi::supplicant::WpsProvisionMethod;
@@ -413,7 +415,12 @@
*/
TEST_P(SupplicantP2pIfaceAidlTest, EnableMacRandomization) {
// Enable twice
- EXPECT_TRUE(p2p_iface_->setMacRandomization(true).isOk());
+ auto status = p2p_iface_->setMacRandomization(true);
+ if (!status.isOk() && status.getServiceSpecificError() ==
+ static_cast<int32_t>(SupplicantStatusCode::FAILURE_UNSUPPORTED)) {
+ GTEST_SKIP() << "Mac randomization is not supported.";
+ }
+ EXPECT_TRUE(status.isOk());
EXPECT_TRUE(p2p_iface_->setMacRandomization(true).isOk());
// Disable twice