Merge changes from topic "presubmit-am-9e9eaaa4b9b24f33a875009fb4a57728"
* changes:
[automerge] Add a new capability to indcate min slot duration. 2p: f5535531e7
Add a new capability to indcate min slot duration.
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index e58ae6a..8c32f14 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -91,12 +91,14 @@
name: "android.hardware.audio.core",
vendor_available: true,
srcs: [
+ "android/hardware/audio/core/AudioMode.aidl",
"android/hardware/audio/core/AudioPatch.aidl",
"android/hardware/audio/core/AudioRoute.aidl",
"android/hardware/audio/core/IConfig.aidl",
"android/hardware/audio/core/IModule.aidl",
"android/hardware/audio/core/IStreamIn.aidl",
"android/hardware/audio/core/IStreamOut.aidl",
+ "android/hardware/audio/core/ITelephony.aidl",
"android/hardware/audio/core/MmapBufferDescriptor.aidl",
"android/hardware/audio/core/ModuleDebug.aidl",
"android/hardware/audio/core/StreamDescriptor.aidl",
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
new file mode 100644
index 0000000..336f9b5
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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;
+@Backing(type="int") @VintfStability
+enum AudioMode {
+ NORMAL = 0,
+ RINGTONE = 1,
+ IN_CALL = 2,
+ IN_COMMUNICATION = 3,
+ CALL_SCREEN = 4,
+}
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 a8bbb15..be382c5 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
@@ -35,6 +35,7 @@
@VintfStability
interface IModule {
void setModuleDebug(in android.hardware.audio.core.ModuleDebug debug);
+ @nullable android.hardware.audio.core.ITelephony getTelephony();
android.media.audio.common.AudioPort connectExternalDevice(in android.media.audio.common.AudioPort templateIdAndAdditionalData);
void disconnectExternalDevice(int portId);
android.hardware.audio.core.AudioPatch[] getAudioPatches();
@@ -49,6 +50,15 @@
boolean setAudioPortConfig(in android.media.audio.common.AudioPortConfig requested, out android.media.audio.common.AudioPortConfig suggested);
void resetAudioPatch(int patchId);
void resetAudioPortConfig(int portConfigId);
+ boolean getMasterMute();
+ void setMasterMute(boolean mute);
+ float getMasterVolume();
+ void setMasterVolume(float volume);
+ boolean getMicMute();
+ void setMicMute(boolean mute);
+ void updateAudioMode(android.hardware.audio.core.AudioMode mode);
+ void updateScreenRotation(android.hardware.audio.core.IModule.ScreenRotation rotation);
+ void updateScreenState(boolean isTurnedOn);
@VintfStability
parcelable OpenInputStreamArguments {
int portConfigId;
@@ -72,4 +82,11 @@
android.hardware.audio.core.IStreamOut stream;
android.hardware.audio.core.StreamDescriptor desc;
}
+ @Backing(type="int") @VintfStability
+ enum ScreenRotation {
+ DEG_0 = 0,
+ DEG_90 = 1,
+ DEG_180 = 2,
+ DEG_270 = 3,
+ }
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl
new file mode 100644
index 0000000..a8c58c1
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.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.audio.core;
+@VintfStability
+interface ITelephony {
+ android.hardware.audio.core.AudioMode[] getSupportedAudioModes();
+ void switchAudioMode(android.hardware.audio.core.AudioMode mode);
+}
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
index da24a10..3a77ad1 100644
--- 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
@@ -55,19 +55,15 @@
DRAIN_PAUSED = 6,
ERROR = 100,
}
- @Backing(type="int") @VintfStability
- enum CommandCode {
- START = 1,
- BURST = 2,
- DRAIN = 3,
- STANDBY = 4,
- PAUSE = 5,
- FLUSH = 6,
- }
@FixedSize @VintfStability
- parcelable Command {
- android.hardware.audio.core.StreamDescriptor.CommandCode code = android.hardware.audio.core.StreamDescriptor.CommandCode.START;
- int fmqByteCount;
+ union Command {
+ int hal_reserved_exit;
+ android.media.audio.common.Void start;
+ int burst;
+ android.media.audio.common.Void drain;
+ android.media.audio.common.Void standby;
+ android.media.audio.common.Void pause;
+ android.media.audio.common.Void flush;
}
@FixedSize @VintfStability
parcelable Reply {
diff --git a/audio/aidl/android/hardware/audio/core/AudioMode.aidl b/audio/aidl/android/hardware/audio/core/AudioMode.aidl
new file mode 100644
index 0000000..0943a55
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/AudioMode.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;
+
+/**
+ * The audio mode describes states of the audio system of the device that
+ * can significantly affect the rules of audio routing, volume control, etc.
+ * The audio mode is controlled by the framework, however the HAL has some
+ * flexibility in the choice of modes to support, see 'IModule.updateAudioMode'.
+ */
+@VintfStability
+@Backing(type="int")
+enum AudioMode {
+ /** No active calls. */
+ NORMAL = 0,
+ /** The device is playing the ringtone. */
+ RINGTONE = 1,
+ /** The call is handled by the telephony stack ("voice call"). */
+ IN_CALL = 2,
+ /** The call is handled by an application ("VoIP call"). */
+ IN_COMMUNICATION = 3,
+ /** Call screening is in progress. */
+ CALL_SCREEN = 4,
+}
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 0959840..be40051 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -18,10 +18,12 @@
import android.hardware.audio.common.SinkMetadata;
import android.hardware.audio.common.SourceMetadata;
+import android.hardware.audio.core.AudioMode;
import android.hardware.audio.core.AudioPatch;
import android.hardware.audio.core.AudioRoute;
import android.hardware.audio.core.IStreamIn;
import android.hardware.audio.core.IStreamOut;
+import android.hardware.audio.core.ITelephony;
import android.hardware.audio.core.ModuleDebug;
import android.hardware.audio.core.StreamDescriptor;
import android.media.audio.common.AudioOffloadInfo;
@@ -60,6 +62,19 @@
void setModuleDebug(in ModuleDebug debug);
/**
+ * Retrieve the interface to control telephony audio.
+ *
+ * If the HAL module supports telephony functions, it must return an
+ * instance of the ITelephony interface. The same instance must be returned
+ * during the lifetime of the HAL module. If the HAL module does not support
+ * telephony, a null must be returned, without throwing any errors.
+ *
+ * @return An instance of the ITelephony interface implementation.
+ * @throws EX_ILLEGAL_STATE If there was an error creating an instance.
+ */
+ @nullable ITelephony getTelephony();
+
+ /**
* 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
@@ -487,4 +502,140 @@
* - If the port config is used by a patch.
*/
void resetAudioPortConfig(int portConfigId);
+
+ /**
+ * Get the current state of audio output muting.
+ *
+ * If the HAL module supports muting its combined output completely,
+ * this method returns whether muting is currently enabled.
+ *
+ * Note that muting operates independently from the master volume.
+ *
+ * @return Whether the output from the module is muted.
+ * @throws EX_UNSUPPORTED_OPERATION If muting of combined output
+ * is not supported by the module.
+ */
+ boolean getMasterMute();
+
+ /**
+ * Set the current value of the audio output muting.
+ *
+ * If the HAL module supports muting its combined output completely, this
+ * method controls the mute. Note that for modules supporting telephony,
+ * muting does not affect the voice call.
+ *
+ * For HAL modules not supporting this operation, it's functionality is
+ * typically emulated by the client, in the digital domain.
+ *
+ * @param mute Whether the output from the module is muted.
+ * @throws EX_UNSUPPORTED_OPERATION If muting of combined output
+ * is not supported by the module.
+ */
+ void setMasterMute(boolean mute);
+
+ /**
+ * Get the current value of the audio output attenuation.
+ *
+ * If the HAL module supports attenuating the level its combined output,
+ * this method returns the current attenuation value.
+ *
+ * @return Volume 1.0f means no attenuation (unity), 0.0f is mute.
+ * @throws EX_UNSUPPORTED_OPERATION If attenuation of combined output
+ * is not supported by the module.
+ */
+ float getMasterVolume();
+
+ /**
+ * Set the current value of the audio output attenuation.
+ *
+ * If the HAL module supports attenuating the level its combined output,
+ * this method sets the attenuation value. Note that for modules supporting
+ * telephony, the attenuation of the voice call volume is set separately
+ * via ITelephony interface.
+ *
+ * For HAL modules not supporting this operation, it's functionality is
+ * typically emulated by the client, in the digital domain.
+ *
+ * @param volume The new value, 1.0f means no attenuation (unity), 0.0f is mute.
+ * @throws EX_ILLEGAL_ARGUMENT If the value of the volume is outside of
+ * accepted range.
+ * @throws EX_UNSUPPORTED_OPERATION If attenuation of combined output
+ * is not supported by the module.
+ */
+ void setMasterVolume(float volume);
+
+ /**
+ * Get the current state of audio input muting.
+ *
+ * If the HAL module supports muting its external input, this method returns
+ * whether muting is currently enabled.
+ *
+ * @return Whether the input is muted.
+ * @throws EX_UNSUPPORTED_OPERATION If muting of input is not supported by
+ * the module.
+ */
+ boolean getMicMute();
+
+ /**
+ * Set the current value of the audio input muting.
+ *
+ * If the HAL module supports muting its external input, this method
+ * controls the mute.
+ *
+ * For HAL modules not supporting this operation, it's functionality is
+ * emulated by the client.
+ *
+ * @param mute Whether input is muted.
+ * @throws EX_UNSUPPORTED_OPERATION If muting of input is not supported by
+ * the module.
+ */
+ void setMicMute(boolean mute);
+
+ /**
+ * Notify the HAL module on the change of the current audio mode.
+ *
+ * The current audio mode is always controlled by the client. This is an
+ * informative notification sent to all modules, no reply is needed. The HAL
+ * module should silently ignore this notification if it does not need to
+ * be aware of the current audio mode.
+ *
+ * The client sends this notification to all HAL modules after successfully
+ * switching the telephony module by calling the 'ITelephony.switchAudioMode'
+ * method.
+ *
+ * @param mode The current mode.
+ */
+ void updateAudioMode(AudioMode mode);
+
+ @VintfStability
+ @Backing(type="int")
+ enum ScreenRotation {
+ /** Natural orientation. */
+ DEG_0 = 0,
+ DEG_90 = 1,
+ /** Upside down. */
+ DEG_180 = 2,
+ DEG_270 = 3,
+ }
+ /**
+ * Notify the HAL module on the change of the screen rotation.
+ *
+ * Informs the HAL of the current orientation of the device screen. This
+ * information can be used to optimize the output of built-in speakers.
+ * This is an informative notification sent to all modules, no reply is
+ * needed.
+ *
+ * @param rotation The current rotation.
+ */
+ void updateScreenRotation(ScreenRotation rotation);
+
+ /**
+ * Notify the HAL module on the change of the screen state.
+ *
+ * Informs the HAL whether the screen of the device is turned on. This is an
+ * informative notification sent to all modules, no reply is needed.
+ *
+ * @param isTurnedOn True if the screen is turned on.
+ */
+ void updateScreenState(boolean isTurnedOn);
}
diff --git a/audio/aidl/android/hardware/audio/core/ITelephony.aidl b/audio/aidl/android/hardware/audio/core/ITelephony.aidl
new file mode 100644
index 0000000..a872c7c
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/ITelephony.aidl
@@ -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.
+ */
+
+package android.hardware.audio.core;
+
+import android.hardware.audio.core.AudioMode;
+
+/**
+ * An instance of ITelephony manages settings which are specific to voice calls
+ * and SMS messaging functionality. This interface is optional to implement and
+ * provide by the vendor. It needs to be provided only if the device actually
+ * supports telephony.
+ */
+@VintfStability
+interface ITelephony {
+ /**
+ * Return the list of supported audio modes.
+ *
+ * The first 4 AudioModes: NORMAL, RINGTONE, IN_CALL, IN_COMMUNICATION must
+ * be supported by all implementations.
+ *
+ * This method is only called once, during the audio system initialization,
+ * and must return the same result all the time.
+ *
+ * @return The list of supported audio modes.
+ */
+ AudioMode[] getSupportedAudioModes();
+
+ /**
+ * Switch the HAL into a new audio mode.
+ *
+ * The current audio mode is always controlled by the client. The HAL must
+ * accept all modes returned by 'getSupportedAudioModes' and reject the
+ * rest. The HAL must return from this method only after switching itself
+ * to the specified mode, or throw an error if there was a problem during
+ * switching.
+ *
+ * @param mode The mode to switch to.
+ * @throws EX_UNSUPPORTED_OPERATION If the HAL does not support the specified mode.
+ * @throws EX_ILLEGAL_STATE If there was an error during switching.
+ */
+ void switchAudioMode(AudioMode mode);
+}
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
index e5e56fc..2b1fc99 100644
--- a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -19,6 +19,7 @@
import android.hardware.audio.core.MmapBufferDescriptor;
import android.hardware.common.fmq.MQDescriptor;
import android.hardware.common.fmq.SynchronizedReadWrite;
+import android.media.audio.common.Void;
/**
* Stream descriptor contains fast message queues and buffers used for sending
@@ -177,76 +178,41 @@
ERROR = 100,
}
- @VintfStability
- @Backing(type="int")
- enum CommandCode {
- /**
- * See the state machines on the applicability of this command to
- * different states. The 'fmqByteCount' field must always be set to 0.
- */
- START = 1,
- /**
- * The BURST command used for audio I/O, see 'AudioBuffer'. Differences
- * for the 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.
- * The client does not synchronize reads and writes into the buffer
- * with sending of this command.
- *
- * - the 'fmqByteCount' must always be set to 0.
- */
- BURST = 2,
- /**
- * See the state machines on the applicability of this command to
- * different states. The 'fmqByteCount' field must always be set to 0.
- */
- DRAIN = 3,
- /**
- * See the state machines on the applicability of this command to
- * different states. The 'fmqByteCount' field must always be set to 0.
- *
- * Note that it's left on the discretion of the HAL implementation to
- * assess all the necessary conditions that could prevent hardware from
- * being suspended. Even if it can not be suspended, the state machine
- * must still enter the 'STANDBY' state for consistency. Since the
- * buffer must remain empty in this state, even if capturing hardware is
- * still active, captured data must be discarded.
- */
- STANDBY = 4,
- /**
- * See the state machines on the applicability of this command to
- * different states. The 'fmqByteCount' field must always be set to 0.
- */
- PAUSE = 5,
- /**
- * See the state machines on the applicability of this command to
- * different states. The 'fmqByteCount' field must always be set to 0.
- */
- FLUSH = 6,
- }
-
/**
* 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.
+ *
+ * Variants of type 'Void' correspond to commands without
+ * arguments. Variants of other types correspond to commands with an
+ * argument. Would in future a need for a command with multiple argument
+ * arise, a Parcelable type should be used for the corresponding variant.
*/
@VintfStability
@FixedSize
- parcelable Command {
+ union Command {
/**
- * The code of the command.
+ * Reserved for the HAL implementation to allow unblocking the wait on a
+ * command and exiting the I/O thread. A command of this variant must
+ * never be sent from the client side. To prevent that, the
+ * implementation must pass a random cookie as the command argument,
+ * which is only known to the implementation.
*/
- CommandCode code = CommandCode.START;
+ int hal_reserved_exit;
/**
- * This field is only used for the BURST command. For all other commands
- * it must be set to 0. The following description applies to the use
- * of this field for the BURST command.
+ * See the state machines on the applicability of this command to
+ * different states.
+ */
+ Void start;
+ /**
+ * The 'burst' command used for audio I/O, see 'AudioBuffer'. The value
+ * specifies:
*
- * For output streams: the amount of bytes that the client requests the
- * HAL module to use out of the data contained in 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.
+ * - for output streams: the amount of bytes that the client requests the
+ * HAL module to use out of the data contained in 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
@@ -258,8 +224,44 @@
* 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.
+ *
+ * Differences for the 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.
+ * The client does not synchronize reads and writes into the buffer
+ * with sending of this command.
+ *
+ * - the value must always be set to 0.
*/
- int fmqByteCount;
+ int burst;
+ /**
+ * See the state machines on the applicability of this command to
+ * different states.
+ */
+ Void drain;
+ /**
+ * See the state machines on the applicability of this command to
+ * different states.
+ *
+ * Note that it's left on the discretion of the HAL implementation to
+ * assess all the necessary conditions that could prevent hardware from
+ * being suspended. Even if it can not be suspended, the state machine
+ * must still enter the 'STANDBY' state for consistency. Since the
+ * buffer must remain empty in this state, even if capturing hardware is
+ * still active, captured data must be discarded.
+ */
+ Void standby;
+ /**
+ * See the state machines on the applicability of this command to
+ * different states.
+ */
+ Void pause;
+ /**
+ * See the state machines on the applicability of this command to
+ * different states.
+ */
+ Void flush;
}
MQDescriptor<Command, SynchronizedReadWrite> command;
@@ -293,15 +295,15 @@
*/
int status;
/**
- * Used with the BURST command only.
+ * Used with the 'burst' command only.
*
* For output streams: the amount of bytes of data actually consumed
* by the HAL module.
* 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.
+ * The returned value must not exceed the value passed as the
+ * argument of the corresponding command, or be negative.
*/
int fmqByteCount;
/**
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
index 889a14b..805dc32 100644
--- a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
@@ -23,16 +23,16 @@
node [style=dashed] ANY_STATE;
node [fillcolor=lightblue style=filled];
I -> STANDBY;
- STANDBY -> IDLE [label="START"]; // producer -> active
- IDLE -> STANDBY [label="STANDBY"]; // producer -> passive, buffer is cleared
- IDLE -> ACTIVE [label="BURST"]; // consumer -> active
- ACTIVE -> ACTIVE [label="BURST"];
- ACTIVE -> PAUSED [label="PAUSE"]; // consumer -> passive
- ACTIVE -> DRAINING [label="DRAIN"]; // producer -> passive
- PAUSED -> ACTIVE [label="BURST"]; // consumer -> active
- PAUSED -> STANDBY [label="FLUSH"]; // producer -> passive, buffer is cleared
- DRAINING -> DRAINING [label="BURST"];
- DRAINING -> ACTIVE [label="START"]; // producer -> active
+ STANDBY -> IDLE [label="start"]; // producer -> active
+ IDLE -> STANDBY [label="standby"]; // producer -> passive, buffer is cleared
+ IDLE -> ACTIVE [label="burst"]; // consumer -> active
+ ACTIVE -> ACTIVE [label="burst"];
+ ACTIVE -> PAUSED [label="pause"]; // consumer -> passive
+ ACTIVE -> DRAINING [label="drain"]; // producer -> passive
+ PAUSED -> ACTIVE [label="burst"]; // consumer -> active
+ PAUSED -> STANDBY [label="flush"]; // producer -> passive, buffer is cleared
+ DRAINING -> DRAINING [label="burst"];
+ DRAINING -> ACTIVE [label="start"]; // producer -> active
DRAINING -> STANDBY [label="<empty buffer>"]; // consumer deactivates
IDLE -> ERROR [label="<hardware failure>"];
ACTIVE -> ERROR [label="<hardware failure>"];
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
index 56dd5290..6aa5c61 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
@@ -24,22 +24,22 @@
node [style=dashed] ANY_STATE;
node [fillcolor=lightblue style=filled];
I -> STANDBY;
- STANDBY -> IDLE [label="START"]; // consumer -> active
- STANDBY -> PAUSED [label="BURST"]; // producer -> active
- IDLE -> STANDBY [label="STANDBY"]; // consumer -> passive
- IDLE -> ACTIVE [label="BURST"]; // producer -> active
- ACTIVE -> ACTIVE [label="BURST"];
- ACTIVE -> PAUSED [label="PAUSE"]; // consumer -> passive (not consuming)
- ACTIVE -> DRAINING [label="DRAIN"]; // producer -> passive
- PAUSED -> PAUSED [label="BURST"];
- PAUSED -> ACTIVE [label="START"]; // consumer -> active
- PAUSED -> IDLE [label="FLUSH"]; // producer -> passive, buffer is cleared
+ STANDBY -> IDLE [label="start"]; // consumer -> active
+ STANDBY -> PAUSED [label="burst"]; // producer -> active
+ IDLE -> STANDBY [label="standby"]; // consumer -> passive
+ IDLE -> ACTIVE [label="burst"]; // producer -> active
+ ACTIVE -> ACTIVE [label="burst"];
+ ACTIVE -> PAUSED [label="pause"]; // consumer -> passive (not consuming)
+ ACTIVE -> DRAINING [label="drain"]; // producer -> passive
+ PAUSED -> PAUSED [label="burst"];
+ PAUSED -> ACTIVE [label="start"]; // consumer -> active
+ PAUSED -> IDLE [label="flush"]; // producer -> passive, buffer is cleared
DRAINING -> IDLE [label="<empty buffer>"];
- DRAINING -> ACTIVE [label="BURST"]; // producer -> active
- DRAINING -> DRAIN_PAUSED [label="PAUSE"]; // consumer -> passive (not consuming)
- DRAIN_PAUSED -> DRAINING [label="START"]; // consumer -> active
- DRAIN_PAUSED -> PAUSED [label="BURST"]; // producer -> active
- DRAIN_PAUSED -> IDLE [label="FLUSH"]; // buffer is cleared
+ DRAINING -> ACTIVE [label="burst"]; // producer -> active
+ DRAINING -> DRAIN_PAUSED [label="pause"]; // consumer -> passive (not consuming)
+ DRAIN_PAUSED -> DRAINING [label="start"]; // consumer -> active
+ DRAIN_PAUSED -> PAUSED [label="burst"]; // producer -> active
+ DRAIN_PAUSED -> IDLE [label="flush"]; // buffer is cleared
IDLE -> ERROR [label="<hardware failure>"];
ACTIVE -> ERROR [label="<hardware failure>"];
DRAINING -> ERROR [label="<hardware failure>"];
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index d34d68c..e16b338 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -39,6 +39,7 @@
"Configuration.cpp",
"Module.cpp",
"Stream.cpp",
+ "Telephony.cpp",
],
visibility: [
":__subpackages__",
@@ -81,6 +82,7 @@
],
header_libs: [
"libaudioaidl_headers",
+ "libaudio_system_headers",
"libsystem_headers",
],
cflags: [
@@ -112,13 +114,15 @@
"libhapticgeneratorsw",
"libloudnessenhancersw",
"libreverbsw",
+ "libtinyxml2",
"libvirtualizersw",
"libvisualizersw",
"libvolumesw",
],
srcs: [
- "EffectMain.cpp",
+ "EffectConfig.cpp",
"EffectFactory.cpp",
+ "EffectMain.cpp",
],
}
diff --git a/audio/aidl/default/EffectConfig.cpp b/audio/aidl/default/EffectConfig.cpp
new file mode 100644
index 0000000..e1427ec
--- /dev/null
+++ b/audio/aidl/default/EffectConfig.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AHAL_EffectConfig"
+#include <android-base/logging.h>
+
+#include "effectFactory-impl/EffectConfig.h"
+
+using aidl::android::media::audio::common::AudioUuid;
+
+namespace aidl::android::hardware::audio::effect {
+
+EffectConfig::EffectConfig(const std::string& file) {
+ tinyxml2::XMLDocument doc;
+ doc.LoadFile(file.c_str());
+ LOG(DEBUG) << __func__ << " loading " << file;
+ // parse the xml file into maps
+ if (doc.Error()) {
+ LOG(ERROR) << __func__ << " tinyxml2 failed to load " << file
+ << " error: " << doc.ErrorStr();
+ return;
+ }
+
+ auto registerFailure = [&](bool result) { mSkippedElements += result ? 0 : 1; };
+
+ for (auto& xmlConfig : getChildren(doc, "audio_effects_conf")) {
+ // Parse library
+ for (auto& xmlLibraries : getChildren(xmlConfig, "libraries")) {
+ for (auto& xmlLibrary : getChildren(xmlLibraries, "library")) {
+ registerFailure(parseLibrary(xmlLibrary));
+ }
+ }
+
+ // Parse effects
+ for (auto& xmlEffects : getChildren(xmlConfig, "effects")) {
+ for (auto& xmlEffect : getChildren(xmlEffects)) {
+ registerFailure(parseEffect(xmlEffect));
+ }
+ }
+
+ // Parse pre processing chains
+ for (auto& xmlPreprocess : getChildren(xmlConfig, "preprocess")) {
+ for (auto& xmlStream : getChildren(xmlPreprocess, "stream")) {
+ registerFailure(parseStream(xmlStream));
+ }
+ }
+
+ // Parse post processing chains
+ for (auto& xmlPostprocess : getChildren(xmlConfig, "postprocess")) {
+ for (auto& xmlStream : getChildren(xmlPostprocess, "stream")) {
+ registerFailure(parseStream(xmlStream));
+ }
+ }
+ }
+ LOG(DEBUG) << __func__ << " successfully parsed " << file << ", skipping " << mSkippedElements
+ << " element(s)";
+}
+
+std::vector<std::reference_wrapper<const tinyxml2::XMLElement>> EffectConfig::getChildren(
+ const tinyxml2::XMLNode& node, const char* childTag) {
+ std::vector<std::reference_wrapper<const tinyxml2::XMLElement>> children;
+ for (auto* child = node.FirstChildElement(childTag); child != nullptr;
+ child = child->NextSiblingElement(childTag)) {
+ children.emplace_back(*child);
+ }
+ return children;
+}
+
+bool EffectConfig::parseLibrary(const tinyxml2::XMLElement& xml) {
+ const char* name = xml.Attribute("name");
+ RETURN_VALUE_IF(!name, false, "noNameAttribute");
+ const char* path = xml.Attribute("path");
+ RETURN_VALUE_IF(!path, false, "noPathAttribute");
+
+ mLibraryMap[name] = path;
+ LOG(DEBUG) << __func__ << " " << name << " : " << path;
+ return true;
+}
+
+bool EffectConfig::parseEffect(const tinyxml2::XMLElement& xml) {
+ struct EffectLibraries effectLibraries;
+ std::vector<LibraryUuid> libraryUuids;
+ std::string name = xml.Attribute("name");
+ RETURN_VALUE_IF(name == "", false, "effectsNoName");
+
+ LOG(DEBUG) << __func__ << dump(xml);
+ struct LibraryUuid libraryUuid;
+ if (std::strcmp(xml.Name(), "effectProxy") == 0) {
+ // proxy lib and uuid
+ RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid, true), false, "parseProxyLibFailed");
+ effectLibraries.proxyLibrary = libraryUuid;
+ // proxy effect libs and UUID
+ auto xmlProxyLib = xml.FirstChildElement();
+ RETURN_VALUE_IF(!xmlProxyLib, false, "noLibForProxy");
+ while (xmlProxyLib) {
+ struct LibraryUuid tempLibraryUuid;
+ RETURN_VALUE_IF(!parseLibraryUuid(*xmlProxyLib, tempLibraryUuid), false,
+ "parseEffectLibFailed");
+ libraryUuids.push_back(std::move(tempLibraryUuid));
+ xmlProxyLib = xmlProxyLib->NextSiblingElement();
+ }
+ } else {
+ // expect only one library if not proxy
+ RETURN_VALUE_IF(!parseLibraryUuid(xml, libraryUuid), false, "parseEffectLibFailed");
+ libraryUuids.push_back(std::move(libraryUuid));
+ }
+
+ effectLibraries.libraries = std::move(libraryUuids);
+ mEffectsMap[name] = std::move(effectLibraries);
+ return true;
+}
+
+bool EffectConfig::parseStream(const tinyxml2::XMLElement& xml) {
+ LOG(DEBUG) << __func__ << dump(xml);
+ const char* type = xml.Attribute("type");
+ RETURN_VALUE_IF(!type, false, "noTypeInProcess");
+ RETURN_VALUE_IF(0 != mProcessingMap.count(type), false, "duplicateType");
+
+ for (auto& apply : getChildren(xml, "apply")) {
+ const char* name = apply.get().Attribute("effect");
+ RETURN_VALUE_IF(!name, false, "noEffectAttribute");
+ mProcessingMap[type].push_back(name);
+ LOG(DEBUG) << __func__ << " " << type << " : " << name;
+ }
+ return true;
+}
+
+bool EffectConfig::parseLibraryUuid(const tinyxml2::XMLElement& xml,
+ struct LibraryUuid& libraryUuid, bool isProxy) {
+ // Retrieve library name only if not effectProxy element
+ if (!isProxy) {
+ const char* name = xml.Attribute("library");
+ RETURN_VALUE_IF(!name, false, "noLibraryAttribute");
+ libraryUuid.name = name;
+ }
+
+ const char* uuid = xml.Attribute("uuid");
+ RETURN_VALUE_IF(!uuid, false, "noUuidAttribute");
+ RETURN_VALUE_IF(!stringToUuid(uuid, &libraryUuid.uuid), false, "invalidUuidAttribute");
+
+ LOG(DEBUG) << __func__ << (isProxy ? " proxy " : libraryUuid.name) << " : "
+ << libraryUuid.uuid.toString();
+ return true;
+}
+
+const char* EffectConfig::dump(const tinyxml2::XMLElement& element,
+ tinyxml2::XMLPrinter&& printer) const {
+ element.Accept(&printer);
+ return printer.CStr();
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index e03dda3..820b447 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "AHAL_EffectFactory"
#include <android-base/logging.h>
#include <dlfcn.h>
+#include <unordered_set>
#include "effect-impl/EffectTypes.h"
#include "effect-impl/EffectUUID.h"
@@ -26,22 +27,9 @@
namespace aidl::android::hardware::audio::effect {
-Factory::Factory() {
- // TODO: get list of library UUID and name from audio_effect.xml.
- openEffectLibrary(EqualizerTypeUUID, EqualizerSwImplUUID, std::nullopt, "libequalizersw.so");
- openEffectLibrary(EqualizerTypeUUID, EqualizerBundleImplUUID, std::nullopt, "libbundleaidl.so");
- openEffectLibrary(BassBoostTypeUUID, BassBoostSwImplUUID, std::nullopt, "libbassboostsw.so");
- openEffectLibrary(DynamicsProcessingTypeUUID, DynamicsProcessingSwImplUUID, std::nullopt,
- "libdynamicsprocessingsw.so");
- openEffectLibrary(HapticGeneratorTypeUUID, HapticGeneratorSwImplUUID, std::nullopt,
- "libhapticgeneratorsw.so");
- openEffectLibrary(LoudnessEnhancerTypeUUID, LoudnessEnhancerSwImplUUID, std::nullopt,
- "libloudnessenhancersw.so");
- openEffectLibrary(ReverbTypeUUID, ReverbSwImplUUID, std::nullopt, "libreverbsw.so");
- openEffectLibrary(VirtualizerTypeUUID, VirtualizerSwImplUUID, std::nullopt,
- "libvirtualizersw.so");
- openEffectLibrary(VisualizerTypeUUID, VisualizerSwImplUUID, std::nullopt, "libvisualizersw.so");
- openEffectLibrary(VolumeTypeUUID, VolumeSwImplUUID, std::nullopt, "libvolumesw.so");
+Factory::Factory(const std::string& file) : mConfig(EffectConfig(file)) {
+ LOG(DEBUG) << __func__ << " with config file: " << file;
+ loadEffectLibs();
}
Factory::~Factory() {
@@ -62,7 +50,7 @@
const std::optional<AudioUuid>& in_proxy_uuid,
std::vector<Descriptor::Identity>* _aidl_return) {
std::copy_if(
- mIdentityList.begin(), mIdentityList.end(), std::back_inserter(*_aidl_return),
+ mIdentitySet.begin(), mIdentitySet.end(), std::back_inserter(*_aidl_return),
[&](auto& desc) {
return (!in_type_uuid.has_value() || in_type_uuid.value() == desc.type) &&
(!in_impl_uuid.has_value() || in_impl_uuid.value() == desc.uuid) &&
@@ -172,8 +160,7 @@
return status;
}
-void Factory::openEffectLibrary(const AudioUuid& type, const AudioUuid& impl,
- const std::optional<AudioUuid>& proxy, const std::string& libName) {
+void Factory::openEffectLibrary(const AudioUuid& impl, const std::string& libName) {
std::function<void(void*)> dlClose = [](void* handle) -> void {
if (handle && dlclose(handle)) {
LOG(ERROR) << "dlclose failed " << dlerror();
@@ -187,19 +174,51 @@
return;
}
- LOG(DEBUG) << __func__ << " dlopen lib:" << libName << " for uuid:\ntype:" << type.toString()
- << "\nimpl:" << impl.toString()
- << "\nproxy:" << (proxy.has_value() ? proxy.value().toString() : "null")
+ LOG(DEBUG) << __func__ << " dlopen lib:" << libName << "\nimpl:" << impl.toString()
<< "\nhandle:" << libHandle;
mEffectLibMap.insert({impl, std::make_pair(std::move(libHandle), nullptr)});
+}
- Descriptor::Identity id;
- id.type = type;
- id.uuid = impl;
- if (proxy.has_value()) {
- id.proxy = proxy.value();
+void Factory::createIdentityWithConfig(const EffectConfig::LibraryUuid& configLib,
+ const AudioUuid& typeUuid,
+ const std::optional<AudioUuid> proxyUuid) {
+ static const auto& libMap = mConfig.getLibraryMap();
+ const std::string& libName = configLib.name;
+ if (auto path = libMap.find(libName); path != libMap.end()) {
+ Descriptor::Identity id;
+ id.type = typeUuid;
+ id.uuid = configLib.uuid;
+ id.proxy = proxyUuid;
+ LOG(DEBUG) << __func__ << ": typeUuid " << id.type.toString() << "\nimplUuid "
+ << id.uuid.toString() << " proxyUuid "
+ << (proxyUuid.has_value() ? proxyUuid->toString() : "null");
+ openEffectLibrary(id.uuid, path->second);
+ mIdentitySet.insert(std::move(id));
+ } else {
+ LOG(ERROR) << __func__ << ": library " << libName << " not exist!";
+ return;
}
- mIdentityList.push_back(id);
+}
+
+void Factory::loadEffectLibs() {
+ const auto& configEffectsMap = mConfig.getEffectsMap();
+ for (const auto& configEffects : configEffectsMap) {
+ if (auto typeUuid = kUuidNameTypeMap.find(configEffects.first /* effect name */);
+ typeUuid != kUuidNameTypeMap.end()) {
+ const auto& configLibs = configEffects.second;
+ std::optional<AudioUuid> proxyUuid;
+ if (configLibs.proxyLibrary.has_value()) {
+ const auto& proxyLib = configLibs.proxyLibrary.value();
+ proxyUuid = proxyLib.uuid;
+ }
+ for (const auto& configLib : configLibs.libraries) {
+ createIdentityWithConfig(configLib, typeUuid->second, proxyUuid);
+ }
+ } else {
+ LOG(ERROR) << __func__ << ": can not find type UUID for effect " << configEffects.first
+ << " skipping!";
+ }
+ }
}
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/EffectMain.cpp b/audio/aidl/default/EffectMain.cpp
index 3219dd6..ca81204 100644
--- a/audio/aidl/default/EffectMain.cpp
+++ b/audio/aidl/default/EffectMain.cpp
@@ -19,21 +19,31 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <system/audio_config.h>
+
+/** Default name of effect configuration file. */
+static const char* kDefaultConfigName = "audio_effects_config.xml";
int main() {
// This is a debug implementation, always enable debug logging.
android::base::SetMinimumLogSeverity(::android::base::DEBUG);
ABinderProcess_setThreadPoolMaxThreadCount(0);
+ auto configFile = android::audio_find_readable_configuration_file(kDefaultConfigName);
+ if (configFile == "") {
+ LOG(ERROR) << __func__ << ": config file " << kDefaultConfigName << " not found!";
+ return EXIT_FAILURE;
+ }
+ LOG(DEBUG) << __func__ << ": start factory with configFile:" << configFile;
auto effectFactory =
- ndk::SharedRefBase::make<aidl::android::hardware::audio::effect::Factory>();
+ ndk::SharedRefBase::make<aidl::android::hardware::audio::effect::Factory>(configFile);
std::string serviceName = std::string() + effectFactory->descriptor + "/default";
binder_status_t status =
AServiceManager_addService(effectFactory->asBinder().get(), serviceName.c_str());
CHECK_EQ(STATUS_OK, status);
- LOG(DEBUG) << __func__ << ": effectFactoryName:" << serviceName;
+ LOG(DEBUG) << __func__ << ": effectFactory: " << serviceName << " start";
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index deaca49..6863fe3 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -25,6 +25,7 @@
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include "core-impl/Module.h"
+#include "core-impl/Telephony.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::SinkMetadata;
@@ -245,6 +246,15 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
+ if (mTelephony == nullptr) {
+ mTelephony = ndk::SharedRefBase::make<Telephony>();
+ }
+ *_aidl_return = mTelephony;
+ LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdAndAdditionalData,
AudioPort* _aidl_return) {
const int32_t templateId = in_templateIdAndAdditionalData.id;
@@ -779,4 +789,60 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ndk::ScopedAStatus Module::getMasterMute(bool* _aidl_return) {
+ *_aidl_return = mMasterMute;
+ LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::setMasterMute(bool in_mute) {
+ LOG(DEBUG) << __func__ << ": " << in_mute;
+ mMasterMute = in_mute;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
+ *_aidl_return = mMasterVolume;
+ LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::setMasterVolume(float in_volume) {
+ LOG(DEBUG) << __func__ << ": " << in_volume;
+ if (in_volume >= 0.0f && in_volume <= 1.0f) {
+ mMasterVolume = in_volume;
+ return ndk::ScopedAStatus::ok();
+ }
+ LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+}
+
+ndk::ScopedAStatus Module::getMicMute(bool* _aidl_return) {
+ *_aidl_return = mMicMute;
+ LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::setMicMute(bool in_mute) {
+ LOG(DEBUG) << __func__ << ": " << in_mute;
+ mMicMute = in_mute;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::updateAudioMode(AudioMode in_mode) {
+ // No checks for supported audio modes here, it's an informative notification.
+ LOG(DEBUG) << __func__ << ": " << toString(in_mode);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::updateScreenRotation(ScreenRotation in_rotation) {
+ LOG(DEBUG) << __func__ << ": " << toString(in_rotation);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::updateScreenState(bool in_isTurnedOn) {
+ LOG(DEBUG) << __func__ << ": " << in_isTurnedOn;
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 7b544a1..21dc4b6 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -106,101 +106,116 @@
return Status::ABORT;
}
StreamDescriptor::Reply reply{};
- if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
- command.fmqByteCount == mInternalCommandCookie) {
- LOG(DEBUG) << __func__ << ": received EXIT command";
- setClosed();
- // This is an internal command, no need to reply.
- return Status::EXIT;
- } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
- LOG(DEBUG) << __func__ << ": received START read command";
- if (mState == StreamDescriptor::State::STANDBY ||
- mState == StreamDescriptor::State::DRAINING) {
- populateReply(&reply, mIsConnected);
- mState = mState == StreamDescriptor::State::STANDBY ? StreamDescriptor::State::IDLE
- : StreamDescriptor::State::ACTIVE;
- } else {
- LOG(WARNING) << __func__ << ": START command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::BURST && command.fmqByteCount >= 0) {
- LOG(DEBUG) << __func__ << ": received BURST read command for " << command.fmqByteCount
- << " bytes";
- if (mState == StreamDescriptor::State::IDLE || mState == StreamDescriptor::State::ACTIVE ||
- mState == StreamDescriptor::State::PAUSED ||
- mState == StreamDescriptor::State::DRAINING) {
- if (!read(command.fmqByteCount, &reply)) {
- mState = StreamDescriptor::State::ERROR;
+ reply.status = STATUS_BAD_VALUE;
+ using Tag = StreamDescriptor::Command::Tag;
+ switch (command.getTag()) {
+ case Tag::hal_reserved_exit:
+ if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
+ cookie == mInternalCommandCookie) {
+ LOG(DEBUG) << __func__ << ": received EXIT command";
+ setClosed();
+ // This is an internal command, no need to reply.
+ return Status::EXIT;
+ } else {
+ LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
}
- if (mState == StreamDescriptor::State::IDLE ||
- mState == StreamDescriptor::State::PAUSED) {
- mState = StreamDescriptor::State::ACTIVE;
- } else if (mState == StreamDescriptor::State::DRAINING) {
- // To simplify the reference code, we assume that the read operation
- // has consumed all the data remaining in the hardware buffer.
- // TODO: Provide parametrization on the duration of draining to test
- // handling of commands during the 'DRAINING' state.
+ break;
+ case Tag::start:
+ LOG(DEBUG) << __func__ << ": received START read command";
+ if (mState == StreamDescriptor::State::STANDBY ||
+ mState == StreamDescriptor::State::DRAINING) {
+ populateReply(&reply, mIsConnected);
+ mState = mState == StreamDescriptor::State::STANDBY
+ ? StreamDescriptor::State::IDLE
+ : StreamDescriptor::State::ACTIVE;
+ } else {
+ LOG(WARNING) << __func__ << ": START command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ break;
+ case Tag::burst:
+ if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
+ LOG(DEBUG) << __func__ << ": received BURST read command for " << fmqByteCount
+ << " bytes";
+ if (mState == StreamDescriptor::State::IDLE ||
+ mState == StreamDescriptor::State::ACTIVE ||
+ mState == StreamDescriptor::State::PAUSED ||
+ mState == StreamDescriptor::State::DRAINING) {
+ if (!read(fmqByteCount, &reply)) {
+ mState = StreamDescriptor::State::ERROR;
+ }
+ if (mState == StreamDescriptor::State::IDLE ||
+ mState == StreamDescriptor::State::PAUSED) {
+ mState = StreamDescriptor::State::ACTIVE;
+ } else if (mState == StreamDescriptor::State::DRAINING) {
+ // To simplify the reference code, we assume that the read operation
+ // has consumed all the data remaining in the hardware buffer.
+ // TODO: Provide parametrization on the duration of draining to test
+ // handling of commands during the 'DRAINING' state.
+ mState = StreamDescriptor::State::STANDBY;
+ }
+ } else {
+ LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ } else {
+ LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
+ }
+ break;
+ case Tag::drain:
+ LOG(DEBUG) << __func__ << ": received DRAIN read command";
+ if (mState == StreamDescriptor::State::ACTIVE) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
+ mState = StreamDescriptor::State::DRAINING;
+ } else {
+ LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ break;
+ case Tag::standby:
+ LOG(DEBUG) << __func__ << ": received STANDBY read command";
+ if (mState == StreamDescriptor::State::IDLE) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
mState = StreamDescriptor::State::STANDBY;
+ } else {
+ LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
}
- } else {
- LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received DRAIN read command";
- if (mState == StreamDescriptor::State::ACTIVE) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::DRAINING;
- } else {
- LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::PAUSE && command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received PAUSE read command";
- if (mState == StreamDescriptor::State::ACTIVE) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::PAUSED;
- } else {
- LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::FLUSH && command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received FLUSH read command";
- if (mState == StreamDescriptor::State::PAUSED) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::STANDBY;
- } else {
- LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::STANDBY &&
- command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received STANDBY read command";
- if (mState == StreamDescriptor::State::IDLE) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::STANDBY;
- } else {
- LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else {
- LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
- << ") or count: " << command.fmqByteCount;
- reply.status = STATUS_BAD_VALUE;
+ break;
+ case Tag::pause:
+ LOG(DEBUG) << __func__ << ": received PAUSE read command";
+ if (mState == StreamDescriptor::State::ACTIVE) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
+ mState = StreamDescriptor::State::PAUSED;
+ } else {
+ LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ break;
+ case Tag::flush:
+ LOG(DEBUG) << __func__ << ": received FLUSH read command";
+ if (mState == StreamDescriptor::State::PAUSED) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
+ mState = StreamDescriptor::State::STANDBY;
+ } else {
+ LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ break;
}
reply.state = mState;
LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
@@ -253,109 +268,123 @@
return Status::ABORT;
}
StreamDescriptor::Reply reply{};
- if (static_cast<int32_t>(command.code) == StreamContext::COMMAND_EXIT &&
- command.fmqByteCount == mInternalCommandCookie) {
- LOG(DEBUG) << __func__ << ": received EXIT command";
- setClosed();
- // This is an internal command, no need to reply.
- return Status::EXIT;
- } else if (command.code == StreamDescriptor::CommandCode::START && command.fmqByteCount >= 0) {
- LOG(DEBUG) << __func__ << ": received START read command";
- switch (mState) {
- case StreamDescriptor::State::STANDBY:
+ reply.status = STATUS_BAD_VALUE;
+ using Tag = StreamDescriptor::Command::Tag;
+ switch (command.getTag()) {
+ case Tag::hal_reserved_exit:
+ if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
+ cookie == mInternalCommandCookie) {
+ LOG(DEBUG) << __func__ << ": received EXIT command";
+ setClosed();
+ // This is an internal command, no need to reply.
+ return Status::EXIT;
+ } else {
+ LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
+ }
+ break;
+ case Tag::start:
+ LOG(DEBUG) << __func__ << ": received START write command";
+ switch (mState) {
+ case StreamDescriptor::State::STANDBY:
+ mState = StreamDescriptor::State::IDLE;
+ break;
+ case StreamDescriptor::State::PAUSED:
+ mState = StreamDescriptor::State::ACTIVE;
+ break;
+ case StreamDescriptor::State::DRAIN_PAUSED:
+ mState = StreamDescriptor::State::PAUSED;
+ break;
+ default:
+ LOG(WARNING) << __func__ << ": START command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ if (reply.status != STATUS_INVALID_OPERATION) {
+ populateReply(&reply, mIsConnected);
+ }
+ break;
+ case Tag::burst:
+ if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
+ LOG(DEBUG) << __func__ << ": received BURST write command for " << fmqByteCount
+ << " bytes";
+ if (mState !=
+ StreamDescriptor::State::ERROR) { // BURST can be handled in all valid states
+ if (!write(fmqByteCount, &reply)) {
+ mState = StreamDescriptor::State::ERROR;
+ }
+ if (mState == StreamDescriptor::State::STANDBY ||
+ mState == StreamDescriptor::State::DRAIN_PAUSED) {
+ mState = StreamDescriptor::State::PAUSED;
+ } else if (mState == StreamDescriptor::State::IDLE ||
+ mState == StreamDescriptor::State::DRAINING) {
+ mState = StreamDescriptor::State::ACTIVE;
+ } // When in 'ACTIVE' and 'PAUSED' do not need to change the state.
+ } else {
+ LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ } else {
+ LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
+ }
+ break;
+ case Tag::drain:
+ LOG(DEBUG) << __func__ << ": received DRAIN write command";
+ if (mState == StreamDescriptor::State::ACTIVE) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
mState = StreamDescriptor::State::IDLE;
- break;
- case StreamDescriptor::State::PAUSED:
- mState = StreamDescriptor::State::ACTIVE;
- break;
- case StreamDescriptor::State::DRAIN_PAUSED:
- mState = StreamDescriptor::State::PAUSED;
- break;
- default:
- LOG(WARNING) << __func__ << ": START command can not be handled in the state "
+ // Since there is no actual hardware that would be draining the buffer,
+ // in order to simplify the reference code, we assume that draining
+ // happens instantly, thus skipping the 'DRAINING' state.
+ // TODO: Provide parametrization on the duration of draining to test
+ // handling of commands during the 'DRAINING' state.
+ } else {
+ LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
<< toString(mState);
reply.status = STATUS_INVALID_OPERATION;
- }
- if (reply.status != STATUS_INVALID_OPERATION) {
- populateReply(&reply, mIsConnected);
- }
- } else if (command.code == StreamDescriptor::CommandCode::BURST && command.fmqByteCount >= 0) {
- LOG(DEBUG) << __func__ << ": received BURST write command for " << command.fmqByteCount
- << " bytes";
- if (mState != StreamDescriptor::State::ERROR) { // BURST can be handled in all valid states
- if (!write(command.fmqByteCount, &reply)) {
- mState = StreamDescriptor::State::ERROR;
}
- if (mState == StreamDescriptor::State::STANDBY ||
+ break;
+ case Tag::standby:
+ LOG(DEBUG) << __func__ << ": received STANDBY write command";
+ if (mState == StreamDescriptor::State::IDLE) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
+ mState = StreamDescriptor::State::STANDBY;
+ } else {
+ LOG(WARNING) << __func__ << ": STANDBY command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ break;
+ case Tag::pause:
+ LOG(DEBUG) << __func__ << ": received PAUSE write command";
+ if (mState == StreamDescriptor::State::ACTIVE ||
+ mState == StreamDescriptor::State::DRAINING) {
+ populateReply(&reply, mIsConnected);
+ mState = mState == StreamDescriptor::State::ACTIVE
+ ? StreamDescriptor::State::PAUSED
+ : StreamDescriptor::State::DRAIN_PAUSED;
+ } else {
+ LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ break;
+ case Tag::flush:
+ LOG(DEBUG) << __func__ << ": received FLUSH write command";
+ if (mState == StreamDescriptor::State::PAUSED ||
mState == StreamDescriptor::State::DRAIN_PAUSED) {
- mState = StreamDescriptor::State::PAUSED;
- } else if (mState == StreamDescriptor::State::IDLE ||
- mState == StreamDescriptor::State::DRAINING) {
- mState = StreamDescriptor::State::ACTIVE;
- } // When in 'ACTIVE' and 'PAUSED' do not need to change the state.
- } else {
- LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::DRAIN && command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received DRAIN write command";
- if (mState == StreamDescriptor::State::ACTIVE) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::IDLE;
- // Since there is no actual hardware that would be draining the buffer,
- // in order to simplify the reference code, we assume that draining
- // happens instantly, thus skipping the 'DRAINING' state.
- // TODO: Provide parametrization on the duration of draining to test
- // handling of commands during the 'DRAINING' state.
- } else {
- LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::STANDBY &&
- command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received STANDBY write command";
- if (mState == StreamDescriptor::State::IDLE) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::STANDBY;
- } else {
- LOG(WARNING) << __func__ << ": STANDBY command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::PAUSE && command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received PAUSE write command";
- if (mState == StreamDescriptor::State::ACTIVE ||
- mState == StreamDescriptor::State::DRAINING) {
- populateReply(&reply, mIsConnected);
- mState = mState == StreamDescriptor::State::ACTIVE
- ? StreamDescriptor::State::PAUSED
- : StreamDescriptor::State::DRAIN_PAUSED;
- } else {
- LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else if (command.code == StreamDescriptor::CommandCode::FLUSH && command.fmqByteCount == 0) {
- LOG(DEBUG) << __func__ << ": received FLUSH write command";
- if (mState == StreamDescriptor::State::PAUSED ||
- mState == StreamDescriptor::State::DRAIN_PAUSED) {
- populateReply(&reply, mIsConnected);
- mState = StreamDescriptor::State::IDLE;
- } else {
- LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
- }
- } else {
- LOG(WARNING) << __func__ << ": invalid command (" << command.toString()
- << ") or count: " << command.fmqByteCount;
- reply.status = STATUS_BAD_VALUE;
+ populateReply(&reply, mIsConnected);
+ mState = StreamDescriptor::State::IDLE;
+ } else {
+ LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
+ << toString(mState);
+ reply.status = STATUS_INVALID_OPERATION;
+ }
+ break;
}
reply.state = mState;
LOG(DEBUG) << __func__ << ": writing reply " << reply.toString();
@@ -421,9 +450,9 @@
void StreamCommon<Metadata, StreamWorker>::stopWorker() {
if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
LOG(DEBUG) << __func__ << ": asking the worker to exit...";
- StreamDescriptor::Command cmd;
- cmd.code = StreamDescriptor::CommandCode(StreamContext::COMMAND_EXIT);
- cmd.fmqByteCount = mContext.getInternalCommandCookie();
+ auto cmd =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::hal_reserved_exit>(
+ mContext.getInternalCommandCookie());
// Note: never call 'pause' and 'resume' methods of StreamWorker
// in the HAL implementation. These methods are to be used by
// the client side only. Preventing the worker loop from running
diff --git a/audio/aidl/default/Telephony.cpp b/audio/aidl/default/Telephony.cpp
new file mode 100644
index 0000000..1854b35
--- /dev/null
+++ b/audio/aidl/default/Telephony.cpp
@@ -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.
+ */
+
+#include <android/binder_to_string.h>
+#define LOG_TAG "AHAL_Telephony"
+#include <android-base/logging.h>
+
+#include "core-impl/Telephony.h"
+
+namespace aidl::android::hardware::audio::core {
+
+ndk::ScopedAStatus Telephony::getSupportedAudioModes(std::vector<AudioMode>* _aidl_return) {
+ *_aidl_return = mSupportedAudioModes;
+ LOG(DEBUG) << __func__ << ": returning " << ::android::internal::ToString(*_aidl_return);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Telephony::switchAudioMode(AudioMode in_mode) {
+ if (std::find(mSupportedAudioModes.begin(), mSupportedAudioModes.end(), in_mode) !=
+ mSupportedAudioModes.end()) {
+ LOG(DEBUG) << __func__ << ": " << toString(in_mode);
+ return ndk::ScopedAStatus::ok();
+ }
+ LOG(ERROR) << __func__ << ": unsupported mode " << toString(in_mode);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml
new file mode 100644
index 0000000..b6fea27
--- /dev/null
+++ b/audio/aidl/default/audio_effects_config.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<audio_effects_conf version="2.0" xmlns="http://schemas.android.com/audio/audio_effects_conf/v2_0">
+ <!-- Overview.
+ This example config file was copy from existing one: frameworks/av/media/libeffects/data/
+ audio_effects.xml, with effect library names updated to AIDL libraries we currently have.
+
+ All "library" attributes in "effect" element must must match a "library" element with the
+ same value of the "name" attribute.
+ All "effect" attributes in "preprocess" and "postprocess" element must match an "effect"
+ element with the same value of the "name" attribute.
+
+ AIDL EffectFactory are relying on the "name" attribute in "effect" element to identify the
+ effect type, so it's necessary to have the mapping from name to effect type UUID. Make
+ sure to either use existing effect name as key of
+ ::android::hardware::audio::effect::kUuidNameTypeMap, or add a new {name, typeUUID} map
+ item to the kUuidNameTypeMap.
+
+ Existing audio_effects.xml should working without any change as long as:
+ 1. "path" attribute of "library" element matches with the actual effect library name.
+ 2. "name" attribute of "effect" and "effectProxy" element correctly added as key of
+ kUuidNameTypeMap, with value matches Identity.type in Descriptor.aidl.
+ 3. "uuid" attribute of "effect" element matches Identity.uuid in Descriptor.aidl.
+ 4. "uuid" attribute of "effectProxy" element matches Identity.proxy in Descriptor.aidl.
+ -->
+
+ <!-- List of effect libraries to load.
+ Each library element must contain a "name" attribute and a "path" attribute giving the
+ name of a library .so file on the target device.
+ -->
+ <libraries>
+ <library name="bassboostsw" path="libbassboostsw.so"/>
+ <library name="bundle" path="libbundleaidl.so"/>
+ <library name="dynamics_processingsw" path="libdynamicsprocessingsw.so"/>
+ <library name="equalizersw" path="libequalizersw.so"/>
+ <library name="haptic_generatorsw" path="libhapticgeneratorsw.so"/>
+ <library name="loudness_enhancersw" path="libloudnessenhancersw.so"/>
+ <library name="reverbsw" path="libreverbsw.so"/>
+ <library name="virtualizersw" path="libvirtualizersw.so"/>
+ <library name="visualizersw" path="libvisualizersw.so"/>
+ <library name="volumesw" path="libvolumesw.so"/>
+ </libraries>
+
+ <!-- list of effects to load.
+ Each "effect" element must contain a "name", "library" and a "uuid" attribute.
+ The value of the "library" attribute must correspond to the name of one library element in
+ the "libraries" element.
+ The "name" attribute used to specific effect type, and should be mapping to a key of
+ aidl::android::hardware::audio::effect::kUuidNameTypeMap.
+ The "uuid" attribute is the implementation specific UUID as specified by the effect vendor.
+
+ Effect proxy can be supported with "effectProxy" element, each sub-element should contain
+ "library" and "uuid" attribute, all other attributes were ignored. Framework side use
+ result of IFactory.queryEffects() to decide which effect implementation should be part of
+ proxy and which not.
+
+ Only "name", "library", and "uuid" attributes in "effects" element are meaningful and
+ parsed out by EffectConfig class, all other attributes are ignored.
+ Only "name" and "uuid" attributes in "effectProxy" element are meaningful and parsed out
+ by EffectConfig class, all other attributes are ignored.
+ -->
+
+ <effects>
+ <effect name="bassboost" library="bassboostsw" uuid="fa8181f2-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="dynamics_processing" library="dynamics_processingsw" uuid="fa818d78-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="haptic_generator" library="haptic_generatorsw" uuid="fa819110-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="loudness_enhancer" library="loudness_enhancersw" uuid="fa819610-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="reverb" library="reverbsw" uuid="fa8199c6-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="virtualizer" library="virtualizersw" uuid="fa819d86-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="visualizer" library="visualizersw" uuid="fa81a0f6-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="volume" library="volumesw" uuid="fa81a718-588b-11ed-9b6a-0242ac120002"/>
+ <effectProxy name="equalizer" uuid="14804144-a5ee-4d24-aa88-0002a5d5c51b">
+ <libsw library="equalizersw" uuid="0bed4300-847d-11df-bb17-0002a5d5c51b"/>
+ <libsw library="bundle" uuid="ce772f20-847d-11df-bb17-0002a5d5c51b"/>
+ </effectProxy>
+ </effects>
+
+ <!-- Audio pre processor configurations.
+ The pre processor configuration is described in a "preprocess" element and consists in a
+ list of elements each describing pre processor settings for a given use case or "stream".
+ Each stream element has a "type" attribute corresponding to the input source used.
+ Valid types are these defined in system/hardware/interfaces/media/aidl/android/media/audio/
+ common/AudioSource.aidl.
+ Each "stream" element contains a list of "apply" elements indicating one effect to apply.
+ The effect to apply is designated by its name in the "effects" elements.
+ If there are more than one effect apply to one stream, the audio framework will apply them
+ in the same equence as they listed in "stream" element.
+
+ <preprocess>
+ <stream type="voice_communication">
+ <apply effect="aec"/>
+ <apply effect="ns"/>
+ </stream>
+ </preprocess>
+ -->
+
+ <!-- Audio post processor configurations.
+ The post processor configuration is described in a "postprocess" element and consists in a
+ list of elements each describing post processor settings for a given use case or "stream".
+ Each stream element has a "type" attribute corresponding to the stream type used.
+ Valid types are these defined in system/hardware/interfaces/media/aidl/android/media/audio/
+ common/AudioStreamType.aidl.
+ Each "stream" element contains a list of "apply" elements indicating one effect to apply.
+ The effect to apply is designated by its name in the "effects" elements.
+ If there are more than one effect apply to one stream, the audio framework will apply them
+ in the same equence as they listed in "stream" element.
+
+ <postprocess>
+ <stream type="music">
+ <apply effect="music_post_proc"/>
+ </stream>
+ <stream type="voice_call">
+ <apply effect="voice_post_proc"/>
+ </stream>
+ <stream type="notification">
+ <apply effect="notification_post_proc"/>
+ </stream>
+ </postprocess>
+ -->
+
+</audio_effects_conf>
diff --git a/audio/aidl/default/config/audio_policy_configuration.xsd b/audio/aidl/default/config/audio_policy_configuration.xsd
new file mode 100644
index 0000000..823b217
--- /dev/null
+++ b/audio/aidl/default/config/audio_policy_configuration.xsd
@@ -0,0 +1,827 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <!-- List the config versions supported by audio policy. -->
+ <xs:simpleType name="version">
+ <xs:restriction base="xs:decimal">
+ <xs:enumeration value="7.0"/>
+ <xs:enumeration value="7.1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="halVersion">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Version of the interface the hal implements. Note that this
+ relates to legacy HAL API versions since HIDL APIs are versioned
+ using other mechanisms.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:decimal">
+ <!-- List of HAL versions supported by the framework. -->
+ <xs:enumeration value="2.0"/>
+ <xs:enumeration value="3.0"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:element name="audioPolicyConfiguration">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="globalConfiguration" type="globalConfiguration"/>
+ <xs:element name="modules" type="modules" maxOccurs="unbounded"/>
+ <xs:element name="volumes" type="volumes" maxOccurs="unbounded"/>
+ <xs:element name="surroundSound" type="surroundSound" minOccurs="0" />
+ </xs:sequence>
+ <xs:attribute name="version" type="version"/>
+ </xs:complexType>
+ <xs:key name="moduleNameKey">
+ <xs:selector xpath="modules/module"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:unique name="volumeTargetUniqueness">
+ <xs:selector xpath="volumes/volume"/>
+ <xs:field xpath="@stream"/>
+ <xs:field xpath="@deviceCategory"/>
+ </xs:unique>
+ <xs:key name="volumeCurveNameKey">
+ <xs:selector xpath="volumes/reference"/>
+ <xs:field xpath="@name"/>
+ </xs:key>
+ <xs:keyref name="volumeCurveRef" refer="volumeCurveNameKey">
+ <xs:selector xpath="volumes/volume"/>
+ <xs:field xpath="@ref"/>
+ </xs:keyref>
+ </xs:element>
+ <xs:complexType name="globalConfiguration">
+ <xs:attribute name="speaker_drc_enabled" type="xs:boolean" use="required"/>
+ <xs:attribute name="call_screen_mode_supported" type="xs:boolean" use="optional"/>
+ <xs:attribute name="engine_library" type="engineSuffix" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="modules">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ There should be one section per audio HW module present on the platform.
+ Each <module/> contains two mandatory tags: “halVersion” and “name”.
+ The module "name" is the same as in previous .conf file.
+ Each module must contain the following sections:
+ - <devicePorts/>: a list of device descriptors for all
+ input and output devices accessible via this module.
+ This contains both permanently attached devices and removable devices.
+ - <mixPorts/>: listing all output and input streams exposed by the audio HAL
+ - <routes/>: list of possible connections between input
+ and output devices or between stream and devices.
+ A <route/> is defined by a set of 3 attributes:
+ -"type": mux|mix means all sources are mutual exclusive (mux) or can be mixed (mix)
+ -"sink": the sink involved in this route
+ -"sources": all the sources than can be connected to the sink via this route
+ - <attachedDevices/>: permanently attached devices.
+ The attachedDevices section is a list of devices names.
+ Their names correspond to device names defined in "devicePorts" section.
+ - <defaultOutputDevice/> is the device to be used when no policy rule applies
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="module" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="attachedDevices" type="attachedDevices" minOccurs="0">
+ <xs:unique name="attachedDevicesUniqueness">
+ <xs:selector xpath="item"/>
+ <xs:field xpath="."/>
+ </xs:unique>
+ </xs:element>
+ <xs:element name="defaultOutputDevice" type="xs:token" minOccurs="0"/>
+ <xs:element name="mixPorts" type="mixPorts" minOccurs="0"/>
+ <xs:element name="devicePorts" type="devicePorts" minOccurs="0"/>
+ <xs:element name="routes" type="routes" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="halVersion" type="halVersion" use="required"/>
+ </xs:complexType>
+ <xs:unique name="mixPortNameUniqueness">
+ <xs:selector xpath="mixPorts/mixPort"/>
+ <xs:field xpath="@name"/>
+ </xs:unique>
+ <xs:key name="devicePortNameKey">
+ <xs:selector xpath="devicePorts/devicePort"/>
+ <xs:field xpath="@tagName"/>
+ </xs:key>
+ <xs:unique name="devicePortUniqueness">
+ <xs:selector xpath="devicePorts/devicePort"/>
+ <xs:field xpath="@type"/>
+ <xs:field xpath="@address"/>
+ </xs:unique>
+ <xs:keyref name="defaultOutputDeviceRef" refer="devicePortNameKey">
+ <xs:selector xpath="defaultOutputDevice"/>
+ <xs:field xpath="."/>
+ </xs:keyref>
+ <xs:keyref name="attachedDeviceRef" refer="devicePortNameKey">
+ <xs:selector xpath="attachedDevices/item"/>
+ <xs:field xpath="."/>
+ </xs:keyref>
+ <!-- The following 3 constraints try to make sure each sink port
+ is reference in one an only one route. -->
+ <xs:key name="routeSinkKey">
+ <!-- predicate [@type='sink'] does not work in xsd 1.0 -->
+ <xs:selector xpath="devicePorts/devicePort|mixPorts/mixPort"/>
+ <xs:field xpath="@tagName|@name"/>
+ </xs:key>
+ <xs:keyref name="routeSinkRef" refer="routeSinkKey">
+ <xs:selector xpath="routes/route"/>
+ <xs:field xpath="@sink"/>
+ </xs:keyref>
+ <xs:unique name="routeUniqueness">
+ <xs:selector xpath="routes/route"/>
+ <xs:field xpath="@sink"/>
+ </xs:unique>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="attachedDevices">
+ <xs:sequence>
+ <xs:element name="item" type="xs:token" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="audioInOutFlag">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ The flags indicate suggested stream attributes supported by the profile.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_PRIMARY" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_FAST" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_DEEP_BUFFER" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_NON_BLOCKING" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_HW_AV_SYNC" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_TTS" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_RAW" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_SYNC" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT_PCM" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_VOIP_RX" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_INCALL_MUSIC" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_SPATIALIZER" />
+ <xs:enumeration value="AUDIO_OUTPUT_FLAG_ULTRASOUND" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_FAST" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_HW_HOTWORD" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_RAW" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_SYNC" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_MMAP_NOIRQ" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_VOIP_TX" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_HW_AV_SYNC" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_DIRECT" />
+ <xs:enumeration value="AUDIO_INPUT_FLAG_ULTRASOUND" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioInOutFlags">
+ <xs:list itemType="audioInOutFlag" />
+ </xs:simpleType>
+ <xs:simpleType name="role">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="sink"/>
+ <xs:enumeration value="source"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="mixPorts">
+ <xs:sequence>
+ <xs:element name="mixPort" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="gains" type="gains" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:token" use="required"/>
+ <xs:attribute name="role" type="role" use="required"/>
+ <xs:attribute name="flags" type="audioInOutFlags"/>
+ <xs:attribute name="maxOpenCount" type="xs:unsignedInt"/>
+ <xs:attribute name="maxActiveCount" type="xs:unsignedInt"/>
+ <xs:attribute name="preferredUsage" type="audioUsageList">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ When choosing the mixPort of an audio track, the audioPolicy
+ first considers the mixPorts with a preferredUsage including
+ the track AudioUsage preferred .
+ If non support the track format, the other mixPorts are considered.
+ Eg: a <mixPort preferredUsage="AUDIO_USAGE_MEDIA" /> will receive
+ the audio of all apps playing with a MEDIA usage.
+ It may receive audio from ALARM if there are no audio compatible
+ <mixPort preferredUsage="AUDIO_USAGE_ALARM" />.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="recommendedMuteDurationMs" type="xs:unsignedInt"/>
+ </xs:complexType>
+ <xs:unique name="mixPortProfileUniqueness">
+ <xs:selector xpath="profile"/>
+ <xs:field xpath="format"/>
+ <xs:field xpath="samplingRate"/>
+ <xs:field xpath="channelMasks"/>
+ </xs:unique>
+ <xs:unique name="mixPortGainUniqueness">
+ <xs:selector xpath="gains/gain"/>
+ <xs:field xpath="@name"/>
+ </xs:unique>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="audioDevice">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_DEVICE_NONE"/>
+
+ <xs:enumeration value="AUDIO_DEVICE_OUT_EARPIECE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADPHONE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_EARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_ACCESSORY"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_DEVICE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_TELEPHONY_TX"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_ARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPDIF"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_FM"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER_SAFE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_IP"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BUS"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_PROXY"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HEARING_AID"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_ECHO_CANCELLER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_BROADCAST"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_DEFAULT"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_STUB"/>
+
+ <xs:enumeration value="AUDIO_DEVICE_IN_COMMUNICATION"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_AMBIENT"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BUILTIN_MIC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_WIRED_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_AUX_DIGITAL"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_VOICE_CALL"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_TELEPHONY_RX"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BACK_MIC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_ACCESSORY"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_DEVICE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_FM_TUNER"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_TV_TUNER"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_SPDIF"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_A2DP"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_LOOPBACK"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_IP"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BUS"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_PROXY"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_EARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLE_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_DEFAULT"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_STUB"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="vendorExtension">
+ <!-- Vendor extension names must be prefixed by "VX_" to distinguish them from
+ AOSP values. Vendors must namespace their names to avoid conflicts. The
+ namespace part must only use capital latin characters and decimal digits and
+ consist of at least 3 characters. The part of the extension name after the
+ namespace may in addition include underscores. Example for a hypothetical
+ Google virtual reality device:
+
+ <devicePort tagName="VR" type="VX_GOOGLE_VR" role="sink" />
+ -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="VX_[A-Z0-9]{3,}_[_A-Z0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="extendableAudioDevice">
+ <xs:union memberTypes="audioDevice vendorExtension"/>
+ </xs:simpleType>
+ <xs:simpleType name="audioFormat">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_FORMAT_DEFAULT" />
+ <xs:enumeration value="AUDIO_FORMAT_PCM_16_BIT" />
+ <xs:enumeration value="AUDIO_FORMAT_PCM_8_BIT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_32_BIT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_8_24_BIT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_FLOAT"/>
+ <xs:enumeration value="AUDIO_FORMAT_PCM_24_BIT_PACKED"/>
+ <xs:enumeration value="AUDIO_FORMAT_MP3"/>
+ <xs:enumeration value="AUDIO_FORMAT_AMR_NB"/>
+ <xs:enumeration value="AUDIO_FORMAT_AMR_WB"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_MAIN"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_SSR"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LTP"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_SCALABLE"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ERLC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ELD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_XHE"/>
+ <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_VORBIS"/>
+ <xs:enumeration value="AUDIO_FORMAT_OPUS"/>
+ <xs:enumeration value="AUDIO_FORMAT_AC3"/>
+ <xs:enumeration value="AUDIO_FORMAT_E_AC3"/>
+ <xs:enumeration value="AUDIO_FORMAT_E_AC3_JOC"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS_HD"/>
+ <xs:enumeration value="AUDIO_FORMAT_IEC61937"/>
+ <xs:enumeration value="AUDIO_FORMAT_DOLBY_TRUEHD"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRC"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRCB"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRCWB"/>
+ <xs:enumeration value="AUDIO_FORMAT_EVRCNW"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADIF"/>
+ <xs:enumeration value="AUDIO_FORMAT_WMA"/>
+ <xs:enumeration value="AUDIO_FORMAT_WMA_PRO"/>
+ <xs:enumeration value="AUDIO_FORMAT_AMR_WB_PLUS"/>
+ <xs:enumeration value="AUDIO_FORMAT_MP2"/>
+ <xs:enumeration value="AUDIO_FORMAT_QCELP"/>
+ <xs:enumeration value="AUDIO_FORMAT_DSD"/>
+ <xs:enumeration value="AUDIO_FORMAT_FLAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_ALAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_APE"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_MAIN"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SSR"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LTP"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SCALABLE"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ERLC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ELD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_XHE"/>
+ <xs:enumeration value="AUDIO_FORMAT_SBC"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_HD"/>
+ <xs:enumeration value="AUDIO_FORMAT_AC4"/>
+ <xs:enumeration value="AUDIO_FORMAT_LDAC"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT_1_0"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT_2_0"/>
+ <xs:enumeration value="AUDIO_FORMAT_MAT_2_1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_LC"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V1"/>
+ <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V2"/>
+ <xs:enumeration value="AUDIO_FORMAT_CELT"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE"/>
+ <xs:enumeration value="AUDIO_FORMAT_LHDC"/>
+ <xs:enumeration value="AUDIO_FORMAT_LHDC_LL"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_TWSP"/>
+ <xs:enumeration value="AUDIO_FORMAT_LC3"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L3"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L4"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L3"/>
+ <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L4"/>
+ <xs:enumeration value="AUDIO_FORMAT_IEC60958"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS_UHD"/>
+ <xs:enumeration value="AUDIO_FORMAT_DRA"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_QLEA"/>
+ <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_R4"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="extendableAudioFormat">
+ <xs:union memberTypes="audioFormat vendorExtension"/>
+ </xs:simpleType>
+ <xs:simpleType name="audioUsage">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio usage specifies the intended use case for the sound being played.
+ Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_USAGE_UNKNOWN" />
+ <xs:enumeration value="AUDIO_USAGE_MEDIA" />
+ <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION" />
+ <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING" />
+ <xs:enumeration value="AUDIO_USAGE_ALARM" />
+ <xs:enumeration value="AUDIO_USAGE_NOTIFICATION" />
+ <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE" />
+ <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_EVENT" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_SONIFICATION" />
+ <xs:enumeration value="AUDIO_USAGE_GAME" />
+ <xs:enumeration value="AUDIO_USAGE_VIRTUAL_SOURCE" />
+ <xs:enumeration value="AUDIO_USAGE_ASSISTANT" />
+ <xs:enumeration value="AUDIO_USAGE_CALL_ASSISTANT" />
+ <xs:enumeration value="AUDIO_USAGE_EMERGENCY" />
+ <xs:enumeration value="AUDIO_USAGE_SAFETY" />
+ <xs:enumeration value="AUDIO_USAGE_VEHICLE_STATUS" />
+ <xs:enumeration value="AUDIO_USAGE_ANNOUNCEMENT" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioUsageList">
+ <xs:list itemType="audioUsage"/>
+ </xs:simpleType>
+ <xs:simpleType name="audioContentType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio content type expresses the general category of the content.
+ Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_UNKNOWN"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_SPEECH"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_MUSIC"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_MOVIE"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_SONIFICATION"/>
+ <xs:enumeration value="AUDIO_CONTENT_TYPE_ULTRASOUND"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="samplingRates">
+ <xs:list itemType="xs:nonNegativeInteger" />
+ </xs:simpleType>
+ <xs:simpleType name="audioChannelMask">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio channel mask specifies presence of particular channels.
+ There are two representations:
+ - representation position (traditional discrete channel specification,
+ e.g. "left", "right");
+ - indexed (this is similar to "tracks" in audio mixing, channels
+ are represented using numbers).
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_CHANNEL_NONE"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_SIDE"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_SURROUND"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_PENTA"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_6POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_9POINT1POINT6"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_13POINT_360RA"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_22POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_A"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_HAPTIC_AB"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_STEREO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_6"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT0POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT1POINT2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_5POINT1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_CALL_MONO"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_1"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_2"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_3"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_5"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_6"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_7"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_8"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_9"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_10"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_11"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_12"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_13"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_14"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_15"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_16"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_17"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_18"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_19"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_20"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_21"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_22"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_23"/>
+ <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_24"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="channelMasks">
+ <xs:list itemType="audioChannelMask" />
+ </xs:simpleType>
+ <xs:simpleType name="audioEncapsulationType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_NONE"/>
+ <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_IEC61937"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="profile">
+ <xs:attribute name="name" type="xs:token" use="optional"/>
+ <xs:attribute name="format" type="extendableAudioFormat" use="optional"/>
+ <xs:attribute name="samplingRates" type="samplingRates" use="optional"/>
+ <xs:attribute name="channelMasks" type="channelMasks" use="optional"/>
+ <xs:attribute name="encapsulationType" type="audioEncapsulationType" use="optional"/>
+ </xs:complexType>
+ <xs:simpleType name="audioGainMode">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_GAIN_MODE_JOINT"/>
+ <xs:enumeration value="AUDIO_GAIN_MODE_CHANNELS"/>
+ <xs:enumeration value="AUDIO_GAIN_MODE_RAMP"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioGainModeMaskUnrestricted">
+ <xs:list itemType="audioGainMode" />
+ </xs:simpleType>
+ <xs:simpleType name='audioGainModeMask'>
+ <xs:restriction base='audioGainModeMaskUnrestricted'>
+ <xs:minLength value='1' />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="gains">
+ <xs:sequence>
+ <xs:element name="gain" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:token" use="required"/>
+ <xs:attribute name="mode" type="audioGainModeMask" use="required"/>
+ <xs:attribute name="channel_mask" type="audioChannelMask" use="optional"/>
+ <xs:attribute name="minValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="maxValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="defaultValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="stepValueMB" type="xs:int" use="optional"/>
+ <xs:attribute name="minRampMs" type="xs:int" use="optional"/>
+ <xs:attribute name="maxRampMs" type="xs:int" use="optional"/>
+ <xs:attribute name="useForVolume" type="xs:boolean" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="devicePorts">
+ <xs:sequence>
+ <xs:element name="devicePort" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="gains" type="gains" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="tagName" type="xs:token" use="required"/>
+ <xs:attribute name="type" type="extendableAudioDevice" use="required"/>
+ <xs:attribute name="role" type="role" use="required"/>
+ <xs:attribute name="address" type="xs:string" use="optional" default=""/>
+ <!-- Note that XSD 1.0 can not check that a type only has one default. -->
+ <xs:attribute name="default" type="xs:boolean" use="optional">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ The default device will be used if multiple have the same type
+ and no explicit route request exists for a specific device of
+ that type.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="encodedFormats" type="audioFormatsList" use="optional"
+ default="" />
+ </xs:complexType>
+ <xs:unique name="devicePortProfileUniqueness">
+ <xs:selector xpath="profile"/>
+ <xs:field xpath="format"/>
+ <xs:field xpath="samplingRate"/>
+ <xs:field xpath="channelMasks"/>
+ </xs:unique>
+ <xs:unique name="devicePortGainUniqueness">
+ <xs:selector xpath="gains/gain"/>
+ <xs:field xpath="@name"/>
+ </xs:unique>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="mixType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="mix"/>
+ <xs:enumeration value="mux"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="routes">
+ <xs:sequence>
+ <xs:element name="route" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ List all available sources for a given sink.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="type" type="mixType" use="required"/>
+ <xs:attribute name="sink" type="xs:string" use="required"/>
+ <xs:attribute name="sources" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="volumes">
+ <xs:sequence>
+ <xs:element name="volume" type="volume" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="reference" type="reference" minOccurs="0" maxOccurs="unbounded">
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <!-- TODO: Always require a ref for better xsd validations.
+ Currently a volume could have no points nor ref
+ as it can not be forbidden by xsd 1.0.-->
+ <xs:simpleType name="volumePoint">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Comma separated pair of number.
+ The fist one is the framework level (between 0 and 100).
+ The second one is the volume to send to the HAL.
+ The framework will interpolate volumes not specified.
+ Their MUST be at least 2 points specified.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:pattern value="([0-9]{1,2}|100),-?[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioStreamType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio stream type describing the intended use case of a stream.
+ Please consult frameworks/base/media/java/android/media/AudioSystem.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_STREAM_VOICE_CALL"/>
+ <xs:enumeration value="AUDIO_STREAM_SYSTEM"/>
+ <xs:enumeration value="AUDIO_STREAM_RING"/>
+ <xs:enumeration value="AUDIO_STREAM_MUSIC"/>
+ <xs:enumeration value="AUDIO_STREAM_ALARM"/>
+ <xs:enumeration value="AUDIO_STREAM_NOTIFICATION"/>
+ <xs:enumeration value="AUDIO_STREAM_BLUETOOTH_SCO"/>
+ <xs:enumeration value="AUDIO_STREAM_ENFORCED_AUDIBLE"/>
+ <xs:enumeration value="AUDIO_STREAM_DTMF"/>
+ <xs:enumeration value="AUDIO_STREAM_TTS"/>
+ <xs:enumeration value="AUDIO_STREAM_ACCESSIBILITY"/>
+ <xs:enumeration value="AUDIO_STREAM_ASSISTANT"/>
+ <xs:enumeration value="AUDIO_STREAM_REROUTING"/>
+ <xs:enumeration value="AUDIO_STREAM_PATCH"/>
+ <xs:enumeration value="AUDIO_STREAM_CALL_ASSISTANT"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="audioSource">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ An audio source defines the intended use case for the sound being recorded.
+ Please consult frameworks/base/media/java/android/media/MediaRecorder.java
+ for the description of each value.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_SOURCE_DEFAULT"/>
+ <xs:enumeration value="AUDIO_SOURCE_MIC"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_UPLINK"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_DOWNLINK"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_CALL"/>
+ <xs:enumeration value="AUDIO_SOURCE_CAMCORDER"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_RECOGNITION"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_COMMUNICATION"/>
+ <xs:enumeration value="AUDIO_SOURCE_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_SOURCE_UNPROCESSED"/>
+ <xs:enumeration value="AUDIO_SOURCE_VOICE_PERFORMANCE"/>
+ <xs:enumeration value="AUDIO_SOURCE_ECHO_REFERENCE"/>
+ <xs:enumeration value="AUDIO_SOURCE_FM_TUNER"/>
+ <xs:enumeration value="AUDIO_SOURCE_HOTWORD"/>
+ <xs:enumeration value="AUDIO_SOURCE_ULTRASOUND"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!-- Enum values of device_category from Volume.h. -->
+ <xs:simpleType name="deviceCategory">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="DEVICE_CATEGORY_HEADSET"/>
+ <xs:enumeration value="DEVICE_CATEGORY_SPEAKER"/>
+ <xs:enumeration value="DEVICE_CATEGORY_EARPIECE"/>
+ <xs:enumeration value="DEVICE_CATEGORY_EXT_MEDIA"/>
+ <xs:enumeration value="DEVICE_CATEGORY_HEARING_AID"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="volume">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Volume section defines a volume curve for a given use case and device category.
+ It contains a list of points of this curve expressing the attenuation in Millibels
+ for a given volume index from 0 to 100.
+ <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER">
+ <point>0,-9600</point>
+ <point>100,0</point>
+ </volume>
+
+ It may also reference a reference/@name to avoid duplicating curves.
+ <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER"
+ ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+ <reference name="DEFAULT_MEDIA_VOLUME_CURVE">
+ <point>0,-9600</point>
+ <point>100,0</point>
+ </reference>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="point" type="volumePoint" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="stream" type="audioStreamType"/>
+ <xs:attribute name="deviceCategory" type="deviceCategory"/>
+ <xs:attribute name="ref" type="xs:token" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="reference">
+ <xs:sequence>
+ <xs:element name="point" type="volumePoint" minOccurs="2" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:token" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="surroundSound">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Surround Sound section provides configuration related to handling of
+ multi-channel formats.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="formats" type="surroundFormats"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="audioFormatsList">
+ <xs:list itemType="extendableAudioFormat" />
+ </xs:simpleType>
+ <xs:complexType name="surroundFormats">
+ <xs:sequence>
+ <xs:element name="format" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
+ <xs:attribute name="subformats" type="audioFormatsList" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:simpleType name="engineSuffix">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="configurable"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 61516b2..0086743 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -35,6 +35,7 @@
private:
ndk::ScopedAStatus setModuleDebug(
const ::aidl::android::hardware::audio::core::ModuleDebug& in_debug) override;
+ ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus connectExternalDevice(
const ::aidl::android::media::audio::common::AudioPort& in_templateIdAndAdditionalData,
::aidl::android::media::audio::common::AudioPort* _aidl_return) override;
@@ -70,6 +71,17 @@
bool* _aidl_return) override;
ndk::ScopedAStatus resetAudioPatch(int32_t in_patchId) override;
ndk::ScopedAStatus resetAudioPortConfig(int32_t in_portConfigId) override;
+ ndk::ScopedAStatus getMasterMute(bool* _aidl_return) override;
+ ndk::ScopedAStatus setMasterMute(bool in_mute) override;
+ ndk::ScopedAStatus getMasterVolume(float* _aidl_return) override;
+ ndk::ScopedAStatus setMasterVolume(float in_volume) override;
+ ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
+ ndk::ScopedAStatus setMicMute(bool in_mute) override;
+ ndk::ScopedAStatus updateAudioMode(
+ ::aidl::android::hardware::audio::core::AudioMode in_mode) override;
+ ndk::ScopedAStatus updateScreenRotation(
+ ::aidl::android::hardware::audio::core::IModule::ScreenRotation in_rotation) override;
+ ndk::ScopedAStatus updateScreenState(bool in_isTurnedOn) override;
void cleanUpPatch(int32_t patchId);
ndk::ScopedAStatus createStreamContext(
@@ -88,12 +100,18 @@
std::unique_ptr<internal::Configuration> mConfig;
ModuleDebug mDebug;
+ // Since it is required to return the same instance of the ITelephony, even
+ // if the client has released it on its side, we need to hold it via a strong pointer.
+ std::shared_ptr<ITelephony> mTelephony;
// 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.
std::multimap<int32_t, int32_t> mPatches;
+ bool mMasterMute = false;
+ float mMasterVolume = 1.0f;
+ bool mMicMute = false;
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 539fa8b..5ee0f82 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -54,8 +54,6 @@
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
- // Ensure that this value is not used by any of StreamDescriptor.CommandCode enums
- static constexpr int32_t COMMAND_EXIT = -1;
// Ensure that this value is not used by any of StreamDescriptor.State enums
static constexpr int32_t STATE_CLOSED = -1;
diff --git a/audio/aidl/default/include/core-impl/Telephony.h b/audio/aidl/default/include/core-impl/Telephony.h
new file mode 100644
index 0000000..597f3d6
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/Telephony.h
@@ -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.
+ */
+
+#pragma once
+
+#include <android/binder_enums.h>
+
+#include <aidl/android/hardware/audio/core/BnTelephony.h>
+
+namespace aidl::android::hardware::audio::core {
+
+class Telephony : public BnTelephony {
+ private:
+ ndk::ScopedAStatus getSupportedAudioModes(std::vector<AudioMode>* _aidl_return) override;
+ ndk::ScopedAStatus switchAudioMode(AudioMode in_mode) override;
+
+ const std::vector<AudioMode> mSupportedAudioModes = {::ndk::enum_range<AudioMode>().begin(),
+ ::ndk::enum_range<AudioMode>().end()};
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h
index edce26b..fc6a01d 100644
--- a/audio/aidl/default/include/effect-impl/EffectTypes.h
+++ b/audio/aidl/default/include/effect-impl/EffectTypes.h
@@ -19,6 +19,7 @@
#include <string>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <android-base/logging.h>
typedef binder_exception_t (*EffectCreateFunctor)(
const ::aidl::android::media::audio::common::AudioUuid*,
@@ -101,4 +102,23 @@
} \
} while (0)
+static inline bool stringToUuid(const char* str,
+ ::aidl::android::media::audio::common::AudioUuid* uuid) {
+ RETURN_VALUE_IF(!uuid || !str, false, "nullPtr");
+
+ uint32_t tmp[10];
+ if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", tmp, tmp + 1, tmp + 2, tmp + 3,
+ tmp + 4, tmp + 5, tmp + 6, tmp + 7, tmp + 8, tmp + 9) < 10) {
+ return false;
+ }
+
+ uuid->timeLow = (uint32_t)tmp[0];
+ uuid->timeMid = (uint16_t)tmp[1];
+ uuid->timeHiAndVersion = (uint16_t)tmp[2];
+ uuid->clockSeq = (uint16_t)tmp[3];
+ uuid->node.insert(uuid->node.end(), {(uint8_t)tmp[4], (uint8_t)tmp[5], (uint8_t)tmp[6],
+ (uint8_t)tmp[7], (uint8_t)tmp[8], (uint8_t)tmp[9]});
+ return true;
+}
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h
index 767cf6c..184a587 100644
--- a/audio/aidl/default/include/effect-impl/EffectUUID.h
+++ b/audio/aidl/default/include/effect-impl/EffectUUID.h
@@ -15,6 +15,8 @@
*/
#pragma once
+#include <map>
+
#include <aidl/android/media/audio/common/AudioUuid.h>
namespace aidl::android::hardware::audio::effect {
@@ -39,14 +41,14 @@
0x8f34,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-// Equalizer implementation UUID.
+// 0bed4300-847d-11df-bb17-0002a5d5c51b
static const AudioUuid EqualizerSwImplUUID = {static_cast<int32_t>(0x0bed4300),
0x847d,
0x11df,
0xbb17,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-// Equalizer bundle implementation UUID.
+// ce772f20-847d-11df-bb17-0002a5d5c51b
static const AudioUuid EqualizerBundleImplUUID = {static_cast<int32_t>(0xce772f20),
0x847d,
0x11df,
@@ -166,4 +168,26 @@
0x9b6a,
{0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+/**
+ * @brief A map between effect name and effect type UUID.
+ * All <name> attribution in effect/effectProxy of audio_effects.xml should be listed in this map.
+ * We need this map is because existing audio_effects.xml don't have a type UUID defined.
+ */
+static const std::map<const std::string /* effect type */, const AudioUuid&> kUuidNameTypeMap = {
+ {"bassboost", BassBoostTypeUUID},
+ {"downmix", DownmixTypeUUID},
+ {"dynamics_processing", DynamicsProcessingTypeUUID},
+ {"equalizer", EqualizerTypeUUID},
+ {"haptic_generator", HapticGeneratorTypeUUID},
+ {"loudness_enhancer", LoudnessEnhancerTypeUUID},
+ {"reverb", ReverbTypeUUID},
+ {"reverb_env_aux", ReverbTypeUUID},
+ {"reverb_env_ins", ReverbTypeUUID},
+ {"reverb_pre_aux", ReverbTypeUUID},
+ {"reverb_pre_ins", ReverbTypeUUID},
+ {"virtualizer", VirtualizerTypeUUID},
+ {"visualizer", VisualizerTypeUUID},
+ {"volume", VolumeTypeUUID},
+};
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectConfig.h b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
new file mode 100644
index 0000000..2b904f5
--- /dev/null
+++ b/audio/aidl/default/include/effectFactory-impl/EffectConfig.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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 <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <cutils/properties.h>
+#include <tinyxml2.h>
+
+#include "effect-impl/EffectTypes.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+/**
+ * Library contains a mapping from library name to path.
+ * Effect contains a mapping from effect name to Libraries and implementation UUID.
+ * Pre/post processor contains a mapping from processing name to effect names.
+ */
+class EffectConfig {
+ public:
+ explicit EffectConfig(const std::string& file);
+
+ // <library>
+ struct Library {
+ std::string name;
+ std::string path;
+ };
+ struct LibraryUuid {
+ std::string name; // library name
+ ::aidl::android::media::audio::common::AudioUuid uuid;
+ };
+ // <effects>
+ struct EffectLibraries {
+ std::optional<struct LibraryUuid> proxyLibrary;
+ std::vector<struct LibraryUuid> libraries;
+ };
+
+ int getSkippedElements() const { return mSkippedElements; }
+ const std::unordered_map<std::string, std::string> getLibraryMap() const { return mLibraryMap; }
+ const std::unordered_map<std::string, struct EffectLibraries> getEffectsMap() const {
+ return mEffectsMap;
+ }
+ const std::unordered_map<std::string, std::vector<std::string>> getProcessingMap() const {
+ return mProcessingMap;
+ }
+
+ private:
+ int mSkippedElements;
+ /* Parsed Libraries result */
+ std::unordered_map<std::string, std::string> mLibraryMap;
+ /* Parsed Effects result */
+ std::unordered_map<std::string, struct EffectLibraries> mEffectsMap;
+ /* Parsed pre/post processing result */
+ std::unordered_map<std::string, std::vector<std::string>> mProcessingMap;
+
+ /** @return all `node`s children that are elements and match the tag if provided. */
+ std::vector<std::reference_wrapper<const tinyxml2::XMLElement>> getChildren(
+ const tinyxml2::XMLNode& node, const char* childTag = nullptr);
+
+ /** Parse a library xml note and push the result in mLibraryMap or return false on failure. */
+ bool parseLibrary(const tinyxml2::XMLElement& xml);
+
+ /** Parse an effect from an xml element describing it.
+ * @return true and pushes the effect in mEffectsMap on success, false on failure.
+ */
+ bool parseEffect(const tinyxml2::XMLElement& xml);
+
+ bool parseStream(const tinyxml2::XMLElement& xml);
+
+ // Function to parse effect.library name and effect.uuid from xml
+ bool parseLibraryUuid(const tinyxml2::XMLElement& xml, struct LibraryUuid& libraryUuid,
+ bool isProxy = false);
+
+ const char* dump(const tinyxml2::XMLElement& element,
+ tinyxml2::XMLPrinter&& printer = {}) const;
+};
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
index d50bd63..7edace0 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -19,15 +19,17 @@
#include <any>
#include <map>
#include <optional>
+#include <set>
#include <vector>
#include <aidl/android/hardware/audio/effect/BnFactory.h>
+#include "EffectConfig.h"
namespace aidl::android::hardware::audio::effect {
class Factory : public BnFactory {
public:
- Factory();
+ explicit Factory(const std::string& file);
/**
* @brief Get identity of all effects supported by the device, with the optional filter by type
* and/or by instance UUID.
@@ -77,9 +79,10 @@
override;
private:
+ const EffectConfig mConfig;
~Factory();
- // List of effect descriptors supported by the devices.
- std::vector<Descriptor::Identity> mIdentityList;
+ // Set of effect descriptors supported by the devices.
+ std::set<Descriptor::Identity> mIdentitySet;
std::map<aidl::android::media::audio::common::AudioUuid /* implementationUUID */,
std::pair<std::unique_ptr<void, std::function<void(void*)>> /* dlHandle */,
@@ -91,10 +94,13 @@
ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
void cleanupEffectMap();
- void openEffectLibrary(
- const ::aidl::android::media::audio::common::AudioUuid& type,
- const ::aidl::android::media::audio::common::AudioUuid& impl,
- const std::optional<::aidl::android::media::audio::common::AudioUuid>& proxy,
- const std::string& libName);
+ void openEffectLibrary(const ::aidl::android::media::audio::common::AudioUuid& impl,
+ const std::string& libName);
+ void createIdentityWithConfig(
+ const EffectConfig::LibraryUuid& configLib,
+ const ::aidl::android::media::audio::common::AudioUuid& typeUuid,
+ const std::optional<::aidl::android::media::audio::common::AudioUuid> proxyUuid);
+ void loadEffectLibs();
};
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/vts/AudioHalBinderServiceUtil.h b/audio/aidl/vts/AudioHalBinderServiceUtil.h
index e928286..c8d81b1 100644
--- a/audio/aidl/vts/AudioHalBinderServiceUtil.h
+++ b/audio/aidl/vts/AudioHalBinderServiceUtil.h
@@ -21,6 +21,7 @@
#include <mutex>
#include <android-base/properties.h>
+#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
@@ -34,7 +35,7 @@
if (mBinder == nullptr) {
LOG(ERROR) << "Failed to get service " << serviceName;
} else {
- LOG(DEBUG) << "succeed to get service " << serviceName;
+ LOG(DEBUG) << "Succeeded to get service " << serviceName;
}
return mBinder;
}
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index b415da4..2f72bb0 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -15,6 +15,7 @@
*/
#include <algorithm>
+#include <cmath>
#include <limits>
#include <memory>
#include <optional>
@@ -29,8 +30,8 @@
#include <Utils.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/hardware/audio/core/ITelephony.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <android-base/chrono_utils.h>
@@ -46,11 +47,13 @@
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::AudioMode;
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::ITelephony;
using aidl::android::hardware::audio::core::ModuleDebug;
using aidl::android::hardware::audio::core::StreamDescriptor;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
@@ -67,6 +70,7 @@
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::AudioSource;
using aidl::android::media::audio::common::AudioUsage;
+using aidl::android::media::audio::common::Void;
using android::hardware::audio::common::isBitPositionFlagSet;
using android::hardware::audio::common::StreamLogic;
using android::hardware::audio::common::StreamWorker;
@@ -173,6 +177,29 @@
AudioPortConfig mConfig;
};
+template <typename PropType, class Instance, typename Getter, typename Setter>
+void TestAccessors(Instance* inst, Getter getter, Setter setter,
+ const std::vector<PropType>& validValues,
+ const std::vector<PropType>& invalidValues, bool* isSupported) {
+ PropType initialValue{};
+ ScopedAStatus status = (inst->*getter)(&initialValue);
+ if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ *isSupported = false;
+ return;
+ }
+ *isSupported = true;
+ for (const auto v : validValues) {
+ EXPECT_IS_OK((inst->*setter)(v)) << "for valid value: " << v;
+ PropType currentValue{};
+ EXPECT_IS_OK((inst->*getter)(¤tValue));
+ EXPECT_EQ(v, currentValue);
+ }
+ for (const auto v : invalidValues) {
+ EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, (inst->*setter)(v)) << "for invalid value: " << v;
+ }
+ EXPECT_IS_OK((inst->*setter)(initialValue)) << "Failed to restore the initial value";
+}
+
// Can be used as a base for any test here, does not depend on the fixture GTest parameters.
class AudioCoreModuleBase {
public:
@@ -429,7 +456,9 @@
LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
return Status::ABORT;
}
- if (reply.fmqByteCount < 0 || reply.fmqByteCount > command.fmqByteCount) {
+ if (reply.fmqByteCount < 0 ||
+ (command.getTag() == StreamDescriptor::Command::Tag::burst &&
+ reply.fmqByteCount > command.get<StreamDescriptor::Command::Tag::burst>())) {
LOG(ERROR) << __func__
<< ": received invalid byte count in the reply: " << reply.fmqByteCount;
return Status::ABORT;
@@ -506,7 +535,9 @@
LOG(ERROR) << __func__ << ": received error status: " << statusToString(reply.status);
return Status::ABORT;
}
- if (reply.fmqByteCount < 0 || reply.fmqByteCount > command.fmqByteCount) {
+ if (reply.fmqByteCount < 0 ||
+ (command.getTag() == StreamDescriptor::Command::Tag::burst &&
+ reply.fmqByteCount > command.get<StreamDescriptor::Command::Tag::burst>())) {
LOG(ERROR) << __func__
<< ": received invalid byte count in the reply: " << reply.fmqByteCount;
return Status::ABORT;
@@ -1242,6 +1273,112 @@
}
}
+TEST_P(AudioCoreModule, MasterMute) {
+ bool isSupported = false;
+ EXPECT_NO_FATAL_FAILURE(TestAccessors<bool>(module.get(), &IModule::getMasterMute,
+ &IModule::setMasterMute, {false, true}, {},
+ &isSupported));
+ if (!isSupported) {
+ GTEST_SKIP() << "Master mute is not supported";
+ }
+ // TODO: Test that master mute actually mutes output.
+}
+
+TEST_P(AudioCoreModule, MasterVolume) {
+ bool isSupported = false;
+ EXPECT_NO_FATAL_FAILURE(TestAccessors<float>(
+ module.get(), &IModule::getMasterVolume, &IModule::setMasterVolume, {0.0f, 0.5f, 1.0f},
+ {-0.1, 1.1, NAN, INFINITY, -INFINITY, 1 + std::numeric_limits<float>::epsilon()},
+ &isSupported));
+ if (!isSupported) {
+ GTEST_SKIP() << "Master volume is not supported";
+ }
+ // TODO: Test that master volume actually attenuates output.
+}
+
+TEST_P(AudioCoreModule, MicMute) {
+ bool isSupported = false;
+ EXPECT_NO_FATAL_FAILURE(TestAccessors<bool>(module.get(), &IModule::getMicMute,
+ &IModule::setMicMute, {false, true}, {},
+ &isSupported));
+ if (!isSupported) {
+ GTEST_SKIP() << "Mic mute is not supported";
+ }
+ // TODO: Test that mic mute actually mutes input.
+}
+
+TEST_P(AudioCoreModule, UpdateAudioMode) {
+ for (const auto mode : ::ndk::enum_range<AudioMode>()) {
+ EXPECT_IS_OK(module->updateAudioMode(mode)) << toString(mode);
+ }
+ EXPECT_IS_OK(module->updateAudioMode(AudioMode::NORMAL));
+}
+
+TEST_P(AudioCoreModule, UpdateScreenRotation) {
+ for (const auto rotation : ::ndk::enum_range<IModule::ScreenRotation>()) {
+ EXPECT_IS_OK(module->updateScreenRotation(rotation)) << toString(rotation);
+ }
+ EXPECT_IS_OK(module->updateScreenRotation(IModule::ScreenRotation::DEG_0));
+}
+
+TEST_P(AudioCoreModule, UpdateScreenState) {
+ EXPECT_IS_OK(module->updateScreenState(false));
+ EXPECT_IS_OK(module->updateScreenState(true));
+}
+
+class AudioCoreTelephony : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam()));
+ ASSERT_IS_OK(module->getTelephony(&telephony));
+ }
+
+ void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
+
+ std::shared_ptr<ITelephony> telephony;
+};
+
+TEST_P(AudioCoreTelephony, GetSupportedAudioModes) {
+ if (telephony == nullptr) {
+ GTEST_SKIP() << "Telephony is not supported";
+ }
+ std::vector<AudioMode> modes1;
+ ASSERT_IS_OK(telephony->getSupportedAudioModes(&modes1));
+ const std::vector<AudioMode> kMandatoryModes = {AudioMode::NORMAL, AudioMode::RINGTONE,
+ AudioMode::IN_CALL,
+ AudioMode::IN_COMMUNICATION};
+ for (const auto mode : kMandatoryModes) {
+ EXPECT_NE(modes1.end(), std::find(modes1.begin(), modes1.end(), mode))
+ << "Mandatory mode not supported: " << toString(mode);
+ }
+ std::vector<AudioMode> modes2;
+ ASSERT_IS_OK(telephony->getSupportedAudioModes(&modes2));
+ ASSERT_EQ(modes1.size(), modes2.size())
+ << "Sizes of audio mode arrays do not match across consequent calls to "
+ << "getSupportedAudioModes";
+ std::sort(modes1.begin(), modes1.end());
+ std::sort(modes2.begin(), modes2.end());
+ EXPECT_EQ(modes1, modes2);
+};
+
+TEST_P(AudioCoreTelephony, SwitchAudioMode) {
+ if (telephony == nullptr) {
+ GTEST_SKIP() << "Telephony is not supported";
+ }
+ std::vector<AudioMode> supportedModes;
+ ASSERT_IS_OK(telephony->getSupportedAudioModes(&supportedModes));
+ std::set<AudioMode> unsupportedModes = {
+ // Start with all, remove supported ones
+ ::ndk::enum_range<AudioMode>().begin(), ::ndk::enum_range<AudioMode>().end()};
+ for (const auto mode : supportedModes) {
+ EXPECT_IS_OK(telephony->switchAudioMode(mode)) << toString(mode);
+ unsupportedModes.erase(mode);
+ }
+ for (const auto mode : unsupportedModes) {
+ EXPECT_STATUS(EX_UNSUPPORTED_OPERATION, telephony->switchAudioMode(mode)) << toString(mode);
+ }
+}
+
class StreamLogicDriverInvalidCommand : public StreamLogicDriver {
public:
StreamLogicDriverInvalidCommand(const std::vector<StreamDescriptor::Command>& commands)
@@ -1419,18 +1556,15 @@
}
void SendInvalidCommandImpl(const AudioPortConfig& portConfig) {
- std::vector<StreamDescriptor::Command> commands(6);
- commands[0].code = StreamDescriptor::CommandCode(-1);
- commands[1].code = StreamDescriptor::CommandCode(
- static_cast<int32_t>(StreamDescriptor::CommandCode::START) - 1);
- commands[2].code = StreamDescriptor::CommandCode(std::numeric_limits<int32_t>::min());
- commands[3].code = StreamDescriptor::CommandCode(std::numeric_limits<int32_t>::max());
- // TODO: For proper testing of input streams, need to put the stream into
- // a state which accepts BURST commands.
- commands[4].code = StreamDescriptor::CommandCode::BURST;
- commands[4].fmqByteCount = -1;
- commands[5].code = StreamDescriptor::CommandCode::BURST;
- commands[5].fmqByteCount = std::numeric_limits<int32_t>::min();
+ std::vector<StreamDescriptor::Command> commands = {
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::hal_reserved_exit>(
+ 0),
+ // TODO: For proper testing of input streams, need to put the stream into
+ // a state which accepts BURST commands.
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(-1),
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(
+ std::numeric_limits<int32_t>::min()),
+ };
WithStream<Stream> stream(portConfig);
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
StreamLogicDriverInvalidCommand driver(commands);
@@ -1496,7 +1630,7 @@
<< "when no offload info is provided for a compressed offload mix port";
}
-using CommandAndState = std::pair<StreamDescriptor::CommandCode, StreamDescriptor::State>;
+using CommandAndState = std::pair<StreamDescriptor::Command, StreamDescriptor::State>;
class StreamLogicDefaultDriver : public StreamLogicDriver {
public:
@@ -1511,15 +1645,17 @@
bool done() override { return mNextCommand >= mCommands.size(); }
StreamDescriptor::Command getNextCommand(int maxDataSize, int* actualSize) override {
- StreamDescriptor::Command command{};
- command.code = mCommands[mNextCommand++].first;
- const int dataSize = command.code == StreamDescriptor::CommandCode::BURST ? maxDataSize : 0;
- command.fmqByteCount = dataSize;
- if (actualSize != nullptr) {
- // In the output scenario, reduce slightly the fmqByteCount to verify
- // that the HAL module always consumes all data from the MQ.
- if (command.fmqByteCount > 1) command.fmqByteCount--;
- *actualSize = dataSize;
+ auto command = mCommands[mNextCommand++].first;
+ if (command.getTag() == StreamDescriptor::Command::Tag::burst) {
+ if (actualSize != nullptr) {
+ // In the output scenario, reduce slightly the fmqByteCount to verify
+ // that the HAL module always consumes all data from the MQ.
+ if (maxDataSize > 1) maxDataSize--;
+ *actualSize = maxDataSize;
+ }
+ command.set<StreamDescriptor::Command::Tag::burst>(maxDataSize);
+ } else {
+ if (actualSize != nullptr) *actualSize = 0;
}
return command;
}
@@ -1541,7 +1677,7 @@
.append(" to ")
.append(toString(reply.state))
.append(" caused by the command ")
- .append(toString(lastCommandState.first));
+ .append(lastCommandState.first.toString());
LOG(ERROR) << __func__ << ": " << s;
mUnexpectedTransition = std::move(s);
return false;
@@ -1825,6 +1961,9 @@
INSTANTIATE_TEST_SUITE_P(AudioCoreModuleTest, AudioCoreModule,
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
android::PrintInstanceNameToString);
+INSTANTIATE_TEST_SUITE_P(AudioCoreTelephonyTest, AudioCoreTelephony,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
+ android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioCoreModule);
INSTANTIATE_TEST_SUITE_P(AudioStreamInTest, AudioStreamIn,
testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
@@ -1835,112 +1974,90 @@
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamOut);
-static const NamedCommandSequence kReadOrWriteSeq = std::make_pair(
- std::string("ReadOrWrite"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE)});
-static const NamedCommandSequence kDrainInSeq = std::make_pair(
- std::string("Drain"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::DRAIN,
- StreamDescriptor::State::DRAINING),
- std::make_pair(StreamDescriptor::CommandCode::START,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::DRAIN,
- StreamDescriptor::State::DRAINING),
- // TODO: This will need to be changed once DRAIN starts taking time.
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kDrainOutSeq = std::make_pair(
- std::string("Drain"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- // TODO: This will need to be changed once DRAIN starts taking time.
- std::make_pair(StreamDescriptor::CommandCode::DRAIN,
- StreamDescriptor::State::IDLE)});
+static const StreamDescriptor::Command kStartCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::start>(Void{});
+static const StreamDescriptor::Command kBurstCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(0);
+static const StreamDescriptor::Command kDrainCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(Void{});
+static const StreamDescriptor::Command kStandbyCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::standby>(Void{});
+static const StreamDescriptor::Command kPauseCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::pause>(Void{});
+static const StreamDescriptor::Command kFlushCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::flush>(Void{});
+static const NamedCommandSequence kReadOrWriteSeq =
+ std::make_pair(std::string("ReadOrWrite"),
+ std::vector<CommandAndState>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE)});
+static const NamedCommandSequence kDrainInSeq =
+ std::make_pair(std::string("Drain"),
+ std::vector<CommandAndState>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kDrainCommand, StreamDescriptor::State::DRAINING),
+ std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kDrainCommand, StreamDescriptor::State::DRAINING),
+ // TODO: This will need to be changed once DRAIN starts taking time.
+ std::make_pair(kBurstCommand, StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kDrainOutSeq =
+ std::make_pair(std::string("Drain"),
+ std::vector<CommandAndState>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ // TODO: This will need to be changed once DRAIN starts taking time.
+ std::make_pair(kDrainCommand, StreamDescriptor::State::IDLE)});
// TODO: This will need to be changed once DRAIN starts taking time so we can pause it.
static const NamedCommandSequence kDrainPauseOutSeq = std::make_pair(
std::string("DrainPause"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::DRAIN,
- StreamDescriptor::State::IDLE)});
-static const NamedCommandSequence kStandbySeq = std::make_pair(
- std::string("Standby"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::STANDBY,
- StreamDescriptor::State::STANDBY),
- // Perform a read or write in order to advance observable position
- // (this is verified by tests).
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE)});
-static const NamedCommandSequence kPauseInSeq = std::make_pair(
- std::string("Pause"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::PAUSE,
- StreamDescriptor::State::PAUSED),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::PAUSE,
- StreamDescriptor::State::PAUSED),
- std::make_pair(StreamDescriptor::CommandCode::FLUSH,
- StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kPauseOutSeq = std::make_pair(
- std::string("Pause"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::PAUSE,
- StreamDescriptor::State::PAUSED),
- std::make_pair(StreamDescriptor::CommandCode::START,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::PAUSE,
- StreamDescriptor::State::PAUSED),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::PAUSED),
- std::make_pair(StreamDescriptor::CommandCode::START,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::PAUSE,
- StreamDescriptor::State::PAUSED)});
-static const NamedCommandSequence kFlushInSeq = std::make_pair(
- std::string("Flush"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::PAUSE,
- StreamDescriptor::State::PAUSED),
- std::make_pair(StreamDescriptor::CommandCode::FLUSH,
- StreamDescriptor::State::STANDBY)});
+ std::vector<CommandAndState>{std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kDrainCommand, StreamDescriptor::State::IDLE)});
+static const NamedCommandSequence kStandbySeq =
+ std::make_pair(std::string("Standby"),
+ std::vector<CommandAndState>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kStandbyCommand, StreamDescriptor::State::STANDBY),
+ // Perform a read or write in order to advance observable position
+ // (this is verified by tests).
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE)});
+static const NamedCommandSequence kPauseInSeq =
+ std::make_pair(std::string("Pause"),
+ std::vector<CommandAndState>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)});
+static const NamedCommandSequence kPauseOutSeq =
+ std::make_pair(std::string("Pause"),
+ std::vector<CommandAndState>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED)});
+static const NamedCommandSequence kFlushInSeq =
+ std::make_pair(std::string("Flush"),
+ std::vector<CommandAndState>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)});
static const NamedCommandSequence kFlushOutSeq = std::make_pair(
std::string("Flush"),
- std::vector<CommandAndState>{
- std::make_pair(StreamDescriptor::CommandCode::START, StreamDescriptor::State::IDLE),
- std::make_pair(StreamDescriptor::CommandCode::BURST,
- StreamDescriptor::State::ACTIVE),
- std::make_pair(StreamDescriptor::CommandCode::PAUSE,
- StreamDescriptor::State::PAUSED),
- std::make_pair(StreamDescriptor::CommandCode::FLUSH,
- StreamDescriptor::State::IDLE)});
+ std::vector<CommandAndState>{std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kFlushCommand, StreamDescriptor::State::IDLE)});
std::string GetStreamIoTestName(const testing::TestParamInfo<StreamIoTestParameters>& info) {
return android::PrintInstanceNameToString(
testing::TestParamInfo<std::string>{std::get<PARAM_MODULE_NAME>(info.param),
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index e46e5b4..0ca6a58 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -1014,9 +1014,8 @@
if (mDataPosition == 0) mOnDataStart();
const size_t dataSize = std::min(mData.size() - mDataPosition, mDataMQ->availableToWrite());
bool success = mDataMQ->write(mData.data() + mDataPosition, dataSize);
+ bool wrapped = false;
ALOGE_IF(!success, "data message queue write failed");
- mDataPosition += dataSize;
- if (mDataPosition >= mData.size()) mDataPosition = 0;
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
uint32_t efState = 0;
@@ -1034,6 +1033,11 @@
ALOGE("bad write status: %d", writeStatus.retval);
success = false;
}
+ mDataPosition += writeStatus.reply.written;
+ if (mDataPosition >= mData.size()) {
+ mDataPosition = 0;
+ wrapped = true;
+ }
}
if (ret == -EAGAIN || ret == -EINTR) {
// Spurious wakeup. This normally retries no more than once.
@@ -1042,7 +1046,7 @@
ALOGE("bad wait status: %d", ret);
success = false;
}
- if (success && mDataPosition == 0) {
+ if (wrapped) {
success = mOnDataWrap();
}
return success;
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 64c7ce7..a50cc80 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
@@ -119,6 +119,7 @@
DOOR_POS = 373295872,
DOOR_MOVE = 373295873,
DOOR_LOCK = 371198722,
+ DOOR_CHILD_LOCK_ENABLED = 371198723,
MIRROR_Z_POS = 339741504,
MIRROR_Z_MOVE = 339741505,
MIRROR_Y_POS = 339741506,
@@ -156,6 +157,8 @@
WINDOW_POS = 322964416,
WINDOW_MOVE = 322964417,
WINDOW_LOCK = 320867268,
+ STEERING_WHEEL_DEPTH_POS = 289410016,
+ STEERING_WHEEL_DEPTH_MOVE = 289410017,
VEHICLE_MAP_SERVICE = 299895808,
OBD2_LIVE_FRAME = 299896064,
OBD2_FREEZE_FRAME = 299896065,
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 1852673..fb43d9a 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -16,6 +16,8 @@
package android.hardware.automotive.vehicle;
+import android.hardware.automotive.vehicle.VehicleArea;
+import android.hardware.automotive.vehicle.VehiclePropertyGroup;
import android.hardware.automotive.vehicle.VehiclePropertyType;
/**
* Declares all vehicle properties. VehicleProperty has a bitwise structure.
@@ -1318,6 +1320,18 @@
DOOR_LOCK = 0x0B02 + 0x10000000 + 0x06000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:DOOR,VehiclePropertyType:BOOLEAN
/**
+ * Door child lock feature enabled
+ *
+ * Returns true if the door child lock feature is enabled and false if it is disabled.
+ *
+ * If enabled, the door is unable to be opened from the inside.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
+ */
+ DOOR_CHILD_LOCK_ENABLED =
+ 0x0B03 + VehiclePropertyGroup.SYSTEM + VehicleArea.DOOR + VehiclePropertyType.BOOLEAN,
+ /**
* Mirror Z Position
*
* Positive value indicates tilt upwards, negative value is downwards
@@ -1748,6 +1762,44 @@
WINDOW_LOCK = 0x0BC4 + 0x10000000 + 0x03000000
+ 0x00200000, // VehiclePropertyGroup:SYSTEM,VehicleArea:WINDOW,VehiclePropertyType:BOOLEAN
/**
+ * Steering wheel depth position
+ *
+ * All steering wheel properties' unique ids start from 0x0BE0.
+ *
+ * The maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All values between
+ * minInt32Value and maxInt32Value must be supported.
+ *
+ * The maxInt32Value in default area's VehicleAreaConfig indicates the steering wheel position
+ * closest to the driver. The minInt32Value in default area's VehicleAreaConfig indicates the
+ * steering wheel position furthest to the driver.
+ *
+ * This value is not in any particular unit but in a specified range of steps.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
+ */
+ STEERING_WHEEL_DEPTH_POS =
+ 0x0BE0 + VehiclePropertyGroup.SYSTEM + VehicleArea.GLOBAL + VehiclePropertyType.INT32,
+ /**
+ * Steering wheel depth movement
+ *
+ * The maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All values between
+ * minInt32Value and maxInt32Value must be supported.
+ *
+ * The maxInt32Value in default area's VehicleAreaConfig indicates the steering wheel moving
+ * towards the driver. The minInt32Value in default area's VehicleAreaConfig indicates the
+ * steering wheel moving away from the driver. Larger integers, either positive or negative,
+ * indicate a faster movement speed. Once the steering wheel reaches the positional limit, the
+ * value resets to 0.
+ *
+ * This value is not in any particular unit but in a specified range of steps.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
+ */
+ STEERING_WHEEL_DEPTH_MOVE =
+ 0x0BE1 + VehiclePropertyGroup.SYSTEM + VehicleArea.GLOBAL + VehiclePropertyType.INT32,
+ /**
* Vehicle Maps Service (VMS) message
*
* This property uses MIXED data to communicate vms messages.
@@ -2889,5 +2941,4 @@
*/
SUPPORTED_PROPERTY_IDS = 0x0F48 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
-
}
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
index c91afe2..e5ce8e1 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
@@ -119,6 +119,7 @@
{VehicleProperty::DOOR_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::DOOR_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::DOOR_LOCK, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::DOOR_CHILD_LOCK_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::MIRROR_Z_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::MIRROR_Z_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::MIRROR_Y_POS, VehiclePropertyAccess::READ_WRITE},
@@ -156,6 +157,8 @@
{VehicleProperty::WINDOW_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::WINDOW_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::WINDOW_LOCK, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::STEERING_WHEEL_DEPTH_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::STEERING_WHEEL_DEPTH_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::VEHICLE_MAP_SERVICE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::OBD2_LIVE_FRAME, VehiclePropertyAccess::READ},
{VehicleProperty::OBD2_FREEZE_FRAME, VehiclePropertyAccess::READ},
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
index 807be5d..625ad19 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
@@ -119,6 +119,7 @@
{VehicleProperty::DOOR_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DOOR_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DOOR_LOCK, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::DOOR_CHILD_LOCK_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::MIRROR_Z_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::MIRROR_Z_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::MIRROR_Y_POS, VehiclePropertyChangeMode::ON_CHANGE},
@@ -156,6 +157,8 @@
{VehicleProperty::WINDOW_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::WINDOW_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::WINDOW_LOCK, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::STEERING_WHEEL_DEPTH_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::STEERING_WHEEL_DEPTH_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::VEHICLE_MAP_SERVICE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::OBD2_LIVE_FRAME, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::OBD2_FREEZE_FRAME, VehiclePropertyChangeMode::ON_CHANGE},
diff --git a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
index e698f84..22e9411 100644
--- a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
@@ -111,6 +111,7 @@
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.DOOR_CHILD_LOCK_ENABLED, 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),
@@ -148,6 +149,8 @@
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.STEERING_WHEEL_DEPTH_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.STEERING_WHEEL_DEPTH_MOVE, 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),
diff --git a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
index fe69450..8d2efe9 100644
--- a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
@@ -111,6 +111,7 @@
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.DOOR_CHILD_LOCK_ENABLED, 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),
@@ -148,6 +149,8 @@
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.STEERING_WHEEL_DEPTH_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.STEERING_WHEEL_DEPTH_MOVE, 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),
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 7b471e1..7e55f9c 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -1929,6 +1929,22 @@
]
},
{
+ "property": "VehicleProperty::DOOR_CHILD_LOCK_ENABLED",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::DOOR_2_LEFT"
+ },
+ {
+ "areaId": "Constants::DOOR_2_RIGHT"
+ }
+ ]
+ },
+ {
"property": "VehicleProperty::DOOR_POS",
"defaultValue": {
"int32Values": [
@@ -2193,6 +2209,36 @@
]
},
{
+ "property": "VehicleProperty::STEERING_WHEEL_DEPTH_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": 0,
+ "minInt32Value": 0,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::STEERING_WHEEL_DEPTH_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": 0,
+ "minInt32Value": -2,
+ "maxInt32Value": 2
+ }
+ ]
+ },
+ {
"property": "VehicleProperty::WHEEL_TICK",
"defaultValue": {
"int64Values": [
diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
index 5de206b..2b37d35 100644
--- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
+++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
@@ -44,6 +44,8 @@
using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
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::VehiclePropertyGroup;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::android::getAidlHalInstanceNames;
using ::android::base::ScopedLockAssertion;
@@ -114,6 +116,9 @@
class VtsHalAutomotiveVehicleTargetTest : public testing::TestWithParam<ServiceDescriptor> {
public:
+ void verifyProperty(VehicleProperty propId, VehiclePropertyAccess access,
+ VehiclePropertyChangeMode changeMode, VehiclePropertyGroup group,
+ VehicleArea area, VehiclePropertyType propertyType);
virtual void SetUp() override {
auto descriptor = GetParam();
if (descriptor.isAidlService) {
@@ -420,6 +425,80 @@
}
}
+// Helper function to compare actual vs expected property config
+void VtsHalAutomotiveVehicleTargetTest::verifyProperty(VehicleProperty propId,
+ VehiclePropertyAccess access,
+ VehiclePropertyChangeMode changeMode,
+ VehiclePropertyGroup group, VehicleArea area,
+ VehiclePropertyType propertyType) {
+ int expectedPropId = toInt(propId);
+ int expectedAccess = toInt(access);
+ int expectedChangeMode = toInt(changeMode);
+ int expectedGroup = toInt(group);
+ int expectedArea = toInt(area);
+ int expectedPropertyType = toInt(propertyType);
+
+ auto result = mVhalClient->getPropConfigs({expectedPropId});
+ ASSERT_TRUE(result.ok()) << "Failed to get required property config, error: "
+ << result.error().message();
+
+ if (result.value().size() == 0) {
+ GTEST_SKIP() << "Property has not been implemented";
+ }
+ ASSERT_EQ(result.value().size(), 1u)
+ << StringPrintf("Expect to get exactly 1 config, got %zu", result.value().size());
+
+ const auto& config = result.value().at(0);
+ int actualPropId = config->getPropId();
+ int actualAccess = config->getAccess();
+ int actualChangeMode = config->getChangeMode();
+ int actualGroup = actualPropId & toInt(VehiclePropertyGroup::MASK);
+ int actualArea = actualPropId & toInt(VehicleArea::MASK);
+ int actualPropertyType = actualPropId & toInt(VehiclePropertyType::MASK);
+
+ ASSERT_EQ(actualPropId, expectedPropId)
+ << StringPrintf("Expect to get property ID: %i, got %i", expectedPropId, actualPropId);
+
+ if (expectedAccess == toInt(VehiclePropertyAccess::READ_WRITE)) {
+ ASSERT_TRUE(actualAccess == expectedAccess ||
+ actualAccess == toInt(VehiclePropertyAccess::READ))
+ << StringPrintf("Expect to get VehiclePropertyAccess: %i or %i, got %i",
+ expectedAccess, toInt(VehiclePropertyAccess::READ), actualAccess);
+ } else {
+ ASSERT_EQ(actualAccess, expectedAccess) << StringPrintf(
+ "Expect to get VehiclePropertyAccess: %i, got %i", expectedAccess, actualAccess);
+ }
+
+ ASSERT_EQ(actualChangeMode, expectedChangeMode)
+ << StringPrintf("Expect to get VehiclePropertyChangeMode: %i, got %i",
+ expectedChangeMode, actualChangeMode);
+ ASSERT_EQ(actualGroup, expectedGroup) << StringPrintf(
+ "Expect to get VehiclePropertyGroup: %i, got %i", expectedGroup, actualGroup);
+ ASSERT_EQ(actualArea, expectedArea)
+ << StringPrintf("Expect to get VehicleArea: %i, got %i", expectedArea, actualArea);
+ ASSERT_EQ(actualPropertyType, expectedPropertyType)
+ << StringPrintf("Expect to get VehiclePropertyType: %i, got %i", expectedPropertyType,
+ actualPropertyType);
+}
+
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifyDoorChildLockEnabledConfig) {
+ verifyProperty(VehicleProperty::DOOR_CHILD_LOCK_ENABLED, VehiclePropertyAccess::READ_WRITE,
+ VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
+ VehicleArea::DOOR, VehiclePropertyType::BOOLEAN);
+}
+
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifySteeringWheelDepthPosConfig) {
+ verifyProperty(VehicleProperty::STEERING_WHEEL_DEPTH_POS, VehiclePropertyAccess::READ_WRITE,
+ VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
+ VehicleArea::GLOBAL, VehiclePropertyType::INT32);
+}
+
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifySteeringWheelDepthMoveConfig) {
+ verifyProperty(VehicleProperty::STEERING_WHEEL_DEPTH_MOVE, VehiclePropertyAccess::READ_WRITE,
+ VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
+ VehicleArea::GLOBAL, VehiclePropertyType::INT32);
+}
+
std::vector<ServiceDescriptor> getDescriptors() {
std::vector<ServiceDescriptor> descriptors;
for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) {
diff --git a/biometrics/common/util/include/util/Util.h b/biometrics/common/util/include/util/Util.h
index 29ec0f8..da19dc6 100644
--- a/biometrics/common/util/include/util/Util.h
+++ b/biometrics/common/util/include/util/Util.h
@@ -40,7 +40,7 @@
// 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
+#define DEFAULT_LATENCY 400
class Util {
public:
@@ -66,4 +66,4 @@
}
};
-} // namespace aidl::android::hardware::biometrics
\ No newline at end of file
+} // namespace aidl::android::hardware::biometrics
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index dc0199c..77c74e1 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -15,6 +15,7 @@
vintf_fragments: ["fingerprint-example.xml"],
local_include_dirs: ["include"],
srcs: [
+ "FakeLockoutTracker.cpp",
"FakeFingerprintEngine.cpp",
"FakeFingerprintEngineRear.cpp",
"FakeFingerprintEngineUdfps.cpp",
@@ -40,6 +41,7 @@
srcs: [
"tests/FakeFingerprintEngineTest.cpp",
"FakeFingerprintEngine.cpp",
+ "FakeLockoutTracker.cpp",
],
shared_libs: [
"libbase",
@@ -65,6 +67,31 @@
"tests/FakeFingerprintEngineUdfpsTest.cpp",
"FakeFingerprintEngineUdfps.cpp",
"FakeFingerprintEngine.cpp",
+ "FakeLockoutTracker.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.biometrics.common.thread",
+ ],
+ 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,
+}
+
+cc_test {
+ name: "android.hardware.biometrics.fingerprint.FakeLockoutTrackerTest",
+ local_include_dirs: ["include"],
+ srcs: [
+ "tests/FakeLockoutTrackerTest.cpp",
+ "FakeLockoutTracker.cpp",
],
shared_libs: [
"libbase",
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 651c9dc..90ec8f2 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -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.
@@ -15,6 +15,7 @@
*/
#include "FakeFingerprintEngine.h"
+#include <regex>
#include "Fingerprint.h"
#include <android-base/logging.h>
@@ -47,7 +48,7 @@
void FakeFingerprintEngine::enrollImpl(ISessionCallback* cb,
const keymaster::HardwareAuthToken& hat,
const std::future<void>& cancel) {
- BEGIN_OP(FingerprintHalProperties::operation_enroll_latency().value_or(DEFAULT_LATENCY));
+ BEGIN_OP(getLatency(FingerprintHalProperties::operation_enroll_latency()));
// Do proper HAT verification in the real implementation.
if (hat.mac.empty()) {
@@ -117,7 +118,7 @@
void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* operationId */,
const std::future<void>& cancel) {
- BEGIN_OP(FingerprintHalProperties::operation_authenticate_latency().value_or(DEFAULT_LATENCY));
+ BEGIN_OP(getLatency(FingerprintHalProperties::operation_authenticate_latency()));
int64_t now = Util::getSystemNanoTime();
int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(10);
@@ -131,10 +132,23 @@
return;
}
+ // got lockout?
+ FakeLockoutTracker::LockoutMode lockoutMode = mLockoutTracker.getMode();
+ if (lockoutMode == FakeLockoutTracker::LockoutMode::kPermanent) {
+ LOG(ERROR) << "Fail: lockout permanent";
+ cb->onLockoutPermanent();
+ return;
+ } else if (lockoutMode == FakeLockoutTracker::LockoutMode::kTimed) {
+ int64_t timeLeft = mLockoutTracker.getLockoutTimeLeft();
+ LOG(ERROR) << "Fail: lockout timed " << timeLeft;
+ cb->onLockoutTimed(timeLeft);
+ }
+
int i = 0;
do {
if (FingerprintHalProperties::operation_authenticate_fails().value_or(false)) {
LOG(ERROR) << "Fail: operation_authenticate_fails";
+ mLockoutTracker.addFailedAttempt();
cb->onAuthenticationFailed();
return;
}
@@ -174,20 +188,30 @@
auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
if (id > 0 && isEnrolled) {
cb->onAuthenticationSucceeded(id, {} /* hat */);
+ mLockoutTracker.reset();
return;
} else {
LOG(ERROR) << "Fail: fingerprint not enrolled";
cb->onAuthenticationFailed();
+ mLockoutTracker.addFailedAttempt();
}
}
void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb,
const std::future<void>& cancel) {
- BEGIN_OP(FingerprintHalProperties::operation_detect_interaction_latency().value_or(
- DEFAULT_LATENCY));
+ BEGIN_OP(getLatency(FingerprintHalProperties::operation_detect_interaction_latency()));
int64_t duration =
FingerprintHalProperties::operation_detect_interaction_duration().value_or(10);
+
+ auto detectInteractionSupported =
+ FingerprintHalProperties::detect_interaction().value_or(false);
+ if (!detectInteractionSupported) {
+ LOG(ERROR) << "Detect interaction is not supported";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
auto acquired = FingerprintHalProperties::operation_detect_interaction_acquired().value_or("1");
auto acquiredInfos = parseIntSequence(acquired);
int N = acquiredInfos.size();
@@ -308,6 +332,7 @@
}
FingerprintHalProperties::lockout(false);
cb->onLockoutCleared();
+ mLockoutTracker.reset();
}
ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/,
@@ -383,49 +408,52 @@
return res;
}
-std::vector<std::vector<int32_t>> FakeFingerprintEngine::parseEnrollmentCapture(
- const std::string& str) {
+bool FakeFingerprintEngine::parseEnrollmentCaptureSingle(const std::string& str,
+ std::vector<std::vector<int32_t>>& res) {
std::vector<int32_t> defaultAcquiredInfo = {(int32_t)AcquiredInfo::GOOD};
- std::vector<std::vector<int32_t>> res;
- int i = 0, N = str.length();
- std::size_t found = 0;
bool aborted = true;
- while (found != std::string::npos) {
- std::string durationStr, acquiredStr;
- found = str.find_first_of("-,", i);
- if (found == std::string::npos) {
- if (N - i < 1) break;
- durationStr = str.substr(i, N - i);
- } else {
- durationStr = str.substr(i, found - i);
- if (str[found] == '-') {
- found = str.find_first_of('[', found + 1);
- if (found == std::string::npos) break;
- i = found + 1;
- found = str.find_first_of(']', found + 1);
- if (found == std::string::npos) break;
- acquiredStr = str.substr(i, found - i);
- found = str.find_first_of(',', found + 1);
- }
- }
- std::vector<int32_t> duration{0};
- if (!ParseInt(durationStr, &duration[0])) break;
- res.push_back(duration);
- if (!acquiredStr.empty()) {
- std::vector<int32_t> acquiredInfo = parseIntSequence(acquiredStr);
- if (acquiredInfo.empty()) break;
- res.push_back(acquiredInfo);
+ do {
+ std::smatch sms;
+ // Parses strings like "1000-[5,1]" or "500"
+ std::regex ex("((\\d+)(-\\[([\\d|,]+)\\])?)");
+ if (!regex_match(str.cbegin(), str.cend(), sms, ex)) break;
+ int32_t duration;
+ if (!ParseInt(sms.str(2), &duration)) break;
+ res.push_back({duration});
+ if (!sms.str(4).empty()) {
+ auto acqv = parseIntSequence(sms.str(4));
+ if (acqv.empty()) break;
+ res.push_back(acqv);
} else
res.push_back(defaultAcquiredInfo);
+ aborted = false;
+ } while (0);
- i = found + 1;
- if (found == std::string::npos || found == N - 1) aborted = false;
+ return !aborted;
+}
+
+std::vector<std::vector<int32_t>> FakeFingerprintEngine::parseEnrollmentCapture(
+ const std::string& str) {
+ std::vector<std::vector<int32_t>> res;
+
+ std::string s(str);
+ s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
+ bool aborted = false;
+ std::smatch sms;
+ // Parses strings like "1000-[5,1],500,800-[6,5,1]"
+ // ---------- --- -----------
+ // into parts: A B C
+ while (regex_search(s, sms, std::regex("^(,)?(\\d+(-\\[[\\d|,]+\\])?)"))) {
+ if (!parseEnrollmentCaptureSingle(sms.str(2), res)) {
+ aborted = true;
+ break;
+ }
+ s = sms.suffix();
}
-
- if (aborted) {
- LOG(ERROR) << "Failed to parse enrollment captures:" + str;
+ if (aborted || s.length() != 0) {
res.clear();
+ LOG(ERROR) << "Failed to parse enrollment captures:" + str;
}
return res;
@@ -455,4 +483,34 @@
return res;
}
+int32_t FakeFingerprintEngine::getLatency(
+ const std::vector<std::optional<std::int32_t>>& latencyIn) {
+ int32_t res = DEFAULT_LATENCY;
+
+ std::vector<int32_t> latency;
+ for (auto x : latencyIn)
+ if (x.has_value()) latency.push_back(*x);
+
+ switch (latency.size()) {
+ case 0:
+ break;
+ case 1:
+ res = latency[0];
+ break;
+ case 2:
+ res = getRandomInRange(latency[0], latency[1]);
+ break;
+ default:
+ LOG(ERROR) << "ERROR: unexpected input of size " << latency.size();
+ break;
+ }
+
+ return res;
+}
+
+int32_t FakeFingerprintEngine::getRandomInRange(int32_t bound1, int32_t bound2) {
+ std::uniform_int_distribution<int32_t> dist(std::min(bound1, bound2), std::max(bound1, bound2));
+ return dist(mRandom);
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
index d8579a4..3cdfc70 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
@@ -23,10 +23,16 @@
#include "util/CancellationSignal.h"
#include "util/Util.h"
+#undef LOG_TAG
+#define LOG_TAG "FingerprintVirtualHalUdfps"
+
using namespace ::android::fingerprint::virt;
namespace aidl::android::hardware::biometrics::fingerprint {
+FakeFingerprintEngineUdfps::FakeFingerprintEngineUdfps()
+ : FakeFingerprintEngine(), mWorkMode(WorkMode::kIdle), mPointerDownTime(0), mUiReadyTime(0) {}
+
SensorLocation FakeFingerprintEngineUdfps::defaultSensorLocation() {
return {0 /* displayId (not used) */, defaultSensorLocationX /* sensorLocationX */,
defaultSensorLocationY /* sensorLocationY */, defaultSensorRadius /* sensorRadius */,
@@ -37,22 +43,95 @@
int32_t /*x*/, int32_t /*y*/,
float /*minor*/, float /*major*/) {
BEGIN_OP(0);
-
- // TODO(b/230515082): if need to handle display touch events
-
+ // verify whetehr touch coordinates/area matching sensor location ?
+ mPointerDownTime = Util::getSystemNanoTime();
+ if (FingerprintHalProperties::control_illumination().value_or(false)) {
+ fingerDownAction();
+ }
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus FakeFingerprintEngineUdfps::onPointerUpImpl(int32_t /*pointerId*/) {
BEGIN_OP(0);
- // TODO(b/230515082)
+ mUiReadyTime = 0;
+ mPointerDownTime = 0;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus FakeFingerprintEngineUdfps::onUiReadyImpl() {
BEGIN_OP(0);
- // TODO(b/230515082)
+
+ if (Util::hasElapsed(mPointerDownTime, uiReadyTimeoutInMs * 100)) {
+ LOG(ERROR) << "onUiReady() arrives too late after onPointerDown()";
+ } else {
+ fingerDownAction();
+ }
return ndk::ScopedAStatus::ok();
}
+void FakeFingerprintEngineUdfps::fingerDownAction() {
+ switch (mWorkMode) {
+ case WorkMode::kAuthenticate:
+ onAuthenticateFingerDown();
+ break;
+ case WorkMode::kEnroll:
+ onEnrollFingerDown();
+ break;
+ case WorkMode::kDetectInteract:
+ onDetectInteractFingerDown();
+ break;
+ default:
+ LOG(WARNING) << "unexpected call: onUiReady()";
+ break;
+ }
+
+ mUiReadyTime = 0;
+ mPointerDownTime = 0;
+}
+
+void FakeFingerprintEngineUdfps::onAuthenticateFingerDown() {
+ FakeFingerprintEngine::authenticateImpl(mCb, mOperationId, mCancelVec[0]);
+}
+
+void FakeFingerprintEngineUdfps::onEnrollFingerDown() {
+ // Any use case to emulate display touch for each capture during enrollment?
+ FakeFingerprintEngine::enrollImpl(mCb, mHat, mCancelVec[0]);
+}
+
+void FakeFingerprintEngineUdfps::onDetectInteractFingerDown() {
+ FakeFingerprintEngine::detectInteractionImpl(mCb, mCancelVec[0]);
+}
+
+void FakeFingerprintEngineUdfps::enrollImpl(ISessionCallback* cb,
+ const keymaster::HardwareAuthToken& hat,
+ const std::future<void>& cancel) {
+ updateContext(WorkMode::kEnroll, cb, const_cast<std::future<void>&>(cancel), 0, hat);
+}
+
+void FakeFingerprintEngineUdfps::authenticateImpl(ISessionCallback* cb, int64_t operationId,
+ const std::future<void>& cancel) {
+ updateContext(WorkMode::kAuthenticate, cb, const_cast<std::future<void>&>(cancel), operationId,
+ keymaster::HardwareAuthToken());
+}
+
+void FakeFingerprintEngineUdfps::detectInteractionImpl(ISessionCallback* cb,
+ const std::future<void>& cancel) {
+ updateContext(WorkMode::kDetectInteract, cb, const_cast<std::future<void>&>(cancel), 0,
+ keymaster::HardwareAuthToken());
+}
+
+void FakeFingerprintEngineUdfps::updateContext(WorkMode mode, ISessionCallback* cb,
+ std::future<void>& cancel, int64_t operationId,
+ const keymaster::HardwareAuthToken& hat) {
+ mPointerDownTime = 0;
+ mUiReadyTime = 0;
+ mCancelVec.clear();
+
+ mCancelVec.push_back(std::move(cancel));
+ mWorkMode = mode;
+ mCb = cb;
+ mOperationId = operationId;
+ mHat = hat;
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp b/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp
new file mode 100644
index 0000000..5996406
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "FakeLockoutTracker.h"
+#include <fingerprint.sysprop.h>
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+void FakeLockoutTracker::reset() {
+ mFailedCount = 0;
+ mLockoutTimedStart = 0;
+ mCurrentMode = LockoutMode::kNone;
+}
+
+void FakeLockoutTracker::addFailedAttempt() {
+ bool enabled = FingerprintHalProperties::lockout_enable().value_or(false);
+ if (enabled) {
+ mFailedCount++;
+ int32_t lockoutTimedThreshold =
+ FingerprintHalProperties::lockout_timed_threshold().value_or(5);
+ int32_t lockoutPermanetThreshold =
+ FingerprintHalProperties::lockout_permanent_threshold().value_or(20);
+ if (mFailedCount >= lockoutPermanetThreshold) {
+ mCurrentMode = LockoutMode::kPermanent;
+ FingerprintHalProperties::lockout(true);
+ } else if (mFailedCount >= lockoutTimedThreshold) {
+ if (mCurrentMode == LockoutMode::kNone) {
+ mCurrentMode = LockoutMode::kTimed;
+ mLockoutTimedStart = Util::getSystemNanoTime();
+ }
+ }
+ } else {
+ reset();
+ }
+}
+
+FakeLockoutTracker::LockoutMode FakeLockoutTracker::getMode() {
+ if (mCurrentMode == LockoutMode::kTimed) {
+ int32_t lockoutTimedDuration =
+ FingerprintHalProperties::lockout_timed_duration().value_or(10 * 100);
+ if (Util::hasElapsed(mLockoutTimedStart, lockoutTimedDuration)) {
+ mCurrentMode = LockoutMode::kNone;
+ mLockoutTimedStart = 0;
+ }
+ }
+
+ return mCurrentMode;
+}
+
+int64_t FakeLockoutTracker::getLockoutTimeLeft() {
+ int64_t res = 0;
+
+ if (mLockoutTimedStart > 0) {
+ auto now = Util::getSystemNanoTime();
+ auto left = now - mLockoutTimedStart;
+ res = (left > 0) ? (left / 1000000LL) : 0;
+ }
+
+ return res;
+}
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 74e7caf..be93224 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -15,11 +15,13 @@
*/
#include "Fingerprint.h"
-
-#include <fingerprint.sysprop.h>
#include "Session.h"
+#include <fingerprint.sysprop.h>
+
+#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
using namespace ::android::fingerprint::virt;
@@ -64,7 +66,6 @@
{HW_COMPONENT_ID, HW_VERSION, FW_VERSION, SERIAL_NUMBER, "" /* softwareVersion */},
{SW_COMPONENT_ID, "" /* hardwareVersion */, "" /* firmwareVersion */,
"" /* serialNumber */, SW_VERSION}};
-
auto sensorId = FingerprintHalProperties::sensor_id().value_or(SENSOR_ID);
auto sensorStrength =
FingerprintHalProperties::sensor_strength().value_or((int)SENSOR_STRENGTH);
@@ -80,7 +81,8 @@
SensorLocation sensorLocation = mEngine->getSensorLocation();
- LOG(INFO) << "sensor type:" << (int)mSensorType << " location:" << sensorLocation.toString();
+ LOG(INFO) << "sensor type:" << ::android::internal::ToString(mSensorType)
+ << " location:" << sensorLocation.toString();
*out = {{commonProps,
mSensorType,
@@ -104,4 +106,96 @@
return ndk::ScopedAStatus::ok();
}
+binder_status_t Fingerprint::dump(int fd, const char** /*args*/, uint32_t numArgs) {
+ if (fd < 0) {
+ LOG(ERROR) << "Fingerprint::dump fd invalid: " << fd;
+ return STATUS_BAD_VALUE;
+ } else {
+ LOG(INFO) << "Fingerprint::dump fd:" << fd << "numArgs:" << numArgs;
+ }
+
+ dprintf(fd, "----- FingerprintVirtualHal::dump -----\n");
+ std::vector<SensorProps> sps(1);
+ getSensorProps(&sps);
+ for (auto& sp : sps) {
+ ::android::base::WriteStringToFd(sp.toString(), fd);
+ }
+ ::android::base::WriteStringToFd(mEngine->toString(), fd);
+
+ fsync(fd);
+ return STATUS_OK;
+}
+
+binder_status_t Fingerprint::handleShellCommand(int in, int out, int err, const char** args,
+ uint32_t numArgs) {
+ LOG(INFO) << "Fingerprint::handleShellCommand in:" << in << " out:" << out << " err:" << err
+ << " numArgs:" << numArgs;
+
+ if (numArgs == 0) {
+ LOG(INFO) << "Fingerprint::handleShellCommand: available commands";
+ onHelp(out);
+ return STATUS_OK;
+ }
+
+ for (auto&& str : std::vector<std::string_view>(args, args + numArgs)) {
+ std::string option = str.data();
+ if (option.find("clearconfig") != std::string::npos ||
+ option.find("resetconfig") != std::string::npos) {
+ resetConfigToDefault();
+ }
+ if (option.find("help") != std::string::npos) {
+ onHelp(out);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+void Fingerprint::onHelp(int fd) {
+ dprintf(fd, "Virtual HAL commands:\n");
+ dprintf(fd, " help: print this help\n");
+ dprintf(fd, " resetconfig: reset all configuration to default\n");
+ dprintf(fd, "\n");
+ fsync(fd);
+}
+
+void Fingerprint::resetConfigToDefault() {
+ LOG(INFO) << "reset virtual HAL configuration to default";
+#define RESET_CONFIG_O(__NAME__) \
+ if (FingerprintHalProperties::__NAME__()) FingerprintHalProperties::__NAME__(std::nullopt)
+#define RESET_CONFIG_V(__NAME__) \
+ if (!FingerprintHalProperties::__NAME__().empty()) \
+ FingerprintHalProperties::__NAME__({std::nullopt})
+
+ RESET_CONFIG_O(type);
+ RESET_CONFIG_V(enrollments);
+ RESET_CONFIG_O(enrollment_hit);
+ RESET_CONFIG_O(authenticator_id);
+ RESET_CONFIG_O(challenge);
+ RESET_CONFIG_O(lockout);
+ RESET_CONFIG_O(operation_authenticate_fails);
+ RESET_CONFIG_O(operation_detect_interaction_error);
+ RESET_CONFIG_O(operation_enroll_error);
+ RESET_CONFIG_V(operation_authenticate_latency);
+ RESET_CONFIG_V(operation_detect_interaction_latency);
+ RESET_CONFIG_V(operation_enroll_latency);
+ RESET_CONFIG_O(operation_authenticate_duration);
+ RESET_CONFIG_O(operation_authenticate_error);
+ RESET_CONFIG_O(sensor_location);
+ RESET_CONFIG_O(operation_authenticate_acquired);
+ RESET_CONFIG_O(operation_detect_interaction_duration);
+ RESET_CONFIG_O(operation_detect_interaction_acquired);
+ RESET_CONFIG_O(sensor_id);
+ RESET_CONFIG_O(sensor_strength);
+ RESET_CONFIG_O(max_enrollments);
+ RESET_CONFIG_O(navigation_guesture);
+ RESET_CONFIG_O(detect_interaction);
+ RESET_CONFIG_O(display_touch);
+ RESET_CONFIG_O(control_illumination);
+ RESET_CONFIG_O(lockout_enable);
+ RESET_CONFIG_O(lockout_timed_threshold);
+ RESET_CONFIG_O(lockout_timed_duration);
+ RESET_CONFIG_O(lockout_permanent_threshold);
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
index ad471f7..49b6c9d 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -57,6 +57,7 @@
```shell
$ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
```
+
3. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the
prompts.
4. Verify the enrollments in the UI:
@@ -119,6 +120,38 @@
```
For vendor specific error, errorCode = 1000 + vendorErrorCode
+## Latency Insertion
+Three HAL operations (authenticate, enrollment and detect interaction) latency can be optionally specified in multiple ways
+1. default latency is fixed at 400 ms if not specified via sysprop
+2. specify authenticate operation latency to 900 ms
+ ```shell adb shell setprop vendor.fingerprint.virtual.operation_authenticate_latency 900```
+3. specify authenticate operation latency between 600 to 1200 ms in unifrom distribution
+ ```shelladb shell setprop vendor.fingerprint.virtual.operation_authenticate_latency 600,1200```
+
+## Lockout
+To force the device into lockout state
+```shell
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout true
+```
+To test permanent lockout based on the failed authentication attempts (e.g. 7)
+```shell
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_permanent_threshold 7
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_enable true
+```
+To test timed lockout based on the failed authentication attempts (e.g. 8 seconds on 5 attempts)
+```shell
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_timed_duration 8000
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_timed_threshold 5
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_enable true
+```
+
+## Reset all configurations to default
+The following command will reset virtual configurations (related system properties) to default value.
+```shell
+$ adb shell cmd android.hardware.biometrics.fingerprint.IFingerprint/virtual resetconfig
+$ adb reboot
+```
+
## View HAL State
To view all the properties of the HAL (see `fingerprint.sysprop` file for the API):
@@ -126,3 +159,7 @@
```shell
$ adb shell getprop | grep vendor.fingerprint.virtual
```
+To dump virtual HAL internal data
+```shell
+adb shell dumpsys android.hardware.biometrics.fingerprint.IFingerprint/virtual
+```
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index e51f677..7ab5af3 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -20,6 +20,9 @@
#include "util/CancellationSignal.h"
+#undef LOG_TAG
+#define LOG_TAG "FingerprintVirtualHalSession"
+
namespace aidl::android::hardware::biometrics::fingerprint {
Session::Session(int sensorId, int userId, std::shared_ptr<ISessionCallback> cb,
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
index fa21663..e69de29 100644
--- 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
@@ -1,155 +0,0 @@
-props {
- owner: Vendor
- module: "android.fingerprint.virt.FingerprintHalProperties"
- prop {
- api_name: "authenticator_id"
- type: Long
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
- }
- prop {
- api_name: "challenge"
- type: Long
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.challenge"
- }
- prop {
- api_name: "control_illumination"
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
- }
- prop {
- api_name: "detect_interaction"
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
- }
- prop {
- api_name: "display_touch"
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
- }
- 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: "persist.vendor.fingerprint.virtual.lockout"
- }
- prop {
- api_name: "max_enrollments"
- type: Integer
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
- }
- prop {
- api_name: "navigation_guesture"
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
- }
- prop {
- api_name: "next_enrollment"
- type: String
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.next_enrollment"
- }
- prop {
- api_name: "operation_authenticate_acquired"
- type: String
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
- }
- prop {
- api_name: "operation_authenticate_duration"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
- }
- prop {
- api_name: "operation_authenticate_error"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
- }
- 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_acquired"
- type: String
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
- }
- prop {
- api_name: "operation_detect_interaction_duration"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
- }
- prop {
- api_name: "operation_detect_interaction_error"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
- }
- 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_error"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
- }
- prop {
- api_name: "operation_enroll_latency"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
- }
- prop {
- api_name: "sensor_id"
- type: Integer
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
- }
- prop {
- api_name: "sensor_location"
- type: String
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
- }
- prop {
- api_name: "sensor_strength"
- type: Integer
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
- }
- 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.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index 9b8fada..6a6c297 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -7,7 +7,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.type"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
enum_values: "default|rear|udfps|side"
api_name: "type"
@@ -17,7 +17,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.enrollments"
type: IntegerList
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "enrollments"
}
@@ -27,7 +27,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.enrollment_hit"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "enrollment_hit"
}
@@ -42,7 +42,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.next_enrollment"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "next_enrollment"
}
@@ -51,7 +51,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
type: Long
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "authenticator_id"
}
@@ -60,25 +60,16 @@
prop {
prop_name: "vendor.fingerprint.virtual.challenge"
type: Long
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "challenge"
}
-# if locked out
-prop {
- prop_name: "persist.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
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_fails"
}
@@ -91,7 +82,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_error"
}
@@ -100,34 +91,40 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_enroll_error"
}
# add a latency to authentication operations
+# default to 400ms
+# [x] = x ms
+# [x,y] = randomly between x and y ms
+# others = invalid
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
- type: Integer
- scope: Public
+ type: IntegerList
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_latency"
}
# add a latency to detectInteraction operations
+# refer to `operation_authenticate_latency` above for usage
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
- type: Integer
- scope: Public
+ type: IntegerList
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_latency"
}
# add a latency to enroll operations
+# refer to `operation_authenticate_latency` above for usage
prop {
prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
- type: Integer
- scope: Public
+ type: IntegerList
+ scope: Internal
access: ReadWrite
api_name: "operation_enroll_latency"
}
@@ -137,7 +134,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_duration"
}
@@ -146,7 +143,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_error"
}
@@ -156,7 +153,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "sensor_location"
}
@@ -165,7 +162,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_acquired"
}
@@ -175,7 +172,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_duration"
}
@@ -187,7 +184,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_acquired"
}
@@ -196,7 +193,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "sensor_id"
}
@@ -206,7 +203,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "sensor_strength"
}
@@ -216,7 +213,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "max_enrollments"
}
@@ -225,7 +222,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "navigation_guesture"
}
@@ -234,7 +231,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "detect_interaction"
}
@@ -243,7 +240,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "display_touch"
}
@@ -252,7 +249,52 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "control_illumination"
}
+
+# force to be locked out (default: false)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout"
+ type: Boolean
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout"
+}
+
+# whether support lockout based on the failed auth attempts (default: false)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_enable"
+ type: Boolean
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_enable"
+}
+
+# temporarily lockout threshold in number of consecutive failed auth attempts (default: 5)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_threshold"
+ type: Integer
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_timed_threshold"
+}
+
+# temporary lockout duration in ms (default: 10000ms)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_duration"
+ type: Integer
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_timed_duration"
+}
+
+# permanently lockout threshold in number of consecutive failed auth attempts (default: 20)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_permanent_threshold"
+ type: Integer
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_permanent_threshold"
+}
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index 22b1744..1279cd9 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -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.
@@ -15,8 +15,13 @@
*/
#pragma once
+
+#define LOG_TAG "FingerprintVirtualHal"
+
#include <aidl/android/hardware/biometrics/common/SensorStrength.h>
#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
+#include <android/binder_to_string.h>
+#include <string>
#include <random>
@@ -24,6 +29,8 @@
#include <future>
#include <vector>
+#include "FakeLockoutTracker.h"
+
using namespace ::aidl::android::hardware::biometrics::common;
namespace aidl::android::hardware::biometrics::fingerprint {
@@ -36,11 +43,11 @@
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);
+ virtual void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ const std::future<void>& cancel);
+ virtual void authenticateImpl(ISessionCallback* cb, int64_t operationId,
+ const std::future<void>& cancel);
+ virtual 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);
@@ -63,13 +70,29 @@
std::vector<std::vector<int32_t>> parseEnrollmentCapture(const std::string& str);
+ int32_t getLatency(const std::vector<std::optional<std::int32_t>>& latencyVec);
+
std::mt19937 mRandom;
+ virtual std::string toString() const {
+ std::ostringstream os;
+ os << "----- FakeFingerprintEngine:: -----" << std::endl;
+ os << "acquiredVendorInfoBase:" << FINGERPRINT_ACQUIRED_VENDOR_BASE;
+ os << ", errorVendorBase:" << FINGERPRINT_ERROR_VENDOR_BASE << std::endl;
+ os << mLockoutTracker.toString();
+ return os.str();
+ }
+
private:
static constexpr int32_t FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
static constexpr int32_t FINGERPRINT_ERROR_VENDOR_BASE = 1000;
std::pair<AcquiredInfo, int32_t> convertAcquiredInfo(int32_t code);
std::pair<Error, int32_t> convertError(int32_t code);
+ bool parseEnrollmentCaptureSingle(const std::string& str,
+ std::vector<std::vector<int32_t>>& res);
+ int32_t getRandomInRange(int32_t bound1, int32_t bound2);
+
+ FakeLockoutTracker mLockoutTracker;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
index 1600a4b..14d5399 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
@@ -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.
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
index 4e44d16..c2fc005 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
@@ -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.
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
index b86af73..c5e93e7 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
@@ -28,17 +28,55 @@
static constexpr int32_t defaultSensorLocationY = 1600;
static constexpr int32_t defaultSensorRadius = 150;
- FakeFingerprintEngineUdfps() : FakeFingerprintEngine() {}
+ static constexpr int32_t uiReadyTimeoutInMs = 5000;
+
+ FakeFingerprintEngineUdfps();
~FakeFingerprintEngineUdfps() {}
- virtual ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y,
- float minor, float major) override;
+ ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y, float minor,
+ float major) override;
- virtual ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId) override;
+ ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId) override;
- virtual ndk::ScopedAStatus onUiReadyImpl() override;
+ ndk::ScopedAStatus onUiReadyImpl() override;
- virtual SensorLocation defaultSensorLocation() override;
+ SensorLocation defaultSensorLocation() override;
+
+ 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);
+
+ enum class WorkMode : int8_t { kIdle = 0, kAuthenticate, kEnroll, kDetectInteract };
+
+ WorkMode getWorkMode() { return mWorkMode; }
+
+ std::string toString() const {
+ std::ostringstream os;
+ os << FakeFingerprintEngine::toString();
+ os << "----- FakeFingerprintEngineUdfps -----" << std::endl;
+ os << "mWorkMode:" << (int)mWorkMode;
+ os << ", mUiReadyTime:" << mUiReadyTime;
+ os << ", mPointerDownTime:" << mPointerDownTime << std::endl;
+ return os.str();
+ }
+
+ private:
+ void onAuthenticateFingerDown();
+ void onEnrollFingerDown();
+ void onDetectInteractFingerDown();
+ void fingerDownAction();
+ void updateContext(WorkMode mode, ISessionCallback* cb, std::future<void>& cancel,
+ int64_t operationId, const keymaster::HardwareAuthToken& hat);
+
+ WorkMode mWorkMode;
+ ISessionCallback* mCb;
+ keymaster::HardwareAuthToken mHat;
+ std::vector<std::future<void>> mCancelVec;
+ int64_t mOperationId;
+ int64_t mPointerDownTime;
+ int64_t mUiReadyTime;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h b/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h
new file mode 100644
index 0000000..a1b6128
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h
@@ -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.
+ */
+
+#pragma once
+
+#include <android/binder_to_string.h>
+#include <stdint.h>
+#include <string>
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class FakeLockoutTracker {
+ public:
+ FakeLockoutTracker() : mFailedCount(0) {}
+ ~FakeLockoutTracker() {}
+
+ enum class LockoutMode : int8_t { kNone = 0, kTimed, kPermanent };
+
+ void reset();
+ LockoutMode getMode();
+ void addFailedAttempt();
+ int64_t getLockoutTimeLeft();
+ inline std::string toString() const {
+ std::ostringstream os;
+ os << "----- FakeLockoutTracker:: -----" << std::endl;
+ os << "FakeLockoutTracker::mFailedCount:" << mFailedCount;
+ os << ", FakeLockoutTracker::mCurrentMode:" << (int)mCurrentMode;
+ os << std::endl;
+ return os.str();
+ }
+
+ private:
+ int32_t mFailedCount;
+ int64_t mLockoutTimedStart;
+ LockoutMode mCurrentMode;
+};
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
index 64aafa3..fc4fb8d 100644
--- a/biometrics/fingerprint/aidl/default/include/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -16,8 +16,6 @@
#pragma once
-#define LOG_TAG "FingerprintVirtualHal"
-
#include <aidl/android/hardware/biometrics/fingerprint/BnFingerprint.h>
#include "FakeFingerprintEngine.h"
@@ -39,8 +37,13 @@
ndk::ScopedAStatus createSession(int32_t sensorId, int32_t userId,
const std::shared_ptr<ISessionCallback>& cb,
std::shared_ptr<ISession>* out) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs);
+ binder_status_t handleShellCommand(int in, int out, int err, const char** argv, uint32_t argc);
private:
+ void resetConfigToDefault();
+ void onHelp(int);
+
std::unique_ptr<FakeFingerprintEngine> mEngine;
WorkerThread mWorker;
std::shared_ptr<Session> mSession;
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
index 32d01f4..a200b39 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -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.
@@ -21,6 +21,7 @@
#include <aidl/android/hardware/biometrics/fingerprint/BnSessionCallback.h>
#include "FakeFingerprintEngine.h"
+#include "util/Util.h"
using namespace ::android::fingerprint::virt;
using namespace ::aidl::android::hardware::biometrics::fingerprint;
@@ -118,9 +119,9 @@
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);
+ FingerprintHalProperties::operation_enroll_latency({0});
+ FingerprintHalProperties::operation_authenticate_latency({0});
+ FingerprintHalProperties::operation_detect_interaction_latency({0});
mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
}
@@ -128,6 +129,9 @@
FingerprintHalProperties::operation_authenticate_error(0);
FingerprintHalProperties::operation_detect_interaction_error(0);
FingerprintHalProperties::operation_authenticate_acquired("");
+ FingerprintHalProperties::operation_enroll_latency({});
+ FingerprintHalProperties::operation_authenticate_latency({});
+ FingerprintHalProperties::operation_detect_interaction_latency({});
}
FakeFingerprintEngine mEngine;
@@ -291,6 +295,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetect) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
FingerprintHalProperties::operation_detect_interaction_acquired("");
@@ -300,6 +305,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectCancel) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
mCancel.set_value();
@@ -309,6 +315,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectNotSet) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit({});
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
@@ -323,6 +330,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectError) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::operation_detect_interaction_error(8);
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
@@ -331,6 +339,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectAcquired) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
FingerprintHalProperties::operation_detect_interaction_acquired("4,1013");
@@ -435,12 +444,29 @@
std::vector<std::string> badStr{"10c", "100-5", "100-[5,6,7", "100-5,6,7]",
"100,2x0,300", "200-[f]", "a,b"};
std::vector<std::vector<int32_t>> ecV;
- for (const auto s : badStr) {
+ for (const auto& s : badStr) {
ecV = mEngine.parseEnrollmentCapture(s);
ASSERT_EQ(ecV.size(), 0);
}
}
+TEST_F(FakeFingerprintEngineTest, randomLatency) {
+ FingerprintHalProperties::operation_detect_interaction_latency({});
+ ASSERT_EQ(DEFAULT_LATENCY,
+ mEngine.getLatency(FingerprintHalProperties::operation_detect_interaction_latency()));
+ FingerprintHalProperties::operation_detect_interaction_latency({10});
+ ASSERT_EQ(10,
+ mEngine.getLatency(FingerprintHalProperties::operation_detect_interaction_latency()));
+ FingerprintHalProperties::operation_detect_interaction_latency({1, 1000});
+ std::set<int32_t> latencySet;
+ for (int i = 0; i < 100; i++) {
+ latencySet.insert(mEngine.getLatency(
+ FingerprintHalProperties::operation_detect_interaction_latency()));
+ }
+ ASSERT_TRUE(latencySet.size() > 95);
+ FingerprintHalProperties::operation_detect_interaction_latency({});
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
int main(int argc, char** argv) {
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
index 7c0021f..f551899 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <aidl/android/hardware/biometrics/fingerprint/BnSessionCallback.h>
#include <android/binder_process.h>
#include <fingerprint.sysprop.h>
#include <gtest/gtest.h>
@@ -29,6 +30,69 @@
namespace aidl::android::hardware::biometrics::fingerprint {
+class TestSessionCallback : public BnSessionCallback {
+ public:
+ ndk::ScopedAStatus onChallengeGenerated(int64_t /*challenge*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onChallengeRevoked(int64_t /*challenge*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onError(fingerprint::Error /*error*/, int32_t /*vendorCode*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentProgress(int32_t /*enrollmentId*/,
+ int32_t /*remaining*/) override {
+ mEnrollmentProgress++;
+ return ndk::ScopedAStatus::ok();
+ };
+
+ ::ndk::ScopedAStatus onAuthenticationSucceeded(int32_t /*enrollmentId*/,
+ const keymaster::HardwareAuthToken&) override {
+ mAuthenticationSuccess++;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticationFailed() override {
+ mAuthenticationFailure++;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onInteractionDetected() override {
+ mDetectInteraction++;
+ 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 {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentsRemoved(
+ const std::vector<int32_t>& /*enrollmentIds*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t /*authenticatorId*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t /*authenticatorId*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onLockoutPermanent() override { 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(); }
+
+ int32_t getAuthenticationCount() { return mAuthenticationSuccess + mAuthenticationFailure; }
+ int32_t getDetectInteractionCount() { return mDetectInteraction; }
+
+ int32_t mAuthenticationSuccess = 0;
+ int32_t mAuthenticationFailure = 0;
+ int32_t mEnrollmentProgress = 0;
+ int32_t mDetectInteraction = 0;
+};
+
class FakeFingerprintEngineUdfpsTest : public ::testing::Test {
protected:
void SetUp() override {}
@@ -65,30 +129,56 @@
}
TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocationBad) {
- FingerprintHalProperties::sensor_location("");
- SensorLocation sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
-
- auto loc = "100";
- FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
-
- loc = "10:20";
- FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
-
- loc = "10,20,5";
- FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
-
- loc = "a:b:c";
- FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
+ const std::vector<std::string> badStr{"", "100", "10:20", "10,20,5", "a:b:c"};
+ SensorLocation sc;
+ for (const auto& s : badStr) {
+ FingerprintHalProperties::sensor_location(s);
+ sc = mEngine.getSensorLocation();
+ ASSERT_TRUE(isDefaultLocation(sc));
+ }
}
+TEST_F(FakeFingerprintEngineUdfpsTest, initialization) {
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kIdle);
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, authenticate) {
+ std::shared_ptr<TestSessionCallback> cb = ndk::SharedRefBase::make<TestSessionCallback>();
+ std::promise<void> cancel;
+ mEngine.authenticateImpl(cb.get(), 1, cancel.get_future());
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kAuthenticate);
+ mEngine.onPointerDownImpl(1, 2, 3, 4.0, 5.0);
+ ASSERT_EQ(cb->getAuthenticationCount(), 0);
+ mEngine.onUiReadyImpl();
+ ASSERT_EQ(cb->getAuthenticationCount(), 1);
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, enroll) {
+ std::shared_ptr<TestSessionCallback> cb = ndk::SharedRefBase::make<TestSessionCallback>();
+ std::promise<void> cancel;
+ keymaster::HardwareAuthToken hat{.mac = {5, 6}};
+ FingerprintHalProperties::next_enrollment("5:0,0:true");
+ mEngine.enrollImpl(cb.get(), hat, cancel.get_future());
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kEnroll);
+ mEngine.onPointerDownImpl(1, 2, 3, 4.0, 5.0);
+ ASSERT_EQ(cb->mEnrollmentProgress, 0);
+ mEngine.onUiReadyImpl();
+ ASSERT_TRUE(cb->mEnrollmentProgress > 0);
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, detectInteraction) {
+ FingerprintHalProperties::detect_interaction(true);
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ FingerprintHalProperties::operation_detect_interaction_acquired("");
+ std::shared_ptr<TestSessionCallback> cb = ndk::SharedRefBase::make<TestSessionCallback>();
+ std::promise<void> cancel;
+ mEngine.detectInteractionImpl(cb.get(), cancel.get_future());
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kDetectInteract);
+ mEngine.onPointerDownImpl(1, 2, 3, 4.0, 5.0);
+ ASSERT_EQ(cb->getDetectInteractionCount(), 0);
+ mEngine.onUiReadyImpl();
+ ASSERT_EQ(cb->getDetectInteractionCount(), 1);
+}
// More
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp
new file mode 100644
index 0000000..1b071ee
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 <fingerprint.sysprop.h>
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "FakeLockoutTracker.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+using namespace ::aidl::android::hardware::biometrics::fingerprint;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class FakeLockoutTrackerTest : public ::testing::Test {
+ protected:
+ static constexpr int32_t LOCKOUT_TIMED_THRESHOLD = 3;
+ static constexpr int32_t LOCKOUT_PERMANENT_THRESHOLD = 5;
+ static constexpr int32_t LOCKOUT_TIMED_DURATION = 100;
+
+ void SetUp() override {
+ FingerprintHalProperties::lockout_timed_threshold(LOCKOUT_TIMED_THRESHOLD);
+ FingerprintHalProperties::lockout_timed_duration(LOCKOUT_TIMED_DURATION);
+ FingerprintHalProperties::lockout_permanent_threshold(LOCKOUT_PERMANENT_THRESHOLD);
+ }
+
+ void TearDown() override {
+ // reset to default
+ FingerprintHalProperties::lockout_timed_threshold(5);
+ FingerprintHalProperties::lockout_timed_duration(20);
+ FingerprintHalProperties::lockout_permanent_threshold(10000);
+ FingerprintHalProperties::lockout_enable(false);
+ FingerprintHalProperties::lockout(false);
+ }
+
+ FakeLockoutTracker mLockoutTracker;
+};
+
+TEST_F(FakeLockoutTrackerTest, addFailedAttemptDisable) {
+ FingerprintHalProperties::lockout_enable(false);
+ for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD + 1; i++) mLockoutTracker.addFailedAttempt();
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
+ mLockoutTracker.reset();
+}
+
+TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimed) {
+ FingerprintHalProperties::lockout_enable(true);
+ for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++) mLockoutTracker.addFailedAttempt();
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kTimed);
+ // time left
+ int N = 5;
+ int64_t prevTimeLeft = INT_MIN;
+ for (int i = 0; i < N; i++) {
+ SLEEP_MS(LOCKOUT_TIMED_DURATION / N + 1);
+ int64_t currTimeLeft = mLockoutTracker.getLockoutTimeLeft();
+ ASSERT_TRUE(currTimeLeft > prevTimeLeft);
+ prevTimeLeft = currTimeLeft;
+ }
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
+ mLockoutTracker.reset();
+}
+
+TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {
+ FingerprintHalProperties::lockout_enable(true);
+ for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD - 1; i++) mLockoutTracker.addFailedAttempt();
+ ASSERT_NE(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
+ mLockoutTracker.addFailedAttempt();
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
+ ASSERT_TRUE(FingerprintHalProperties::lockout());
+ mLockoutTracker.reset();
+}
+
+} // 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/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index 674dd11..70797a7 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -63,6 +63,31 @@
generated_headers: ["le_audio_codec_capabilities"],
}
+cc_test {
+ name: "BluetoothLeAudioCodecsProviderTest",
+ srcs: [
+ "aidl_session/BluetoothLeAudioCodecsProvider.cpp",
+ "aidl_session/BluetoothLeAudioCodecsProviderTest.cpp",
+ ],
+ header_libs: [
+ "libxsdc-utils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.bluetooth.audio-V2-ndk",
+ "libxml2",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ test_options: {
+ unit_test: false,
+ },
+ generated_sources: ["le_audio_codec_capabilities"],
+ generated_headers: ["le_audio_codec_capabilities"],
+}
+
xsd_config {
name: "le_audio_codec_capabilities",
srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"],
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
index 855dd28..faebbbf 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
@@ -398,8 +398,11 @@
}
if (kDefaultOffloadLeAudioCapabilities.empty()) {
+ auto le_audio_offload_setting =
+ BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile();
kDefaultOffloadLeAudioCapabilities =
- BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities();
+ BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
+ le_audio_offload_setting);
}
return kDefaultOffloadLeAudioCapabilities;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
index bf49270..1dec900 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp
@@ -34,20 +34,40 @@
static std::vector<LeAudioCodecCapabilitiesSetting> leAudioCodecCapabilities;
-std::vector<LeAudioCodecCapabilitiesSetting>
-BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities() {
- if (!leAudioCodecCapabilities.empty()) {
- return leAudioCodecCapabilities;
- }
+static bool isInvalidFileContent = false;
- const auto le_audio_offload_setting =
+std::optional<setting::LeAudioOffloadSetting>
+BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile() {
+ if (!leAudioCodecCapabilities.empty() || isInvalidFileContent) {
+ return std::nullopt;
+ }
+ auto le_audio_offload_setting =
setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile);
if (!le_audio_offload_setting.has_value()) {
LOG(ERROR) << __func__ << ": Failed to read "
<< kLeAudioCodecCapabilitiesFile;
+ }
+ return le_audio_offload_setting;
+}
+
+std::vector<LeAudioCodecCapabilitiesSetting>
+BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
+ const std::optional<setting::LeAudioOffloadSetting>&
+ le_audio_offload_setting) {
+ if (!leAudioCodecCapabilities.empty()) {
+ return leAudioCodecCapabilities;
+ }
+
+ if (!le_audio_offload_setting.has_value()) {
+ LOG(ERROR)
+ << __func__
+ << ": input le_audio_offload_setting content need to be non empty";
return {};
}
+ ClearLeAudioCodecCapabilities();
+ isInvalidFileContent = true;
+
std::vector<setting::Scenario> supported_scenarios =
GetScenarios(le_audio_offload_setting);
if (supported_scenarios.empty()) {
@@ -79,9 +99,18 @@
leAudioCodecCapabilities =
ComposeLeAudioCodecCapabilities(supported_scenarios);
+ isInvalidFileContent = leAudioCodecCapabilities.empty();
+
return leAudioCodecCapabilities;
}
+void BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities() {
+ leAudioCodecCapabilities.clear();
+ configuration_map_.clear();
+ codec_configuration_map_.clear();
+ strategy_configuration_map_.clear();
+}
+
std::vector<setting::Scenario> BluetoothLeAudioCodecsProvider::GetScenarios(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
index 402235f..e879984 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h
@@ -31,8 +31,13 @@
class BluetoothLeAudioCodecsProvider {
public:
+ static std::optional<setting::LeAudioOffloadSetting>
+ ParseFromLeAudioOffloadSettingFile();
static std::vector<LeAudioCodecCapabilitiesSetting>
- GetLeAudioCodecCapabilities();
+ GetLeAudioCodecCapabilities(
+ const std::optional<setting::LeAudioOffloadSetting>&
+ le_audio_offload_setting);
+ static void ClearLeAudioCodecCapabilities();
private:
static inline std::unordered_map<std::string, setting::Configuration>
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp
new file mode 100644
index 0000000..5393cd7
--- /dev/null
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProviderTest.cpp
@@ -0,0 +1,373 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <optional>
+#include <tuple>
+
+#include "BluetoothLeAudioCodecsProvider.h"
+
+using aidl::android::hardware::bluetooth::audio::BluetoothLeAudioCodecsProvider;
+using aidl::android::hardware::bluetooth::audio::
+ LeAudioCodecCapabilitiesSetting;
+using aidl::android::hardware::bluetooth::audio::setting::AudioLocation;
+using aidl::android::hardware::bluetooth::audio::setting::CodecConfiguration;
+using aidl::android::hardware::bluetooth::audio::setting::
+ CodecConfigurationList;
+using aidl::android::hardware::bluetooth::audio::setting::CodecType;
+using aidl::android::hardware::bluetooth::audio::setting::Configuration;
+using aidl::android::hardware::bluetooth::audio::setting::ConfigurationList;
+using aidl::android::hardware::bluetooth::audio::setting::LeAudioOffloadSetting;
+using aidl::android::hardware::bluetooth::audio::setting::Scenario;
+using aidl::android::hardware::bluetooth::audio::setting::ScenarioList;
+using aidl::android::hardware::bluetooth::audio::setting::StrategyConfiguration;
+using aidl::android::hardware::bluetooth::audio::setting::
+ StrategyConfigurationList;
+
+typedef std::tuple<std::vector<ScenarioList>, std::vector<ConfigurationList>,
+ std::vector<CodecConfigurationList>,
+ std::vector<StrategyConfigurationList>>
+ OffloadSetting;
+
+// Define valid components for each list
+// Scenario
+static const Scenario kValidScenario(std::make_optional("OneChanStereo_16_1"),
+ std::make_optional("OneChanStereo_16_1"));
+// Configuration
+static const Configuration kValidConfigOneChanStereo_16_1(
+ std::make_optional("OneChanStereo_16_1"), std::make_optional("LC3_16k_1"),
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"));
+// CodecConfiguration
+static const CodecConfiguration kValidCodecLC3_16k_1(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::make_optional(16000), std::make_optional(7500),
+ std::make_optional(30), std::nullopt);
+// StrategyConfiguration
+static const StrategyConfiguration kValidStrategyStereoOneCis(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO), std::make_optional(2),
+ std::make_optional(1));
+static const StrategyConfiguration kValidStrategyStereoTwoCis(
+ std::make_optional("STEREO_TWO_CISES_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO), std::make_optional(1),
+ std::make_optional(2));
+static const StrategyConfiguration kValidStrategyMonoOneCis(
+ std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::MONO), std::make_optional(1),
+ std::make_optional(1));
+
+// Define valid test list built from above valid components
+// Scenario, Configuration, CodecConfiguration, StrategyConfiguration
+static const std::vector<ScenarioList> kValidScenarioList = {
+ ScenarioList(std::vector<Scenario>{kValidScenario})};
+static const std::vector<ConfigurationList> kValidConfigurationList = {
+ ConfigurationList(
+ std::vector<Configuration>{kValidConfigOneChanStereo_16_1})};
+static const std::vector<CodecConfigurationList> kValidCodecConfigurationList =
+ {CodecConfigurationList(
+ std::vector<CodecConfiguration>{kValidCodecLC3_16k_1})};
+static const std::vector<StrategyConfigurationList>
+ kValidStrategyConfigurationList = {
+ StrategyConfigurationList(std::vector<StrategyConfiguration>{
+ kValidStrategyStereoOneCis, kValidStrategyStereoTwoCis,
+ kValidStrategyMonoOneCis})};
+
+class BluetoothLeAudioCodecsProviderTest
+ : public ::testing::TestWithParam<OffloadSetting> {
+ public:
+ static std::vector<OffloadSetting> CreateTestCases(
+ const std::vector<ScenarioList>& scenario_lists,
+ const std::vector<ConfigurationList>& configuration_lists,
+ const std::vector<CodecConfigurationList>& codec_configuration_lists,
+ const std::vector<StrategyConfigurationList>&
+ strategy_configuration_lists) {
+ // make each vector in output test_cases has only one element
+ // to match the input of test params
+ // normally only one vector in input has multiple elements
+ // we just split elements in this vector to several vector
+ std::vector<OffloadSetting> test_cases;
+ for (const auto& scenario_list : scenario_lists) {
+ for (const auto& configuration_list : configuration_lists) {
+ for (const auto& codec_configuration_list : codec_configuration_lists) {
+ for (const auto& strategy_configuration_list :
+ strategy_configuration_lists) {
+ test_cases.push_back(CreateTestCase(
+ scenario_list, configuration_list, codec_configuration_list,
+ strategy_configuration_list));
+ }
+ }
+ }
+ }
+ return test_cases;
+ }
+
+ protected:
+ void Initialize() {
+ BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities();
+ }
+
+ std::vector<LeAudioCodecCapabilitiesSetting> RunTestCase() {
+ auto& [scenario_lists, configuration_lists, codec_configuration_lists,
+ strategy_configuration_lists] = GetParam();
+ LeAudioOffloadSetting le_audio_offload_setting(
+ scenario_lists, configuration_lists, codec_configuration_lists,
+ strategy_configuration_lists);
+ auto le_audio_codec_capabilities =
+ BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
+ std::make_optional(le_audio_offload_setting));
+ return le_audio_codec_capabilities;
+ }
+
+ private:
+ static inline OffloadSetting CreateTestCase(
+ const ScenarioList& scenario_list,
+ const ConfigurationList& configuration_list,
+ const CodecConfigurationList& codec_configuration_list,
+ const StrategyConfigurationList& strategy_configuration_list) {
+ return std::make_tuple(
+ std::vector<ScenarioList>{scenario_list},
+ std::vector<ConfigurationList>{configuration_list},
+ std::vector<CodecConfigurationList>{codec_configuration_list},
+ std::vector<StrategyConfigurationList>{strategy_configuration_list});
+ }
+};
+
+class GetScenariosTest : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<ScenarioList> CreateInvalidScenarios() {
+ std::vector<ScenarioList> invalid_scenario_test_cases;
+ invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
+ Scenario(std::nullopt, std::make_optional("OneChanStereo_16_1"))}));
+
+ invalid_scenario_test_cases.push_back(ScenarioList(std::vector<Scenario>{
+ Scenario(std::make_optional("OneChanStereo_16_1"), std::nullopt)}));
+
+ invalid_scenario_test_cases.push_back(ScenarioList(
+ std::vector<Scenario>{Scenario(std::nullopt, std::nullopt)}));
+
+ invalid_scenario_test_cases.push_back(
+ ScenarioList(std::vector<Scenario>{}));
+
+ return invalid_scenario_test_cases;
+ }
+};
+
+TEST_P(GetScenariosTest, InvalidScenarios) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class UpdateConfigurationsToMapTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<ConfigurationList> CreateInvalidConfigurations() {
+ std::vector<ConfigurationList> invalid_configuration_test_cases;
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{
+ Configuration(std::nullopt, std::make_optional("LC3_16k_1"),
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
+
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{Configuration(
+ std::make_optional("OneChanStereo_16_1"), std::nullopt,
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"))}));
+
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{
+ Configuration(std::make_optional("OneChanStereo_16_1"),
+ std::make_optional("LC3_16k_1"), std::nullopt)}));
+
+ invalid_configuration_test_cases.push_back(
+ ConfigurationList(std::vector<Configuration>{}));
+
+ return invalid_configuration_test_cases;
+ }
+};
+
+TEST_P(UpdateConfigurationsToMapTest, InvalidConfigurations) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class UpdateCodecConfigurationsToMapTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<CodecConfigurationList>
+ CreateInvalidCodecConfigurations() {
+ std::vector<CodecConfigurationList> invalid_codec_configuration_test_cases;
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::nullopt, std::make_optional(CodecType::LC3), std::nullopt,
+ std::make_optional(16000), std::make_optional(7500),
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::nullopt, std::nullopt,
+ std::make_optional(16000), std::make_optional(7500),
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::nullopt, std::make_optional(7500),
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::make_optional(16000), std::nullopt,
+ std::make_optional(30), std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(CodecConfigurationList(
+ std::vector<CodecConfiguration>{CodecConfiguration(
+ std::make_optional("LC3_16k_1"), std::make_optional(CodecType::LC3),
+ std::nullopt, std::make_optional(16000), std::make_optional(7500),
+ std::nullopt, std::nullopt)}));
+
+ invalid_codec_configuration_test_cases.push_back(
+ CodecConfigurationList(std::vector<CodecConfiguration>{}));
+
+ return invalid_codec_configuration_test_cases;
+ }
+};
+
+TEST_P(UpdateCodecConfigurationsToMapTest, InvalidCodecConfigurations) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class UpdateStrategyConfigurationsToMapTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+ static std::vector<StrategyConfigurationList>
+ CreateInvalidStrategyConfigurations() {
+ std::vector<StrategyConfigurationList>
+ invalid_strategy_configuration_test_cases;
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::make_optional(2))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("MONO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::make_optional(2))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::nullopt, std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::make_optional(1))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"), std::nullopt,
+ std::make_optional(2), std::make_optional(1))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO), std::nullopt,
+ std::make_optional(1))}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(
+ std::vector<StrategyConfiguration>{StrategyConfiguration(
+ std::make_optional("STEREO_ONE_CIS_PER_DEVICE"),
+ std::make_optional(AudioLocation::STEREO),
+ std::make_optional(2), std::nullopt)}));
+
+ invalid_strategy_configuration_test_cases.push_back(
+ StrategyConfigurationList(std::vector<StrategyConfiguration>{}));
+
+ return invalid_strategy_configuration_test_cases;
+ }
+};
+
+TEST_P(UpdateStrategyConfigurationsToMapTest, InvalidStrategyConfigurations) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(le_audio_codec_capabilities.empty());
+}
+
+class ComposeLeAudioCodecCapabilitiesTest
+ : public BluetoothLeAudioCodecsProviderTest {
+ public:
+};
+
+TEST_P(ComposeLeAudioCodecCapabilitiesTest, CodecCapabilitiesNotEmpty) {
+ Initialize();
+ auto le_audio_codec_capabilities = RunTestCase();
+ ASSERT_TRUE(!le_audio_codec_capabilities.empty());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetScenariosTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, GetScenariosTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ GetScenariosTest::CreateInvalidScenarios(), kValidConfigurationList,
+ kValidCodecConfigurationList, kValidStrategyConfigurationList)));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UpdateConfigurationsToMapTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, UpdateConfigurationsToMapTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList,
+ UpdateConfigurationsToMapTest::CreateInvalidConfigurations(),
+ kValidCodecConfigurationList, kValidStrategyConfigurationList)));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ UpdateCodecConfigurationsToMapTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, UpdateCodecConfigurationsToMapTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList, kValidConfigurationList,
+ UpdateCodecConfigurationsToMapTest::CreateInvalidCodecConfigurations(),
+ kValidStrategyConfigurationList)));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ UpdateStrategyConfigurationsToMapTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, UpdateStrategyConfigurationsToMapTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList, kValidConfigurationList,
+ kValidCodecConfigurationList,
+ UpdateStrategyConfigurationsToMapTest::
+ CreateInvalidStrategyConfigurations())));
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+ ComposeLeAudioCodecCapabilitiesTest);
+INSTANTIATE_TEST_SUITE_P(
+ BluetoothLeAudioCodecsProviderTest, ComposeLeAudioCodecCapabilitiesTest,
+ ::testing::ValuesIn(BluetoothLeAudioCodecsProviderTest::CreateTestCases(
+ kValidScenarioList, kValidConfigurationList,
+ kValidCodecConfigurationList, kValidStrategyConfigurationList)));
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/camera/README.md b/camera/README.md
index 8ce3352..25badfd 100644
--- a/camera/README.md
+++ b/camera/README.md
@@ -10,3 +10,8 @@
More complete information about the Android camera HAL and subsystem can be found at
[source.android.com](http://source.android.com/devices/camera/index.html).
+
+### AIDL Camera HAL Interfaces
+
+The AIDL Camera HAL interfaces can be found in the respective <interface>/aidl
+directories.
diff --git a/camera/provider/README.md b/camera/provider/README.md
index 0718fb1..7666a58 100644
--- a/camera/provider/README.md
+++ b/camera/provider/README.md
@@ -35,3 +35,9 @@
First HIDL version of the camara provider HAL callback interface, closely
matching the feature set and operation of the pre-HIDL camera HAL module
callbacks v2.4.
+
+### AIDL Camera HAL Default Implementation ###
+
+The default implementation can be found at
+$ANDROID_BUILD_TOP/hardware/google/camera/common/hal/aidl_service and
+$ANDROID_BUILD_TOP/hardware/google/camera/devices/EmulatedCamera
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index e4fd65e..e9690c5 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -173,7 +173,7 @@
<regex-instance>.*</regex-instance>
</interface>
</hal>
- <hal format="aidl" optional="true">
+ <hal format="aidl" optional="true" updatable-via-apex="true">
<name>android.hardware.camera.provider</name>
<version>1-2</version>
<interface>
diff --git a/fastboot/aidl/default/Android.bp b/fastboot/aidl/default/Android.bp
index 5cd4542..0c96b33 100644
--- a/fastboot/aidl/default/Android.bp
+++ b/fastboot/aidl/default/Android.bp
@@ -22,16 +22,20 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
-cc_library {
- name: "android.hardware.fastboot-impl-mock",
- recovery: true,
+cc_binary {
+ name: "android.hardware.fastboot-service.example_recovery",
+ init_rc: ["android.hardware.fastboot-service.example_recovery.rc"],
+ vintf_fragments: ["android.hardware.fastboot-service.example.xml"],
+ recovery_available: true,
srcs: [
"Fastboot.cpp",
+ "main.cpp",
],
relative_install_path: "hw",
shared_libs: [
"libbase",
"libbinder_ndk",
+ "liblog",
"libutils",
"libcutils",
"android.hardware.fastboot-V1-ndk",
diff --git a/fastboot/aidl/default/android.hardware.fastboot-service.example.xml b/fastboot/aidl/default/android.hardware.fastboot-service.example.xml
new file mode 100644
index 0000000..9490f98
--- /dev/null
+++ b/fastboot/aidl/default/android.hardware.fastboot-service.example.xml
@@ -0,0 +1,8 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.fastboot</name>
+ <version>1</version>
+ <fqname>IFastboot/default</fqname>
+ </hal>
+</manifest>
+
diff --git a/fastboot/aidl/default/android.hardware.fastboot-service.example_recovery.rc b/fastboot/aidl/default/android.hardware.fastboot-service.example_recovery.rc
new file mode 100644
index 0000000..5d4ee13
--- /dev/null
+++ b/fastboot/aidl/default/android.hardware.fastboot-service.example_recovery.rc
@@ -0,0 +1,6 @@
+service vendor.fastboot-default /system/bin/hw/android.hardware.fastboot-service.example_recovery
+ class hal
+ seclabel u:r:hal_fastboot_default:s0
+ user system
+ group system
+ interface aidl android.hardware.fastboot.IFastboot/default
diff --git a/fastboot/aidl/default/main.cpp b/fastboot/aidl/default/main.cpp
new file mode 100644
index 0000000..1b1b41d
--- /dev/null
+++ b/fastboot/aidl/default/main.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 <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include "Fastboot.h"
+
+using aidl::android::hardware::fastboot::Fastboot;
+using aidl::android::hardware::fastboot::IFastboot;
+
+int main(int, char* argv[]) {
+ android::base::InitLogging(argv, android::base::KernelLogger);
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<IFastboot> service = ndk::SharedRefBase::make<Fastboot>();
+
+ const std::string instance = std::string(IFastboot::descriptor) + "/default";
+ auto status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
+ CHECK_EQ(status, STATUS_OK) << "Failed to add service " << instance << " " << status;
+ LOG(INFO) << "IFastboot AIDL service running...";
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/fastboot/aidl/fastbootshim/Android.bp b/fastboot/aidl/fastbootshim/Android.bp
new file mode 100644
index 0000000..c843c12
--- /dev/null
+++ b/fastboot/aidl/fastbootshim/Android.bp
@@ -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.
+
+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_defaults {
+ name: "libfastbootshim_defaults",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "android.hardware.fastboot-V1-ndk",
+ "android.hardware.fastboot@1.0",
+ "android.hardware.fastboot@1.1",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
+
+// Shim library that wraps a HIDL Fastboot object into an AIDL Fastboot object.
+cc_library_static {
+ name: "libfastbootshim",
+ defaults: ["libfastbootshim_defaults"],
+ recovery_available: true,
+ srcs: [
+ "fastbootshim.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/fastboot/aidl/fastbootshim/fastbootshim.cpp b/fastboot/aidl/fastbootshim/fastbootshim.cpp
new file mode 100644
index 0000000..4ab67f3
--- /dev/null
+++ b/fastboot/aidl/fastbootshim/fastbootshim.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 <fastbootshim.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Void;
+using ::android::hardware::fastboot::V1_0::FileSystemType;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
+
+using ndk::ScopedAStatus;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace fastboot {
+ScopedAStatus ResultToAStatus(Result result) {
+ switch (result.status) {
+ case Status::SUCCESS:
+ return ScopedAStatus::ok();
+ case Status::NOT_SUPPORTED:
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ case Status::INVALID_ARGUMENT:
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ case Status::FAILURE_UNKNOWN:
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ BnFastboot::FAILURE_UNKNOWN, ("Error " + std::string(result.message)).c_str());
+ }
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ BnFastboot::FAILURE_UNKNOWN,
+ ("Unrecognized status value " + toString(result.status)).c_str());
+}
+FastbootShim::FastbootShim(const sp<HidlFastboot>& service) : service_(service) {}
+
+ScopedAStatus FastbootShim::getPartitionType(const std::string& in_partitionName,
+ FileSystemType* _aidl_return) {
+ Result out_result = {Status::FAILURE_UNKNOWN, ""};
+ if (in_partitionName.empty()) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid partition name");
+ }
+ const hidl_string partition = in_partitionName;
+ auto ret = service_->getPartitionType(partition, [&](auto type, auto& result) {
+ out_result = result;
+ if (out_result.status != Status::SUCCESS) return;
+ *_aidl_return = static_cast<aidl::android::hardware::fastboot::FileSystemType>(type);
+ });
+ return ResultToAStatus(out_result);
+}
+
+ScopedAStatus FastbootShim::doOemCommand(const std::string& in_oemCmd, std::string* _aidl_return) {
+ Result out_result = {Status::FAILURE_UNKNOWN, ""};
+ *_aidl_return = "";
+ if (in_oemCmd.empty()) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "Invalid command");
+ }
+ const hidl_string oemCmdArgs = in_oemCmd;
+ auto ret = service_->doOemCommand(oemCmdArgs, [&](auto& result) {
+ out_result = result;
+ if (out_result.status != Status::SUCCESS) return;
+ *_aidl_return = std::string(result.message.c_str());
+ });
+ return ResultToAStatus(out_result);
+}
+
+ScopedAStatus FastbootShim::getVariant(std::string* _aidl_return) {
+ Result out_result = {Status::FAILURE_UNKNOWN, ""};
+ *_aidl_return = "";
+ auto ret = service_->getVariant([&](auto& variant, auto& result) {
+ out_result = result;
+ if (out_result.status != Status::SUCCESS) return;
+ *_aidl_return = std::string(variant.c_str());
+ });
+ return ResultToAStatus(out_result);
+}
+
+ScopedAStatus FastbootShim::getOffModeChargeState(bool* _aidl_return) {
+ Result out_result = {Status::FAILURE_UNKNOWN, ""};
+ *_aidl_return = false;
+ auto ret = service_->getOffModeChargeState([&](auto state, auto& result) {
+ out_result = result;
+ if (out_result.status != Status::SUCCESS) return;
+ *_aidl_return = state;
+ });
+ return ResultToAStatus(out_result);
+}
+
+ScopedAStatus FastbootShim::getBatteryVoltageFlashingThreshold(int32_t* _aidl_return) {
+ Result out_result = {Status::FAILURE_UNKNOWN, ""};
+ *_aidl_return = 0;
+ auto ret = service_->getBatteryVoltageFlashingThreshold([&](auto batteryVoltage, auto& result) {
+ out_result = result;
+ if (out_result.status != Status::SUCCESS) return;
+ *_aidl_return = batteryVoltage;
+ });
+ return ResultToAStatus(out_result);
+}
+
+ScopedAStatus FastbootShim::doOemSpecificErase() {
+ Result out_result = {Status::FAILURE_UNKNOWN, ""};
+ auto ret = service_->doOemSpecificErase([&](auto& result) { out_result = result; });
+ return ResultToAStatus(out_result);
+}
+
+} // namespace fastboot
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/fastboot/aidl/fastbootshim/include/fastbootshim.h b/fastboot/aidl/fastbootshim/include/fastbootshim.h
new file mode 100644
index 0000000..410a03e
--- /dev/null
+++ b/fastboot/aidl/fastbootshim/include/fastbootshim.h
@@ -0,0 +1,49 @@
+/*
+ * 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/fastboot/BnFastboot.h>
+#include <android/hardware/fastboot/1.1/IFastboot.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace fastboot {
+// Shim that wraps HIDL IFastboot with AIDL BnFastboot
+class FastbootShim : public BnFastboot {
+ using HidlFastboot = ::android::hardware::fastboot::V1_1::IFastboot;
+
+ public:
+ explicit FastbootShim(const ::android::sp<HidlFastboot>& service);
+ ::ndk::ScopedAStatus doOemCommand(const std::string& in_oemCmd,
+ std::string* _aidl_return) override;
+ ::ndk::ScopedAStatus doOemSpecificErase() override;
+ ::ndk::ScopedAStatus getBatteryVoltageFlashingThreshold(int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus getOffModeChargeState(bool* _aidl_return) override;
+ ::ndk::ScopedAStatus getPartitionType(
+ const std::string& in_partitionName,
+ ::aidl::android::hardware::fastboot::FileSystemType* _aidl_return) override;
+ ::ndk::ScopedAStatus getVariant(std::string* _aidl_return) override;
+
+ private:
+ ::android::sp<HidlFastboot> service_;
+};
+
+} // namespace fastboot
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h
index 27dce76..76ba24b 100644
--- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientReader.h
@@ -19,8 +19,8 @@
#include <algorithm>
#include <limits>
#include <memory>
+#include <optional>
#include <unordered_map>
-#include <unordered_set>
#include <vector>
#include <inttypes.h>
@@ -41,8 +41,15 @@
class ComposerClientReader {
public:
+ explicit ComposerClientReader(std::optional<int64_t> display = {}) : mDisplay(display) {}
+
~ComposerClientReader() { resetData(); }
+ ComposerClientReader(ComposerClientReader&&) = default;
+
+ ComposerClientReader(const ComposerClientReader&) = delete;
+ ComposerClientReader& operator=(const ComposerClientReader&) = delete;
+
// Parse and execute commands from the command queue. The commands are
// actually return values from the server and will be saved in ReturnData.
void parse(std::vector<CommandResultPayload>&& results) {
@@ -85,6 +92,7 @@
void hasChanges(int64_t display, uint32_t* outNumChangedCompositionTypes,
uint32_t* outNumLayerRequestMasks) const {
+ LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
auto found = mReturnData.find(display);
if (found == mReturnData.end()) {
*outNumChangedCompositionTypes = 0;
@@ -100,6 +108,7 @@
// Get and clear saved changed composition types.
std::vector<ChangedCompositionLayer> takeChangedCompositionTypes(int64_t display) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
auto found = mReturnData.find(display);
if (found == mReturnData.end()) {
return {};
@@ -111,6 +120,7 @@
// Get and clear saved display requests.
DisplayRequest takeDisplayRequests(int64_t display) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
auto found = mReturnData.find(display);
if (found == mReturnData.end()) {
return {};
@@ -122,6 +132,7 @@
// Get and clear saved release fences.
std::vector<ReleaseFences::Layer> takeReleaseFences(int64_t display) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
auto found = mReturnData.find(display);
if (found == mReturnData.end()) {
return {};
@@ -133,6 +144,7 @@
// Get and clear saved present fence.
ndk::ScopedFileDescriptor takePresentFence(int64_t display) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
auto found = mReturnData.find(display);
if (found == mReturnData.end()) {
return {};
@@ -144,6 +156,7 @@
// Get what stage succeeded during PresentOrValidate: Present or Validate
std::optional<PresentOrValidate::Result> takePresentOrValidateStage(int64_t display) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
auto found = mReturnData.find(display);
if (found == mReturnData.end()) {
return std::nullopt;
@@ -154,6 +167,7 @@
// Get the client target properties requested by hardware composer.
ClientTargetPropertyWithBrightness takeClientTargetProperty(int64_t display) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && display != *mDisplay);
auto found = mReturnData.find(display);
// If not found, return the default values.
@@ -177,32 +191,38 @@
void parseSetError(CommandError&& error) { mErrors.emplace_back(error); }
void parseSetChangedCompositionTypes(ChangedCompositionTypes&& changedCompositionTypes) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && changedCompositionTypes.display != *mDisplay);
auto& data = mReturnData[changedCompositionTypes.display];
data.changedLayers = std::move(changedCompositionTypes.layers);
}
void parseSetDisplayRequests(DisplayRequest&& displayRequest) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && displayRequest.display != *mDisplay);
auto& data = mReturnData[displayRequest.display];
data.displayRequests = std::move(displayRequest);
}
void parseSetPresentFence(PresentFence&& presentFence) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && presentFence.display != *mDisplay);
auto& data = mReturnData[presentFence.display];
data.presentFence = std::move(presentFence.fence);
}
void parseSetReleaseFences(ReleaseFences&& releaseFences) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && releaseFences.display != *mDisplay);
auto& data = mReturnData[releaseFences.display];
data.releasedLayers = std::move(releaseFences.layers);
}
void parseSetPresentOrValidateDisplayResult(const PresentOrValidate&& presentOrValidate) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && presentOrValidate.display != *mDisplay);
auto& data = mReturnData[presentOrValidate.display];
data.presentOrValidateState = std::move(presentOrValidate.result);
}
void parseSetClientTargetProperty(
const ClientTargetPropertyWithBrightness&& clientTargetProperty) {
+ LOG_ALWAYS_FATAL_IF(mDisplay && clientTargetProperty.display != *mDisplay);
auto& data = mReturnData[clientTargetProperty.display];
data.clientTargetProperty = std::move(clientTargetProperty);
}
@@ -222,6 +242,7 @@
std::vector<CommandError> mErrors;
std::unordered_map<int64_t, ReturnData> mReturnData;
+ const std::optional<int64_t> mDisplay;
};
} // namespace aidl::android::hardware::graphics::composer3
diff --git a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
index 775ae9f..0c8742f 100644
--- a/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
+++ b/graphics/composer/aidl/include/android/hardware/graphics/composer3/ComposerClientWriter.h
@@ -19,8 +19,6 @@
#include <algorithm>
#include <limits>
#include <memory>
-#include <unordered_map>
-#include <unordered_set>
#include <vector>
#include <inttypes.h>
@@ -63,10 +61,15 @@
public:
static constexpr std::optional<ClockMonotonicTimestamp> kNoTimestamp = std::nullopt;
- ComposerClientWriter() { reset(); }
+ explicit ComposerClientWriter(int64_t display) : mDisplay(display) { reset(); }
~ComposerClientWriter() { reset(); }
+ ComposerClientWriter(ComposerClientWriter&&) = default;
+
+ ComposerClientWriter(const ComposerClientWriter&) = delete;
+ ComposerClientWriter& operator=(const ComposerClientWriter&) = delete;
+
void reset() {
mDisplayCommand.reset();
mLayerCommand.reset();
@@ -229,6 +232,7 @@
std::optional<DisplayCommand> mDisplayCommand;
std::optional<LayerCommand> mLayerCommand;
std::vector<DisplayCommand> mCommands;
+ const int64_t mDisplay;
Buffer getBuffer(uint32_t slot, const native_handle_t* bufferHandle, int fence) {
Buffer bufferCommand;
@@ -254,6 +258,7 @@
DisplayCommand& getDisplayCommand(int64_t display) {
if (!mDisplayCommand.has_value() || mDisplayCommand->display != display) {
+ LOG_ALWAYS_FATAL_IF(display != mDisplay);
flushLayerCommand();
flushDisplayCommand();
mDisplayCommand.emplace();
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.h b/graphics/composer/aidl/vts/VtsComposerClient.h
index 6358b85..1883336 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.h
+++ b/graphics/composer/aidl/vts/VtsComposerClient.h
@@ -35,6 +35,7 @@
#include <string>
#include <thread>
#include <unordered_map>
+#include <unordered_set>
#include "GraphicsComposerCallback.h"
using aidl::android::hardware::graphics::common::Dataspace;
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
index 46dde09..6fa3392 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
@@ -53,6 +53,7 @@
const auto& [status, displays] = mComposerClient->getDisplays();
ASSERT_TRUE(status.isOk());
mDisplays = displays;
+ mWriter.reset(new ComposerClientWriter(getPrimaryDisplayId()));
setTestColorModes();
@@ -200,15 +201,15 @@
void writeLayers(const std::vector<std::shared_ptr<TestLayer>>& layers) {
for (const auto& layer : layers) {
- layer->write(mWriter);
+ layer->write(*mWriter);
}
execute();
}
void execute() {
- const auto& commands = mWriter.getPendingCommands();
+ const auto& commands = mWriter->getPendingCommands();
if (commands.empty()) {
- mWriter.reset();
+ mWriter->reset();
return;
}
@@ -216,7 +217,7 @@
ASSERT_TRUE(status.isOk()) << "executeCommands failed " << status.getDescription();
mReader.parse(std::move(results));
- mWriter.reset();
+ mWriter->reset();
}
bool getHasReadbackBuffer() {
@@ -236,7 +237,7 @@
std::vector<VtsDisplay> mDisplays;
// use the slot count usually set by SF
std::vector<ColorMode> mTestColorModes;
- ComposerClientWriter mWriter;
+ std::unique_ptr<ComposerClientWriter> mWriter;
ComposerClientReader mReader;
std::unique_ptr<TestRenderEngine> mTestRenderEngine;
common::PixelFormat mPixelFormat;
@@ -297,7 +298,7 @@
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
// if hwc cannot handle and asks for composition change,
// just succeed the test
@@ -306,7 +307,7 @@
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -349,14 +350,14 @@
getDisplayHeight(), common::PixelFormat::RGBA_8888);
layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
layer->setZOrder(10);
- layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors));
std::vector<std::shared_ptr<TestLayer>> layers = {layer};
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
@@ -365,7 +366,7 @@
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -395,7 +396,7 @@
layer->setColor(BLUE);
layer->setDisplayFrame(coloredSquare);
layer->setZOrder(10);
- layer->write(mWriter);
+ layer->write(*mWriter);
// This following buffer call should have no effect
const auto usage = static_cast<uint32_t>(common::BufferUsage::CPU_WRITE_OFTEN) |
@@ -403,8 +404,8 @@
const auto& [graphicBufferStatus, graphicBuffer] = allocateBuffer(usage);
ASSERT_TRUE(graphicBufferStatus);
const auto& buffer = graphicBuffer->handle;
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer->getLayer(), /*slot*/ 0, buffer,
- /*acquireFence*/ -1);
+ mWriter->setLayerBuffer(getPrimaryDisplayId(), layer->getLayer(), /*slot*/ 0, buffer,
+ /*acquireFence*/ -1);
// expected color for each pixel
std::vector<Color> expectedColors(
@@ -415,7 +416,7 @@
getDisplayHeight(), mPixelFormat, mDataspace);
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
@@ -423,7 +424,7 @@
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -533,7 +534,7 @@
getDisplayHeight(), PixelFormat::RGBA_FP16);
layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
layer->setZOrder(10);
- layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
std::vector<std::shared_ptr<TestLayer>> layers = {layer};
@@ -542,7 +543,7 @@
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
auto changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
@@ -572,17 +573,17 @@
int32_t clientFence;
const auto unlockStatus = graphicBuffer->unlockAsync(&clientFence);
ASSERT_EQ(::android::OK, unlockStatus);
- mWriter.setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, buffer, clientFence,
- clientDataspace, std::vector<common::Rect>(1, damage));
- layer->setToClientComposition(mWriter);
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, buffer, clientFence,
+ clientDataspace, std::vector<common::Rect>(1, damage));
+ layer->setToClientComposition(*mWriter);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
ASSERT_TRUE(changedCompositionTypes.empty());
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -631,9 +632,9 @@
deviceLayer->setDisplayFrame({0, 0, static_cast<int32_t>(deviceLayer->getWidth()),
static_cast<int32_t>(deviceLayer->getHeight())});
deviceLayer->setZOrder(10);
- deviceLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ deviceLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
ASSERT_NO_FATAL_FAILURE(deviceLayer->setBuffer(deviceColors));
- deviceLayer->write(mWriter);
+ deviceLayer->write(*mWriter);
PixelFormat clientFormat = PixelFormat::RGBA_8888;
auto clientUsage = static_cast<uint32_t>(
@@ -651,8 +652,8 @@
getDisplayHeight()};
clientLayer->setDisplayFrame(clientFrame);
clientLayer->setZOrder(0);
- clientLayer->write(mWriter);
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ clientLayer->write(*mWriter);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
auto changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
@@ -678,16 +679,16 @@
int32_t clientFence;
const auto unlockStatus = graphicBuffer->unlockAsync(&clientFence);
ASSERT_EQ(::android::OK, unlockStatus);
- mWriter.setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, buffer, clientFence,
- clientDataspace, std::vector<common::Rect>(1, clientFrame));
- clientLayer->setToClientComposition(mWriter);
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, buffer, clientFence,
+ clientDataspace, std::vector<common::Rect>(1, clientFrame));
+ clientLayer->setToClientComposition(*mWriter);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
changedCompositionTypes = mReader.takeChangedCompositionTypes(getPrimaryDisplayId());
ASSERT_TRUE(changedCompositionTypes.empty());
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
@@ -718,7 +719,7 @@
getDisplayHeight(), PixelFormat::RGBA_8888);
layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
layer->setZOrder(10);
- layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors));
std::vector<std::shared_ptr<TestLayer>> layers = {layer};
@@ -729,14 +730,14 @@
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -757,11 +758,11 @@
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
ASSERT_TRUE(mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -798,7 +799,7 @@
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
@@ -806,7 +807,7 @@
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -846,7 +847,7 @@
getDisplayHeight(), PixelFormat::RGBA_8888);
layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
layer->setZOrder(10);
- layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
layer->setSourceCrop({0, static_cast<float>(getDisplayHeight() / 2),
static_cast<float>(getDisplayWidth()),
static_cast<float>(getDisplayHeight())});
@@ -862,14 +863,14 @@
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
@@ -920,13 +921,13 @@
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -942,11 +943,11 @@
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
ASSERT_TRUE(mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty());
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -980,7 +981,7 @@
// Preconditions to successfully run are knowing the max brightness and successfully applying
// the max brightness
ASSERT_GT(maxBrightnessNits, 0.f);
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.f, maxBrightnessNits);
+ mWriter->setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.f, maxBrightnessNits);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -1030,7 +1031,7 @@
writeLayers(layers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED()
@@ -1038,7 +1039,7 @@
<< toString(mode);
continue;
}
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -1088,7 +1089,7 @@
getDisplayHeight(), PixelFormat::RGBA_8888);
layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
layer->setZOrder(10);
- layer->setDataspace(Dataspace::UNKNOWN, mWriter);
+ layer->setDataspace(Dataspace::UNKNOWN, *mWriter);
ASSERT_NO_FATAL_FAILURE(layer->setBuffer(topLayerPixelColors));
layer->setBlendMode(blendMode);
@@ -1165,14 +1166,14 @@
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
writeLayers(mLayers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -1210,14 +1211,14 @@
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
writeLayers(mLayers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
@@ -1250,14 +1251,14 @@
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
writeLayers(mLayers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
@@ -1323,7 +1324,7 @@
getDisplayHeight(), mPixelFormat, mDataspace);
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
mLayer->setTransform(Transform::FLIP_H);
- mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
std::vector<Color> expectedColors(
static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1334,14 +1335,14 @@
writeLayers(mLayers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -1369,7 +1370,7 @@
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
mLayer->setTransform(Transform::FLIP_V);
- mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
std::vector<Color> expectedColors(
static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1380,14 +1381,14 @@
writeLayers(mLayers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
@@ -1414,7 +1415,7 @@
ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
mLayer->setTransform(Transform::ROT_180);
- mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), mWriter);
+ mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
std::vector<Color> expectedColors(
static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1426,14 +1427,14 @@
writeLayers(mLayers);
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED();
return;
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ mWriter->presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
ASSERT_NO_FATAL_FAILURE(readbackBuffer.checkReadbackBuffer(expectedColors));
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index ed8a06c..fa66812 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -33,9 +33,11 @@
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
#include <algorithm>
+#include <iterator>
#include <numeric>
#include <string>
#include <thread>
+#include <unordered_map>
#include "GraphicsComposerCallback.h"
#include "VtsComposerClient.h"
@@ -1078,17 +1080,23 @@
}
void execute() {
- const auto& commands = mWriter.getPendingCommands();
- if (commands.empty()) {
- mWriter.reset();
- return;
+ std::vector<CommandResultPayload> payloads;
+ for (auto& [_, writer] : mWriters) {
+ const auto& commands = writer.getPendingCommands();
+ if (commands.empty()) {
+ writer.reset();
+ continue;
+ }
+
+ auto [status, results] = mComposerClient->executeCommands(commands);
+ ASSERT_TRUE(status.isOk()) << "executeCommands failed " << status.getDescription();
+ writer.reset();
+
+ payloads.reserve(payloads.size() + results.size());
+ payloads.insert(payloads.end(), std::make_move_iterator(results.begin()),
+ std::make_move_iterator(results.end()));
}
-
- auto [status, results] = mComposerClient->executeCommands(commands);
- ASSERT_TRUE(status.isOk()) << "executeCommands failed " << status.getDescription();
-
- mReader.parse(std::move(results));
- mWriter.reset();
+ mReader.parse(std::move(payloads));
}
static inline auto toTimePoint(nsecs_t time) {
@@ -1152,6 +1160,7 @@
const auto& [status, layer] =
mComposerClient->createLayer(display.getDisplayId(), kBufferSlotCount);
EXPECT_TRUE(status.isOk());
+ auto& writer = getWriter(display.getDisplayId());
{
const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
ASSERT_NE(nullptr, buffer);
@@ -1160,15 +1169,15 @@
configureLayer(display, layer, Composition::DEVICE, display.getFrameRect(),
display.getCrop());
- mWriter.setLayerBuffer(display.getDisplayId(), layer, /*slot*/ 0, buffer->handle,
- /*acquireFence*/ -1);
- mWriter.setLayerDataspace(display.getDisplayId(), layer, common::Dataspace::UNKNOWN);
+ writer.setLayerBuffer(display.getDisplayId(), layer, /*slot*/ 0, buffer->handle,
+ /*acquireFence*/ -1);
+ writer.setLayerDataspace(display.getDisplayId(), layer, common::Dataspace::UNKNOWN);
- mWriter.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
+ writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(display.getDisplayId());
+ writer.presentDisplay(display.getDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1177,15 +1186,15 @@
const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
ASSERT_NE(nullptr, buffer->handle);
- mWriter.setLayerBuffer(display.getDisplayId(), layer, /*slot*/ 0, buffer->handle,
- /*acquireFence*/ -1);
- mWriter.setLayerSurfaceDamage(display.getDisplayId(), layer,
- std::vector<Rect>(1, {0, 0, 10, 10}));
- mWriter.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
+ writer.setLayerBuffer(display.getDisplayId(), layer, /*slot*/ 0, buffer->handle,
+ /*acquireFence*/ -1);
+ writer.setLayerSurfaceDamage(display.getDisplayId(), layer,
+ std::vector<Rect>(1, {0, 0, 10, 10}));
+ writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(display.getDisplayId());
+ writer.presentDisplay(display.getDisplayId());
execute();
}
@@ -1194,11 +1203,12 @@
sp<::android::Fence> presentAndGetFence(
std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
- mWriter.validateDisplay(getPrimaryDisplayId(), expectedPresentTime);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.validateDisplay(getPrimaryDisplayId(), expectedPresentTime);
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ writer.presentDisplay(getPrimaryDisplayId());
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
@@ -1230,7 +1240,8 @@
FRect cropRect{0, 0, (float)getPrimaryDisplay().getDisplayWidth(),
(float)getPrimaryDisplay().getDisplayHeight()};
configureLayer(getPrimaryDisplay(), layer, Composition::DEVICE, displayFrame, cropRect);
- mWriter.setLayerDataspace(getPrimaryDisplayId(), layer, common::Dataspace::UNKNOWN);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerDataspace(getPrimaryDisplayId(), layer, common::Dataspace::UNKNOWN);
return layer;
}
@@ -1330,8 +1341,9 @@
ASSERT_NE(nullptr, buffer2);
const auto layer = createOnScreenLayer();
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer1->handle,
- /*acquireFence*/ -1);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer1->handle,
+ /*acquireFence*/ -1);
const sp<::android::Fence> presentFence1 =
presentAndGetFence(ComposerClientWriter::kNoTimestamp);
presentFence1->waitForever(LOG_TAG);
@@ -1341,8 +1353,8 @@
expectedPresentTime += *framesDelay * vsyncPeriod;
}
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer2->handle,
- /*acquireFence*/ -1);
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer2->handle,
+ /*acquireFence*/ -1);
const auto setExpectedPresentTime = [&]() -> std::optional<ClockMonotonicTimestamp> {
if (!framesDelay.has_value()) {
return ComposerClientWriter::kNoTimestamp;
@@ -1363,17 +1375,18 @@
void configureLayer(const VtsDisplay& display, int64_t layer, Composition composition,
const Rect& displayFrame, const FRect& cropRect) {
- mWriter.setLayerCompositionType(display.getDisplayId(), layer, composition);
- mWriter.setLayerDisplayFrame(display.getDisplayId(), layer, displayFrame);
- mWriter.setLayerPlaneAlpha(display.getDisplayId(), layer, /*alpha*/ 1);
- mWriter.setLayerSourceCrop(display.getDisplayId(), layer, cropRect);
- mWriter.setLayerTransform(display.getDisplayId(), layer, static_cast<Transform>(0));
- mWriter.setLayerVisibleRegion(display.getDisplayId(), layer,
- std::vector<Rect>(1, displayFrame));
- mWriter.setLayerZOrder(display.getDisplayId(), layer, /*z*/ 10);
- mWriter.setLayerBlendMode(display.getDisplayId(), layer, BlendMode::NONE);
- mWriter.setLayerSurfaceDamage(display.getDisplayId(), layer,
- std::vector<Rect>(1, displayFrame));
+ auto& writer = getWriter(display.getDisplayId());
+ writer.setLayerCompositionType(display.getDisplayId(), layer, composition);
+ writer.setLayerDisplayFrame(display.getDisplayId(), layer, displayFrame);
+ writer.setLayerPlaneAlpha(display.getDisplayId(), layer, /*alpha*/ 1);
+ writer.setLayerSourceCrop(display.getDisplayId(), layer, cropRect);
+ writer.setLayerTransform(display.getDisplayId(), layer, static_cast<Transform>(0));
+ writer.setLayerVisibleRegion(display.getDisplayId(), layer,
+ std::vector<Rect>(1, displayFrame));
+ writer.setLayerZOrder(display.getDisplayId(), layer, /*z*/ 10);
+ writer.setLayerBlendMode(display.getDisplayId(), layer, BlendMode::NONE);
+ writer.setLayerSurfaceDamage(display.getDisplayId(), layer,
+ std::vector<Rect>(1, displayFrame));
}
// clang-format off
const std::array<float, 16> kIdentity = {{
@@ -1384,12 +1397,20 @@
}};
// clang-format on
- ComposerClientWriter mWriter;
+ ComposerClientWriter& getWriter(int64_t display) {
+ auto [it, _] = mWriters.try_emplace(display, display);
+ return it->second;
+ }
+
ComposerClientReader mReader;
+
+ private:
+ std::unordered_map<int64_t, ComposerClientWriter> mWriters;
};
TEST_P(GraphicsComposerAidlCommandTest, SetColorTransform) {
- mWriter.setColorTransform(getPrimaryDisplayId(), kIdentity.data());
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setColorTransform(getPrimaryDisplayId(), kIdentity.data());
execute();
}
@@ -1397,7 +1418,8 @@
const auto& [status, layer] =
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(status.isOk());
- mWriter.setLayerColorTransform(getPrimaryDisplayId(), layer, kIdentity.data());
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerColorTransform(getPrimaryDisplayId(), layer, kIdentity.data());
execute();
const auto errors = mReader.takeErrors();
@@ -1413,8 +1435,9 @@
ASSERT_TRUE(status.isOk());
bool brightnessSupport = std::find(capabilities.begin(), capabilities.end(),
DisplayCapability::BRIGHTNESS) != capabilities.end();
+ auto& writer = getWriter(getPrimaryDisplayId());
if (!brightnessSupport) {
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f);
+ writer.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f);
execute();
const auto errors = mReader.takeErrors();
EXPECT_EQ(1, errors.size());
@@ -1423,23 +1446,23 @@
return;
}
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.0f, -1.f);
+ writer.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.0f, -1.f);
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f);
+ writer.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 0.5f, -1.f);
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.0f, -1.f);
+ writer.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 1.0f, -1.f);
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -1.0f, -1.f);
+ writer.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -1.0f, -1.f);
execute();
EXPECT_TRUE(mReader.takeErrors().empty());
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 2.0f, -1.f);
+ writer.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ 2.0f, -1.f);
execute();
{
const auto errors = mReader.takeErrors();
@@ -1447,7 +1470,7 @@
EXPECT_EQ(IComposerClient::EX_BAD_PARAMETER, errors[0].errorCode);
}
- mWriter.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -2.0f, -1.f);
+ writer.setDisplayBrightness(getPrimaryDisplayId(), /*brightness*/ -2.0f, -1.f);
execute();
{
const auto errors = mReader.takeErrors();
@@ -1460,8 +1483,9 @@
EXPECT_TRUE(mComposerClient->setClientTargetSlotCount(getPrimaryDisplayId(), kBufferSlotCount)
.isOk());
- mWriter.setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, nullptr, /*acquireFence*/ -1,
- Dataspace::UNKNOWN, std::vector<Rect>());
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setClientTarget(getPrimaryDisplayId(), /*slot*/ 0, nullptr, /*acquireFence*/ -1,
+ Dataspace::UNKNOWN, std::vector<Rect>());
execute();
}
@@ -1481,24 +1505,28 @@
const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
const auto handle = buffer->handle;
- mWriter.setOutputBuffer(display.display, /*slot*/ 0, handle, /*releaseFence*/ -1);
+ auto& writer = getWriter(display.display);
+ writer.setOutputBuffer(display.display, /*slot*/ 0, handle, /*releaseFence*/ -1);
execute();
}
TEST_P(GraphicsComposerAidlCommandTest, ValidDisplay) {
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
}
TEST_P(GraphicsComposerAidlCommandTest, AcceptDisplayChanges) {
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
- mWriter.acceptDisplayChanges(getPrimaryDisplayId());
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ writer.acceptDisplayChanges(getPrimaryDisplayId());
execute();
}
TEST_P(GraphicsComposerAidlCommandTest, PresentDisplay) {
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
- mWriter.presentDisplay(getPrimaryDisplayId());
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ writer.presentDisplay(getPrimaryDisplayId());
execute();
}
@@ -1519,6 +1547,7 @@
const auto& [renderIntentsStatus, renderIntents] =
mComposerClient->getRenderIntents(getPrimaryDisplayId(), ColorMode::NATIVE);
EXPECT_TRUE(renderIntentsStatus.isOk());
+ auto& writer = getWriter(getPrimaryDisplayId());
for (auto intent : renderIntents) {
EXPECT_TRUE(mComposerClient->setColorMode(getPrimaryDisplayId(), ColorMode::NATIVE, intent)
.isOk());
@@ -1536,10 +1565,10 @@
FRect cropRect{0, 0, (float)getPrimaryDisplay().getDisplayWidth(),
(float)getPrimaryDisplay().getDisplayHeight()};
configureLayer(getPrimaryDisplay(), layer, Composition::CURSOR, displayFrame, cropRect);
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle,
- /*acquireFence*/ -1);
- mWriter.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle,
+ /*acquireFence*/ -1);
+ writer.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
+ writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
GTEST_SUCCEED() << "Composition change requested, skipping test";
@@ -1547,18 +1576,18 @@
}
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.presentDisplay(getPrimaryDisplayId());
+ writer.presentDisplay(getPrimaryDisplayId());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
const auto buffer2 = allocate(::android::PIXEL_FORMAT_RGBA_8888);
const auto handle2 = buffer2->handle;
ASSERT_NE(nullptr, handle2);
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle2,
- /*acquireFence*/ -1);
- mWriter.setLayerSurfaceDamage(getPrimaryDisplayId(), layer,
- std::vector<Rect>(1, {0, 0, 10, 10}));
- mWriter.presentDisplay(getPrimaryDisplayId());
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle2,
+ /*acquireFence*/ -1);
+ writer.setLayerSurfaceDamage(getPrimaryDisplayId(), layer,
+ std::vector<Rect>(1, {0, 0, 10, 10}));
+ writer.presentDisplay(getPrimaryDisplayId());
execute();
}
}
@@ -1572,15 +1601,16 @@
const auto handle = buffer->handle;
ASSERT_NE(nullptr, handle);
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle, /*acquireFence*/ -1);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle, /*acquireFence*/ -1);
Rect displayFrame{0, 0, getPrimaryDisplay().getDisplayWidth(),
getPrimaryDisplay().getDisplayHeight()};
FRect cropRect{0, 0, (float)getPrimaryDisplay().getDisplayWidth(),
(float)getPrimaryDisplay().getDisplayHeight()};
configureLayer(getPrimaryDisplay(), layer, Composition::CURSOR, displayFrame, cropRect);
- mWriter.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ writer.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
+ writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
@@ -1588,15 +1618,15 @@
GTEST_SUCCEED() << "Composition change requested, skipping test";
return;
}
- mWriter.presentDisplay(getPrimaryDisplayId());
+ writer.presentDisplay(getPrimaryDisplayId());
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerCursorPosition(getPrimaryDisplayId(), layer, /*x*/ 1, /*y*/ 1);
+ writer.setLayerCursorPosition(getPrimaryDisplayId(), layer, /*x*/ 1, /*y*/ 1);
execute();
- mWriter.setLayerCursorPosition(getPrimaryDisplayId(), layer, /*x*/ 0, /*y*/ 0);
- mWriter.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
- mWriter.presentDisplay(getPrimaryDisplayId());
+ writer.setLayerCursorPosition(getPrimaryDisplayId(), layer, /*x*/ 0, /*y*/ 0);
+ writer.validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+ writer.presentDisplay(getPrimaryDisplayId());
execute();
}
@@ -1608,7 +1638,8 @@
const auto& [layerStatus, layer] =
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle, /*acquireFence*/ -1);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, handle, /*acquireFence*/ -1);
execute();
}
@@ -1620,15 +1651,16 @@
Rect empty{0, 0, 0, 0};
Rect unit{0, 0, 1, 1};
- mWriter.setLayerSurfaceDamage(getPrimaryDisplayId(), layer, std::vector<Rect>(1, empty));
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerSurfaceDamage(getPrimaryDisplayId(), layer, std::vector<Rect>(1, empty));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerSurfaceDamage(getPrimaryDisplayId(), layer, std::vector<Rect>(1, unit));
+ writer.setLayerSurfaceDamage(getPrimaryDisplayId(), layer, std::vector<Rect>(1, unit));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerSurfaceDamage(getPrimaryDisplayId(), layer, std::vector<Rect>());
+ writer.setLayerSurfaceDamage(getPrimaryDisplayId(), layer, std::vector<Rect>());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1641,15 +1673,16 @@
Rect empty{0, 0, 0, 0};
Rect unit{0, 0, 1, 1};
- mWriter.setLayerBlockingRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, empty));
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerBlockingRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, empty));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerBlockingRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, unit));
+ writer.setLayerBlockingRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, unit));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerBlockingRegion(getPrimaryDisplayId(), layer, std::vector<Rect>());
+ writer.setLayerBlockingRegion(getPrimaryDisplayId(), layer, std::vector<Rect>());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1659,15 +1692,16 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerBlendMode(getPrimaryDisplayId(), layer, BlendMode::NONE);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerBlendMode(getPrimaryDisplayId(), layer, BlendMode::NONE);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerBlendMode(getPrimaryDisplayId(), layer, BlendMode::PREMULTIPLIED);
+ writer.setLayerBlendMode(getPrimaryDisplayId(), layer, BlendMode::PREMULTIPLIED);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerBlendMode(getPrimaryDisplayId(), layer, BlendMode::COVERAGE);
+ writer.setLayerBlendMode(getPrimaryDisplayId(), layer, BlendMode::COVERAGE);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1677,11 +1711,12 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerColor(getPrimaryDisplayId(), layer, Color{1.0f, 1.0f, 1.0f, 1.0f});
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerColor(getPrimaryDisplayId(), layer, Color{1.0f, 1.0f, 1.0f, 1.0f});
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerColor(getPrimaryDisplayId(), layer, Color{0.0f, 0.0f, 0.0f, 0.0f});
+ writer.setLayerColor(getPrimaryDisplayId(), layer, Color{0.0f, 0.0f, 0.0f, 0.0f});
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1691,19 +1726,20 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::CLIENT);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::CLIENT);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::DEVICE);
+ writer.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::DEVICE);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::SOLID_COLOR);
+ writer.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::SOLID_COLOR);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::CURSOR);
+ writer.setLayerCompositionType(getPrimaryDisplayId(), layer, Composition::CURSOR);
execute();
}
@@ -1734,9 +1770,10 @@
configureLayer(display, layer, Composition::DISPLAY_DECORATION, display.getFrameRect(),
display.getCrop());
- mWriter.setLayerBuffer(display.getDisplayId(), layer, /*slot*/ 0, decorBuffer->handle,
- /*acquireFence*/ -1);
- mWriter.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
+ auto& writer = getWriter(display.getDisplayId());
+ writer.setLayerBuffer(display.getDisplayId(), layer, /*slot*/ 0, decorBuffer->handle,
+ /*acquireFence*/ -1);
+ writer.validateDisplay(display.getDisplayId(), ComposerClientWriter::kNoTimestamp);
execute();
if (support) {
ASSERT_TRUE(mReader.takeErrors().empty());
@@ -1753,7 +1790,8 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerDataspace(getPrimaryDisplayId(), layer, Dataspace::UNKNOWN);
execute();
}
@@ -1762,7 +1800,8 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerDisplayFrame(getPrimaryDisplayId(), layer, Rect{0, 0, 1, 1});
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerDisplayFrame(getPrimaryDisplayId(), layer, Rect{0, 0, 1, 1});
execute();
}
@@ -1771,11 +1810,12 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerPlaneAlpha(getPrimaryDisplayId(), layer, /*alpha*/ 0.0f);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerPlaneAlpha(getPrimaryDisplayId(), layer, /*alpha*/ 0.0f);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerPlaneAlpha(getPrimaryDisplayId(), layer, /*alpha*/ 1.0f);
+ writer.setLayerPlaneAlpha(getPrimaryDisplayId(), layer, /*alpha*/ 1.0f);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1794,7 +1834,8 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerSidebandStream(getPrimaryDisplayId(), layer, handle);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerSidebandStream(getPrimaryDisplayId(), layer, handle);
execute();
}
@@ -1803,7 +1844,8 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerSourceCrop(getPrimaryDisplayId(), layer, FRect{0.0f, 0.0f, 1.0f, 1.0f});
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerSourceCrop(getPrimaryDisplayId(), layer, FRect{0.0f, 0.0f, 1.0f, 1.0f});
execute();
}
@@ -1812,39 +1854,40 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer, static_cast<Transform>(0));
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerTransform(getPrimaryDisplayId(), layer, static_cast<Transform>(0));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer, Transform::FLIP_H);
+ writer.setLayerTransform(getPrimaryDisplayId(), layer, Transform::FLIP_H);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer, Transform::FLIP_V);
+ writer.setLayerTransform(getPrimaryDisplayId(), layer, Transform::FLIP_V);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer, Transform::ROT_90);
+ writer.setLayerTransform(getPrimaryDisplayId(), layer, Transform::ROT_90);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer, Transform::ROT_180);
+ writer.setLayerTransform(getPrimaryDisplayId(), layer, Transform::ROT_180);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer, Transform::ROT_270);
+ writer.setLayerTransform(getPrimaryDisplayId(), layer, Transform::ROT_270);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer,
- static_cast<Transform>(static_cast<int>(Transform::FLIP_H) |
- static_cast<int>(Transform::ROT_90)));
+ writer.setLayerTransform(getPrimaryDisplayId(), layer,
+ static_cast<Transform>(static_cast<int>(Transform::FLIP_H) |
+ static_cast<int>(Transform::ROT_90)));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerTransform(getPrimaryDisplayId(), layer,
- static_cast<Transform>(static_cast<int>(Transform::FLIP_V) |
- static_cast<int>(Transform::ROT_90)));
+ writer.setLayerTransform(getPrimaryDisplayId(), layer,
+ static_cast<Transform>(static_cast<int>(Transform::FLIP_V) |
+ static_cast<int>(Transform::ROT_90)));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1857,15 +1900,16 @@
Rect empty{0, 0, 0, 0};
Rect unit{0, 0, 1, 1};
- mWriter.setLayerVisibleRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, empty));
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerVisibleRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, empty));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerVisibleRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, unit));
+ writer.setLayerVisibleRegion(getPrimaryDisplayId(), layer, std::vector<Rect>(1, unit));
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerVisibleRegion(getPrimaryDisplayId(), layer, std::vector<Rect>());
+ writer.setLayerVisibleRegion(getPrimaryDisplayId(), layer, std::vector<Rect>());
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1875,11 +1919,12 @@
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
EXPECT_TRUE(layerStatus.isOk());
- mWriter.setLayerZOrder(getPrimaryDisplayId(), layer, /*z*/ 10);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerZOrder(getPrimaryDisplayId(), layer, /*z*/ 10);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerZOrder(getPrimaryDisplayId(), layer, /*z*/ 0);
+ writer.setLayerZOrder(getPrimaryDisplayId(), layer, /*z*/ 0);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
}
@@ -1901,6 +1946,7 @@
* white (D65) 0.3127 0.3290
*/
+ auto& writer = getWriter(getPrimaryDisplayId());
std::vector<PerFrameMetadata> aidlMetadata;
aidlMetadata.push_back({PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, 0.680f});
aidlMetadata.push_back({PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, 0.320f});
@@ -1914,7 +1960,7 @@
aidlMetadata.push_back({PerFrameMetadataKey::MIN_LUMINANCE, 0.1f});
aidlMetadata.push_back({PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, 78.0});
aidlMetadata.push_back({PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, 62.0});
- mWriter.setLayerPerFrameMetadata(getPrimaryDisplayId(), layer, aidlMetadata);
+ writer.setLayerPerFrameMetadata(getPrimaryDisplayId(), layer, aidlMetadata);
execute();
const auto errors = mReader.takeErrors();
@@ -1931,19 +1977,20 @@
const auto& [layerStatus, layer] =
mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
- mWriter.setLayerBrightness(getPrimaryDisplayId(), layer, 0.2f);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerBrightness(getPrimaryDisplayId(), layer, 0.2f);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerBrightness(getPrimaryDisplayId(), layer, 1.f);
+ writer.setLayerBrightness(getPrimaryDisplayId(), layer, 1.f);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerBrightness(getPrimaryDisplayId(), layer, 0.f);
+ writer.setLayerBrightness(getPrimaryDisplayId(), layer, 0.f);
execute();
ASSERT_TRUE(mReader.takeErrors().empty());
- mWriter.setLayerBrightness(getPrimaryDisplayId(), layer, -1.f);
+ writer.setLayerBrightness(getPrimaryDisplayId(), layer, -1.f);
execute();
{
const auto errors = mReader.takeErrors();
@@ -1951,7 +1998,7 @@
EXPECT_EQ(IComposerClient::EX_BAD_PARAMETER, errors[0].errorCode);
}
- mWriter.setLayerBrightness(getPrimaryDisplayId(), layer, std::nanf(""));
+ writer.setLayerBrightness(getPrimaryDisplayId(), layer, std::nanf(""));
execute();
{
const auto errors = mReader.takeErrors();
@@ -2116,8 +2163,9 @@
ASSERT_NE(nullptr, buffer->handle);
const auto layer = createOnScreenLayer();
- mWriter.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer->handle,
- /*acquireFence*/ -1);
+ auto& writer = getWriter(getPrimaryDisplayId());
+ writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer->handle,
+ /*acquireFence*/ -1);
int32_t vsyncIdleCount = mComposerClient->getVsyncIdleCount();
auto earlyVsyncIdleTime = systemTime() + std::chrono::nanoseconds(2s).count();
EXPECT_TRUE(
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
index c05dd33..2090473 100644
--- a/identity/aidl/Android.bp
+++ b/identity/aidl/Android.bp
@@ -67,20 +67,20 @@
cc_defaults {
name: "identity_use_latest_hal_aidl_ndk_static",
static_libs: [
- "android.hardware.identity-V5-ndk",
+ "android.hardware.identity-V4-ndk",
],
}
cc_defaults {
name: "identity_use_latest_hal_aidl_ndk_shared",
shared_libs: [
- "android.hardware.identity-V5-ndk",
+ "android.hardware.identity-V4-ndk",
],
}
cc_defaults {
name: "identity_use_latest_hal_aidl_cpp_static",
static_libs: [
- "android.hardware.identity-V5-cpp",
+ "android.hardware.identity-V4-cpp",
],
}
diff --git a/media/bufferpool/aidl/Android.bp b/media/bufferpool/aidl/Android.bp
index 3b2ea3c..68ac489 100644
--- a/media/bufferpool/aidl/Android.bp
+++ b/media/bufferpool/aidl/Android.bp
@@ -12,6 +12,15 @@
// 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"],
+}
+
aidl_interface {
name: "android.hardware.media.bufferpool2",
vendor_available: true,
diff --git a/radio/aidl/compat/libradiocompat/Android.bp b/radio/aidl/compat/libradiocompat/Android.bp
index f79e045..0ceaec4 100644
--- a/radio/aidl/compat/libradiocompat/Android.bp
+++ b/radio/aidl/compat/libradiocompat/Android.bp
@@ -37,6 +37,7 @@
"android.hardware.radio.config@1.2",
"android.hardware.radio.config@1.3",
"android.hardware.radio.data-V1-ndk",
+ "android.hardware.radio.ims-V1-ndk",
"android.hardware.radio.messaging-V1-ndk",
"android.hardware.radio.modem-V1-ndk",
"android.hardware.radio.network-V2-ndk",
@@ -69,6 +70,9 @@
"data/RadioResponse-data.cpp",
"data/RadioData.cpp",
"data/structs.cpp",
+ "ims/RadioIndication-ims.cpp",
+ "ims/RadioResponse-ims.cpp",
+ "ims/RadioIms.cpp",
"messaging/RadioIndication-messaging.cpp",
"messaging/RadioMessaging.cpp",
"messaging/RadioResponse-messaging.cpp",
diff --git a/radio/aidl/compat/libradiocompat/ims/RadioIms.cpp b/radio/aidl/compat/libradiocompat/ims/RadioIms.cpp
new file mode 100644
index 0000000..d2bdfff
--- /dev/null
+++ b/radio/aidl/compat/libradiocompat/ims/RadioIms.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 <libradiocompat/RadioIms.h>
+
+#include "commonStructs.h"
+#include "debug.h"
+
+#include "collections.h"
+
+#define RADIO_MODULE "Ims"
+
+namespace android::hardware::radio::compat {
+
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::ims;
+constexpr auto ok = &ScopedAStatus::ok;
+
+std::shared_ptr<aidl::IRadioImsResponse> RadioIms::respond() {
+ return mCallbackManager->response().imsCb();
+}
+
+ScopedAStatus RadioIms::setSrvccCallInfo(
+ int32_t serial, const std::vector<aidl::SrvccCall>& /*srvccCalls*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " setSrvccCallInfo is unsupported by HIDL HALs";
+ return ok();
+}
+ScopedAStatus RadioIms::updateImsRegistrationInfo(
+ int32_t serial, const aidl::ImsRegistration& /*imsRegistration*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " updateImsRegistrationInfo is unsupported by HIDL HALs";
+ return ok();
+}
+ScopedAStatus RadioIms::startImsTraffic(
+ int32_t serial, int32_t /*token*/, aidl::ImsTrafficType /*imsTrafficType*/,
+ ::aidl::android::hardware::radio::AccessNetwork /*accessNetworkType*/,
+ ::aidl::android::hardware::radio::ims::ImsCall::Direction /*trafficDirection*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " startImsTraffic is unsupported by HIDL HALs";
+ return ok();
+}
+ScopedAStatus RadioIms::stopImsTraffic(int32_t serial, int32_t /*token*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " stopImsTraffic is unsupported by HIDL HALs";
+ return ok();
+}
+ScopedAStatus RadioIms::triggerEpsFallback(int32_t serial, aidl::EpsFallbackReason /*reason*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " triggerEpsFallback is unsupported by HIDL HALs";
+ return ok();
+}
+ScopedAStatus RadioIms::sendAnbrQuery(
+ int32_t serial, aidl::ImsStreamType /*mediaType*/, aidl::ImsStreamDirection /*direction*/,
+ int32_t /*bitsPerSecond*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " sendAnbrQuery is unsupported by HIDL HALs";
+ return ok();
+}
+ScopedAStatus RadioIms::updateImsCallStatus(
+ int32_t serial, const std::vector<aidl::ImsCall>& /*imsCalls*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " updateImsCallStatus is unsupported by HIDL HALs";
+ return ok();
+}
+
+ScopedAStatus RadioIms::setResponseFunctions(
+ const std::shared_ptr<aidl::IRadioImsResponse>& response,
+ const std::shared_ptr<aidl::IRadioImsIndication>& indication) {
+ LOG_CALL << response << ' ' << indication;
+ mCallbackManager->setResponseFunctions(response, indication);
+ return ok();
+}
+
+} // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/ims/RadioIndication-ims.cpp b/radio/aidl/compat/libradiocompat/ims/RadioIndication-ims.cpp
new file mode 100644
index 0000000..10109b8
--- /dev/null
+++ b/radio/aidl/compat/libradiocompat/ims/RadioIndication-ims.cpp
@@ -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.
+ */
+
+#include <libradiocompat/RadioIndication.h>
+
+#include "commonStructs.h"
+#include "debug.h"
+
+#include "collections.h"
+
+#define RADIO_MODULE "ImsIndication"
+
+namespace android::hardware::radio::compat {
+
+using ::aidl::android::hardware::radio::RadioTechnology;
+namespace aidl = ::aidl::android::hardware::radio::ims;
+
+void RadioIndication::setResponseFunction(std::shared_ptr<aidl::IRadioImsIndication> imsCb) {
+ mImsCb = imsCb;
+}
+
+std::shared_ptr<aidl::IRadioImsIndication> RadioIndication::imsCb() {
+ return mImsCb.get();
+}
+
+} // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/ims/RadioResponse-ims.cpp b/radio/aidl/compat/libradiocompat/ims/RadioResponse-ims.cpp
new file mode 100644
index 0000000..831a0ae
--- /dev/null
+++ b/radio/aidl/compat/libradiocompat/ims/RadioResponse-ims.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 <libradiocompat/RadioResponse.h>
+
+#include "commonStructs.h"
+#include "debug.h"
+
+#include "collections.h"
+
+#define RADIO_MODULE "ImsResponse"
+
+namespace android::hardware::radio::compat {
+
+namespace aidl = ::aidl::android::hardware::radio::ims;
+
+void RadioResponse::setResponseFunction(std::shared_ptr<aidl::IRadioImsResponse> imsCb) {
+ mImsCb = imsCb;
+}
+
+std::shared_ptr<aidl::IRadioImsResponse> RadioResponse::imsCb() {
+ return mImsCb.get();
+}
+
+} // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIms.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIms.h
new file mode 100644
index 0000000..0dbc565
--- /dev/null
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIms.h
@@ -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.
+ */
+#pragma once
+
+#include "RadioCompatBase.h"
+
+#include <aidl/android/hardware/radio/ims/BnRadioIms.h>
+
+namespace android::hardware::radio::compat {
+
+class RadioIms : public RadioCompatBase, public aidl::android::hardware::radio::ims::BnRadioIms {
+ ::ndk::ScopedAStatus setSrvccCallInfo(
+ int32_t serial,
+ const std::vector<::aidl::android::hardware::radio::ims::SrvccCall>& srvccCalls)
+ override;
+ ::ndk::ScopedAStatus updateImsRegistrationInfo(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::ims::ImsRegistration& imsRegistration) override;
+ ::ndk::ScopedAStatus startImsTraffic(
+ int32_t serial, int32_t token,
+ ::aidl::android::hardware::radio::ims::ImsTrafficType imsTrafficType,
+ ::aidl::android::hardware::radio::AccessNetwork accessNetworkType,
+ ::aidl::android::hardware::radio::ims::ImsCall::Direction trafficDirection) override;
+ ::ndk::ScopedAStatus stopImsTraffic(int32_t serial, int32_t token) override;
+ ::ndk::ScopedAStatus triggerEpsFallback(
+ int32_t serial,
+ ::aidl::android::hardware::radio::ims::EpsFallbackReason reason) override;
+ ::ndk::ScopedAStatus sendAnbrQuery(
+ int32_t serial, ::aidl::android::hardware::radio::ims::ImsStreamType mediaType,
+ ::aidl::android::hardware::radio::ims::ImsStreamDirection direction,
+ int32_t bitsPerSecond) override;
+ ::ndk::ScopedAStatus updateImsCallStatus(
+ int32_t serial,
+ const std::vector<::aidl::android::hardware::radio::ims::ImsCall>& imsCalls) override;
+
+ ::ndk::ScopedAStatus setResponseFunctions(
+ const std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsResponse>&
+ radioImsResponse,
+ const std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsIndication>&
+ radioImsIndication) override;
+
+ protected:
+ std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsResponse> respond();
+
+ public:
+ using RadioCompatBase::RadioCompatBase;
+};
+
+} // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h
index 6cfd59c..f042456 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioIndication.h
@@ -19,6 +19,7 @@
#include "GuaranteedCallback.h"
#include <aidl/android/hardware/radio/data/IRadioDataIndication.h>
+#include <aidl/android/hardware/radio/ims/IRadioImsIndication.h>
#include <aidl/android/hardware/radio/messaging/IRadioMessagingIndication.h>
#include <aidl/android/hardware/radio/modem/IRadioModemIndication.h>
#include <aidl/android/hardware/radio/network/IRadioNetworkIndication.h>
@@ -55,6 +56,10 @@
::aidl::android::hardware::radio::voice::IRadioVoiceIndication,
::aidl::android::hardware::radio::voice::IRadioVoiceIndicationDefault, true>
mVoiceCb;
+ GuaranteedCallback< //
+ ::aidl::android::hardware::radio::ims::IRadioImsIndication,
+ ::aidl::android::hardware::radio::ims::IRadioImsIndicationDefault, true>
+ mImsCb;
// IRadioIndication @ 1.0
Return<void> radioStateChanged(V1_0::RadioIndicationType type,
@@ -220,6 +225,8 @@
std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimIndication> simCb);
void setResponseFunction(
std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceIndication> voicCb);
+ void setResponseFunction(
+ std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsIndication> imsCb);
std::shared_ptr<::aidl::android::hardware::radio::data::IRadioDataIndication> dataCb();
std::shared_ptr<::aidl::android::hardware::radio::messaging::IRadioMessagingIndication>
@@ -228,6 +235,7 @@
std::shared_ptr<::aidl::android::hardware::radio::network::IRadioNetworkIndication> networkCb();
std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimIndication> simCb();
std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceIndication> voiceCb();
+ std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsIndication> imsCb();
};
} // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h
index 1f82dd1..22451ae 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioResponse.h
@@ -19,6 +19,7 @@
#include "GuaranteedCallback.h"
#include <aidl/android/hardware/radio/data/IRadioDataResponse.h>
+#include <aidl/android/hardware/radio/ims/IRadioImsResponse.h>
#include <aidl/android/hardware/radio/messaging/IRadioMessagingResponse.h>
#include <aidl/android/hardware/radio/modem/IRadioModemResponse.h>
#include <aidl/android/hardware/radio/network/IRadioNetworkResponse.h>
@@ -49,6 +50,9 @@
GuaranteedCallback<::aidl::android::hardware::radio::voice::IRadioVoiceResponse,
::aidl::android::hardware::radio::voice::IRadioVoiceResponseDefault>
mVoiceCb;
+ GuaranteedCallback<::aidl::android::hardware::radio::ims::IRadioImsResponse,
+ ::aidl::android::hardware::radio::ims::IRadioImsResponseDefault>
+ mImsCb;
// IRadioResponse @ 1.0
Return<void> getIccCardStatusResponse(const V1_0::RadioResponseInfo& info,
@@ -440,6 +444,8 @@
std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimResponse> simCb);
void setResponseFunction(
std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceResponse> voiceCb);
+ void setResponseFunction(
+ std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsResponse> imsCb);
std::shared_ptr<::aidl::android::hardware::radio::data::IRadioDataResponse> dataCb();
std::shared_ptr<::aidl::android::hardware::radio::messaging::IRadioMessagingResponse>
@@ -448,6 +454,7 @@
std::shared_ptr<::aidl::android::hardware::radio::network::IRadioNetworkResponse> networkCb();
std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimResponse> simCb();
std::shared_ptr<::aidl::android::hardware::radio::voice::IRadioVoiceResponse> voiceCb();
+ std::shared_ptr<::aidl::android::hardware::radio::ims::IRadioImsResponse> imsCb();
};
} // namespace android::hardware::radio::compat
diff --git a/radio/aidl/compat/service/Android.bp b/radio/aidl/compat/service/Android.bp
index 4dbaef4..d16773e 100644
--- a/radio/aidl/compat/service/Android.bp
+++ b/radio/aidl/compat/service/Android.bp
@@ -40,6 +40,7 @@
"android.hardware.radio.config@1.2",
"android.hardware.radio.config@1.3",
"android.hardware.radio.data-V1-ndk",
+ "android.hardware.radio.ims-V1-ndk",
"android.hardware.radio.messaging-V1-ndk",
"android.hardware.radio.modem-V1-ndk",
"android.hardware.radio.network-V2-ndk",
diff --git a/radio/aidl/vts/radio_aidl_hal_utils.cpp b/radio/aidl/vts/radio_aidl_hal_utils.cpp
index efc4f26..6ed8e7d 100644
--- a/radio/aidl/vts/radio_aidl_hal_utils.cpp
+++ b/radio/aidl/vts/radio_aidl_hal_utils.cpp
@@ -92,6 +92,10 @@
return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsds");
}
+bool isDsDaEnabled() {
+ return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsda");
+}
+
bool isTsTsEnabled() {
return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "tsts");
}
diff --git a/radio/aidl/vts/radio_aidl_hal_utils.h b/radio/aidl/vts/radio_aidl_hal_utils.h
index 4ed6b3f..d515e1a 100644
--- a/radio/aidl/vts/radio_aidl_hal_utils.h
+++ b/radio/aidl/vts/radio_aidl_hal_utils.h
@@ -106,6 +106,11 @@
bool isDsDsEnabled();
/*
+ * Check if device is in DSDA (Dual SIM Dual Active).
+ */
+bool isDsDaEnabled();
+
+/*
* Check if device is in TSTS (Triple SIM Triple Standby).
*/
bool isTsTsEnabled();
diff --git a/radio/aidl/vts/radio_config_test.cpp b/radio/aidl/vts/radio_config_test.cpp
index 258b172..c979d28 100644
--- a/radio/aidl/vts/radio_config_test.cpp
+++ b/radio/aidl/vts/radio_config_test.cpp
@@ -176,7 +176,7 @@
slotPortMapping.physicalSlotId = -1;
slotPortMapping.portId = -1;
std::vector<SlotPortMapping> slotPortMappingList = {slotPortMapping};
- if (isDsDsEnabled()) {
+ if (isDsDsEnabled() || isDsDaEnabled()) {
slotPortMappingList.push_back(slotPortMapping);
} else if (isTsTsEnabled()) {
slotPortMappingList.push_back(slotPortMapping);
@@ -252,7 +252,7 @@
}
if (isSsSsEnabled()) {
EXPECT_EQ(1, simCount);
- } else if (isDsDsEnabled()) {
+ } else if (isDsDsEnabled() || isDsDaEnabled()) {
EXPECT_EQ(2, simCount);
} else if (isTsTsEnabled()) {
EXPECT_EQ(3, simCount);
diff --git a/security/keymint/TEST_MAPPING b/security/keymint/TEST_MAPPING
index 9ce5e9b..4ab60b6 100644
--- a/security/keymint/TEST_MAPPING
+++ b/security/keymint/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"presubmit": [
{
+ "name": "VtsAidlKeyMintTargetTest"
+ },
+ {
"name": "VtsHalRemotelyProvisionedComponentTargetTest"
}
]
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 5473062..80abd92 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -1919,30 +1919,32 @@
// The following check assumes that canonical CBOR encoding is used for the COSE_Key.
if (testMode) {
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n" // kty: EC2
- " 3 : -7,\n" // alg: ES256
- " -1 : 1,\n" // EC id: P256
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
- // sequence of 32 hexadecimal bytes, enclosed in braces and
- // separated by commas. In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
- " -70000 : null,\n" // test marker
- "}"));
+ EXPECT_THAT(
+ cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("\\{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : \\{(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}\\},\n" // pub_x: data
+ " -3 : \\{(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}\\},\n" // pub_y: data
+ " -70000 : null,\n" // test marker
+ "\\}"));
} else {
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n" // kty: EC2
- " 3 : -7,\n" // alg: ES256
- " -1 : 1,\n" // EC id: P256
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
- // sequence of 32 hexadecimal bytes, enclosed in braces and
- // separated by commas. In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
- "}"));
+ EXPECT_THAT(
+ cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("\\{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : \\{(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}\\},\n" // pub_x: data
+ " -3 : \\{(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}\\},\n" // pub_y: data
+ "\\}"));
}
}
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 69fe434..b8d0c20 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -1027,6 +1027,15 @@
* without providing NOT_BEFORE and NOT_AFTER parameters.
*/
TEST_P(NewKeyGenerationTest, RsaWithMissingValidity) {
+ if (AidlVersion() < 2) {
+ /*
+ * The KeyMint V1 spec required that CERTIFICATE_NOT_{BEFORE,AFTER} be
+ * specified for asymmetric key generation. However, this was not
+ * checked at the time so we can only be strict about checking this for
+ * implementations of KeyMint version 2 and above.
+ */
+ GTEST_SKIP() << "Validity strict since KeyMint v2";
+ }
// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to
// GeneralizedTime 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
constexpr uint64_t kUndefinedExpirationDateTime = 253402300799000;
@@ -1680,6 +1689,15 @@
* without providing NOT_BEFORE and NOT_AFTER parameters.
*/
TEST_P(NewKeyGenerationTest, EcdsaWithMissingValidity) {
+ if (AidlVersion() < 2) {
+ /*
+ * The KeyMint V1 spec required that CERTIFICATE_NOT_{BEFORE,AFTER} be
+ * specified for asymmetric key generation. However, this was not
+ * checked at the time so we can only be strict about checking this for
+ * implementations of KeyMint version 2 and above.
+ */
+ GTEST_SKIP() << "Validity strict since KeyMint v2";
+ }
// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to
// GeneralizedTime 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
constexpr uint64_t kUndefinedExpirationDateTime = 253402300799000;
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 4f361bb..5b11741 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -701,7 +701,8 @@
}
/**
- * Generate a non-empty certificate request. Make sure contents are reproducible.
+ * Generate a non-empty certificate request. Make sure contents are reproducible but allow for the
+ * signature to be different since algorithms including ECDSA P-256 can include a random value.
*/
TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
generateKeys(false /* testMode */, 1 /* numKeys */);
@@ -711,19 +712,16 @@
auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto firstBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
- ASSERT_TRUE(firstBcc) << firstBcc.message();
+ auto firstCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+ ASSERT_TRUE(firstCsr) << firstCsr.message();
status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto secondBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
- ASSERT_TRUE(secondBcc) << secondBcc.message();
+ auto secondCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+ ASSERT_TRUE(secondCsr) << secondCsr.message();
- ASSERT_EQ(firstBcc->size(), secondBcc->size());
- for (auto i = 0; i < firstBcc->size(); i++) {
- ASSERT_EQ(firstBcc->at(i).pubKey, secondBcc->at(i).pubKey);
- }
+ ASSERT_EQ(**firstCsr, **secondCsr);
}
/**
diff --git a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
index 5bbae4c..0c61c25 100644
--- a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
+++ b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
@@ -16,16 +16,21 @@
#define LOG_TAG "keymint_benchmark"
+#include <iostream>
+
#include <base/command_line.h>
#include <benchmark/benchmark.h>
-#include <iostream>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
#include <android/binder_manager.h>
#include <binder/IServiceManager.h>
+
#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
+#include <openssl/curve25519.h>
+#include <openssl/x509.h>
#define SMALL_MESSAGE_SIZE 64
#define MEDIUM_MESSAGE_SIZE 1024
@@ -119,6 +124,22 @@
return {};
}
+ string getAlgorithmString(string transform) {
+ if (transform.find("AES") != string::npos) {
+ return "AES";
+ } else if (transform.find("Hmac") != string::npos) {
+ return "HMAC";
+ } else if (transform.find("DESede") != string::npos) {
+ return "TRIPLE_DES";
+ } else if (transform.find("RSA") != string::npos) {
+ return "RSA";
+ } else if (transform.find("EC") != string::npos) {
+ return "EC";
+ }
+ std::cerr << "Can't find algorithm for " << transform << std::endl;
+ return "";
+ }
+
Digest getDigest(string transform) {
if (transform.find("MD5") != string::npos) {
return Digest::MD5;
@@ -135,29 +156,56 @@
return Digest::SHA_2_512;
} else if (transform.find("RSA") != string::npos &&
transform.find("OAEP") != string::npos) {
- return Digest::SHA1;
+ if (securityLevel_ == SecurityLevel::STRONGBOX) {
+ return Digest::SHA_2_256;
+ } else {
+ return Digest::SHA1;
+ }
} else if (transform.find("Hmac") != string::npos) {
return Digest::SHA_2_256;
}
return Digest::NONE;
}
+ string getDigestString(string transform) {
+ if (transform.find("MD5") != string::npos) {
+ return "MD5";
+ } else if (transform.find("SHA1") != string::npos ||
+ transform.find("SHA-1") != string::npos) {
+ return "SHA1";
+ } else if (transform.find("SHA224") != string::npos) {
+ return "SHA_2_224";
+ } else if (transform.find("SHA256") != string::npos) {
+ return "SHA_2_256";
+ } else if (transform.find("SHA384") != string::npos) {
+ return "SHA_2_384";
+ } else if (transform.find("SHA512") != string::npos) {
+ return "SHA_2_512";
+ } else if (transform.find("RSA") != string::npos &&
+ transform.find("OAEP") != string::npos) {
+ if (securityLevel_ == SecurityLevel::STRONGBOX) {
+ return "SHA_2_256";
+ } else {
+ return "SHA1";
+ }
+ } else if (transform.find("Hmac") != string::npos) {
+ return "SHA_2_256";
+ }
+ return "";
+ }
+
optional<EcCurve> getCurveFromLength(int keySize) {
switch (keySize) {
case 224:
return EcCurve::P_224;
- break;
case 256:
return EcCurve::P_256;
- break;
case 384:
return EcCurve::P_384;
- break;
case 521:
return EcCurve::P_521;
- break;
default:
- return {};
+ return std::nullopt;
}
}
@@ -261,6 +309,109 @@
return GetReturnErrorCode(result);
}
+ /* Copied the function LocalRsaEncryptMessage from
+ * hardware/interfaces/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp in VTS.
+ * Replaced asserts with the condition check and return false in case of failure condition.
+ * Require return value to skip the benchmark test case from further execution in case
+ * LocalRsaEncryptMessage fails.
+ */
+ optional<string> LocalRsaEncryptMessage(const string& message, const AuthorizationSet& params) {
+ // Retrieve the public key from the leaf certificate.
+ if (cert_chain_.empty()) {
+ std::cerr << "Local RSA encrypt Error: invalid cert_chain_" << std::endl;
+ return "Failure";
+ }
+ X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get()));
+ RSA_Ptr rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pub_key.get())));
+
+ // Retrieve relevant tags.
+ Digest digest = Digest::NONE;
+ Digest mgf_digest = Digest::SHA1;
+ PaddingMode padding = PaddingMode::NONE;
+
+ auto digest_tag = params.GetTagValue(TAG_DIGEST);
+ if (digest_tag.has_value()) digest = digest_tag.value();
+ auto pad_tag = params.GetTagValue(TAG_PADDING);
+ if (pad_tag.has_value()) padding = pad_tag.value();
+ auto mgf_tag = params.GetTagValue(TAG_RSA_OAEP_MGF_DIGEST);
+ if (mgf_tag.has_value()) mgf_digest = mgf_tag.value();
+
+ const EVP_MD* md = openssl_digest(digest);
+ const EVP_MD* mgf_md = openssl_digest(mgf_digest);
+
+ // Set up encryption context.
+ EVP_PKEY_CTX_Ptr ctx(EVP_PKEY_CTX_new(pub_key.get(), /* engine= */ nullptr));
+ if (EVP_PKEY_encrypt_init(ctx.get()) <= 0) {
+ std::cerr << "Local RSA encrypt Error: Encryption init failed" << std::endl;
+ return "Failure";
+ }
+
+ int rc = -1;
+ switch (padding) {
+ case PaddingMode::NONE:
+ rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
+ break;
+ case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
+ rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING);
+ break;
+ case PaddingMode::RSA_OAEP:
+ rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING);
+ break;
+ default:
+ break;
+ }
+ if (rc <= 0) {
+ std::cerr << "Local RSA encrypt Error: Set padding failed" << std::endl;
+ return "Failure";
+ }
+ if (padding == PaddingMode::RSA_OAEP) {
+ if (!EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), md)) {
+ std::cerr << "Local RSA encrypt Error: Set digest failed: " << ERR_peek_last_error()
+ << std::endl;
+ return "Failure";
+ }
+ if (!EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), mgf_md)) {
+ std::cerr << "Local RSA encrypt Error: Set digest failed: " << ERR_peek_last_error()
+ << std::endl;
+ return "Failure";
+ }
+ }
+
+ // Determine output size.
+ size_t outlen;
+ if (EVP_PKEY_encrypt(ctx.get(), nullptr /* out */, &outlen,
+ reinterpret_cast<const uint8_t*>(message.data()),
+ message.size()) <= 0) {
+ std::cerr << "Local RSA encrypt Error: Determine output size failed: "
+ << ERR_peek_last_error() << std::endl;
+ return "Failure";
+ }
+
+ // Left-zero-pad the input if necessary.
+ const uint8_t* to_encrypt = reinterpret_cast<const uint8_t*>(message.data());
+ size_t to_encrypt_len = message.size();
+
+ std::unique_ptr<string> zero_padded_message;
+ if (padding == PaddingMode::NONE && to_encrypt_len < outlen) {
+ zero_padded_message.reset(new string(outlen, '\0'));
+ memcpy(zero_padded_message->data() + (outlen - to_encrypt_len), message.data(),
+ message.size());
+ to_encrypt = reinterpret_cast<const uint8_t*>(zero_padded_message->data());
+ to_encrypt_len = outlen;
+ }
+
+ // Do the encryption.
+ string output(outlen, '\0');
+ if (EVP_PKEY_encrypt(ctx.get(), reinterpret_cast<uint8_t*>(output.data()), &outlen,
+ to_encrypt, to_encrypt_len) <= 0) {
+ std::cerr << "Local RSA encrypt Error: Encryption failed: " << ERR_peek_last_error()
+ << std::endl;
+ return "Failure";
+ }
+ return output;
+ }
+
SecurityLevel securityLevel_;
string name_;
@@ -268,12 +419,13 @@
ErrorCode GenerateKey(const AuthorizationSet& key_desc,
const optional<AttestationKey>& attest_key = std::nullopt) {
key_blob_.clear();
+ cert_chain_.clear();
KeyCreationResult creationResult;
Status result = keymint_->generateKey(key_desc.vector_data(), attest_key, &creationResult);
if (result.isOk()) {
key_blob_ = std::move(creationResult.keyBlob);
+ cert_chain_ = std::move(creationResult.certificateChain);
creationResult.keyCharacteristics.clear();
- creationResult.certificateChain.clear();
}
return GetReturnErrorCode(result);
}
@@ -338,6 +490,11 @@
return ErrorCode::UNKNOWN_ERROR;
}
+ X509_Ptr parse_cert_blob(const vector<uint8_t>& blob) {
+ const uint8_t* p = blob.data();
+ return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
+ }
+
std::shared_ptr<IKeyMintOperation> op_;
vector<Certificate> cert_chain_;
vector<uint8_t> key_blob_;
@@ -390,6 +547,10 @@
BENCHMARK_KM_MSG(encrypt, transform, keySize, msgSize) \
BENCHMARK_KM_MSG(decrypt, transform, keySize, msgSize)
+// Skip public key operations as they are not supported in KeyMint.
+#define BENCHMARK_KM_ASYM_CIPHER(transform, keySize, msgSize) \
+ BENCHMARK_KM_MSG(decrypt, transform, keySize, msgSize)
+
#define BENCHMARK_KM_CIPHER_ALL_MSGS(transform, keySize) \
BENCHMARK_KM_ALL_MSGS(encrypt, transform, keySize) \
BENCHMARK_KM_ALL_MSGS(decrypt, transform, keySize)
@@ -397,12 +558,43 @@
#define BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, keySize) \
BENCHMARK_KM_ALL_MSGS(sign, transform, keySize) \
BENCHMARK_KM_ALL_MSGS(verify, transform, keySize)
-// clang-format on
+
+// Skip public key operations as they are not supported in KeyMint.
+#define BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, keySize) \
+ BENCHMARK_KM_ALL_MSGS(sign, transform, keySize) \
+ // clang-format on
/*
* ============= KeyGen TESTS ==================
*/
+
+static bool isValidSBKeySize(string transform, int keySize) {
+ std::optional<Algorithm> algorithm = keymintTest->getAlgorithm(transform);
+ switch (algorithm.value()) {
+ case Algorithm::AES:
+ return (keySize == 128 || keySize == 256);
+ case Algorithm::HMAC:
+ return (keySize % 8 == 0 && keySize >= 64 && keySize <= 512);
+ case Algorithm::TRIPLE_DES:
+ return (keySize == 168);
+ case Algorithm::RSA:
+ return (keySize == 2048);
+ case Algorithm::EC:
+ return (keySize == 256);
+ }
+ return false;
+}
+
static void keygen(benchmark::State& state, string transform, int keySize) {
+ // Skip the test for unsupported key size in StrongBox
+ if (keymintTest->securityLevel_ == SecurityLevel::STRONGBOX &&
+ !isValidSBKeySize(transform, keySize)) {
+ state.SkipWithError(("Skipped for STRONGBOX: Keysize: " + std::to_string(keySize) +
+ " is not supported in StrongBox for algorithm: " +
+ keymintTest->getAlgorithmString(transform))
+ .c_str());
+ return;
+ }
addDefaultLabel(state);
for (auto _ : state) {
if (!keymintTest->GenerateKey(transform, keySize)) {
@@ -438,8 +630,24 @@
/*
* ============= SIGNATURE TESTS ==================
*/
-
static void sign(benchmark::State& state, string transform, int keySize, int msgSize) {
+ // Skip the test for unsupported key size or unsupported digest in StrongBox
+ if (keymintTest->securityLevel_ == SecurityLevel::STRONGBOX) {
+ if (!isValidSBKeySize(transform, keySize)) {
+ state.SkipWithError(("Skipped for STRONGBOX: Keysize: " + std::to_string(keySize) +
+ " is not supported in StrongBox for algorithm: " +
+ keymintTest->getAlgorithmString(transform))
+ .c_str());
+ return;
+ }
+ if (keymintTest->getDigest(transform) != Digest::SHA_2_256) {
+ state.SkipWithError(
+ ("Skipped for STRONGBOX: Digest: " + keymintTest->getDigestString(transform) +
+ " is not supported in StrongBox")
+ .c_str());
+ return;
+ }
+ }
addDefaultLabel(state);
if (!keymintTest->GenerateKey(transform, keySize, true)) {
state.SkipWithError(
@@ -469,6 +677,23 @@
}
static void verify(benchmark::State& state, string transform, int keySize, int msgSize) {
+ // Skip the test for unsupported key size or unsupported digest in StrongBox
+ if (keymintTest->securityLevel_ == SecurityLevel::STRONGBOX) {
+ if (!isValidSBKeySize(transform, keySize)) {
+ state.SkipWithError(("Skipped for STRONGBOX: Keysize: " + std::to_string(keySize) +
+ " is not supported in StrongBox for algorithm: " +
+ keymintTest->getAlgorithmString(transform))
+ .c_str());
+ return;
+ }
+ if (keymintTest->getDigest(transform) != Digest::SHA_2_256) {
+ state.SkipWithError(
+ ("Skipped for STRONGBOX: Digest: " + keymintTest->getDigestString(transform) +
+ " is not supported in StrongBox")
+ .c_str());
+ return;
+ }
+ }
addDefaultLabel(state);
if (!keymintTest->GenerateKey(transform, keySize, true)) {
state.SkipWithError(
@@ -525,10 +750,10 @@
BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA512)
#define BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(transform) \
- BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 224) \
- BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 256) \
- BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 384) \
- BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 521)
+ BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, 224) \
+ BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, 256) \
+ BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, 384) \
+ BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, 521)
BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(NONEwithECDSA);
BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA1withECDSA);
@@ -538,13 +763,14 @@
BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA512withECDSA);
#define BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(transform) \
- BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 2048) \
- BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 3072) \
- BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 4096)
+ BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, 2048) \
+ BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, 3072) \
+ BENCHMARK_KM_ASYM_SIGNATURE_ALL_MSGS(transform, 4096)
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(MD5withRSA);
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA1withRSA);
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA224withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA256withRSA);
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA384withRSA);
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA512withRSA);
@@ -553,6 +779,7 @@
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA224withRSA/PSS);
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA384withRSA/PSS);
BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA512withRSA/PSS);
+
// clang-format on
/*
@@ -560,6 +787,15 @@
*/
static void encrypt(benchmark::State& state, string transform, int keySize, int msgSize) {
+ // Skip the test for unsupported key size in StrongBox
+ if (keymintTest->securityLevel_ == SecurityLevel::STRONGBOX &&
+ (!isValidSBKeySize(transform, keySize))) {
+ state.SkipWithError(("Skipped for STRONGBOX: Keysize: " + std::to_string(keySize) +
+ " is not supported in StrongBox for algorithm: " +
+ keymintTest->getAlgorithmString(transform))
+ .c_str());
+ return;
+ }
addDefaultLabel(state);
if (!keymintTest->GenerateKey(transform, keySize)) {
state.SkipWithError(
@@ -589,6 +825,15 @@
}
static void decrypt(benchmark::State& state, string transform, int keySize, int msgSize) {
+ // Skip the test for unsupported key size in StrongBox
+ if (keymintTest->securityLevel_ == SecurityLevel::STRONGBOX &&
+ (!isValidSBKeySize(transform, keySize))) {
+ state.SkipWithError(("Skipped for STRONGBOX: Keysize: " + std::to_string(keySize) +
+ " is not supported in StrongBox for algorithm: " +
+ keymintTest->getAlgorithmString(transform))
+ .c_str());
+ return;
+ }
addDefaultLabel(state);
if (!keymintTest->GenerateKey(transform, keySize)) {
state.SkipWithError(
@@ -598,23 +843,34 @@
AuthorizationSet out_params;
AuthorizationSet in_params = keymintTest->getOperationParams(transform);
string message = keymintTest->GenerateMessage(msgSize);
- auto error = keymintTest->Begin(KeyPurpose::ENCRYPT, in_params, &out_params);
- if (error != ErrorCode::OK) {
- state.SkipWithError(
- ("Encryption begin error, " + std::to_string(keymintTest->getError())).c_str());
- return;
+ optional<string> encryptedMessage;
+
+ if (keymintTest->getAlgorithm(transform).value() == Algorithm::RSA) {
+ // Public key operation not supported, doing local Encryption
+ encryptedMessage = keymintTest->LocalRsaEncryptMessage(message, in_params);
+ if ((keySize / 8) != (*encryptedMessage).size()) {
+ state.SkipWithError("Local Encryption falied");
+ return;
+ }
+ } else {
+ auto error = keymintTest->Begin(KeyPurpose::ENCRYPT, in_params, &out_params);
+ if (error != ErrorCode::OK) {
+ state.SkipWithError(
+ ("Encryption begin error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ encryptedMessage = keymintTest->Process(message);
+ if (!encryptedMessage) {
+ state.SkipWithError(
+ ("Encryption error, " + std::to_string(keymintTest->getError())).c_str());
+ return;
+ }
+ in_params.push_back(out_params);
+ out_params.Clear();
}
- auto encryptedMessage = keymintTest->Process(message);
- if (!encryptedMessage) {
- state.SkipWithError(
- ("Encryption error, " + std::to_string(keymintTest->getError())).c_str());
- return;
- }
- in_params.push_back(out_params);
- out_params.Clear();
for (auto _ : state) {
state.PauseTiming();
- error = keymintTest->Begin(KeyPurpose::DECRYPT, in_params, &out_params);
+ auto error = keymintTest->Begin(KeyPurpose::DECRYPT, in_params, &out_params);
if (error != ErrorCode::OK) {
state.SkipWithError(
("Decryption begin error, " + std::to_string(keymintTest->getError())).c_str());
@@ -649,9 +905,9 @@
BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/ECB/PKCS7Padding, 168);
#define BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(transform, msgSize) \
- BENCHMARK_KM_CIPHER(transform, 2048, msgSize) \
- BENCHMARK_KM_CIPHER(transform, 3072, msgSize) \
- BENCHMARK_KM_CIPHER(transform, 4096, msgSize)
+ BENCHMARK_KM_ASYM_CIPHER(transform, 2048, msgSize) \
+ BENCHMARK_KM_ASYM_CIPHER(transform, 3072, msgSize) \
+ BENCHMARK_KM_ASYM_CIPHER(transform, 4096, msgSize)
BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/NoPadding, SMALL_MESSAGE_SIZE);
BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/PKCS1Padding, SMALL_MESSAGE_SIZE);
diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h
index 6871e1b..1b94c62 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -181,14 +181,13 @@
* Verify the CSR as if the device is still early in the factory process and may not
* have all device identifiers provisioned yet.
*/
-ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
- const std::vector<uint8_t>& csr,
- IRemotelyProvisionedComponent* provisionable,
- const std::vector<uint8_t>& challenge);
+ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
+ const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
+ IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
/**
* Verify the CSR as if the device is a final production sample.
*/
-ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
+ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index f7ab3ac..7e164fd 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -521,11 +521,10 @@
return errMsg;
}
- std::unique_ptr<cppbor::Map> parsed(parsedVerifiedDeviceInfo->asMap());
+ std::unique_ptr<cppbor::Map> parsed(parsedVerifiedDeviceInfo.release()->asMap());
if (!parsed) {
return "DeviceInfo must be a CBOR map.";
}
- parsedVerifiedDeviceInfo.release();
if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
return "DeviceInfo ordering is non-canonical.";
@@ -846,54 +845,79 @@
return "";
}
-ErrMsgOr<cppbor::Array> parseAndValidateCsrPayload(const cppbor::Array& keysToSign,
- const std::vector<uint8_t>& csrPayload,
- IRemotelyProvisionedComponent* provisionable,
- const std::vector<uint8_t>& challenge,
- bool isFactory) {
+ErrMsgOr<std::unique_ptr<cppbor::Array>> parseAndValidateCsrPayload(
+ const cppbor::Array& keysToSign, const std::vector<uint8_t>& csrPayload,
+ IRemotelyProvisionedComponent* provisionable, bool isFactory) {
auto [parsedCsrPayload, _, errMsg] = cppbor::parse(csrPayload);
if (!parsedCsrPayload) {
return errMsg;
}
- if (!parsedCsrPayload->asArray()) {
+
+ std::unique_ptr<cppbor::Array> parsed(parsedCsrPayload.release()->asArray());
+ if (!parsed) {
return "CSR payload is not a CBOR array.";
}
- if (parsedCsrPayload->asArray()->size() != 5U) {
- return "CSR payload must contain version, certificate type, device info, challenge, keys. "
+
+ if (parsed->size() != 4U) {
+ return "CSR payload must contain version, certificate type, device info, keys. "
"However, the parsed CSR payload has " +
- std::to_string(parsedCsrPayload->asArray()->size()) + " entries.";
+ std::to_string(parsed->size()) + " entries.";
}
- auto& signedVersion = parsedCsrPayload->asArray()->get(0);
- auto& signedCertificateType = parsedCsrPayload->asArray()->get(1);
- auto& signedDeviceInfo = parsedCsrPayload->asArray()->get(2);
- auto& signedChallenge = parsedCsrPayload->asArray()->get(3);
- auto& signedKeys = parsedCsrPayload->asArray()->get(4);
+ auto signedVersion = parsed->get(0)->asUint();
+ auto signedCertificateType = parsed->get(1)->asTstr();
+ auto signedDeviceInfo = parsed->get(2)->asMap();
+ auto signedKeys = parsed->get(3)->asArray();
- if (!signedVersion || !signedVersion->asUint() || signedVersion->asUint()->value() != 1U) {
- return "CSR payload version must be an unsigned integer and must be equal to 1.";
+ if (!signedVersion || signedVersion->value() != 3U) {
+ return "CSR payload version must be an unsigned integer and must be equal to 3.";
}
- if (!signedCertificateType || !signedCertificateType->asTstr()) {
+ if (!signedCertificateType) {
// Certificate type is allowed to be extendend by vendor, i.e. we can't
// enforce its value.
return "Certificate type must be a Tstr.";
}
- if (!signedDeviceInfo || !signedDeviceInfo->asMap()) {
+ if (!signedDeviceInfo) {
return "Device info must be an Map.";
}
- if (!signedChallenge || !signedChallenge->asBstr()) {
- return "Challenge must be a Bstr.";
- }
- if (!signedKeys || !signedKeys->asArray()) {
+ if (!signedKeys) {
return "Keys must be an Array.";
}
- auto result = parseAndValidateDeviceInfo(signedDeviceInfo->asMap()->encode(), provisionable,
- isFactory);
+ auto result = parseAndValidateDeviceInfo(signedDeviceInfo->encode(), provisionable, isFactory);
if (!result) {
return result.message();
}
+ if (signedKeys->encode() != keysToSign.encode()) {
+ return "Signed keys do not match.";
+ }
+
+ return std::move(parsed);
+}
+
+ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequestSignedPayload(
+ const std::vector<uint8_t>& signedPayload, const std::vector<uint8_t>& challenge) {
+ auto [parsedSignedPayload, _, errMsg] = cppbor::parse(signedPayload);
+ if (!parsedSignedPayload) {
+ return errMsg;
+ }
+ if (!parsedSignedPayload->asArray()) {
+ return "SignedData payload is not a CBOR array.";
+ }
+ if (parsedSignedPayload->asArray()->size() != 2U) {
+ return "SignedData payload must contain the challenge and request. However, the parsed "
+ "SignedData payload has " +
+ std::to_string(parsedSignedPayload->asArray()->size()) + " entries.";
+ }
+
+ auto signedChallenge = parsedSignedPayload->asArray()->get(0)->asBstr();
+ auto signedRequest = parsedSignedPayload->asArray()->get(1)->asBstr();
+
+ if (!signedChallenge) {
+ return "Challenge must be a Bstr.";
+ }
+
if (challenge.size() < 32 || challenge.size() > 64) {
return "Challenge size must be between 32 and 64 bytes inclusive. "
"However, challenge is " +
@@ -901,68 +925,57 @@
}
auto challengeBstr = cppbor::Bstr(challenge);
- if (*signedChallenge->asBstr() != challengeBstr) {
+ if (*signedChallenge != challengeBstr) {
return "Signed challenge does not match."
"\n Actual: " +
cppbor::prettyPrint(signedChallenge->asBstr(), 64 /* maxBStrSize */) +
"\nExpected: " + cppbor::prettyPrint(&challengeBstr, 64 /* maxBStrSize */);
}
- if (signedKeys->asArray()->encode() != keysToSign.encode()) {
- return "Signed keys do not match.";
+ if (!signedRequest) {
+ return "Request must be a Bstr.";
}
- return std::move(*parsedCsrPayload->asArray());
+ return signedRequest->value();
}
-ErrMsgOr<std::vector<BccEntryData>> verifyCsr(const cppbor::Array& keysToSign,
- const std::vector<uint8_t>& csr,
- IRemotelyProvisionedComponent* provisionable,
- const std::vector<uint8_t>& challenge,
- bool isFactory) {
- auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
- if (!parsedCsr) {
+ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t>& request,
+ const std::vector<uint8_t>& challenge) {
+ auto [parsedRequest, _, csrErrMsg] = cppbor::parse(request);
+ if (!parsedRequest) {
return csrErrMsg;
}
- if (!parsedCsr->asArray()) {
- return "CSR is not a CBOR array.";
+ if (!parsedRequest->asArray()) {
+ return "AuthenticatedRequest is not a CBOR array.";
}
- if (parsedCsr->asArray()->size() != 4U) {
- return "CSR must contain version, UDS certificates, DICE chain, and signed data. "
- "However, the parsed CSR has " +
- std::to_string(parsedCsr->asArray()->size()) + " entries.";
+ if (parsedRequest->asArray()->size() != 4U) {
+ return "AuthenticatedRequest must contain version, UDS certificates, DICE chain, and "
+ "signed data. However, the parsed AuthenticatedRequest has " +
+ std::to_string(parsedRequest->asArray()->size()) + " entries.";
}
- auto& version = parsedCsr->asArray()->get(0);
- auto& udsCerts = parsedCsr->asArray()->get(1);
- auto& diceCertChain = parsedCsr->asArray()->get(2);
- auto& signedData = parsedCsr->asArray()->get(3);
+ auto version = parsedRequest->asArray()->get(0)->asUint();
+ auto udsCerts = parsedRequest->asArray()->get(1)->asMap();
+ auto diceCertChain = parsedRequest->asArray()->get(2)->asArray();
+ auto signedData = parsedRequest->asArray()->get(3)->asArray();
- if (!version || !version->asUint() || version->asUint()->value() != 3U) {
- return "Version must be an unsigned integer and must be equal to 3.";
+ if (!version || version->value() != 1U) {
+ return "AuthenticatedRequest version must be an unsigned integer and must be equal to 1.";
}
- if (!udsCerts || !udsCerts->asMap()) {
- return "UdsCerts must be an Map.";
+ if (!udsCerts) {
+ return "AuthenticatedRequest UdsCerts must be an Map.";
}
- if (!diceCertChain || !diceCertChain->asArray()) {
- return "DiceCertChain must be an Array.";
+ if (!diceCertChain) {
+ return "AuthenticatedRequest DiceCertChain must be an Array.";
}
- if (!signedData || !signedData->asArray()) {
- return "SignedData must be an Array.";
- }
-
- RpcHardwareInfo info;
- provisionable->getHardwareInfo(&info);
- if (version->asUint()->value() != info.versionNumber) {
- return "CSR version (" + std::to_string(version->asUint()->value()) +
- ") does not match the remotely provisioned component version (" +
- std::to_string(info.versionNumber) + ").";
+ if (!signedData) {
+ return "AuthenticatedRequest SignedData must be an Array.";
}
// DICE chain is [ pubkey, + DiceChainEntry ]. Its format is the same as BCC from RKP v1-2.
- auto diceContents = validateBcc(diceCertChain->asArray());
+ auto diceContents = validateBcc(diceCertChain);
if (!diceContents) {
- return diceContents.message() + "\n" + prettyPrint(diceCertChain.get());
+ return diceContents.message() + "\n" + prettyPrint(diceCertChain);
}
if (diceContents->size() == 0U) {
return "The DICE chain is empty. It must contain at least one entry.";
@@ -970,33 +983,51 @@
auto& udsPub = diceContents->back().pubKey;
- auto error = validateUdsCerts(*udsCerts->asMap(), udsPub);
+ auto error = validateUdsCerts(*udsCerts, udsPub);
if (!error.empty()) {
return error;
}
- auto csrPayload = verifyAndParseCoseSign1(signedData->asArray(), udsPub, {} /* aad */);
+ auto signedPayload = verifyAndParseCoseSign1(signedData, udsPub, {} /* aad */);
+ if (!signedPayload) {
+ return signedPayload.message();
+ }
+
+ auto payload = parseAndValidateAuthenticatedRequestSignedPayload(*signedPayload, challenge);
+ if (!payload) {
+ return payload.message();
+ }
+
+ return payload;
+}
+
+ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSign,
+ const std::vector<uint8_t>& csr,
+ IRemotelyProvisionedComponent* provisionable,
+ const std::vector<uint8_t>& challenge,
+ bool isFactory) {
+ RpcHardwareInfo info;
+ provisionable->getHardwareInfo(&info);
+ if (info.versionNumber != 3) {
+ return "Remotely provisioned component version (" + std::to_string(info.versionNumber) +
+ ") does not match expected version (3).";
+ }
+
+ auto csrPayload = parseAndValidateAuthenticatedRequest(csr, challenge);
if (!csrPayload) {
return csrPayload.message();
}
- auto parsedCsrPayload = parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable,
- challenge, isFactory);
- if (!parsedCsrPayload) {
- return parsedCsrPayload.message();
- }
-
- return *diceContents;
+ return parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable, isFactory);
}
-ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
- const std::vector<uint8_t>& csr,
- IRemotelyProvisionedComponent* provisionable,
- const std::vector<uint8_t>& challenge) {
+ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
+ const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
+ IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/true);
}
-ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
+ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/false);
diff --git a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 86c1717..78969d1 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -315,13 +315,12 @@
*
* @return the following CBOR Certificate Signing Request (Csr) serialized into a byte array:
*
- * Csr = AuthenticatedMessage<CsrPayload>
+ * Csr = AuthenticatedRequest<CsrPayload>
*
* CsrPayload = [ ; CBOR Array defining the payload for Csr
- * version: 1, ; The CsrPayload CDDL Schema version.
+ * version: 3, ; The CsrPayload CDDL Schema version.
* CertificateType, ; The type of certificate being requested.
* DeviceInfo, ; Defined in DeviceInfo.aidl
- * challenge: bstr .size (32..64), ; Provided by the method parameters
* KeysToSign, ; Provided by the method parameters
* ]
*
@@ -335,11 +334,14 @@
*
* KeysToSign = [ * PublicKey ] ; Please see MacedPublicKey.aidl for the PublicKey definition.
*
- * AuthenticatedMessage<T> = [
- * version: 3, ; The AuthenticatedMessage CDDL Schema version.
- * UdsCerts,
- * DiceCertChain,
- * SignedData<T>,
+ * AuthenticatedRequest<T> = [
+ * version: 1, ; The AuthenticatedRequest CDDL Schema version.
+ * UdsCerts,
+ * DiceCertChain,
+ * SignedData<[
+ * challenge: bstr .size (32..64), ; Provided by the method parameters
+ * bstr .cbor T,
+ * ]>,
* ]
*
* ; COSE_Sign1 (untagged)
diff --git a/wifi/1.6/default/wifi_legacy_hal.cpp b/wifi/1.6/default/wifi_legacy_hal.cpp
index c9bcdc5..43cb7c4 100644
--- a/wifi/1.6/default/wifi_legacy_hal.cpp
+++ b/wifi/1.6/default/wifi_legacy_hal.cpp
@@ -366,6 +366,14 @@
}
}
+// Callback to report cached scan results
+std::function<void(wifi_cached_scan_report*)> on_cached_scan_results_internal_callback;
+void onSyncCachedScanResults(wifi_cached_scan_report* cache_report) {
+ if (on_cached_scan_results_internal_callback) {
+ on_cached_scan_results_internal_callback(cache_report);
+ }
+}
+
// End of the free-standing "C" style callbacks.
WifiLegacyHal::WifiLegacyHal(const std::weak_ptr<wifi_system::InterfaceTool> iface_tool,
@@ -1589,6 +1597,17 @@
return global_func_table_.wifi_enable_tx_power_limits(getIfaceHandle(iface_name), enable);
}
+wifi_error WifiLegacyHal::getWifiCachedScanResults(
+ const std::string& iface_name, const CachedScanResultsCallbackHandlers& handler) {
+ on_cached_scan_results_internal_callback = handler.on_cached_scan_results;
+
+ wifi_error status = global_func_table_.wifi_get_cached_scan_results(getIfaceHandle(iface_name),
+ {onSyncCachedScanResults});
+
+ on_cached_scan_results_internal_callback = nullptr;
+ return status;
+}
+
void WifiLegacyHal::invalidate() {
global_handle_ = nullptr;
iface_name_to_handle_.clear();
diff --git a/wifi/1.6/default/wifi_legacy_hal.h b/wifi/1.6/default/wifi_legacy_hal.h
index 2b923b4..1fd784a 100644
--- a/wifi/1.6/default/wifi_legacy_hal.h
+++ b/wifi/1.6/default/wifi_legacy_hal.h
@@ -218,6 +218,7 @@
using ::WIFI_BAND_ABG_WITH_DFS;
using ::WIFI_BAND_BG;
using ::WIFI_BAND_UNSPECIFIED;
+using ::wifi_cached_scan_report;
using ::wifi_cached_scan_results;
using ::WIFI_CHAN_WIDTH_10;
using ::WIFI_CHAN_WIDTH_160;
@@ -465,6 +466,12 @@
std::function<void(chre_nan_rtt_state)> on_wifi_chre_nan_rtt_state;
};
+// Cached Scan Results response and event callbacks struct.
+struct CachedScanResultsCallbackHandlers {
+ // Callback for Cached Scan Results
+ std::function<void(wifi_cached_scan_report*)> on_cached_scan_results;
+};
+
/**
* Class that encapsulates all legacy HAL interactions.
* This class manages the lifetime of the event loop thread used by legacy HAL.
@@ -684,6 +691,8 @@
const ChreCallbackHandlers& handler);
wifi_error enableWifiTxPowerLimits(const std::string& iface_name, bool enable);
+ wifi_error getWifiCachedScanResults(const std::string& iface_name,
+ const CachedScanResultsCallbackHandlers& handler);
private:
// Retrieve interface handles for all the available interfaces.
diff --git a/wifi/1.6/default/wifi_legacy_hal_stubs.cpp b/wifi/1.6/default/wifi_legacy_hal_stubs.cpp
index b3bd373..8f8527a 100644
--- a/wifi/1.6/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.6/default/wifi_legacy_hal_stubs.cpp
@@ -167,6 +167,7 @@
populateStubFor(&hal_fn->wifi_nan_rtt_chre_disable_request);
populateStubFor(&hal_fn->wifi_chre_register_handler);
populateStubFor(&hal_fn->wifi_enable_tx_power_limits);
+ populateStubFor(&hal_fn->wifi_get_cached_scan_results);
return true;
}
} // namespace legacy_hal