Merge "Create API for reporting color space support to Camera2 framework consumers."
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index d6e6f50..4e50b7a 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -100,6 +100,7 @@
"android/hardware/audio/core/MmapBufferDescriptor.aidl",
"android/hardware/audio/core/ModuleDebug.aidl",
"android/hardware/audio/core/StreamDescriptor.aidl",
+ "android/hardware/audio/core/SurroundSoundConfig.aidl",
],
imports: [
"android.hardware.common-V2",
@@ -147,11 +148,20 @@
name: "android.hardware.audio.effect",
vendor_available: true,
srcs: [
+ "android/hardware/audio/effect/Capability.aidl",
+ "android/hardware/audio/effect/CommandId.aidl",
"android/hardware/audio/effect/Descriptor.aidl",
+ "android/hardware/audio/effect/Equalizer.aidl",
+ "android/hardware/audio/effect/Flags.aidl",
"android/hardware/audio/effect/IEffect.aidl",
"android/hardware/audio/effect/IFactory.aidl",
+ "android/hardware/audio/effect/Parameter.aidl",
+ "android/hardware/audio/effect/Processing.aidl",
+ "android/hardware/audio/effect/State.aidl",
],
imports: [
+ "android.hardware.common-V2",
+ "android.hardware.common.fmq-V1",
"android.hardware.audio.common-V1",
"android.media.audio.common.types-V2",
],
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IConfig.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IConfig.aidl
index fd80715..163b7a0 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IConfig.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IConfig.aidl
@@ -34,4 +34,5 @@
package android.hardware.audio.core;
@VintfStability
interface IConfig {
+ android.hardware.audio.core.SurroundSoundConfig getSurroundSoundConfig();
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/SurroundSoundConfig.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/SurroundSoundConfig.aidl
new file mode 100644
index 0000000..08a1537
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/SurroundSoundConfig.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.core;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable SurroundSoundConfig {
+ android.hardware.audio.core.SurroundSoundConfig.SurroundFormatFamily[] formatFamilies;
+ @VintfStability
+ parcelable SurroundFormatFamily {
+ android.media.audio.common.AudioFormatDescription primaryFormat;
+ android.media.audio.common.AudioFormatDescription[] subFormats;
+ }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl
new file mode 100644
index 0000000..11acf5e
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Capability.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union Capability {
+ android.hardware.audio.effect.Capability.VendorEffectCapability vendor;
+ android.hardware.audio.effect.Equalizer.Capability equalizer;
+ @VintfStability
+ parcelable VendorEffectCapability {
+ ParcelableHolder extension;
+ }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl
new file mode 100644
index 0000000..79299ee
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.effect;
+@Backing(type="int") @VintfStability
+enum CommandId {
+ START = 0,
+ STOP = 1,
+ RESET = 2,
+ VENDOR_COMMAND_0 = 256,
+ VENDOR_COMMAND_1 = 257,
+ VENDOR_COMMAND_2 = 258,
+ VENDOR_COMMAND_3 = 259,
+ VENDOR_COMMAND_4 = 260,
+ VENDOR_COMMAND_5 = 261,
+ VENDOR_COMMAND_6 = 262,
+ VENDOR_COMMAND_7 = 263,
+ VENDOR_COMMAND_8 = 264,
+ VENDOR_COMMAND_9 = 265,
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl
index 94cacd9..4707011 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl
@@ -35,6 +35,7 @@
@VintfStability
parcelable Descriptor {
android.hardware.audio.effect.Descriptor.Common common;
+ android.hardware.audio.effect.Capability capability;
const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e";
const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b";
const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b";
@@ -52,9 +53,15 @@
parcelable Identity {
android.media.audio.common.AudioUuid type;
android.media.audio.common.AudioUuid uuid;
+ @nullable android.media.audio.common.AudioUuid proxy;
+ android.hardware.audio.effect.Flags flags;
}
@VintfStability
parcelable Common {
android.hardware.audio.effect.Descriptor.Identity id;
+ int cpuLoad;
+ int memoryUsage;
+ @utf8InCpp String name;
+ @utf8InCpp String implementor;
}
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl
new file mode 100644
index 0000000..f7af300
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@VintfStability
+union Equalizer {
+ android.hardware.audio.effect.Equalizer.VendorExtension vendor;
+ android.hardware.audio.effect.Equalizer.BandLevel[] bandLevels;
+ int preset;
+ @VintfStability
+ parcelable VendorExtension {
+ ParcelableHolder extension;
+ }
+ @VintfStability
+ parcelable Capability {
+ ParcelableHolder extension;
+ android.hardware.audio.effect.Equalizer.BandFrequency[] bandFrequencies;
+ android.hardware.audio.effect.Equalizer.Preset[] presets;
+ }
+ @VintfStability
+ parcelable BandLevel {
+ int index;
+ int level;
+ }
+ @VintfStability
+ parcelable BandFrequency {
+ int index;
+ int min;
+ int max;
+ }
+ @VintfStability
+ parcelable Preset {
+ int index;
+ @utf8InCpp String name;
+ }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
new file mode 100644
index 0000000..285ff18
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl
@@ -0,0 +1,74 @@
+/*
+ * 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.effect;
+@VintfStability
+parcelable Flags {
+ android.hardware.audio.effect.Flags.Type type = android.hardware.audio.effect.Flags.Type.INSERT;
+ android.hardware.audio.effect.Flags.Insert insert = android.hardware.audio.effect.Flags.Insert.ANY;
+ android.hardware.audio.effect.Flags.Volume volume = android.hardware.audio.effect.Flags.Volume.NONE;
+ android.hardware.audio.effect.Flags.HardwareAccelerator hwAcceleratorMode = android.hardware.audio.effect.Flags.HardwareAccelerator.NONE;
+ boolean offloadIndication;
+ boolean deviceIndication;
+ boolean audioModeIndication;
+ boolean audioSourceIndication;
+ boolean noProcessing;
+ @Backing(type="byte") @VintfStability
+ enum Type {
+ INSERT = 0,
+ AUXILIARY = 1,
+ REPLACE = 2,
+ PRE_PROC = 3,
+ POST_PROC = 4,
+ }
+ @Backing(type="byte") @VintfStability
+ enum Insert {
+ ANY = 0,
+ FIRST = 1,
+ LAST = 2,
+ EXCLUSIVE = 3,
+ }
+ @Backing(type="byte") @VintfStability
+ enum Volume {
+ NONE = 0,
+ CTRL = 1,
+ IND = 2,
+ MONITOR = 3,
+ }
+ @Backing(type="byte") @VintfStability
+ enum HardwareAccelerator {
+ NONE = 0,
+ SIMPLE = 1,
+ TUNNEL = 2,
+ }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl
index 7f868ad..e5c96f5 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IEffect.aidl
@@ -34,7 +34,23 @@
package android.hardware.audio.effect;
@VintfStability
interface IEffect {
- void open();
+ android.hardware.audio.effect.IEffect.OpenEffectReturn open(in android.hardware.audio.effect.Parameter.Common common, in android.hardware.audio.effect.Parameter.Specific specific);
void close();
android.hardware.audio.effect.Descriptor getDescriptor();
+ void command(in android.hardware.audio.effect.CommandId commandId);
+ android.hardware.audio.effect.State getState();
+ void setParameter(in android.hardware.audio.effect.Parameter param);
+ android.hardware.audio.effect.Parameter getParameter(in android.hardware.audio.effect.Parameter.Id paramId);
+ @FixedSize @VintfStability
+ parcelable Status {
+ int status;
+ int fmqByteConsumed;
+ int fmqByteProduced;
+ }
+ @VintfStability
+ parcelable OpenEffectReturn {
+ android.hardware.common.fmq.MQDescriptor<android.hardware.audio.effect.IEffect.Status,android.hardware.common.fmq.SynchronizedReadWrite> statusMQ;
+ android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> inputDataMQ;
+ android.hardware.common.fmq.MQDescriptor<byte,android.hardware.common.fmq.SynchronizedReadWrite> outputDataMQ;
+ }
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl
index db06475..a22c591 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/IFactory.aidl
@@ -35,6 +35,7 @@
@VintfStability
interface IFactory {
android.hardware.audio.effect.Descriptor.Identity[] queryEffects(in @nullable android.media.audio.common.AudioUuid type, in @nullable android.media.audio.common.AudioUuid implementation);
+ android.hardware.audio.effect.Processing[] queryProcessing(in @nullable android.hardware.audio.effect.Processing.Type type);
android.hardware.audio.effect.IEffect createEffect(in android.media.audio.common.AudioUuid implUuid);
void destroyEffect(in android.hardware.audio.effect.IEffect handle);
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
new file mode 100644
index 0000000..547112a
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
@@ -0,0 +1,76 @@
+/*
+ * 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.effect;
+@VintfStability
+union Parameter {
+ android.hardware.audio.effect.Parameter.Common common;
+ android.media.audio.common.AudioDeviceType device;
+ android.media.audio.common.AudioMode mode;
+ android.media.audio.common.AudioSource source;
+ android.hardware.audio.effect.Parameter.Volume volume;
+ boolean offload;
+ android.hardware.audio.effect.Parameter.VendorEffectParameter vendorEffect;
+ android.hardware.audio.effect.Parameter.Specific specific;
+ @VintfStability
+ union Id {
+ int commonTag;
+ int vendorTag;
+ android.hardware.audio.effect.Parameter.Specific.Id specificId;
+ }
+ @VintfStability
+ parcelable Common {
+ int session;
+ int ioHandle;
+ android.media.audio.common.AudioConfig input;
+ android.media.audio.common.AudioConfig output;
+ }
+ @VintfStability
+ parcelable Volume {
+ float left;
+ float right;
+ }
+ @VintfStability
+ parcelable VendorEffectParameter {
+ ParcelableHolder extension;
+ }
+ @VintfStability
+ union Specific {
+ android.hardware.audio.effect.Parameter.Specific.Id id;
+ android.hardware.audio.effect.Equalizer equalizer;
+ @VintfStability
+ union Id {
+ android.hardware.audio.effect.Equalizer.Tag equalizerTag = android.hardware.audio.effect.Equalizer.Tag.vendor;
+ }
+ }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Processing.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Processing.aidl
new file mode 100644
index 0000000..a779ae4
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Processing.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.effect;
+@VintfStability
+parcelable Processing {
+ android.hardware.audio.effect.Processing.Type type;
+ android.hardware.audio.effect.Descriptor.Identity[] ids;
+ @VintfStability
+ union Type {
+ android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
+ android.media.audio.common.AudioSource source;
+ }
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl
new file mode 100644
index 0000000..3176b01
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.effect;
+@Backing(type="byte") @VintfStability
+enum State {
+ INIT = 0,
+ IDLE = 1,
+ PROCESSING = 2,
+}
diff --git a/audio/aidl/android/hardware/audio/core/IConfig.aidl b/audio/aidl/android/hardware/audio/core/IConfig.aidl
index c7bb414..c8ba6be 100644
--- a/audio/aidl/android/hardware/audio/core/IConfig.aidl
+++ b/audio/aidl/android/hardware/audio/core/IConfig.aidl
@@ -16,9 +16,22 @@
package android.hardware.audio.core;
+import android.hardware.audio.core.SurroundSoundConfig;
+
/**
* This interface provides system-wide configuration parameters for audio I/O
* (by "system" here we mean the device running Android).
*/
@VintfStability
-interface IConfig {}
+interface IConfig {
+ /**
+ * Returns the surround sound configuration used for the Audio Policy
+ * Manager initial configuration.
+ *
+ * This method will only be called during the initialization of the Audio
+ * Policy Manager, and must always return the same result.
+ *
+ * @return The surround sound configuration
+ */
+ SurroundSoundConfig getSurroundSoundConfig();
+}
diff --git a/audio/aidl/android/hardware/audio/core/SurroundSoundConfig.aidl b/audio/aidl/android/hardware/audio/core/SurroundSoundConfig.aidl
new file mode 100644
index 0000000..eeda12a
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/SurroundSoundConfig.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.media.audio.common.AudioFormatDescription;
+
+/**
+ * SurroundSoundConfig defines the multi-channel formats that can be enabled on
+ * (primarily TV) devices.
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable SurroundSoundConfig {
+ @VintfStability
+ parcelable SurroundFormatFamily {
+ /**
+ * A primaryFormat shall get an entry in the Surround Settings dialog on TV
+ * devices. There must be a corresponding Java ENCODING_... constant
+ * defined in AudioFormat.java, and a display name defined in
+ * AudioFormat.toDisplayName.
+ */
+ AudioFormatDescription primaryFormat;
+ /**
+ * List of formats that shall be equivalent to the primaryFormat from the
+ * users' point of view and don't need a dedicated Surround Settings
+ * dialog entry.
+ */
+ AudioFormatDescription[] subFormats;
+ }
+ SurroundFormatFamily[] formatFamilies;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Capability.aidl b/audio/aidl/android/hardware/audio/effect/Capability.aidl
new file mode 100644
index 0000000..e792f86
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Capability.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.effect;
+
+import android.hardware.audio.effect.Equalizer;
+
+/**
+ * Effect capability definitions.
+ * This data structure is used as part of effect Descriptor to identify effect capabilities which
+ * not meant to change at runtime.
+ */
+@VintfStability
+union Capability {
+ /**
+ * Vendor defined effect capability.
+ * This extension can be used when vendor have a new effect implementated and need
+ * capability definition for this new type of effect.
+ * If vendor want to extend existing effect capabilities, it is recommended to expose though
+ * the ParcelableHolder in each effect capability definition. For example:
+ * Equalizer.Capability.extension.
+ */
+ @VintfStability
+ parcelable VendorEffectCapability {
+ ParcelableHolder extension;
+ }
+ VendorEffectCapability vendor;
+
+ /**
+ * Equalizer capability definition.
+ */
+ Equalizer.Capability equalizer;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/android/hardware/audio/effect/CommandId.aidl
new file mode 100644
index 0000000..d940b42
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/CommandId.aidl
@@ -0,0 +1,75 @@
+/*
+ * 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.effect;
+
+/**
+ * Defines all commands supported by the effect instance.
+ *
+ * There are three groups of commands:
+ * 1. Common part which MUST be supported by all effects.
+ * 2. Commands MUST be supported by a specific type of effect.
+ * 3. Extension commands for vendor.
+ */
+@VintfStability
+@Backing(type="int")
+enum CommandId {
+ /**
+ * Commands MUST be supported by all effects.
+ */
+ /**
+ * Start effect engine processing.
+ * An effect instance must start processing data and transfer to PROCESSING state if it is in
+ * IDLE state and have all necessary information. Otherwise it must:
+ * 1. Throw a EX_ILLEGAL_STATE exception if effect is not in IDLE state, or
+ * 2. Throw a EX_TRANSACTION_FAILED for all other errors.
+ *
+ * Depending on parameters set to the effect instance, effect may do process or reverse
+ * process after START command.
+ */
+ START = 0,
+ /**
+ * Stop effect engine processing with all resource kept.
+ * The currently processed audio data will be discarded if the effect engine is in PROCESSING
+ * state.
+ * Effect instance must do nothing and return ok when it receive STOP command in IDLE state.
+ */
+ STOP = 1,
+ /**
+ * Keep all parameter settings but reset the buffer content, stop engine processing, and transit
+ * instance state to IDLE if its in PROCESSING state.
+ * Effect instance must be able to handle RESET command at IDLE and PROCESSING states.
+ */
+ RESET = 2,
+
+ /**
+ * Commands MUST be supported by a specific type of effect.
+ */
+
+ /**
+ * Extension commands for vendor.
+ */
+ VENDOR_COMMAND_0 = 0x100,
+ VENDOR_COMMAND_1,
+ VENDOR_COMMAND_2,
+ VENDOR_COMMAND_3,
+ VENDOR_COMMAND_4,
+ VENDOR_COMMAND_5,
+ VENDOR_COMMAND_6,
+ VENDOR_COMMAND_7,
+ VENDOR_COMMAND_8,
+ VENDOR_COMMAND_9,
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
index 51b31c2..562c249 100644
--- a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl
@@ -16,43 +16,77 @@
package android.hardware.audio.effect;
+import android.hardware.audio.effect.Capability;
+import android.hardware.audio.effect.Flags;
import android.media.audio.common.AudioUuid;
/**
- * Effect descriptor contains all information (capabilities, attributes, and ownership) for an
- * effect implemented in the Audio Effect HAL. Framework uses this information to decide when and
- * how to apply the effect.
+ * Descriptor contains all information (capabilities, attributes, etc) for an effect implementation.
+ * The client uses this information to decide when and how to apply an effect implementation.
+ *
+ * Each type of effect can have more than one implementation (differentiated by implementation
+ * UUID), the effect proxy act as a combination of two implementations (usually one software and
+ * one offload implementation), so effect processing can be seamlessly switched between
+ * implementations in same proxy depending on the configuration and/or use case. If the optional
+ * proxy UUID is specified in Descriptor.Identity, then client must consider the effect instance as
+ * part of the effect proxy.
*/
@VintfStability
parcelable Descriptor {
/**
* UUID for effect types, these definitions are in sync with SDK, see @c AudioEffect.java.
*/
- // UUID for environmental reverberation effect type.
+ /**
+ * UUID for environmental reverberation effect type.
+ */
const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e";
- // UUID for preset reverberation effect type.
+ /**
+ * UUID for preset reverberation effect type.
+ */
const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b";
- // UUID for equalizer effect type.
+ /**
+ * UUID for equalizer effect type.
+ */
const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b";
- // UUID for bass boost effect type.
+ /**
+ * UUID for bass boost effect type.
+ */
const String EFFECT_TYPE_UUID_BASS_BOOST = "0634f220-ddd4-11db-a0fc-0002a5d5c51b";
- // UUID for virtualizer effect type.
+ /**
+ * UUID for virtualizer effect type.
+ */
const String EFFECT_TYPE_UUID_VIRTUALIZER = "37cc2c00-dddd-11db-8577-0002a5d5c51b";
- // UUID for Automatic Gain Control (AGC) type.
+ /**
+ * UUID for Automatic Gain Control (AGC) type.
+ */
const String EFFECT_TYPE_UUID_AGC = "0a8abfe0-654c-11e0-ba26-0002a5d5c51b";
- // UUID for Acoustic Echo Canceler (AEC) type.
+ /**
+ * UUID for Acoustic Echo Canceler (AEC) type.
+ */
const String EFFECT_TYPE_UUID_AEC = "7b491460-8d4d-11e0-bd61-0002a5d5c51b";
- // UUID for Noise Suppressor (NS) type.
+ /**
+ * UUID for Noise Suppressor (NS) type.
+ */
const String EFFECT_TYPE_UUID_NS = "58b4b260-8e06-11e0-aa8e-0002a5d5c51b";
- // UUID for Loudness Enhancer type.
+ /**
+ * UUID for Loudness Enhancer type.
+ */
const String EFFECT_TYPE_UUID_LOUDNESS_ENHANCER = "fe3199be-aed0-413f-87bb-11260eb63cf1";
- // UUID for Dynamics Processing type.
+ /**
+ * UUID for Dynamics Processing type.
+ */
const String EFFECT_TYPE_UUID_DYNAMICS_PROCESSING = "7261676f-6d75-7369-6364-28e2fd3ac39e";
- // UUID for Haptic Generator type.
+ /**
+ * UUID for Haptic Generator type.
+ */
const String EFFECT_TYPE_UUID_HAPTIC_GENERATOR = "1411e6d6-aecd-4021-a1cf-a6aceb0d71e5";
- // UUID for Spatializer type.
+ /**
+ * UUID for Spatializer type.
+ */
const String EFFECT_TYPE_UUID_SPATIALIZER = "ccd4cf09-a79d-46c2-9aae-06a1698d6c8f";
- // UUID for Volume type. The volume effect is used for automated tests only.
+ /**
+ * UUID for Volume type. The volume effect is used for automated tests only.
+ */
const String EFFECT_TYPE_UUID_VOLUME = "09e8ede0-ddde-11db-b4f6-0002a5d5c51b";
/**
@@ -68,15 +102,48 @@
* UUID for this particular implementation.
*/
AudioUuid uuid;
+ /**
+ * Optional proxy UUID. This field must be set to the proxy effect type UUID if the effect
+ * implementation is part of a proxy effect.
+ */
+ @nullable AudioUuid proxy;
+ /**
+ * Capability flags defined for the effect implementation.
+ */
+ Flags flags;
}
- // Common attributes of all effect implementation.
+ /**
+ * Common attributes of all effect implementation.
+ */
@VintfStability
parcelable Common {
/**
* Identity of effect implementation.
*/
Identity id;
+ /**
+ * CPU load indication expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE)
+ * with 0 WS.
+ */
+ int cpuLoad;
+ /**
+ * Data memory usage expressed in KB and includes only dynamically allocated memory.
+ */
+ int memoryUsage;
+ /**
+ * Human readable effect name, no intended to display on UI directly.
+ */
+ @utf8InCpp String name;
+ /**
+ * Human readable effect implementor name, no intended to display on UI directly.
+ */
+ @utf8InCpp String implementor;
}
Common common;
+
+ /**
+ * Effect implementation capability.
+ */
+ Capability capability;
}
diff --git a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
new file mode 100644
index 0000000..7fe9bb2
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl
@@ -0,0 +1,99 @@
+/*
+ * 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.effect;
+
+import android.media.audio.common.AudioProfile;
+
+/**
+ * Equalizer specific definitions.
+ *
+ * All parameters defined in union Equalizer must be gettable and settable. The capabilities defined
+ * in Equalizer.Capability can only acquired with IEffect.getDescriptor() and not settable.
+ */
+@VintfStability
+union Equalizer {
+ /**
+ * Vendor Equalizer implementation definition for additional parameters.
+ */
+ @VintfStability
+ parcelable VendorExtension {
+ ParcelableHolder extension;
+ }
+ VendorExtension vendor;
+
+ /**
+ * Capability MUST be supported by Equalizer implementation.
+ */
+ @VintfStability
+ parcelable Capability {
+ /**
+ * Equalizer capability extension, vendor can use this extension in case existing capability
+ * definition not enough.
+ */
+ ParcelableHolder extension;
+
+ /**
+ * Bands frequency ranges supported.
+ */
+ BandFrequency[] bandFrequencies;
+
+ /**
+ * Presets name and index.
+ */
+ Preset[] presets;
+ }
+
+ /**
+ * Level setting for each band.
+ */
+ @VintfStability
+ parcelable BandLevel {
+ int index;
+ int level;
+ }
+
+ /**
+ * Supported minimal and maximal frequency for each band.
+ */
+ @VintfStability
+ parcelable BandFrequency {
+ int index;
+ int min;
+ int max;
+ }
+
+ /**
+ * Factory presets supported.
+ */
+ @VintfStability
+ parcelable Preset {
+ int index;
+ /**
+ * Preset name, used to identify presets but no intended to display on UI directly.
+ */
+ @utf8InCpp String name;
+ }
+
+ /**
+ * Level for each band.
+ */
+ BandLevel[] bandLevels;
+ /**
+ * Index of current preset.
+ */
+ int preset;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Flags.aidl b/audio/aidl/android/hardware/audio/effect/Flags.aidl
new file mode 100644
index 0000000..f449c2d
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Flags.aidl
@@ -0,0 +1,147 @@
+/*
+ * 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.effect;
+
+/**
+ * Some common capability for an effect instance.
+ */
+@VintfStability
+parcelable Flags {
+ /**
+ * Type of connection.
+ */
+ @VintfStability
+ @Backing(type="byte")
+ enum Type {
+ /**
+ * After track process.
+ */
+ INSERT = 0,
+ /**
+ * Connect to track auxiliary output and use send level.
+ */
+ AUXILIARY = 1,
+ /**
+ * Rreplaces track process function; must implement SRC, volume and mono to stereo.
+ */
+ REPLACE = 2,
+ /**
+ * Applied below audio HAL on in.
+ */
+ PRE_PROC = 3,
+ /**
+ * Applied below audio HAL on out.
+ */
+ POST_PROC = 4,
+ }
+ Type type = Type.INSERT;
+
+ /**
+ * Insertion preference.
+ */
+ @VintfStability
+ @Backing(type="byte")
+ enum Insert {
+ ANY = 0,
+ /**
+ * First of the chain.
+ */
+ FIRST = 1,
+ /**
+ * Last of the chain.
+ */
+ LAST = 2,
+ /**
+ * Exclusive (only effect in the insert chain.
+ */
+ EXCLUSIVE = 3,
+ }
+ Insert insert = Insert.ANY;
+
+ @VintfStability
+ @Backing(type="byte")
+ enum Volume {
+ NONE = 0,
+ /**
+ * Implements volume control.
+ */
+ CTRL = 1,
+ /**
+ * Requires volume indication.
+ */
+ IND = 2,
+ /**
+ * Monitors requested volume.
+ */
+ MONITOR = 3,
+ }
+ Volume volume = Volume.NONE;
+
+ @VintfStability
+ @Backing(type="byte")
+ enum HardwareAccelerator {
+ /**
+ * No hardware acceleration
+ */
+ NONE = 0,
+ /**
+ * Non tunneled hw acceleration: effect reads the samples, send them to HW accelerated
+ * effect processor, reads back the processed samples and returns them to the output buffer.
+ */
+ SIMPLE = 1,
+ /**
+ * The effect interface is only used to control the effect engine. This mode is relevant for
+ * global effects actually applied by the audio hardware on the output stream.
+ */
+ TUNNEL = 2,
+ }
+ HardwareAccelerator hwAcceleratorMode = HardwareAccelerator.NONE;
+
+ /**
+ * Effect instance set this flag to true if it requires update on if the playback thread the
+ * effect attached to is offloaded or not. In this case the framework must call
+ * IEffect.setParameter(Parameter.offload) to notify effect instance when playback thread
+ * offload changes.
+ */
+ boolean offloadIndication;
+
+ /**
+ * Effect instance set this flag to true if it requires device change update. In this case the
+ * framework must call IEffect.setParameter(Parameter.device) to notify effect instance when the
+ * device changes.
+ */
+ boolean deviceIndication;
+
+ /**
+ * Effect instance set this flag to true if it requires audio mode change update. In this case
+ * the framework must call IEffect.setParameter(Parameter.mode) to notify effect instance when
+ * the audio mode changes.
+ */
+ boolean audioModeIndication;
+
+ /**
+ * Effect instance set this flag to true if it requires audio source change update. In this case
+ * the framework must call IEffect.setParameter(Parameter.source) to notify effect instance when
+ * the audio source changes.
+ */
+ boolean audioSourceIndication;
+
+ /**
+ * Set to true if no processing done for this effect instance.
+ */
+ boolean noProcessing;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/android/hardware/audio/effect/IEffect.aidl
index d7a9501..5dd390f 100644
--- a/audio/aidl/android/hardware/audio/effect/IEffect.aidl
+++ b/audio/aidl/android/hardware/audio/effect/IEffect.aidl
@@ -16,24 +16,76 @@
package android.hardware.audio.effect;
+import android.hardware.audio.effect.CommandId;
import android.hardware.audio.effect.Descriptor;
+import android.hardware.audio.effect.Parameter;
+import android.hardware.audio.effect.State;
+import android.hardware.common.fmq.MQDescriptor;
+import android.hardware.common.fmq.SynchronizedReadWrite;
/**
* Effect interfaces definitions to configure and control the effect instance.
*/
@VintfStability
interface IEffect {
+ @VintfStability
+ @FixedSize
+ parcelable Status {
+ /**
+ * One of Binder STATUS_* statuses:
+ * - STATUS_OK: the command has completed successfully;
+ * - STATUS_BAD_VALUE: invalid value in the 'Command' structure;
+ * - STATUS_INVALID_OPERATION: the mix port is not connected
+ * to any producer or consumer, thus
+ * positions can not be reported;
+ * - STATUS_NOT_ENOUGH_DATA: a read or write error has
+ * occurred for the 'audio.fmq' queue;
+ *
+ */
+ int status;
+ /**
+ * The amount of bytes consumed by the effect instance.
+ */
+ int fmqByteConsumed;
+ /**
+ * The amount of bytes produced by the effect instance.
+ */
+ int fmqByteProduced;
+ }
+
/**
- * Open an effect instance, effect should not start processing data before receive START
- * command. All necessary information should be allocated and instance should transfer to IDLE
- * state after open() call has been handled successfully.
- * After open, the effect instance should be able to handle all IEffect interface calls.
+ * Return data structure of IEffect.open() interface.
+ */
+ @VintfStability
+ parcelable OpenEffectReturn {
+ /**
+ * Message queue for effect processing status.
+ */
+ MQDescriptor<Status, SynchronizedReadWrite> statusMQ;
+ /**
+ * Message queue for input data buffer.
+ */
+ MQDescriptor<byte, SynchronizedReadWrite> inputDataMQ;
+ /**
+ * Message queue for output data buffer.
+ */
+ MQDescriptor<byte, SynchronizedReadWrite> outputDataMQ;
+ }
+
+ /**
+ * Open an effect instance, effect must not start processing data before receive
+ * CommandId::START command. All necessary information should be allocated and instance must
+ * transfer to State::IDLE state after open() call has been handled successfully. After open,
+ * the effect instance must be able to handle all IEffect interface calls.
*
+ * @param common Parameters which MUST pass from client at open time.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported command.
* @throws a EX_UNSUPPORTED_OPERATION if device capability/resource is not enough or system
* failure happens.
* @note Open an already-opened effect instance should do nothing and should not throw an error.
*/
- void open();
+ OpenEffectReturn open(in Parameter.Common common, in Parameter.Specific specific);
/**
* Called by the client to close the effect instance, processing thread should be destroyed and
@@ -45,7 +97,7 @@
*
* Effect instance close interface should always succeed unless:
* 1. The effect instance is not in a proper state to be closed, for example it's still in
- * processing state.
+ * State::PROCESSING state.
* 2. There is system/hardware related failure when close.
*
* @throws EX_ILLEGAL_STATE if the effect instance is not in a proper state to be closed.
@@ -62,4 +114,53 @@
* @return Descriptor The @c Descriptor of this effect instance.
*/
Descriptor getDescriptor();
+
+ /**
+ * Send a command (defined in enum CommandId) to the effect instance, instance state can be
+ * changed as result of command handling.
+ *
+ * Must be available for the effect instance after it has been open().
+ *
+ * @param commandId ID of the command send to the effect instance.
+ *
+ * @throws EX_ILLEGAL_STATE if the effect instance is not in a proper state to handle the
+ * command.
+ * @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported command.
+ */
+ void command(in CommandId commandId);
+
+ /**
+ * Get current state of the effect instance.
+ *
+ * Must be available for the effect instance at anytime and should always succeed.
+ *
+ * @return Current effect instance state.
+ */
+ State getState();
+
+ /**
+ * Set a parameter to the effect instance.
+ *
+ * Must be available for the effect instance after open().
+ *
+ * @param param Parameter data to set to the effect instance.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported parameter.
+ */
+ void setParameter(in Parameter param);
+
+ /**
+ * Get a parameter from the effect instance with parameter ID.
+ *
+ * This interface must return the current parameter of the effect instance, if no parameter
+ * has been set by client yet, the default value must be returned.
+ *
+ * Must be available for the effect instance after open().
+ *
+ * @param paramId The tag enum of parameter to get.
+ * @return Parameter The parameter to get from the effect instance.
+ *
+ * @throws EX_ILLEGAL_ARGUMENT if the effect instance receive unsupported parameter tag.
+ */
+ Parameter getParameter(in Parameter.Id paramId);
}
diff --git a/audio/aidl/android/hardware/audio/effect/IFactory.aidl b/audio/aidl/android/hardware/audio/effect/IFactory.aidl
index 4873d3a..e56c24f 100644
--- a/audio/aidl/android/hardware/audio/effect/IFactory.aidl
+++ b/audio/aidl/android/hardware/audio/effect/IFactory.aidl
@@ -18,6 +18,7 @@
import android.hardware.audio.effect.Descriptor;
import android.hardware.audio.effect.IEffect;
+import android.hardware.audio.effect.Processing;
import android.media.audio.common.AudioUuid;
/**
@@ -45,6 +46,16 @@
in @nullable AudioUuid type, in @nullable AudioUuid implementation);
/**
+ * Return a list of defined processings, with the optional filter by Processing type.
+ * An effect can exist more than once in the returned list, which means this effect must be used
+ * in more than one processing type.
+ *
+ * @param type Type of processing to query, can be AudioStreamType, AudioSource, or null.
+ * @return list of processing defined with the optional filter by Processing.Type.
+ */
+ Processing[] queryProcessing(in @nullable Processing.Type type);
+
+ /**
* Called by the audio framework to create the effect (identified by the implementation UUID
* parameter).
*
diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
new file mode 100644
index 0000000..739c9ff
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
@@ -0,0 +1,146 @@
+/*
+ * 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.effect;
+
+import android.hardware.audio.effect.Equalizer;
+import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioDeviceType;
+import android.media.audio.common.AudioMode;
+import android.media.audio.common.AudioSource;
+
+/**
+ * Defines all parameters supported by the effect instance.
+ *
+ * There are three groups of parameters:
+ * 1. Common parameters are essential parameters, MUST pass to effects at open() interface.
+ * 2. Parameters defined for a specific effect type.
+ * 3. Extension parameters for vendor.
+ *
+ * For all supported parameter, implementation MUST support both set and get.
+ */
+@VintfStability
+union Parameter {
+ /**
+ * Client can pass in Parameter.Id with the corresponding tag value in IEffect.getParameter()
+ * call to get android.hardware.audio.effect.Parameter.
+ *
+ * As an example, if a client want to get audio.hardware.audio.effect.Specific.Equalizer, the
+ * value of Id should be audio.hardware.audio.effect.Parameter.Specific.equalizer.
+ */
+ @VintfStability
+ union Id {
+ /**
+ * Common parameter tag.
+ */
+ int commonTag;
+ /**
+ * Vendor defined parameter tag.
+ */
+ int vendorTag;
+ /**
+ * Specific effect parameter tag.
+ */
+ Specific.Id specificId;
+ }
+
+ /**
+ * Common parameters MUST be supported by all effect implementations.
+ */
+ @VintfStability
+ parcelable Common {
+ /**
+ * Type of Audio device.
+ */
+ int session;
+ /**
+ * I/O Handle.
+ */
+ int ioHandle;
+ /**
+ * Input config.
+ */
+ AudioConfig input;
+ /**
+ * Output config.
+ */
+ AudioConfig output;
+ }
+ Common common;
+
+ /**
+ * Used by audio framework to set the device type to effect engine.
+ * Effect must implement setParameter(device) if Flags.deviceIndication set to true.
+ */
+ AudioDeviceType device;
+ /**
+ * Used by audio framework to set the audio mode to effect engine.
+ * Effect must implement setParameter(mode) if Flags.audioModeIndication set to true.
+ */
+ AudioMode mode;
+ /**
+ * Used by audio framework to set the audio source to effect engine.
+ * Effect must implement setParameter(source) if Flags.audioSourceIndication set to true.
+ */
+ AudioSource source;
+
+ /**
+ * The volume gain for left and right channel, left and right equals to same value if it's mono.
+ */
+ @VintfStability
+ parcelable Volume {
+ float left;
+ float right;
+ }
+ /**
+ * Used by audio framework to delegate volume control to effect engine.
+ * Effect must implement setParameter(volume) if Flags.volume set to Volume.IND.
+ */
+ Volume volume;
+
+ /**
+ * Used by audio framework to delegate offload information to effect engine.
+ * Effect must implement setParameter(offload) if Flags.offloadSupported set to true.
+ */
+ boolean offload;
+
+ /**
+ * Parameters for vendor extension effect implementation usage.
+ */
+ @VintfStability
+ parcelable VendorEffectParameter {
+ ParcelableHolder extension;
+ }
+ VendorEffectParameter vendorEffect;
+
+ /**
+ * Parameters MUST be supported by a Specific type of effect.
+ */
+ @VintfStability
+ union Specific {
+ @VintfStability
+ union Id {
+ /**
+ * Equalizer.Tag to identify the parameters in Equalizer.
+ */
+ Equalizer.Tag equalizerTag = Equalizer.Tag.vendor;
+ }
+ Id id;
+
+ Equalizer equalizer;
+ }
+ Specific specific;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/Processing.aidl b/audio/aidl/android/hardware/audio/effect/Processing.aidl
new file mode 100644
index 0000000..ef32e8c
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/Processing.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.effect;
+
+import android.hardware.audio.effect.Descriptor;
+import android.media.audio.common.AudioSource;
+import android.media.audio.common.AudioStreamType;
+import android.media.audio.common.AudioUuid;
+
+/**
+ * List of effects which must be used for certain pre-processing or post-processing.
+ */
+@VintfStability
+parcelable Processing {
+ @VintfStability
+ union Type {
+ AudioStreamType streamType = AudioStreamType.INVALID;
+ AudioSource source;
+ }
+
+ /**
+ * Specifies the type of processing by referring to the output stream type (AudioStreamType) or
+ * the input stream source (AudioSource).
+ */
+ Type type;
+ /**
+ * List of effect identities for this processing.
+ */
+ Descriptor.Identity[] ids;
+}
diff --git a/audio/aidl/android/hardware/audio/effect/State.aidl b/audio/aidl/android/hardware/audio/effect/State.aidl
new file mode 100644
index 0000000..85a4afc
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/State.aidl
@@ -0,0 +1,86 @@
+/*
+ * 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.effect;
+
+/**
+ * Possible states of an effect instance.
+ * A typical effect instance will be in INIT state when it is created with IFactory.createEffect()
+ * interface, transfer to IDLE after open(), and to PROCESSING after
+ * IEffect.command(Command.Id.START) command. When an effect instance receive STOP or RESET command,
+ * it should transfer to IDLE state after handle the command successfully. Effect instance should
+ * consume minimal resource and transfer to INIT state after it was close().
+ *
+ * Refer to State.gv for detailed state diagram.
+ */
+@VintfStability
+@Backing(type="byte")
+enum State {
+
+ /**
+ * An effect instance is in INIT state by default after it was created with
+ * IFactory.createEffect(). When an effect instance is in INIT state, it should have instance
+ * context initialized, and ready to handle IEffect.setParameter(), IEffect.open() as well as
+ * all getter interfaces.
+ *
+ * In INIT state, effect instance must:
+ * 1. Not handle any IEffect.command() and return EX_ILLEGAL_STATE with any Command.Id.
+ * 2. Be able to handle all parameter setting with IEffect.setParameter().
+ * 3. Be able to handle all getter interface calls like IEffect.getParameter() and
+ * IEffect.getState().
+ * 4. Be able to handle IEffect.open() successfully after configuration.
+ *
+ * Client is expected to do necessary configuration with IEffect.setParameter(), get all
+ * resource ready with IEffect.open(), and make sure effect instance transfer to IDLE state
+ * before sending commands with IEffect.command() interface. Effect instance must transfer
+ * from INIT to IDLE state after handle IEffect.open() call successfully.
+ */
+ INIT,
+ /**
+ * An effect instance transfer to IDLE state after it was open successfully with IEffect.open()
+ * in INIT state, or after it was stop/reset with Command.Id.STOP/RESET in PROCESSING state.
+ *
+ * In IDLE state, effect instance must:
+ * 1. Be able to start effect processing engine with IEffect.command(Command.Id.START) call.
+ * 2. Be able to handle all parameter setting with IEffect.setParameter().
+ * 3. Be able to handle all getter interface calls like IEffect.getParameter() and
+ * IEffect.getState().
+ *
+ * The following state transfer can happen in IDLE state:
+ * 1. Transfer to PROCESSING if instance receive an START command and start processing data
+ * successfully.
+ * 2. Transfer to INIT if instance receive a close() call.
+ */
+ IDLE,
+ /**
+ * An effect instance is in PROCESSING state after it receive an START command and start
+ * processing data successfully. Effect instance will transfer from PROCESSING to IDLE state if
+ * it receive an STOP or RESET command and handle the command successfully.
+ *
+ * When an instance is in PROCESSING state, client should try not to close() it directly,
+ * instead client should try to stop processing data first with STOP command before close(). In
+ * the case of a close() call received when instance in PROCESSING state, it should try to stop
+ * processing and transfer to IDLE first before close().
+ *
+ * In PROCESSING state, effect instance must:
+ * 1. Return EX_ILLEGAL_STATE if it's not able to handle any parameter settings at runtime.
+ * 2. Be able to handle STOP and RESET for IEffect.command() interface, and return
+ * EX_ILLEGAL_STATE for all other commands.
+ * 3. Must be able to handle all get* interface calls like IEffect.getParameter() and
+ * IEffect.getState().
+ */
+ PROCESSING,
+}
diff --git a/audio/aidl/android/hardware/audio/effect/state.gv b/audio/aidl/android/hardware/audio/effect/state.gv
new file mode 100644
index 0000000..e19e6c7
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/effect/state.gv
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+// To render: "dot -Tpng state.gv -o state.png"
+digraph effect_state_machine {
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle] F;
+ node [shape=oval width=1];
+ node [fillcolor=lightgreen] INIT;
+ node [fillcolor=lightblue] IDLE;
+ node [fillcolor=lightyellow] PROCESSING;
+
+ I -> INIT [label="IFactory.createEffect" labelfontcolor="navy"];
+ INIT -> F [label="IFactory.destroyEffect"];
+ INIT -> IDLE [label="open()" labelfontcolor="lime"];
+ IDLE -> PROCESSING [label="command(START"];
+ PROCESSING -> IDLE [label="command(STOP)\ncommand(RESET)"];
+ IDLE -> INIT [label="close()"];
+
+ INIT -> INIT [label="getState\ngetDescriptor"];
+ IDLE -> IDLE [label="getXXX\nsetParameter\ncommand(RESET)"];
+ PROCESSING -> PROCESSING [label="getXXX\nsetParameter"];
+}
\ No newline at end of file
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 53ed908..e64b90c 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -21,6 +21,9 @@
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
],
+ header_libs: [
+ "libaudioaidl_headers",
+ ],
}
cc_library_static {
@@ -81,18 +84,23 @@
cc_library_static {
name: "libaudioeffectserviceexampleimpl",
defaults: ["aidlaudioeffectservice_defaults"],
- export_include_dirs: [
- "include",
- "include/equalizer-impl/",
- ],
+ export_include_dirs: ["include"],
srcs: [
"EffectFactory.cpp",
],
+ header_libs: [
+ "libsystem_headers",
+ ],
visibility: [
":__subpackages__",
],
}
+filegroup {
+ name: "effectCommonFile",
+ srcs: ["EffectThread.cpp"],
+}
+
cc_binary {
name: "android.hardware.audio.effect.service-aidl.example",
relative_install_path: "hw",
@@ -104,3 +112,10 @@
],
srcs: ["EffectMain.cpp"],
}
+
+cc_library_headers {
+ name: "libaudioaidl_headers",
+ export_include_dirs: ["include"],
+ vendor_available: true,
+ host_supported: true,
+}
diff --git a/audio/aidl/default/Config.cpp b/audio/aidl/default/Config.cpp
index 3f7a3d3..0fdd5b4 100644
--- a/audio/aidl/default/Config.cpp
+++ b/audio/aidl/default/Config.cpp
@@ -13,7 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "AHAL_Module"
+#include <android-base/logging.h>
#include "core-impl/Config.h"
-namespace aidl::android::hardware::audio::core {} // namespace aidl::android::hardware::audio::core
+namespace aidl::android::hardware::audio::core {
+ndk::ScopedAStatus Config::getSurroundSoundConfig(SurroundSoundConfig* _aidl_return) {
+ SurroundSoundConfig surroundSoundConfig;
+ // TODO: parse from XML; for now, use empty config as default
+ *_aidl_return = std::move(surroundSoundConfig);
+ LOG(DEBUG) << __func__ << ": returning " << _aidl_return->toString();
+ return ndk::ScopedAStatus::ok();
+}
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index a9848fd..8e107a2 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -16,57 +16,167 @@
#define LOG_TAG "AHAL_EffectFactory"
#include <android-base/logging.h>
+#include <dlfcn.h>
+#include "effect-impl/EffectUUID.h"
#include "effectFactory-impl/EffectFactory.h"
-#include "equalizer-impl/Equalizer.h"
-#include "visualizer-impl/Visualizer.h"
using aidl::android::media::audio::common::AudioUuid;
namespace aidl::android::hardware::audio::effect {
Factory::Factory() {
- // TODO: implement this with xml parser on audio_effect.xml, and filter with optional
- // parameters.
+ std::function<void(void*)> dlClose = [](void* handle) -> void {
+ if (handle && dlclose(handle)) {
+ LOG(ERROR) << "dlclose failed " << dlerror();
+ }
+ };
+ // TODO: implement this with audio_effect.xml.
+ auto libHandle =
+ std::unique_ptr<void, decltype(dlClose)>{dlopen("libequalizer.so", RTLD_LAZY), dlClose};
+ if (!libHandle) {
+ LOG(ERROR) << __func__ << ": dlopen failed, err: " << dlerror();
+ return;
+ }
+
+ LOG(DEBUG) << __func__ << " dlopen uuid: " << EqualizerSwImplUUID.toString() << " handle "
+ << libHandle;
+ mEffectLibMap.insert({EqualizerSwImplUUID, std::make_pair(std::move(libHandle), nullptr)});
+
Descriptor::Identity id;
id.type = EqualizerTypeUUID;
id.uuid = EqualizerSwImplUUID;
mIdentityList.push_back(id);
}
-ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type,
- const std::optional<AudioUuid>& in_instance,
+Factory::~Factory() {
+ if (auto count = mEffectUuidMap.size()) {
+ LOG(ERROR) << __func__ << " remaining " << count
+ << " effect instances not destroyed indicating resource leak!";
+ for (const auto& it : mEffectUuidMap) {
+ if (auto spEffect = it.first.lock()) {
+ LOG(ERROR) << __func__ << " erase remaining instance UUID " << it.second.toString();
+ destroyEffectImpl(spEffect);
+ }
+ }
+ }
+}
+
+ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type_uuid,
+ const std::optional<AudioUuid>& in_impl_uuid,
std::vector<Descriptor::Identity>* _aidl_return) {
std::copy_if(mIdentityList.begin(), mIdentityList.end(), std::back_inserter(*_aidl_return),
[&](auto& desc) {
- return (!in_type.has_value() || in_type.value() == desc.type) &&
- (!in_instance.has_value() || in_instance.value() == desc.uuid);
+ return (!in_type_uuid.has_value() || in_type_uuid.value() == desc.type) &&
+ (!in_impl_uuid.has_value() || in_impl_uuid.value() == desc.uuid);
});
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Factory::createEffect(
- const AudioUuid& in_impl_uuid,
- std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return) {
+ndk::ScopedAStatus Factory::queryProcessing(const std::optional<Processing::Type>& in_type,
+ std::vector<Processing>* _aidl_return) {
+ // TODO: implement this with audio_effect.xml.
+ if (in_type.has_value()) {
+ // return all matching process filter
+ LOG(DEBUG) << __func__ << " process type: " << in_type.value().toString();
+ }
+ LOG(DEBUG) << __func__ << " return " << _aidl_return->size();
+ return ndk::ScopedAStatus::ok();
+}
+
+#define RETURN_IF_BINDER_EXCEPTION(functor) \
+ { \
+ binder_exception_t exception = functor; \
+ if (EX_NONE != exception) { \
+ LOG(ERROR) << #functor << ": failed with error " << exception; \
+ return ndk::ScopedAStatus::fromExceptionCode(exception); \
+ } \
+ }
+
+ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
+ std::shared_ptr<IEffect>* _aidl_return) {
LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
if (in_impl_uuid == EqualizerSwImplUUID) {
- *_aidl_return = ndk::SharedRefBase::make<Equalizer>();
+ if (mEffectLibMap.count(in_impl_uuid)) {
+ auto& lib = mEffectLibMap[in_impl_uuid];
+ // didn't do dlsym yet
+ if (nullptr == lib.second) {
+ void* libHandle = lib.first.get();
+ struct effect_interface_s intf = {
+ .createEffectFunc = (EffectCreateFunctor)dlsym(libHandle, "createEffect"),
+ .destroyEffectFunc =
+ (EffectDestroyFunctor)dlsym(libHandle, "destroyEffect")};
+ auto dlInterface = std::make_unique<struct effect_interface_s>(intf);
+ if (!dlInterface->createEffectFunc || !dlInterface->destroyEffectFunc) {
+ LOG(ERROR) << __func__
+ << ": create or destroy symbol not exist in library: " << libHandle
+ << "!";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
+ }
+ lib.second = std::move(dlInterface);
+ }
+
+ auto& libInterface = lib.second;
+ std::shared_ptr<IEffect> effectSp;
+ RETURN_IF_BINDER_EXCEPTION(libInterface->createEffectFunc(&effectSp));
+ if (!effectSp) {
+ LOG(ERROR) << __func__ << ": library created null instance without return error!";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
+ }
+ *_aidl_return = effectSp;
+ mEffectUuidMap[std::weak_ptr<IEffect>(effectSp)] = in_impl_uuid;
+ LOG(DEBUG) << __func__ << ": instance " << effectSp.get() << " created successfully";
+ return ndk::ScopedAStatus::ok();
+ } else {
+ LOG(ERROR) << __func__ << ": library doesn't exist";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
} else {
- LOG(ERROR) << __func__ << ": UUID "
- << " not supported";
+ LOG(ERROR) << __func__ << ": UUID not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Factory::destroyEffect(
- const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle) {
- if (in_handle) {
- // TODO: b/245393900 need check the instance state with IEffect.getState before destroy.
+ndk::ScopedAStatus Factory::destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle) {
+ std::weak_ptr<IEffect> wpHandle(in_handle);
+ // find UUID with key (std::weak_ptr<IEffect>)
+ if (auto uuidIt = mEffectUuidMap.find(wpHandle); uuidIt != mEffectUuidMap.end()) {
+ auto& uuid = uuidIt->second;
+ // find implementation library with UUID
+ if (auto libIt = mEffectLibMap.find(uuid); libIt != mEffectLibMap.end()) {
+ if (libIt->second.second->destroyEffectFunc) {
+ RETURN_IF_BINDER_EXCEPTION(libIt->second.second->destroyEffectFunc(in_handle));
+ }
+ } else {
+ LOG(ERROR) << __func__ << ": UUID " << uuid.toString() << " does not exist in libMap!";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ mEffectUuidMap.erase(uuidIt);
return ndk::ScopedAStatus::ok();
} else {
+ LOG(ERROR) << __func__ << ": instance " << in_handle << " does not exist!";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
+// go over the map and cleanup all expired weak_ptrs.
+void Factory::cleanupEffectMap() {
+ for (auto it = mEffectUuidMap.begin(); it != mEffectUuidMap.end();) {
+ if (nullptr == it->first.lock()) {
+ it = mEffectUuidMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+ndk::ScopedAStatus Factory::destroyEffect(const std::shared_ptr<IEffect>& in_handle) {
+ LOG(DEBUG) << __func__ << ": instance " << in_handle.get();
+ ndk::ScopedAStatus status = destroyEffectImpl(in_handle);
+ // always do the cleanup
+ cleanupEffectMap();
+ return status;
+}
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/EffectThread.cpp b/audio/aidl/default/EffectThread.cpp
new file mode 100644
index 0000000..80f120b
--- /dev/null
+++ b/audio/aidl/default/EffectThread.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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_EffectThread"
+#include <android-base/logging.h>
+#include <pthread.h>
+#include <sys/resource.h>
+
+#include "effect-impl/EffectThread.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+EffectThread::EffectThread() {
+ LOG(DEBUG) << __func__;
+}
+
+EffectThread::~EffectThread() {
+ destroyThread();
+ LOG(DEBUG) << __func__ << " done";
+};
+
+RetCode EffectThread::createThread(const std::string& name, const int priority) {
+ if (mThread.joinable()) {
+ LOG(WARNING) << __func__ << " thread already created, no-op";
+ return RetCode::SUCCESS;
+ }
+ mName = name;
+ mPriority = priority;
+ mThread = std::thread(&EffectThread::threadLoop, this);
+ LOG(DEBUG) << __func__ << " " << name << " priority " << mPriority << " done";
+ return RetCode::SUCCESS;
+}
+
+RetCode EffectThread::destroyThread() {
+ {
+ std::lock_guard lg(mMutex);
+ mStop = mExit = true;
+ }
+ mCv.notify_one();
+
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+ LOG(DEBUG) << __func__ << " done";
+ return RetCode::SUCCESS;
+}
+
+RetCode EffectThread::startThread() {
+ if (!mThread.joinable()) {
+ LOG(ERROR) << __func__ << " thread already destroyed";
+ return RetCode::ERROR_THREAD;
+ }
+
+ {
+ std::lock_guard lg(mMutex);
+ if (!mStop) {
+ LOG(WARNING) << __func__ << " already start";
+ return RetCode::SUCCESS;
+ }
+ mStop = false;
+ }
+
+ mCv.notify_one();
+ LOG(DEBUG) << __func__ << " done";
+ return RetCode::SUCCESS;
+}
+
+RetCode EffectThread::stopThread() {
+ if (!mThread.joinable()) {
+ LOG(ERROR) << __func__ << " thread already destroyed";
+ return RetCode::ERROR_THREAD;
+ }
+
+ {
+ std::lock_guard lg(mMutex);
+ if (mStop) {
+ LOG(WARNING) << __func__ << " already stop";
+ return RetCode::SUCCESS;
+ }
+ mStop = true;
+ }
+ LOG(DEBUG) << __func__ << " done";
+ return RetCode::SUCCESS;
+}
+
+void EffectThread::threadLoop() {
+ pthread_setname_np(pthread_self(), mName.substr(0, MAX_TASK_COMM_LEN - 1).c_str());
+ setpriority(PRIO_PROCESS, 0, mPriority);
+ while (true) {
+ bool needExit = false;
+ {
+ std::unique_lock l(mMutex);
+ mCv.wait(l, [&]() REQUIRES(mMutex) {
+ needExit = mExit;
+ return mExit || !mStop;
+ });
+ }
+ if (needExit) {
+ LOG(WARNING) << __func__ << " EXIT!";
+ return;
+ }
+ // process without lock
+ process();
+ }
+}
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/equalizer/Android.bp b/audio/aidl/default/equalizer/Android.bp
index b842149..2a2ddbc 100644
--- a/audio/aidl/default/equalizer/Android.bp
+++ b/audio/aidl/default/equalizer/Android.bp
@@ -27,17 +27,24 @@
name: "libequalizer",
vendor: true,
shared_libs: [
+ "libaudioaidlcommon",
"libbase",
- "libbinder_ndk",
- "libstagefright_foundation",
+ "android.hardware.common-V2-ndk",
],
defaults: [
+ "aidlaudioservice_defaults",
"latest_android_media_audio_common_types_ndk_shared",
"latest_android_hardware_audio_effect_ndk_shared",
],
- include_dirs: ["hardware/interfaces/audio/aidl/default/include/equalizer-impl"],
srcs: [
"Equalizer.cpp",
+ ":effectCommonFile",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
],
visibility: [
"//hardware/interfaces/audio/aidl/default",
diff --git a/audio/aidl/default/equalizer/Equalizer.cpp b/audio/aidl/default/equalizer/Equalizer.cpp
index 8b157fa..43fa206 100644
--- a/audio/aidl/default/equalizer/Equalizer.cpp
+++ b/audio/aidl/default/equalizer/Equalizer.cpp
@@ -14,27 +14,309 @@
* limitations under the License.
*/
-#define LOG_TAG "AHAL_Equalizer"
-#include <android-base/logging.h>
+#define LOG_TAG "AHAL_EqualizerSw"
+#include <Utils.h>
+#include <algorithm>
+#include <unordered_set>
-#include "Equalizer.h"
+#include <android-base/logging.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "equalizer-impl/EqualizerSw.h"
+
+using android::hardware::audio::common::getFrameSizeInBytes;
namespace aidl::android::hardware::audio::effect {
-ndk::ScopedAStatus Equalizer::open() {
+extern "C" binder_exception_t createEffect(std::shared_ptr<IEffect>* instanceSpp) {
+ if (instanceSpp) {
+ *instanceSpp = ndk::SharedRefBase::make<EqualizerSw>();
+ LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
+ return EX_NONE;
+ } else {
+ LOG(ERROR) << __func__ << " invalid input parameter!";
+ return EX_ILLEGAL_ARGUMENT;
+ }
+}
+
+extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
+ State state;
+ ndk::ScopedAStatus status = instanceSp->getState(&state);
+ if (!status.isOk() || State::INIT != state) {
+ LOG(ERROR) << __func__ << " instance " << instanceSp.get()
+ << " in state: " << toString(state) << ", status: " << status.getDescription();
+ return EX_ILLEGAL_STATE;
+ }
+ LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
+ return EX_NONE;
+}
+
+ndk::ScopedAStatus EqualizerSw::open(const Parameter::Common& common,
+ const Parameter::Specific& specific,
+ OpenEffectReturn* _aidl_return) {
+ LOG(DEBUG) << __func__;
+ if (mState != State::INIT) {
+ LOG(WARNING) << __func__ << " eq already open";
+ return ndk::ScopedAStatus::ok();
+ }
+
+ // Set essential parameters before create worker thread.
+ setCommonParameter(common);
+ setSpecificParameter(specific);
+
+ LOG(DEBUG) << " common: " << common.toString() << " specific " << specific.toString();
+
+ auto& input = common.input;
+ auto& output = common.output;
+ size_t inputFrameSize = getFrameSizeInBytes(input.base.format, input.base.channelMask);
+ size_t outputFrameSize = getFrameSizeInBytes(output.base.format, output.base.channelMask);
+ mContext = std::make_shared<EqualizerSwContext>(1, input.frameCount * inputFrameSize,
+ output.frameCount * outputFrameSize);
+ if (!mContext) {
+ LOG(ERROR) << __func__ << " created EqualizerSwContext failed";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
+ "FailedToCreateFmq");
+ }
+ setContext(mContext);
+
+ // create the worker thread
+ if (RetCode::SUCCESS != createThread(LOG_TAG)) {
+ LOG(ERROR) << __func__ << " created worker thread failed";
+ mContext.reset();
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
+ "FailedToCreateWorker");
+ }
+
+ _aidl_return->statusMQ = mContext->getStatusFmq()->dupeDesc();
+ _aidl_return->inputDataMQ = mContext->getInputDataFmq()->dupeDesc();
+ _aidl_return->outputDataMQ = mContext->getOutputDataFmq()->dupeDesc();
+ mState = State::IDLE;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EqualizerSw::close() {
+ if (mState == State::INIT) {
+ LOG(WARNING) << __func__ << " instance already closed";
+ return ndk::ScopedAStatus::ok();
+ } else if (mState == State::PROCESSING) {
+ LOG(ERROR) << __func__ << " instance still processing";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
+ "EqInstanceProcessing");
+ }
+
+ // stop the worker thread
+ mState = State::INIT;
+ destroyThread();
+ mContext.reset();
+
LOG(DEBUG) << __func__;
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Equalizer::close() {
- LOG(DEBUG) << __func__;
- return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus Equalizer::getDescriptor(Descriptor* _aidl_return) {
- LOG(DEBUG) << __func__ << "descriptor " << mDesc.toString();
+ndk::ScopedAStatus EqualizerSw::getDescriptor(Descriptor* _aidl_return) {
+ LOG(DEBUG) << __func__ << mDesc.toString();
*_aidl_return = mDesc;
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus EqualizerSw::command(CommandId in_commandId) {
+ LOG(DEBUG) << __func__ << ": receive command:" << toString(in_commandId);
+ if (mState == State::INIT) {
+ LOG(ERROR) << __func__ << ": instance not open yet";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
+ "CommandStateError");
+ }
+ switch (in_commandId) {
+ case CommandId::START:
+ // start processing.
+ mState = State::PROCESSING;
+ startThread();
+ LOG(DEBUG) << __func__ << " state: " << toString(mState);
+ return ndk::ScopedAStatus::ok();
+ case CommandId::STOP:
+ // stop processing.
+ mState = State::IDLE;
+ stopThread();
+ LOG(DEBUG) << __func__ << " state: " << toString(mState);
+ return ndk::ScopedAStatus::ok();
+ case CommandId::RESET:
+ // TODO: reset buffer status.
+ mState = State::IDLE;
+ stopThread();
+ LOG(DEBUG) << __func__ << " state: " << toString(mState);
+ return ndk::ScopedAStatus::ok();
+ default:
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "CommandIdNotSupported");
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EqualizerSw::setParameter(const Parameter& in_param) {
+ if (mState == State::INIT) {
+ LOG(ERROR) << __func__ << ": instance not open yet";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "StateError");
+ }
+ LOG(DEBUG) << __func__ << " with: " << in_param.toString();
+ auto tag = in_param.getTag();
+ switch (tag) {
+ case Parameter::common: {
+ return setCommonParameter(in_param.get<Parameter::common>());
+ }
+ case Parameter::specific: {
+ return setSpecificParameter(in_param.get<Parameter::specific>());
+ }
+ default:
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "ParameterNotSupported");
+ }
+}
+
+ndk::ScopedAStatus EqualizerSw::getParameter(const Parameter::Id& in_paramId,
+ Parameter* _aidl_return) {
+ LOG(DEBUG) << __func__ << in_paramId.toString();
+ auto tag = in_paramId.getTag();
+ switch (tag) {
+ case Parameter::Id::commonTag: {
+ _aidl_return->set<Parameter::common>(mCommonParam);
+ LOG(DEBUG) << __func__ << " get: " << _aidl_return->toString();
+ return ndk::ScopedAStatus::ok();
+ }
+ case Parameter::Id::specificId: {
+ auto& id = in_paramId.get<Parameter::Id::specificId>();
+ Parameter::Specific specific;
+ ndk::ScopedAStatus status = getSpecificParameter(id, &specific);
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__
+ << " getSpecificParameter error: " << status.getDescription();
+ return status;
+ }
+ _aidl_return->set<Parameter::specific>(specific);
+ LOG(DEBUG) << __func__ << _aidl_return->toString();
+ return ndk::ScopedAStatus::ok();
+ }
+ case Parameter::Id::vendorTag: {
+ LOG(DEBUG) << __func__ << " noop for vendor tag now";
+ return ndk::ScopedAStatus::ok();
+ }
+ }
+ LOG(ERROR) << " unsupported tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Parameter:IdNotSupported");
+}
+
+ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) {
+ *_aidl_return = mState;
+ return ndk::ScopedAStatus::ok();
+}
+
+/// Private methods.
+ndk::ScopedAStatus EqualizerSw::setCommonParameter(const Parameter::Common& common) {
+ mCommonParam = common;
+ LOG(DEBUG) << __func__ << " set: " << mCommonParam.toString();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus EqualizerSw::setSpecificParameter(const Parameter::Specific& specific) {
+ if (Parameter::Specific::equalizer != specific.getTag()) {
+ LOG(ERROR) << " unsupported effect: " << specific.toString();
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "EffectNotSupported");
+ }
+
+ auto& eqParam = specific.get<Parameter::Specific::equalizer>();
+ auto tag = eqParam.getTag();
+ switch (tag) {
+ case Equalizer::bandLevels: {
+ auto& bandLevels = eqParam.get<Equalizer::bandLevels>();
+ const auto& [minItem, maxItem] = std::minmax_element(
+ bandLevels.begin(), bandLevels.end(),
+ [](const auto& a, const auto& b) { return a.index < b.index; });
+ if (bandLevels.size() >= NUM_OF_BANDS || minItem->index < 0 ||
+ maxItem->index >= NUM_OF_BANDS) {
+ LOG(ERROR) << " bandLevels " << bandLevels.size() << "minIndex " << minItem->index
+ << "maxIndex " << maxItem->index << " illegal ";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "ExceedMaxBandNum");
+ }
+ mBandLevels = bandLevels;
+ return ndk::ScopedAStatus::ok();
+ }
+ case Equalizer::preset: {
+ int preset = eqParam.get<Equalizer::preset>();
+ if (preset < 0 || preset >= NUM_OF_PRESETS) {
+ LOG(ERROR) << " preset: " << preset << " invalid";
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "ExceedMaxBandNum");
+ }
+ mPreset = preset;
+ LOG(DEBUG) << __func__ << " preset set to " << mPreset;
+ return ndk::ScopedAStatus::ok();
+ }
+ case Equalizer::vendor: {
+ LOG(DEBUG) << __func__ << " noop for vendor tag now";
+ return ndk::ScopedAStatus::ok();
+ }
+ }
+
+ LOG(ERROR) << __func__ << " unsupported eq param tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "ParamNotSupported");
+}
+
+ndk::ScopedAStatus EqualizerSw::getSpecificParameter(Parameter::Specific::Id id,
+ Parameter::Specific* specific) {
+ Equalizer eqParam;
+ auto tag = id.getTag();
+ if (tag != Parameter::Specific::Id::equalizerTag) {
+ LOG(ERROR) << " invalid tag: " << toString(tag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "UnsupportedTag");
+ }
+ auto eqTag = id.get<Parameter::Specific::Id::equalizerTag>();
+ switch (eqTag) {
+ case Equalizer::bandLevels: {
+ eqParam.set<Equalizer::bandLevels>(mBandLevels);
+ specific->set<Parameter::Specific::equalizer>(eqParam);
+ return ndk::ScopedAStatus::ok();
+ }
+ case Equalizer::preset: {
+ eqParam.set<Equalizer::preset>(mPreset);
+ LOG(DEBUG) << __func__ << " preset " << mPreset;
+ specific->set<Parameter::Specific::equalizer>(eqParam);
+ return ndk::ScopedAStatus::ok();
+ }
+ case Equalizer::vendor: {
+ LOG(DEBUG) << __func__ << " noop for vendor tag now";
+ return ndk::ScopedAStatus::ok();
+ }
+ }
+ LOG(ERROR) << __func__ << " unsupported eq param: " << toString(eqTag);
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "ParamNotSupported");
+}
+
+void EqualizerSw::cleanUp() {
+ if (State::PROCESSING == mState) {
+ command(CommandId::STOP);
+ }
+ if (State::INIT != mState) {
+ close();
+ }
+}
+
+IEffect::Status EqualizerSw::status(binder_status_t status, size_t consumed, size_t produced) {
+ IEffect::Status ret;
+ ret.status = status;
+ ret.fmqByteConsumed = consumed;
+ ret.fmqByteProduced = produced;
+ return ret;
+}
+
+// Processing method running in EffectWorker thread.
+IEffect::Status EqualizerSw::effectProcessImpl() {
+ // TODO: get data buffer and process.
+ return status(STATUS_OK, mContext->availableToRead(), mContext->availableToWrite());
+}
+
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/core-impl/Config.h b/audio/aidl/default/include/core-impl/Config.h
index b62a14b..4555efd 100644
--- a/audio/aidl/default/include/core-impl/Config.h
+++ b/audio/aidl/default/include/core-impl/Config.h
@@ -20,6 +20,8 @@
namespace aidl::android::hardware::audio::core {
-class Config : public BnConfig {};
+class Config : public BnConfig {
+ ndk::ScopedAStatus getSurroundSoundConfig(SurroundSoundConfig* _aidl_return) override;
+};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
new file mode 100644
index 0000000..36492ec
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <cstdint>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+
+namespace aidl::android::hardware::audio::effect {
+
+class EffectContext {
+ public:
+ typedef ::android::AidlMessageQueue<
+ IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+ StatusMQ;
+ typedef ::android::AidlMessageQueue<
+ int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+ DataMQ;
+
+ EffectContext(size_t statusDepth, size_t inBufferSize, size_t outBufferSize) {
+ mStatusMQ = std::make_shared<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
+ mInputMQ = std::make_shared<DataMQ>(inBufferSize);
+ mOutputMQ = std::make_shared<DataMQ>(outBufferSize);
+
+ if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
+ LOG(ERROR) << __func__ << " created invalid FMQ";
+ }
+ mWorkBuffer.reserve(std::max(inBufferSize, outBufferSize));
+ };
+
+ std::shared_ptr<StatusMQ> getStatusFmq() { return mStatusMQ; };
+ std::shared_ptr<DataMQ> getInputDataFmq() { return mInputMQ; };
+ std::shared_ptr<DataMQ> getOutputDataFmq() { return mOutputMQ; };
+
+ int8_t* getWorkBuffer() { return static_cast<int8_t*>(mWorkBuffer.data()); };
+ // TODO: update with actual available size
+ size_t availableToRead() { return mWorkBuffer.capacity(); };
+ size_t availableToWrite() { return mWorkBuffer.capacity(); };
+
+ private:
+ std::shared_ptr<StatusMQ> mStatusMQ;
+ std::shared_ptr<DataMQ> mInputMQ;
+ std::shared_ptr<DataMQ> mOutputMQ;
+ // TODO handle effect process input and output
+ // work buffer set by effect instances, the access and update are in same thread
+ std::vector<int8_t> mWorkBuffer;
+};
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectThread.h b/audio/aidl/default/include/effect-impl/EffectThread.h
new file mode 100644
index 0000000..09a0000
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectThread.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <atomic>
+#include <string>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+#include <system/thread_defs.h>
+
+#include "effect-impl/EffectTypes.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class EffectThread {
+ public:
+ // default priority is same as HIDL: ANDROID_PRIORITY_URGENT_AUDIO
+ EffectThread();
+ virtual ~EffectThread();
+
+ // called by effect implementation.
+ RetCode createThread(const std::string& name,
+ const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
+ RetCode destroyThread();
+ RetCode startThread();
+ RetCode stopThread();
+
+ // Will call process() in a loop if the thread is running.
+ void threadLoop();
+
+ // User of EffectThread must implement the effect processing logic in this method.
+ virtual void process() = 0;
+ const int MAX_TASK_COMM_LEN = 15;
+
+ private:
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ bool mExit GUARDED_BY(mMutex) = false;
+ bool mStop GUARDED_BY(mMutex) = true;
+ std::thread mThread;
+ int mPriority;
+ std::string mName;
+};
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h
new file mode 100644
index 0000000..46cfc0c
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectTypes.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 <ostream>
+#include <string>
+
+namespace aidl::android::hardware::audio::effect {
+
+enum class RetCode {
+ SUCCESS,
+ ERROR_ILLEGAL_PARAMETER, /* Illegal parameter */
+ ERROR_THREAD, /* Effect thread error */
+ ERROR_NULL_POINTER, /* NULL pointer */
+ ERROR_ALIGNMENT_ERROR, /* Memory alignment error */
+ ERROR_BLOCK_SIZE_EXCEED /* Maximum block size exceeded */
+};
+
+inline std::ostream& operator<<(std::ostream& out, const RetCode& code) {
+ switch (code) {
+ case RetCode::SUCCESS:
+ return out << "SUCCESS";
+ case RetCode::ERROR_ILLEGAL_PARAMETER:
+ return out << "ERROR_ILLEGAL_PARAMETER";
+ case RetCode::ERROR_THREAD:
+ return out << "ERROR_THREAD";
+ case RetCode::ERROR_NULL_POINTER:
+ return out << "ERROR_NULL_POINTER";
+ case RetCode::ERROR_ALIGNMENT_ERROR:
+ return out << "ERROR_ALIGNMENT_ERROR";
+ case RetCode::ERROR_BLOCK_SIZE_EXCEED:
+ return out << "ERROR_BLOCK_SIZE_EXCEED";
+ }
+
+ return out << "EnumError: " << code;
+}
+
+} // 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
new file mode 100644
index 0000000..48b7137
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectUUID.h
@@ -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.
+ */
+
+#pragma once
+#include <aidl/android/media/audio/common/AudioUuid.h>
+
+namespace aidl::android::hardware::audio::effect {
+
+using ::aidl::android::media::audio::common::AudioUuid;
+
+// Null UUID
+static const AudioUuid EffectNullUuid = {static_cast<int32_t>(0xec7178ec),
+ 0xe5e1,
+ 0x4432,
+ 0xa3f4,
+ {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
+
+// Zero UUID
+static const AudioUuid EffectZeroUuid = {
+ static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
+
+// Equalizer type UUID.
+static const AudioUuid EqualizerTypeUUID = {static_cast<int32_t>(0x0bed4300),
+ 0xddd6,
+ 0x11db,
+ 0x8f34,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+// Equalizer implementation UUID.
+static const AudioUuid EqualizerSwImplUUID = {static_cast<int32_t>(0x0bed4300),
+ 0x847d,
+ 0x11df,
+ 0xbb17,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+// Visualizer type UUID.
+static const AudioUuid VisualizerTypeUUID = {static_cast<int32_t>(0x1d4033c0),
+ 0x8557,
+ 0x11df,
+ 0x9f2d,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h
new file mode 100644
index 0000000..0fe69ff
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectWorker.h
@@ -0,0 +1,74 @@
+/*
+ * 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 <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "EffectContext.h"
+#include "EffectThread.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+std::string toString(RetCode& code);
+
+class EffectWorker : public EffectThread {
+ public:
+ // set effect context for worker, suppose to only happen once here
+ void setContext(std::shared_ptr<EffectContext> context) {
+ std::call_once(mOnceFlag, [&]() { mContext = context; });
+ };
+
+ // handle FMQ and call effect implemented virtual function
+ void process() override {
+ if (!mContext) {
+ LOG(ERROR) << __func__ << " invalid context!";
+ return;
+ }
+ std::shared_ptr<EffectContext::StatusMQ> statusMQ = mContext->getStatusFmq();
+ std::shared_ptr<EffectContext::DataMQ> inputMQ = mContext->getInputDataFmq();
+ std::shared_ptr<EffectContext::DataMQ> outputMQ = mContext->getOutputDataFmq();
+
+ // Only this worker will read from input data MQ and write to output data MQ.
+ auto readSize = inputMQ->availableToRead(), writeSize = outputMQ->availableToWrite();
+ if (readSize && writeSize) {
+ LOG(DEBUG) << __func__ << " available to read " << readSize << " available to write "
+ << writeSize;
+ auto buffer = mContext->getWorkBuffer();
+ inputMQ->read(buffer, readSize);
+ IEffect::Status status = effectProcessImpl();
+ writeSize = std::min((int32_t)writeSize, status.fmqByteProduced);
+ outputMQ->write(buffer, writeSize);
+ statusMQ->writeBlocking(&status, 1);
+ LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqByteConsumed
+ << " produced " << status.fmqByteProduced;
+ } else {
+ // TODO: maybe add some sleep here to avoid busy waiting
+ }
+ }
+
+ // must implement by each effect implementation
+ virtual IEffect::Status effectProcessImpl() = 0;
+
+ private:
+ // make sure the context only set once.
+ std::once_flag mOnceFlag;
+ std::shared_ptr<EffectContext> mContext;
+};
+
+} // 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 8da5525..6195d8a 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -16,6 +16,8 @@
#pragma once
+#include <any>
+#include <map>
#include <optional>
#include <vector>
@@ -41,6 +43,16 @@
std::vector<Descriptor::Identity>* out_descriptor) override;
/**
+ * @brief Query list of defined processing, with the optional filter by AudioStreamType
+ *
+ * @param in_type Type of processing, could be AudioStreamType or AudioSource. Optional.
+ * @param _aidl_return List of processing filtered by in_type.
+ * @return ndk::ScopedAStatus
+ */
+ ndk::ScopedAStatus queryProcessing(const std::optional<Processing::Type>& in_type,
+ std::vector<Processing>* _aidl_return) override;
+
+ /**
* @brief Create an effect instance for a certain implementation (identified by UUID).
*
* @param in_impl_uuid Effect implementation UUID.
@@ -63,7 +75,26 @@
override;
private:
+ ~Factory();
// List of effect descriptors supported by the devices.
std::vector<Descriptor::Identity> mIdentityList;
+
+ typedef binder_exception_t (*EffectCreateFunctor)(std::shared_ptr<IEffect>*);
+ typedef binder_exception_t (*EffectDestroyFunctor)(const std::shared_ptr<IEffect>&);
+ struct effect_interface_s {
+ EffectCreateFunctor createEffectFunc;
+ EffectDestroyFunctor destroyEffectFunc;
+ };
+
+ std::map<aidl::android::media::audio::common::AudioUuid /* implementationUUID */,
+ std::pair<std::unique_ptr<void, std::function<void(void*)>> /* dlHandle */,
+ std::unique_ptr<struct effect_interface_s>>>
+ mEffectLibMap;
+ std::map<std::weak_ptr<IEffect>, aidl::android::media::audio::common::AudioUuid,
+ std::owner_less<>>
+ mEffectUuidMap;
+
+ ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
+ void cleanupEffectMap();
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/equalizer-impl/Equalizer.h b/audio/aidl/default/include/equalizer-impl/Equalizer.h
deleted file mode 100644
index ea16cb9..0000000
--- a/audio/aidl/default/include/equalizer-impl/Equalizer.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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/audio/effect/BnEffect.h>
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Equalizer type UUID.
-static const ::aidl::android::media::audio::common::AudioUuid EqualizerTypeUUID = {
- static_cast<int32_t>(0x0bed4300),
- 0xddd6,
- 0x11db,
- 0x8f34,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-// Equalizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid EqualizerSwImplUUID = {
- static_cast<int32_t>(0x0bed4300),
- 0x847d,
- 0x11df,
- 0xbb17,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-class Equalizer : public BnEffect {
- public:
- Equalizer() = default;
- ndk::ScopedAStatus open() override;
- ndk::ScopedAStatus close() override;
- ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
-
- private:
- // Effect descriptor.
- Descriptor mDesc = {.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
-};
-} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
new file mode 100644
index 0000000..aa3a727
--- /dev/null
+++ b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
@@ -0,0 +1,105 @@
+/*
+ * 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/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+#include <cstdlib>
+#include <memory>
+
+#include "effect-impl/EffectContext.h"
+#include "effect-impl/EffectTypes.h"
+#include "effect-impl/EffectUUID.h"
+#include "effect-impl/EffectWorker.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+class EqualizerSwContext : public EffectContext {
+ public:
+ EqualizerSwContext(int statusDepth, int inBufferSize, int outBufferSize)
+ : EffectContext(statusDepth, inBufferSize, outBufferSize) {
+ LOG(DEBUG) << __func__;
+ }
+
+ private:
+ // Add equalizer specific context for processing here
+};
+
+class EqualizerSw : public BnEffect, EffectWorker {
+ public:
+ EqualizerSw() {
+ Equalizer::Capability eqCap = {.bandFrequencies = mBandFrequency, .presets = mPresets};
+ mDesc.capability.set<Capability::equalizer>(eqCap);
+ LOG(DEBUG) << __func__;
+ };
+ ~EqualizerSw() {
+ cleanUp();
+ LOG(DEBUG) << __func__;
+ };
+ ndk::ScopedAStatus open(const Parameter::Common& common, const Parameter::Specific& specific,
+ OpenEffectReturn* _aidl_return) override;
+ ndk::ScopedAStatus close() override;
+ ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
+
+ ndk::ScopedAStatus getState(State* _aidl_return) override;
+ ndk::ScopedAStatus command(CommandId in_commandId) override;
+ ndk::ScopedAStatus setParameter(const Parameter& in_param) override;
+ ndk::ScopedAStatus getParameter(const Parameter::Id& in_paramId,
+ Parameter* _aidl_return) override;
+
+ IEffect::Status effectProcessImpl() override;
+
+ private:
+ // Effect descriptor.
+ Descriptor mDesc = {.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
+
+ // Parameters.
+ Parameter::Common mCommonParam;
+ Equalizer mEqualizerParam; // TODO: the equalizer parameter needs to update
+
+ // Instance state INIT by default.
+ State mState = State::INIT;
+
+ int mPreset = PRESET_CUSTOM; // the current preset
+ const std::vector<Equalizer::BandFrequency> mBandFrequency = {{0, 30000, 120000},
+ {1, 120001, 460000},
+ {2, 460001, 1800000},
+ {3, 1800001, 7000000},
+ {4, 7000001, 20000000}};
+ // preset band level
+ std::vector<Equalizer::BandLevel> mBandLevels = {{0, 3}, {1, 0}, {2, 0}, {3, 0}, {4, 3}};
+ // presets supported by the device
+ const std::vector<Equalizer::Preset> mPresets = {
+ {0, "Normal"}, {1, "Classical"}, {2, "Dance"}, {3, "Flat"}, {4, "Folk"},
+ {5, "Heavy Metal"}, {6, "Hip Hop"}, {7, "Jazz"}, {8, "Pop"}, {9, "Rock"}};
+ static const int NUM_OF_BANDS = 5;
+ static const int NUM_OF_PRESETS = 10;
+ static const int PRESET_CUSTOM = -1;
+
+ // Equalizer worker context
+ std::shared_ptr<EqualizerSwContext> mContext;
+
+ ndk::ScopedAStatus setCommonParameter(const Parameter::Common& common_param);
+ ndk::ScopedAStatus setSpecificParameter(const Parameter::Specific& specific);
+ ndk::ScopedAStatus getSpecificParameter(Parameter::Specific::Id id,
+ Parameter::Specific* specific);
+
+ void cleanUp();
+
+ IEffect::Status status(binder_status_t status, size_t consumed, size_t produced);
+};
+} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 6ea7cef..8de1d79 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -43,8 +43,40 @@
}
cc_test {
+ name: "VtsHalAudioEffectFactoryTargetTest",
+ defaults: [
+ "latest_android_media_audio_common_types_ndk_static",
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalAudioEffectFactoryTargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.audio.effect-V1-ndk",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ ],
+ header_libs: ["libaudioaidl_headers"],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+cc_test {
name: "VtsHalAudioEffectTargetTest",
defaults: [
+ "latest_android_hardware_audio_common_ndk_static",
"latest_android_media_audio_common_types_ndk_static",
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
@@ -54,10 +86,49 @@
],
shared_libs: [
"libbinder_ndk",
+ "libfmq",
],
static_libs: [
"android.hardware.audio.effect-V1-ndk",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ "libaudioaidlcommon",
],
+ header_libs: ["libaudioaidl_headers"],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+cc_test {
+ name: "VtsHalEqualizerTargetTest",
+ defaults: [
+ "latest_android_hardware_audio_common_ndk_static",
+ "latest_android_media_audio_common_types_ndk_static",
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalEqualizerTargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libfmq",
+ ],
+ static_libs: [
+ "android.hardware.audio.effect-V1-ndk",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ "libaudioaidlcommon",
+ ],
+ header_libs: ["libaudioaidl_headers"],
cflags: [
"-Wall",
"-Wextra",
diff --git a/audio/aidl/vts/EffectFactoryHelper.h b/audio/aidl/vts/EffectFactoryHelper.h
new file mode 100644
index 0000000..63efae0
--- /dev/null
+++ b/audio/aidl/vts/EffectFactoryHelper.h
@@ -0,0 +1,166 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android/binder_auto_utils.h>
+
+#include "TestUtils.h"
+#include "effect-impl/EffectUUID.h"
+
+using namespace android;
+
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::Processing;
+using aidl::android::media::audio::common::AudioUuid;
+
+class EffectFactoryHelper {
+ public:
+ explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
+
+ void ConnectToFactoryService() {
+ mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
+ ASSERT_NE(mEffectFactory, nullptr);
+ }
+
+ void RestartFactoryService() {
+ ASSERT_NE(mEffectFactory, nullptr);
+ mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
+ ASSERT_NE(mEffectFactory, nullptr);
+ ClearEffectMap();
+ }
+
+ void QueryEffects(const std::optional<AudioUuid>& in_type,
+ const std::optional<AudioUuid>& in_instance,
+ std::vector<Descriptor::Identity>* _aidl_return) {
+ ASSERT_NE(mEffectFactory, nullptr);
+ EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, _aidl_return));
+ mIds = *_aidl_return;
+ }
+
+ void QueryProcessing(const std::optional<Processing::Type>& in_type,
+ std::vector<Processing>* _aidl_return) {
+ ASSERT_NE(mEffectFactory, nullptr);
+ EXPECT_IS_OK(mEffectFactory->queryProcessing(in_type, _aidl_return));
+ // only update the whole list if no filter applied
+ if (!in_type.has_value()) {
+ mProcesses = *_aidl_return;
+ }
+ }
+
+ void CreateEffects() {
+ for (const auto& id : mIds) {
+ std::shared_ptr<IEffect> effect;
+ EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
+ EXPECT_NE(effect, nullptr) << id.toString();
+ if (effect) {
+ mEffectIdMap[effect] = id;
+ }
+ }
+ }
+
+ void QueryAndCreateEffects(const AudioUuid& type = EffectNullUuid) {
+ std::vector<Descriptor::Identity> ids;
+ ASSERT_NE(mEffectFactory, nullptr);
+
+ if (type == EffectNullUuid) {
+ EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &ids));
+ } else {
+ EXPECT_IS_OK(mEffectFactory->queryEffects(type, std::nullopt, &ids));
+ }
+ for (const auto& id : ids) {
+ ASSERT_EQ(id.type, type);
+ std::shared_ptr<IEffect> effect;
+ EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
+ EXPECT_NE(effect, nullptr) << id.toString();
+ if (effect) {
+ mEffectIdMap[effect] = id;
+ }
+ }
+ }
+
+ void CreateEffectsAndExpect(
+ const std::vector<std::pair<Descriptor::Identity, binder_exception_t>>& uuid_status) {
+ ASSERT_NE(mEffectFactory, nullptr);
+ for (const auto& it : uuid_status) {
+ std::shared_ptr<IEffect> effect;
+ auto status = mEffectFactory->createEffect(it.first.uuid, &effect);
+ EXPECT_STATUS(it.second, status);
+ if (effect) {
+ mEffectIdMap[effect] = it.first;
+ }
+ }
+ }
+
+ void DestroyEffectAndExpect(std::shared_ptr<IEffect>& instance, binder_exception_t exception) {
+ ASSERT_NE(mEffectFactory, nullptr);
+ auto status = mEffectFactory->destroyEffect(instance);
+ EXPECT_STATUS(exception, status);
+ }
+
+ void QueryAndCreateAllEffects() {
+ ASSERT_NE(mEffectFactory, nullptr);
+ EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds));
+ for (const auto& id : mCompleteIds) {
+ std::shared_ptr<IEffect> effect;
+ EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
+ EXPECT_NE(effect, nullptr) << id.toString();
+ mEffectIdMap[effect] = id;
+ }
+ }
+
+ void DestroyEffects(const binder_exception_t expected = EX_NONE, const int remaining = 0) {
+ ASSERT_NE(mEffectFactory, nullptr);
+
+ for (auto it = mEffectIdMap.begin(); it != mEffectIdMap.end();) {
+ auto erased = it++;
+ auto status = mEffectFactory->destroyEffect(erased->first);
+ EXPECT_STATUS(expected, status);
+ if (status.isOk()) {
+ mEffectIdMap.erase(erased);
+ }
+ }
+ EXPECT_EQ((unsigned int)remaining, mEffectIdMap.size());
+ }
+
+ std::shared_ptr<IFactory> GetFactory() { return mEffectFactory; }
+ const std::vector<Descriptor::Identity>& GetEffectIds() { return mIds; }
+ const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() const {
+ return mCompleteIds;
+ }
+ const std::map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() const {
+ return mEffectIdMap;
+ }
+ void ClearEffectMap() { mEffectIdMap.clear(); }
+
+ private:
+ std::shared_ptr<IFactory> mEffectFactory;
+ std::string mServiceName;
+ AudioHalBinderServiceUtil binderUtil;
+ std::vector<Descriptor::Identity> mIds;
+ std::vector<Descriptor::Identity> mCompleteIds;
+ std::vector<Processing> mProcesses;
+
+ std::map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
+};
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
new file mode 100644
index 0000000..c58ed13
--- /dev/null
+++ b/audio/aidl/vts/EffectHelper.h
@@ -0,0 +1,310 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <aidl/android/hardware/audio/effect/IEffect.h>
+#include <aidl/android/hardware/audio/effect/IFactory.h>
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioDeviceType.h>
+#include <android/binder_auto_utils.h>
+#include <fmq/AidlMessageQueue.h>
+
+#include "AudioHalBinderServiceUtil.h"
+#include "EffectFactoryHelper.h"
+#include "TestUtils.h"
+
+using namespace android;
+using aidl::android::hardware::audio::effect::CommandId;
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
+using aidl::android::hardware::audio::effect::EffectZeroUuid;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioDeviceType;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioUuid;
+using aidl::android::media::audio::common::PcmType;
+
+const AudioFormatDescription DefaultFormat = {
+ .type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT, .encoding = ""};
+
+class EffectHelper {
+ public:
+ explicit EffectHelper(const std::string& name) : mFactoryHelper(EffectFactoryHelper(name)) {
+ mFactoryHelper.ConnectToFactoryService();
+ }
+
+ void OpenEffects(const AudioUuid& type = EffectNullUuid) {
+ auto open = [&](const std::shared_ptr<IEffect>& effect) {
+ IEffect::OpenEffectReturn ret;
+ EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret));
+ EffectParam params;
+ params.statusMQ = std::make_unique<StatusMQ>(ret.statusMQ);
+ params.inputMQ = std::make_unique<DataMQ>(ret.inputDataMQ);
+ params.outputMQ = std::make_unique<DataMQ>(ret.outputDataMQ);
+ mEffectParams.push_back(std::move(params));
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(open, type));
+ }
+
+ void CloseEffects(const binder_status_t status = EX_NONE) {
+ auto close = [&](const std::shared_ptr<IEffect>& effect) {
+ EXPECT_STATUS(status, effect->close());
+ };
+
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
+ }
+
+ void CreateEffects(const int n = 1) {
+ for (int i = 0; i < n; i++) {
+ ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects());
+ }
+ }
+
+ void CreateEffectsWithUUID(const AudioUuid& type = EffectNullUuid) {
+ ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateEffects(type));
+ }
+
+ void QueryEffects() { ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects()); }
+
+ void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) {
+ ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining));
+ mEffectDescriptors.clear();
+ }
+
+ void GetEffectDescriptors() {
+ auto get = [&](const std::shared_ptr<IEffect>& effect) {
+ Descriptor desc;
+ EXPECT_IS_OK(effect->getDescriptor(&desc));
+ mEffectDescriptors.push_back(std::move(desc));
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
+ }
+
+ void CommandEffects(CommandId command) {
+ auto close = [&](const std::shared_ptr<IEffect>& effect) {
+ EXPECT_IS_OK(effect->command(command));
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
+ }
+
+ void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) {
+ auto func = [&](const std::shared_ptr<IEffect>& effect) {
+ EXPECT_STATUS(status, effect->command(command));
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
+ }
+
+ void ExpectState(State expected) {
+ auto get = [&](const std::shared_ptr<IEffect>& effect) {
+ State state = State::INIT;
+ EXPECT_IS_OK(effect->getState(&state));
+ EXPECT_EQ(expected, state);
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
+ }
+
+ void SetParameter() {
+ auto func = [&](const std::shared_ptr<IEffect>& effect) {
+ Parameter param;
+ param.set<Parameter::common>(mCommon);
+ EXPECT_IS_OK(effect->setParameter(param));
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
+ }
+
+ void VerifyParameters() {
+ auto func = [&](const std::shared_ptr<IEffect>& effect) {
+ Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter();
+ Parameter::Id id;
+ id.set<Parameter::Id::commonTag>(0);
+ paramCommonExpect.set<Parameter::common>(mCommon);
+ EXPECT_IS_OK(effect->getParameter(id, ¶mCommonGet));
+ EXPECT_EQ(paramCommonExpect, paramCommonGet)
+ << paramCommonExpect.toString() << " vs " << paramCommonGet.toString();
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
+ }
+
+ void QueryEffects(const std::optional<AudioUuid>& in_type,
+ const std::optional<AudioUuid>& in_instance,
+ std::vector<Descriptor::Identity>* _aidl_return) {
+ mFactoryHelper.QueryEffects(in_type, in_instance, _aidl_return);
+ }
+
+ template <typename Functor>
+ void ForEachEffect(Functor functor, const std::optional<AudioUuid>& type = EffectNullUuid) {
+ auto effectMap = mFactoryHelper.GetEffectMap();
+ for (const auto& it : effectMap) {
+ SCOPED_TRACE(it.second.toString());
+ if (type != EffectNullUuid && it.second.type != type) continue;
+ functor(it.first);
+ }
+ }
+
+ template <typename Functor>
+ void ForEachDescriptor(Functor functor) {
+ for (size_t i = 0; i < mEffectDescriptors.size(); i++) {
+ SCOPED_TRACE(mEffectDescriptors[i].toString());
+ functor(i, mEffectDescriptors[i]);
+ }
+ }
+
+ static const size_t mWriteMQSize = 0x400;
+
+ enum class IO : char { INPUT = 0, OUTPUT = 1, INOUT = 2 };
+
+ void initParamCommonFormat(IO io = IO::INOUT,
+ const AudioFormatDescription& format = DefaultFormat) {
+ if (io == IO::INPUT || io == IO::INOUT) {
+ mCommon.input.base.format = format;
+ }
+ if (io == IO::OUTPUT || io == IO::INOUT) {
+ mCommon.output.base.format = format;
+ }
+ }
+
+ void initParamCommonSampleRate(IO io = IO::INOUT, const int& sampleRate = 48000) {
+ if (io == IO::INPUT || io == IO::INOUT) {
+ mCommon.input.base.sampleRate = sampleRate;
+ }
+ if (io == IO::OUTPUT || io == IO::INOUT) {
+ mCommon.output.base.sampleRate = sampleRate;
+ }
+ }
+
+ void initParamCommonFrameCount(IO io = IO::INOUT, const long& frameCount = 48000) {
+ if (io == IO::INPUT || io == IO::INOUT) {
+ mCommon.input.frameCount = frameCount;
+ }
+ if (io == IO::OUTPUT || io == IO::INOUT) {
+ mCommon.output.frameCount = frameCount;
+ }
+ }
+ void initParamCommon(int session = -1, int ioHandle = -1, int iSampleRate = 48000,
+ int oSampleRate = 48000, long iFrameCount = 0x100,
+ long oFrameCount = 0x100) {
+ mCommon.session = session;
+ mCommon.ioHandle = ioHandle;
+
+ auto& input = mCommon.input;
+ auto& output = mCommon.output;
+ input.base.sampleRate = iSampleRate;
+ input.base.channelMask = mInputChannelLayout;
+ input.frameCount = iFrameCount;
+ output.base.sampleRate = oSampleRate;
+ output.base.channelMask = mOutputChannelLayout;
+ output.frameCount = oFrameCount;
+ inputFrameSize = android::hardware::audio::common::getFrameSizeInBytes(
+ input.base.format, input.base.channelMask);
+ outputFrameSize = android::hardware::audio::common::getFrameSizeInBytes(
+ output.base.format, output.base.channelMask);
+ }
+
+ void setSpecific(Parameter::Specific& specific) { mSpecific = specific; }
+
+ // usually this function only call once.
+ void PrepareInputData(size_t s = mWriteMQSize) {
+ size_t maxInputSize = s;
+ for (auto& it : mEffectParams) {
+ auto& mq = it.inputMQ;
+ EXPECT_NE(nullptr, mq);
+ EXPECT_TRUE(mq->isValid());
+ const size_t bytesToWrite = mq->availableToWrite();
+ EXPECT_EQ(inputFrameSize * mCommon.input.frameCount, bytesToWrite);
+ EXPECT_NE(0UL, bytesToWrite);
+ EXPECT_TRUE(s <= bytesToWrite);
+ maxInputSize = std::max(maxInputSize, bytesToWrite);
+ }
+ mInputBuffer.resize(maxInputSize);
+ std::fill(mInputBuffer.begin(), mInputBuffer.end(), 0x5a);
+ }
+
+ void writeToFmq(size_t s = mWriteMQSize) {
+ for (auto& it : mEffectParams) {
+ auto& mq = it.inputMQ;
+ EXPECT_NE(nullptr, mq);
+ const size_t bytesToWrite = mq->availableToWrite();
+ EXPECT_NE(0Ul, bytesToWrite);
+ EXPECT_TRUE(s <= bytesToWrite);
+ EXPECT_TRUE(mq->write(mInputBuffer.data(), s));
+ }
+ }
+
+ void readFromFmq(size_t expectSize = mWriteMQSize) {
+ for (auto& it : mEffectParams) {
+ IEffect::Status status{};
+ auto& statusMq = it.statusMQ;
+ EXPECT_NE(nullptr, statusMq);
+ EXPECT_TRUE(statusMq->readBlocking(&status, 1));
+ EXPECT_EQ(STATUS_OK, status.status);
+ EXPECT_EQ(expectSize, (unsigned)status.fmqByteProduced);
+
+ auto& outputMq = it.outputMQ;
+ EXPECT_NE(nullptr, outputMq);
+ EXPECT_EQ(expectSize, outputMq->availableToRead());
+ }
+ }
+
+ void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; }
+ void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; }
+ const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() const {
+ return mFactoryHelper.GetCompleteEffectIdList();
+ }
+ const std::vector<Descriptor>& getDescriptorVec() const { return mEffectDescriptors; }
+
+ private:
+ EffectFactoryHelper mFactoryHelper;
+
+ AudioChannelLayout mInputChannelLayout =
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+ AudioChannelLayout::LAYOUT_STEREO);
+ AudioChannelLayout mOutputChannelLayout =
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+ AudioChannelLayout::LAYOUT_STEREO);
+
+ Parameter::Common mCommon;
+ Parameter::Specific mSpecific;
+
+ size_t inputFrameSize, outputFrameSize;
+ std::vector<int8_t> mInputBuffer; // reuse same buffer for all effects testing
+
+ typedef ::android::AidlMessageQueue<
+ IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+ StatusMQ;
+ typedef ::android::AidlMessageQueue<
+ int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+ DataMQ;
+
+ class EffectParam {
+ public:
+ std::unique_ptr<StatusMQ> statusMQ;
+ std::unique_ptr<DataMQ> inputMQ;
+ std::unique_ptr<DataMQ> outputMQ;
+ };
+ std::vector<EffectParam> mEffectParams;
+ std::vector<Descriptor> mEffectDescriptors;
+};
diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
new file mode 100644
index 0000000..da8ca37
--- /dev/null
+++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
@@ -0,0 +1,235 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <vector>
+
+#define LOG_TAG "VtsHalAudioEffectFactory"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <aidl/android/hardware/audio/effect/IFactory.h>
+
+#include "AudioHalBinderServiceUtil.h"
+#include "EffectFactoryHelper.h"
+#include "TestUtils.h"
+
+using namespace android;
+
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
+using aidl::android::hardware::audio::effect::EffectZeroUuid;
+using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::Processing;
+using aidl::android::media::audio::common::AudioUuid;
+
+/// Effect factory testing.
+class EffectFactoryTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
+
+ void TearDown() override { mFactory.DestroyEffects(); }
+
+ EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
+
+ const Descriptor::Identity nullDesc = {.uuid = EffectNullUuid};
+ const Descriptor::Identity zeroDesc = {.uuid = EffectZeroUuid};
+};
+
+TEST_P(EffectFactoryTest, SetupAndTearDown) {
+ // Intentionally empty test body.
+}
+
+TEST_P(EffectFactoryTest, CanBeRestarted) {
+ ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
+}
+
+TEST_P(EffectFactoryTest, QueriedDescriptorList) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ EXPECT_NE(descriptors.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ // TODO: Factory eventually need to return the full list of MUST supported AOSP effects.
+ for (auto& desc : descriptors) {
+ EXPECT_NE(desc.type, EffectNullUuid);
+ EXPECT_NE(desc.uuid, EffectNullUuid);
+ }
+}
+
+TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(EffectNullUuid, std::nullopt, &descriptors);
+ EXPECT_EQ(descriptors.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, EffectNullUuid, &descriptors);
+ EXPECT_EQ(descriptors.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, CreateAndDestroyOnce) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ auto numIds = mFactory.GetEffectIds().size();
+ EXPECT_NE(numIds, 0UL);
+
+ auto& effectMap = mFactory.GetEffectMap();
+ EXPECT_EQ(effectMap.size(), 0UL);
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ mFactory.DestroyEffects();
+ EXPECT_EQ(effectMap.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ auto numIds = mFactory.GetEffectIds().size();
+ EXPECT_NE(numIds, 0UL);
+
+ auto& effectMap = mFactory.GetEffectMap();
+ EXPECT_EQ(effectMap.size(), 0UL);
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ mFactory.DestroyEffects();
+ EXPECT_EQ(effectMap.size(), 0UL);
+
+ // Create and destroy again
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ mFactory.DestroyEffects();
+ EXPECT_EQ(effectMap.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ auto numIds = mFactory.GetEffectIds().size();
+ EXPECT_NE(numIds, 0UL);
+
+ auto& effectMap = mFactory.GetEffectMap();
+ EXPECT_EQ(effectMap.size(), 0UL);
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ // Create effect instances of same implementation
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), 2 * numIds);
+
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), 3 * numIds);
+
+ mFactory.DestroyEffects();
+ EXPECT_EQ(effectMap.size(), 0UL);
+}
+
+// Expect EX_ILLEGAL_ARGUMENT when create with invalid UUID.
+TEST_P(EffectFactoryTest, CreateWithInvalidUuid) {
+ std::vector<std::pair<Descriptor::Identity, binder_status_t>> descriptors;
+ descriptors.push_back(std::make_pair(nullDesc, EX_ILLEGAL_ARGUMENT));
+ descriptors.push_back(std::make_pair(zeroDesc, EX_ILLEGAL_ARGUMENT));
+
+ auto& effectMap = mFactory.GetEffectMap();
+ mFactory.CreateEffectsAndExpect(descriptors);
+ EXPECT_EQ(effectMap.size(), 0UL);
+}
+
+// Expect EX_ILLEGAL_ARGUMENT when destroy null interface.
+TEST_P(EffectFactoryTest, DestroyWithInvalidInterface) {
+ std::shared_ptr<IEffect> spDummyEffect(nullptr);
+
+ mFactory.DestroyEffectAndExpect(spDummyEffect, EX_ILLEGAL_ARGUMENT);
+}
+
+TEST_P(EffectFactoryTest, CreateAndRemoveReference) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ auto numIds = mFactory.GetEffectIds().size();
+ EXPECT_NE(numIds, 0UL);
+
+ auto& effectMap = mFactory.GetEffectMap();
+ EXPECT_EQ(effectMap.size(), 0UL);
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ // remove all reference
+ mFactory.ClearEffectMap();
+ EXPECT_EQ(effectMap.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, CreateRemoveReferenceAndCreateDestroy) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ auto numIds = mFactory.GetEffectIds().size();
+ EXPECT_NE(numIds, 0UL);
+
+ auto& effectMap = mFactory.GetEffectMap();
+ EXPECT_EQ(effectMap.size(), 0UL);
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ // remove all reference
+ mFactory.ClearEffectMap();
+ EXPECT_EQ(effectMap.size(), 0UL);
+
+ // Create and destroy again
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ mFactory.DestroyEffects();
+ EXPECT_EQ(effectMap.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, CreateRestartAndCreateDestroy) {
+ std::vector<Descriptor::Identity> descriptors;
+ mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
+ auto numIds = mFactory.GetEffectIds().size();
+ auto& effectMap = mFactory.GetEffectMap();
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
+
+ mFactory.CreateEffects();
+ EXPECT_EQ(effectMap.size(), numIds);
+ mFactory.DestroyEffects();
+ EXPECT_EQ(effectMap.size(), 0UL);
+}
+
+TEST_P(EffectFactoryTest, QueryProcess) {
+ std::vector<Processing> processing;
+ mFactory.QueryProcessing(std::nullopt, &processing);
+ // TODO: verify the number of process in example implementation after audio_effects.xml migrated
+}
+
+INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
+ android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 8b5eb13..7ed1f01 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
+#define LOG_TAG "VtsHalAudioEffectTargetTest"
+
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
-#define LOG_TAG "VtsHalAudioEffect"
-
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/logging.h>
@@ -30,272 +30,91 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <Utils.h>
+#include <aidl/android/hardware/audio/effect/IEffect.h>
#include <aidl/android/hardware/audio/effect/IFactory.h>
+#include <aidl/android/media/audio/common/AudioDeviceType.h>
#include "AudioHalBinderServiceUtil.h"
+#include "EffectHelper.h"
#include "TestUtils.h"
using namespace android;
using ndk::ScopedAStatus;
+using aidl::android::hardware::audio::effect::CommandId;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
-using aidl::android::media::audio::common::AudioUuid;
+using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioDeviceType;
-class EffectFactoryHelper {
+class AudioEffectTest : public testing::TestWithParam<std::string>, public EffectHelper {
public:
- explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
+ AudioEffectTest() : EffectHelper(GetParam()) {}
- void ConnectToFactoryService() {
- mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
- ASSERT_NE(mEffectFactory, nullptr);
- }
-
- void RestartFactoryService() {
- ASSERT_NE(mEffectFactory, nullptr);
- mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
- ASSERT_NE(mEffectFactory, nullptr);
- }
-
- void QueryAllEffects() {
- EXPECT_NE(mEffectFactory, nullptr);
- EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds));
- }
-
- void QueryEffects(const std::optional<AudioUuid>& in_type,
- const std::optional<AudioUuid>& in_instance,
- std::vector<Descriptor::Identity>* _aidl_return) {
- EXPECT_NE(mEffectFactory, nullptr);
- EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, _aidl_return));
- mIds = *_aidl_return;
- }
-
- void CreateEffects() {
- EXPECT_NE(mEffectFactory, nullptr);
- for (const auto& id : mIds) {
- std::shared_ptr<IEffect> effect;
- EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
- EXPECT_NE(effect, nullptr) << id.toString();
- mEffectIdMap[effect] = id;
- }
- }
-
- void DestroyEffects() {
- EXPECT_NE(mEffectFactory, nullptr);
- for (const auto& it : mEffectIdMap) {
- EXPECT_IS_OK(mEffectFactory->destroyEffect(it.first));
- }
- mEffectIdMap.clear();
- }
-
- std::shared_ptr<IFactory> GetFactory() { return mEffectFactory; }
- const std::vector<Descriptor::Identity>& GetEffectIds() { return mIds; }
- const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() { return mCompleteIds; }
- const std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
- return mEffectIdMap;
- }
-
- private:
- std::shared_ptr<IFactory> mEffectFactory;
- std::string mServiceName;
- AudioHalBinderServiceUtil binderUtil;
- std::vector<Descriptor::Identity> mIds;
- std::vector<Descriptor::Identity> mCompleteIds;
- std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
-};
-
-/// Effect factory testing.
-class EffectFactoryTest : public testing::TestWithParam<std::string> {
- public:
- void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
-
- void TearDown() override { mFactory.DestroyEffects(); }
-
- EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
-
- // TODO: these UUID can get from config file
- // ec7178ec-e5e1-4432-a3f4-4657e6795210
- const AudioUuid nullUuid = {static_cast<int32_t>(0xec7178ec),
- 0xe5e1,
- 0x4432,
- 0xa3f4,
- {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
- const AudioUuid zeroUuid = {
- static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
-};
-
-TEST_P(EffectFactoryTest, SetupAndTearDown) {
- // Intentionally empty test body.
-}
-
-TEST_P(EffectFactoryTest, CanBeRestarted) {
- ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
-}
-
-TEST_P(EffectFactoryTest, QueriedDescriptorList) {
- std::vector<Descriptor::Identity> descriptors;
- mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
- EXPECT_NE(descriptors.size(), 0UL);
-}
-
-TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
- std::vector<Descriptor::Identity> descriptors;
- mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
- // TODO: Factory eventually need to return the full list of MUST supported AOSP effects.
- for (auto& desc : descriptors) {
- EXPECT_NE(desc.type, zeroUuid);
- EXPECT_NE(desc.uuid, zeroUuid);
- }
-}
-
-TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
- std::vector<Descriptor::Identity> descriptors;
- mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
- EXPECT_EQ(descriptors.size(), 0UL);
-}
-
-TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
- std::vector<Descriptor::Identity> descriptors;
- mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
- EXPECT_EQ(descriptors.size(), 0UL);
-}
-
-TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
- std::vector<Descriptor::Identity> descriptors;
- mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
- auto numIds = mFactory.GetEffectIds().size();
- EXPECT_NE(numIds, 0UL);
-
- EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
- mFactory.CreateEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
- mFactory.DestroyEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
-
- // Create and destroy again
- mFactory.CreateEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
- mFactory.DestroyEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
-}
-
-TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
- std::vector<Descriptor::Identity> descriptors;
- mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
- auto numIds = mFactory.GetEffectIds().size();
- EXPECT_NE(numIds, 0UL);
-
- EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
- mFactory.CreateEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
- // Create effect instances of same implementation
- mFactory.CreateEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), 2 * numIds);
-
- mFactory.CreateEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), 3 * numIds);
-
- mFactory.DestroyEffects();
- EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
-}
-
-INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
- testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
- android::PrintInstanceNameToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
-
-/// Effect testing.
-class AudioEffect : public testing::TestWithParam<std::string> {
- public:
void SetUp() override {
- ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService());
- ASSERT_NO_FATAL_FAILURE(mFactory.CreateEffects());
+ CreateEffects();
+ initParamCommonFormat();
+ initParamCommon();
+ // initParamSpecific();
}
void TearDown() override {
CloseEffects();
- ASSERT_NO_FATAL_FAILURE(mFactory.DestroyEffects());
+ DestroyEffects();
}
-
- void OpenEffects() {
- auto open = [](const std::shared_ptr<IEffect>& effect) { EXPECT_IS_OK(effect->open()); };
- EXPECT_NO_FATAL_FAILURE(ForEachEffect(open));
- }
-
- void CloseEffects() {
- auto close = [](const std::shared_ptr<IEffect>& effect) { EXPECT_IS_OK(effect->close()); };
- EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
- }
-
- void GetEffectDescriptors() {
- auto get = [](const std::shared_ptr<IEffect>& effect) {
- Descriptor desc;
- EXPECT_IS_OK(effect->getDescriptor(&desc));
- };
- EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
- }
-
- template <typename Functor>
- void ForEachEffect(Functor functor) {
- auto effectMap = mFactory.GetEffectMap();
- for (const auto& it : effectMap) {
- SCOPED_TRACE(it.second.toString());
- functor(it.first);
- }
- }
-
- EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
};
-TEST_P(AudioEffect, OpenEffectTest) {
- EXPECT_NO_FATAL_FAILURE(OpenEffects());
+TEST_P(AudioEffectTest, OpenEffectTest) {
+ OpenEffects();
}
-TEST_P(AudioEffect, OpenAndCloseEffect) {
- EXPECT_NO_FATAL_FAILURE(OpenEffects());
- EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, OpenAndCloseEffect) {
+ OpenEffects();
+ CloseEffects();
}
-TEST_P(AudioEffect, CloseUnopenedEffectTest) {
- EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, CloseUnopenedEffectTest) {
+ CloseEffects();
}
-TEST_P(AudioEffect, DoubleOpenCloseEffects) {
- EXPECT_NO_FATAL_FAILURE(OpenEffects());
- EXPECT_NO_FATAL_FAILURE(CloseEffects());
- EXPECT_NO_FATAL_FAILURE(OpenEffects());
- EXPECT_NO_FATAL_FAILURE(CloseEffects());
+TEST_P(AudioEffectTest, DoubleOpenCloseEffects) {
+ OpenEffects();
+ CloseEffects();
+ OpenEffects();
+ CloseEffects();
- EXPECT_NO_FATAL_FAILURE(OpenEffects());
- EXPECT_NO_FATAL_FAILURE(OpenEffects());
- EXPECT_NO_FATAL_FAILURE(CloseEffects());
+ OpenEffects();
+ OpenEffects();
+ CloseEffects();
- EXPECT_NO_FATAL_FAILURE(OpenEffects());
- EXPECT_NO_FATAL_FAILURE(CloseEffects());
- EXPECT_NO_FATAL_FAILURE(CloseEffects());
+ OpenEffects();
+ CloseEffects();
+ CloseEffects();
}
-TEST_P(AudioEffect, GetDescriptors) {
- EXPECT_NO_FATAL_FAILURE(GetEffectDescriptors());
+TEST_P(AudioEffectTest, GetDescriptors) {
+ GetEffectDescriptors();
}
-TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
+TEST_P(AudioEffectTest, DescriptorIdExistAndUnique) {
auto checker = [&](const std::shared_ptr<IEffect>& effect) {
Descriptor desc;
std::vector<Descriptor::Identity> idList;
EXPECT_IS_OK(effect->getDescriptor(&desc));
- mFactory.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
+ QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
EXPECT_EQ(idList.size(), 1UL);
};
- EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker));
+ ForEachEffect(checker);
// Check unique with a set
auto stringHash = [](const Descriptor::Identity& id) {
return std::hash<std::string>()(id.toString());
};
- auto vec = mFactory.GetCompleteEffectIdList();
+ auto vec = GetCompleteEffectIdList();
std::unordered_set<Descriptor::Identity, decltype(stringHash)> idSet(0, stringHash);
for (auto it : vec) {
EXPECT_EQ(idSet.count(it), 0UL);
@@ -303,14 +122,237 @@
}
}
-INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect,
+/// State testing.
+// An effect instance is in INIT state by default after it was created.
+TEST_P(AudioEffectTest, InitStateAfterCreation) {
+ ExpectState(State::INIT);
+}
+
+// An effect instance transfer to INIT state after it was open successfully with IEffect.open().
+TEST_P(AudioEffectTest, IdleStateAfterOpen) {
+ OpenEffects();
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// An effect instance is in PROCESSING state after it receive an START command.
+TEST_P(AudioEffectTest, ProcessingStateAfterStart) {
+ OpenEffects();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::STOP);
+ CloseEffects();
+}
+
+// An effect instance transfer to IDLE state after Command.Id.STOP in PROCESSING state.
+TEST_P(AudioEffectTest, IdleStateAfterStop) {
+ OpenEffects();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// An effect instance transfer to IDLE state after Command.Id.RESET in PROCESSING state.
+TEST_P(AudioEffectTest, IdleStateAfterReset) {
+ OpenEffects();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::RESET);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// An effect instance transfer to INIT if instance receive a close() call.
+TEST_P(AudioEffectTest, InitStateAfterClose) {
+ OpenEffects();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CloseEffects();
+ ExpectState(State::INIT);
+}
+
+// An effect instance shouldn't accept any command before open.
+TEST_P(AudioEffectTest, NoCommandAcceptedBeforeOpen) {
+ ExpectState(State::INIT);
+ CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE);
+ CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE);
+ CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE);
+ ExpectState(State::INIT);
+}
+
+// No-op when receive STOP command in IDLE state.
+TEST_P(AudioEffectTest, StopCommandInIdleStateNoOp) {
+ ExpectState(State::INIT);
+ OpenEffects();
+ ExpectState(State::IDLE);
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// No-op when receive STOP command in IDLE state.
+TEST_P(AudioEffectTest, ResetCommandInIdleStateNoOp) {
+ ExpectState(State::INIT);
+ OpenEffects();
+ ExpectState(State::IDLE);
+ CommandEffects(CommandId::RESET);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// Repeat START and STOP command.
+TEST_P(AudioEffectTest, RepeatStartAndStop) {
+ OpenEffects();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// Repeat START and RESET command.
+TEST_P(AudioEffectTest, RepeatStartAndReset) {
+ OpenEffects();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::RESET);
+ ExpectState(State::IDLE);
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::RESET);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// Repeat START and STOP command, try to close at PROCESSING state.
+TEST_P(AudioEffectTest, CloseProcessingStateEffects) {
+ OpenEffects();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ CloseEffects(EX_ILLEGAL_STATE);
+ // cleanup
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+}
+
+// Expect EX_ILLEGAL_STATE if the effect instance is not in a proper state to be destroyed.
+TEST_P(AudioEffectTest, DestroyOpenEffects) {
+ // cleanup all effects.
+ CloseEffects();
+ DestroyEffects();
+
+ // open effects, destroy without close, expect to get EX_ILLEGAL_STATE status.
+ CreateEffects();
+ OpenEffects();
+ DestroyEffects(EX_ILLEGAL_STATE, 1);
+ CloseEffects();
+}
+
+/// Parameter testing.
+// Verify parameters pass in open can be successfully get.
+TEST_P(AudioEffectTest, VerifyParametersAfterOpen) {
+ OpenEffects();
+ VerifyParameters();
+ CloseEffects();
+}
+
+// Verify parameters pass in set can be successfully get.
+TEST_P(AudioEffectTest, SetAndGetParameter) {
+ OpenEffects();
+ VerifyParameters();
+ initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+ 44100 /* oSampleRate */);
+ SetParameter();
+ VerifyParameters();
+ CloseEffects();
+}
+
+// Verify parameters pass in set can be successfully get.
+TEST_P(AudioEffectTest, SetAndGetParameterInProcessing) {
+ OpenEffects();
+ VerifyParameters();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+ 44100 /* oSampleRate */);
+ SetParameter();
+ VerifyParameters();
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+// Parameters kept after reset.
+TEST_P(AudioEffectTest, ResetAndVerifyParameter) {
+ OpenEffects();
+ VerifyParameters();
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+ 44100 /* oSampleRate */);
+ SetParameter();
+ VerifyParameters();
+ CommandEffects(CommandId::RESET);
+ ExpectState(State::IDLE);
+ VerifyParameters();
+ CloseEffects();
+}
+
+// Multiple instances of same implementation running.
+TEST_P(AudioEffectTest, MultipleInstancesRunning) {
+ CreateEffects(3);
+ ExpectState(State::INIT);
+ OpenEffects();
+ ExpectState(State::IDLE);
+ CommandEffects(CommandId::START);
+ ExpectState(State::PROCESSING);
+ initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */,
+ 44100 /* oSampleRate */);
+ SetParameter();
+ VerifyParameters();
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ VerifyParameters();
+ CloseEffects();
+}
+
+// Send data to effects and expect it to consume by check statusMQ.
+TEST_P(AudioEffectTest, ExpectEffectsToConsumeDataInMQ) {
+ OpenEffects();
+ PrepareInputData(mWriteMQSize);
+
+ CommandEffects(CommandId::START);
+ writeToFmq(mWriteMQSize);
+ readFromFmq(mWriteMQSize);
+
+ ExpectState(State::PROCESSING);
+ CommandEffects(CommandId::STOP);
+ // cleanup
+ CommandEffects(CommandId::STOP);
+ ExpectState(State::IDLE);
+ CloseEffects();
+}
+
+INSTANTIATE_TEST_SUITE_P(AudioEffectTestTest, AudioEffectTest,
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
android::PrintInstanceNameToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffect);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectTest);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
new file mode 100644
index 0000000..3b9699b
--- /dev/null
+++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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 <algorithm>
+#include <limits>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#define LOG_TAG "VtsHalEqualizerTest"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+
+#include <Utils.h>
+#include <aidl/android/hardware/audio/effect/IEffect.h>
+#include <aidl/android/hardware/audio/effect/IFactory.h>
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioDeviceType.h>
+
+#include "AudioHalBinderServiceUtil.h"
+#include "EffectHelper.h"
+#include "TestUtils.h"
+#include "effect-impl/EffectUUID.h"
+
+using namespace android;
+
+using aidl::android::hardware::audio::effect::Capability;
+using aidl::android::hardware::audio::effect::Descriptor;
+using aidl::android::hardware::audio::effect::EffectNullUuid;
+using aidl::android::hardware::audio::effect::Equalizer;
+using aidl::android::hardware::audio::effect::EqualizerTypeUUID;
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::Parameter;
+
+/**
+ * Here we focus on specific parameter checking, general IEffect interfaces testing performed in
+ * VtsAudioEfectTargetTest.
+ */
+using EqualizerParamTestParam = std::tuple<int, int, int>;
+
+class EqualizerParamTest : public ::testing::TestWithParam<EqualizerParamTestParam>,
+ public EffectHelper {
+ public:
+ EqualizerParamTest()
+ : EffectHelper(android::getAidlHalInstanceNames(IFactory::descriptor)[0]),
+ mParamPresetIndex(std::get<0 /* kPresetIndexRange */>(GetParam())),
+ mParamBandIndex(std::get<1 /* kBandIndexRange */>(GetParam())),
+ mParamBandLevel(std::get<2 /* kBandLevelRange */>(GetParam())) {}
+
+ void SetUp() override {
+ CreateEffectsWithUUID(EqualizerTypeUUID);
+ initParamCommonFormat();
+ initParamCommon();
+ initParamSpecific();
+ OpenEffects(EqualizerTypeUUID);
+ SCOPED_TRACE(testing::Message() << "preset: " << mParamPresetIndex << " bandIdx "
+ << mParamBandIndex << " level " << mParamBandLevel);
+ }
+
+ void TearDown() override {
+ CloseEffects();
+ DestroyEffects();
+ CleanUp();
+ }
+
+ const int mParamPresetIndex;
+ const int mParamBandIndex;
+ const int mParamBandLevel;
+
+ void SetAndGetEqualizerParameters() {
+ auto functor = [&](const std::shared_ptr<IEffect>& effect) {
+ for (auto& it : mTags) {
+ auto& tag = it.first;
+ auto& eq = it.second;
+
+ // validate parameter
+ Descriptor desc;
+ ASSERT_STATUS(EX_NONE, effect->getDescriptor(&desc));
+ const bool valid = isTagInRange(it.first, it.second, desc);
+ const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
+
+ // set
+ Parameter expectParam;
+ Parameter::Specific specific;
+ specific.set<Parameter::Specific::equalizer>(*eq.get());
+ expectParam.set<Parameter::specific>(specific);
+ EXPECT_STATUS(expected, effect->setParameter(expectParam))
+ << expectParam.toString();
+
+ // get
+ if (expected == EX_NONE) {
+ Parameter getParam;
+ Parameter::Specific::Id id;
+ id.set<Parameter::Specific::Id::equalizerTag>(tag);
+ // if set success, then get should match
+ EXPECT_STATUS(expected, effect->getParameter(id, &getParam));
+ EXPECT_EQ(expectParam, getParam) << "\n"
+ << expectParam.toString() << "\n"
+ << getParam.toString();
+ }
+ }
+ };
+ EXPECT_NO_FATAL_FAILURE(ForEachEffect(functor));
+ }
+
+ void addPresetParam(int preset) {
+ Equalizer eq;
+ eq.set<Equalizer::preset>(preset);
+ mTags.push_back({Equalizer::preset, std::make_unique<Equalizer>(std::move(eq))});
+ }
+
+ void addBandLevelsParam(std::vector<Equalizer::BandLevel>& bandLevels) {
+ Equalizer eq;
+ eq.set<Equalizer::bandLevels>(bandLevels);
+ mTags.push_back({Equalizer::bandLevels, std::make_unique<Equalizer>(std::move(eq))});
+ }
+
+ bool isTagInRange(const Equalizer::Tag& tag, const std::unique_ptr<Equalizer>& eq,
+ const Descriptor& desc) const {
+ std::cout << "xxx" << toString(tag) << " " << desc.toString();
+ const Equalizer::Capability& eqCap = desc.capability.get<Capability::equalizer>();
+ switch (tag) {
+ case Equalizer::preset: {
+ int index = eq->get<Equalizer::preset>();
+ return isPresetIndexInRange(eqCap, index);
+ }
+ case Equalizer::bandLevels: {
+ auto& bandLevel = eq->get<Equalizer::bandLevels>();
+ return isBandIndexInRange(eqCap, bandLevel);
+ }
+ default:
+ return false;
+ }
+ return false;
+ }
+
+ bool isPresetIndexInRange(const Equalizer::Capability& cap, int idx) const {
+ const auto [min, max] =
+ std::minmax_element(cap.presets.begin(), cap.presets.end(),
+ [](const auto& a, const auto& b) { return a.index < b.index; });
+ return idx >= min->index && idx <= max->index;
+ }
+
+ bool isBandIndexInRange(const Equalizer::Capability& cap,
+ const std::vector<Equalizer::BandLevel>& bandLevel) const {
+ for (auto& it : bandLevel) {
+ if (!isBandIndexInRange(cap, it.index)) return false;
+ }
+ return true;
+ }
+
+ bool isBandIndexInRange(const Equalizer::Capability& cap, int idx) const {
+ const auto [min, max] =
+ std::minmax_element(cap.bandFrequencies.begin(), cap.bandFrequencies.end(),
+ [](const auto& a, const auto& b) { return a.index < b.index; });
+ return idx >= min->index && idx <= max->index;
+ }
+
+ private:
+ Equalizer::VendorExtension mVendorExtension;
+ std::vector<std::pair<Equalizer::Tag, std::unique_ptr<Equalizer>>> mTags;
+
+ bool validCapabilityTag(Capability& cap) { return cap.getTag() == Capability::equalizer; }
+
+ void initParamSpecific() {
+ Equalizer eq;
+ eq.set<Equalizer::preset>(0);
+ Parameter::Specific specific;
+ specific.set<Parameter::Specific::equalizer>(eq);
+ setSpecific(specific);
+ }
+
+ void CleanUp() { mTags.clear(); }
+};
+
+TEST_P(EqualizerParamTest, SetAndGetPreset) {
+ EXPECT_NO_FATAL_FAILURE(addPresetParam(mParamPresetIndex));
+ SetAndGetEqualizerParameters();
+}
+
+TEST_P(EqualizerParamTest, SetAndGetSingleBand) {
+ Equalizer::BandLevel bandLevel = {mParamBandIndex, mParamBandLevel};
+ std::vector<Equalizer::BandLevel> bandLevels;
+ bandLevels.push_back(bandLevel);
+ EXPECT_NO_FATAL_FAILURE(addBandLevelsParam(bandLevels));
+ SetAndGetEqualizerParameters();
+}
+
+/**
+ Testing preset index range with [-10, 10], assuming the min/max preset index supported by
+effect is in this range.
+ This range is verified with IEffect.getDescriptor(): for any index supported vts expect EX_NONE
+from IEffect.setParameter(), otherwise expect EX_ILLEGAL_ARGUMENT.
+ */
+constexpr std::pair<int, int> kPresetIndexRange = {-1, 10}; // valid range [0, 9]
+constexpr std::pair<int, int> kBandIndexRange = {-1, 5}; // valid range [0, 4]
+constexpr std::pair<int, int> kBandLevelRange = {-5, 5}; // needs update with implementation
+
+INSTANTIATE_TEST_SUITE_P(
+ EqualizerTest, EqualizerParamTest,
+ ::testing::Combine(testing::Range(kPresetIndexRange.first, kPresetIndexRange.second),
+ testing::Range(kBandIndexRange.first, kBandIndexRange.second),
+ testing::Range(kBandLevelRange.first, kBandLevelRange.second)));
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EqualizerTest);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
index c295aaa..93688fc 100644
--- a/audio/common/all-versions/default/tests/hidlutils_tests.cpp
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -17,7 +17,6 @@
#include <array>
#include <string>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#define LOG_TAG "HidlUtils_Test"
@@ -1125,12 +1124,15 @@
TYPED_TEST_SUITE(FilterTest, FilterTestTypeParams);
TYPED_TEST(FilterTest, FilterOutNonVendorTags) {
- SKIP_WITH_HWASAN; // b/230535046
TypeParam emptyTags;
EXPECT_EQ(emptyTags, HidlUtils::filterOutNonVendorTags(emptyTags));
- TypeParam allVendorTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
- EXPECT_EQ(allVendorTags, HidlUtils::filterOutNonVendorTags(allVendorTags));
+ // b/248421569, allocate two vendor tags at a time can run out of memory
+ // TypeParam allVendorTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
+ TypeParam allVendorTags1 = {{"VX_GOOGLE_VR_42"}};
+ EXPECT_EQ(allVendorTags1, HidlUtils::filterOutNonVendorTags(allVendorTags1));
+ TypeParam allVendorTags2 = {{"VX_GOOGLE_1E100"}};
+ EXPECT_EQ(allVendorTags2, HidlUtils::filterOutNonVendorTags(allVendorTags2));
TypeParam oneVendorTag = {{"", "VX_GOOGLE_VR", "random_string"}};
TypeParam oneVendorTagOnly = HidlUtils::filterOutNonVendorTags(oneVendorTag);
diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index b57dc63..5dc42dc 100644
--- a/audio/effect/all-versions/default/Effect.cpp
+++ b/audio/effect/all-versions/default/Effect.cpp
@@ -700,8 +700,21 @@
void* dataPtr = halDataSize > 0 ? &halData[0] : NULL;
void* resultPtr = halResultSize > 0 ? &halResult[0] : NULL;
- status_t status =
- (*mHandle)->command(mHandle, commandId, halDataSize, dataPtr, &halResultSize, resultPtr);
+ status_t status = BAD_VALUE;
+ switch (commandId) {
+ case 'gtid': // retrieve the tid, used for spatializer priority boost
+ if (halDataSize == 0 && resultMaxSize == sizeof(int32_t)) {
+ auto ptid = (int32_t*)resultPtr;
+ ptid[0] = mProcessThread ? mProcessThread->getTid() : -1;
+ status = OK;
+ break; // we have handled 'gtid' here.
+ }
+ [[fallthrough]]; // allow 'gtid' overload (checked halDataSize and resultMaxSize).
+ default:
+ status = (*mHandle)->command(mHandle, commandId, halDataSize, dataPtr, &halResultSize,
+ resultPtr);
+ break;
+ }
hidl_vec<uint8_t> result;
if (status == OK && resultPtr != NULL) {
result.setToExternal(&halResult[0], halResultSize);
diff --git a/automotive/remoteaccess/Android.bp b/automotive/remoteaccess/Android.bp
new file mode 100644
index 0000000..ac04354
--- /dev/null
+++ b/automotive/remoteaccess/Android.bp
@@ -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.
+
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.automotive.remoteaccess",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/automotive/remoteaccess/**/*.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "module_current",
+ min_sdk_version: "31",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.car.framework",
+ ],
+ },
+ },
+}
diff --git a/automotive/remoteaccess/OWNERS b/automotive/remoteaccess/OWNERS
new file mode 100644
index 0000000..d6969e5
--- /dev/null
+++ b/automotive/remoteaccess/OWNERS
@@ -0,0 +1,2 @@
+ericjeong@google.com
+shanyu@google.com
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ApState.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ApState.aidl
new file mode 100644
index 0000000..da4f1d4
--- /dev/null
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/ApState.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.remoteaccess;
+@VintfStability
+parcelable ApState {
+ boolean isReadyForRemoteTask;
+ boolean isWakeupRequired;
+}
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
new file mode 100644
index 0000000..9b6eb2f
--- /dev/null
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteAccess.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.automotive.remoteaccess;
+@VintfStability
+interface IRemoteAccess {
+ String getDeviceId();
+ String getWakeupServiceName();
+ void setRemoteTaskCallback(android.hardware.automotive.remoteaccess.IRemoteTaskCallback callback);
+ void clearRemoteTaskCallback();
+ void notifyApStateChange(in android.hardware.automotive.remoteaccess.ApState state);
+}
diff --git a/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
new file mode 100644
index 0000000..295100e
--- /dev/null
+++ b/automotive/remoteaccess/aidl_api/android.hardware.automotive.remoteaccess/current/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.remoteaccess;
+@VintfStability
+interface IRemoteTaskCallback {
+ oneway void onRemoteTaskRequested(String clientId, in byte[] data);
+}
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/ApState.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/ApState.aidl
new file mode 100644
index 0000000..c8eb3ef
--- /dev/null
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/ApState.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.automotive.remoteaccess;
+
+@VintfStability
+parcelable ApState {
+ /**
+ * Whether AP (application processor) is ready to receive remote tasks.
+ *
+ * If this is true. AP is powered on and the car service is ready to handle
+ * remote tasks.
+ */
+ boolean isReadyForRemoteTask;
+ /**
+ * Whether AP (application processor) needs to be woken up.
+ *
+ * While the AP is shutting down, this will be set to false to prevent the
+ * wakeup signal to interrupt the shutdown process. At the last step of the
+ * shutdown process, this will be set to true so that AP will be waken
+ * up when task arrives. After AP starts up, this will be set to false
+ * to prevent unnecessary wakeup signal.
+ */
+ boolean isWakeupRequired;
+}
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
new file mode 100644
index 0000000..a198b03
--- /dev/null
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteAccess.aidl
@@ -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.
+ */
+
+package android.hardware.automotive.remoteaccess;
+
+import android.hardware.automotive.remoteaccess.ApState;
+import android.hardware.automotive.remoteaccess.IRemoteTaskCallback;
+
+/**
+ * Interface representing a remote wakeup client.
+ *
+ * A wakeup client is a binary outside Android framework that communicates with
+ * a wakeup server and receives wake up command.
+ */
+@VintfStability
+interface IRemoteAccess {
+ /**
+ * Gets a unique device ID that could be recognized by wake up server.
+ *
+ * This device ID is provisioned during car production and is registered
+ * with the wake up server.
+ *
+ * @return a unique device ID.
+ */
+ String getDeviceId();
+
+ /**
+ * Gets the name for the remote wakeup server.
+ *
+ * This name will be provided to remote task server during registration
+ * and used by remote task server to find the remote wakeup server to
+ * use for waking up the device. This name must be pre-negotiated between
+ * the remote wakeup server/client and the remote task server/client and
+ * must be unique. We recommend the format to be a human readable string
+ * with reverse domain name notation (reverse-DNS), e.g.
+ * "com.google.vehicle.wakeup".
+ */
+ String getWakeupServiceName();
+
+ /**
+ * Sets a callback to be called when a remote task is requested.
+ *
+ * @param callback A callback to be called when a remote task is requested.
+ */
+ void setRemoteTaskCallback(IRemoteTaskCallback callback);
+
+ /**
+ * Clears a previously set remote task callback.
+ *
+ * If no callback was set, this operation is no-op.
+ */
+ void clearRemoteTaskCallback();
+
+ /**
+ * Notifies whether AP is ready to receive remote tasks.
+ *
+ * <p>Wakeup client should store and use this state until a new call with a
+ * different state arrives.
+ *
+ * <p>If {@code isReadyForRemoteTask} is true, the wakeup client may send
+ * the task received from the server to AP immediately.
+ *
+ * <p>If {@code isReadyForRemoteTask} is false, it must store the received
+ * remote tasks and wait until AP is ready to receive tasks. If it takes too
+ * long for AP to become ready, the task must be reported to remote task
+ * server as failed. Implementation must make sure no duplicate tasks are
+ * delivered to AP.
+ *
+ * <p>If {@code isWakeupRequired} is true, it must try to wake up AP when a
+ * remote task arrives or when there are pending requests.
+ *
+ * <p>If {@code isWakeupRequired} is false, it must not try to wake up AP.
+ */
+ void notifyApStateChange(in ApState state);
+}
diff --git a/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
new file mode 100644
index 0000000..7a1616f
--- /dev/null
+++ b/automotive/remoteaccess/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.remoteaccess;
+
+/**
+ * The callback interface for car service to receive tasks from wakup client.
+ */
+@VintfStability
+interface IRemoteTaskCallback {
+ /**
+ * A callback that is called when a remote task is requested.
+ *
+ * @param clientId An ID to uniquely identify a remote task client.
+ * @param data Opaque task data passed to the remote task client.
+ */
+ oneway void onRemoteTaskRequested(String clientId, in byte[] data);
+}
diff --git a/automotive/remoteaccess/hal/default/Android.bp b/automotive/remoteaccess/hal/default/Android.bp
new file mode 100644
index 0000000..a2bf86c
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/Android.bp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "android.hardware.automotive.remoteaccess@V1-default-service",
+ vendor: true,
+ vintf_fragments: ["remoteaccess-default-service.xml"],
+ init_rc: ["remoteaccess-default-service.rc"],
+ relative_install_path: "hw",
+ srcs: ["src/RemoteAccessImpl.cpp"],
+ whole_static_libs: [
+ "RemoteAccessService",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "liblog",
+ "libutils",
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ defaults: [
+ "vhalclient_defaults",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+ ],
+}
+
+cc_library {
+ name: "RemoteAccessService",
+ vendor_available: true,
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ srcs: [
+ "src/RemoteAccessService.cpp",
+ ],
+ whole_static_libs: [
+ "android.hardware.automotive.remoteaccess-V1-ndk",
+ "wakeup_client_protos",
+ "libvhalclient",
+ ],
+ defaults: [
+ "vhalclient_defaults",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+}
+
+cc_fuzz {
+ name: "android.hardware.automotive.remoteaccess@V1-default-service.aidl_fuzzer",
+ srcs: ["fuzzer/fuzzer.cpp"],
+ whole_static_libs: [
+ "RemoteAccessService",
+ ],
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "liblog",
+ "libutils",
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ defaults: [
+ "vhalclient_defaults",
+ "service_fuzzer_defaults",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+ ],
+ fuzz_config: {
+ cc: [
+ "shanyu@google.com",
+ ],
+ },
+}
diff --git a/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp b/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
new file mode 100644
index 0000000..292c80e
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 <RemoteAccessService.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gmock/gmock.h>
+#include <grpcpp/test/mock_stream.h>
+#include <wakeup_client.grpc.pb.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+using ::grpc::ClientAsyncReaderInterface;
+using ::grpc::ClientAsyncResponseReaderInterface;
+using ::grpc::ClientContext;
+using ::grpc::ClientReader;
+using ::grpc::ClientReaderInterface;
+using ::grpc::CompletionQueue;
+using ::grpc::Status;
+using ::grpc::testing::MockClientReader;
+using ::testing::_;
+using ::testing::Return;
+
+class MockGrpcClientStub : public WakeupClient::StubInterface {
+ public:
+ ClientReaderInterface<GetRemoteTasksResponse>* GetRemoteTasksRaw(
+ [[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const GetRemoteTasksRequest& request) override {
+ MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+ new MockClientReader<GetRemoteTasksResponse>();
+ ON_CALL(*mockClientReader, Finish()).WillByDefault(Return(Status::OK));
+ ON_CALL(*mockClientReader, Read(_)).WillByDefault(Return(false));
+ return mockClientReader;
+ }
+
+ Status NotifyWakeupRequired([[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const NotifyWakeupRequiredRequest& request,
+ [[maybe_unused]] NotifyWakeupRequiredResponse* response) {
+ return Status::OK;
+ }
+
+ // Async methods which we do not care.
+ ClientAsyncReaderInterface<GetRemoteTasksResponse>* AsyncGetRemoteTasksRaw(
+ [[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const GetRemoteTasksRequest& request,
+ [[maybe_unused]] CompletionQueue* cq, [[maybe_unused]] void* tag) {
+ return nullptr;
+ }
+
+ ClientAsyncReaderInterface<GetRemoteTasksResponse>* PrepareAsyncGetRemoteTasksRaw(
+ [[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const GetRemoteTasksRequest& request,
+ [[maybe_unused]] CompletionQueue* cq) {
+ return nullptr;
+ }
+
+ ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>* AsyncNotifyWakeupRequiredRaw(
+ [[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const NotifyWakeupRequiredRequest& request,
+ [[maybe_unused]] CompletionQueue* cq) {
+ return nullptr;
+ }
+
+ ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*
+ PrepareAsyncNotifyWakeupRequiredRaw([[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const NotifyWakeupRequiredRequest& request,
+ [[maybe_unused]] CompletionQueue* c) {
+ return nullptr;
+ }
+};
+
+} // namespace remoteaccess
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ android::hardware::automotive::remoteaccess::MockGrpcClientStub stub;
+ std::shared_ptr<android::hardware::automotive::remoteaccess::RemoteAccessService> service =
+ ndk::SharedRefBase::make<
+ android::hardware::automotive::remoteaccess::RemoteAccessService>(&stub);
+ android::fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/automotive/remoteaccess/hal/default/include/RemoteAccessService.h b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
new file mode 100644
index 0000000..74c2af4
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
@@ -0,0 +1,116 @@
+/*
+ * 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 <IVhalClient.h>
+#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteAccess.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
+#include <aidl/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_auto_utils.h>
+#include <utils/SystemClock.h>
+#include <wakeup_client.grpc.pb.h>
+
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+// A IRemoteTaskCallback implementation for debug purpose.
+class DebugRemoteTaskCallback final
+ : public aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback {
+ public:
+ DebugRemoteTaskCallback() { mStartTimeMillis = android::uptimeMillis(); };
+
+ ndk::ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
+ const std::vector<uint8_t>& data) override;
+ std::string printTasks();
+
+ private:
+ struct TaskData {
+ std::string clientId;
+ std::vector<uint8_t> data;
+ };
+
+ std::mutex mLock;
+ int64_t mStartTimeMillis;
+ std::vector<TaskData> mTasks;
+};
+
+class RemoteAccessService
+ : public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess {
+ public:
+ explicit RemoteAccessService(WakeupClient::StubInterface* grpcStub);
+
+ ~RemoteAccessService();
+
+ ndk::ScopedAStatus getDeviceId(std::string* deviceId) override;
+
+ ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) override;
+
+ ndk::ScopedAStatus setRemoteTaskCallback(
+ const std::shared_ptr<
+ aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>&
+ callback) override;
+
+ ndk::ScopedAStatus clearRemoteTaskCallback() override;
+
+ ndk::ScopedAStatus notifyApStateChange(
+ const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override;
+
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ private:
+ // For testing.
+ friend class RemoteAccessServiceUnitTest;
+
+ static bool checkDumpPermission();
+
+ WakeupClient::StubInterface* mGrpcStub;
+ std::thread mThread;
+ std::mutex mLock;
+ std::condition_variable mCv;
+ std::shared_ptr<aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>
+ mRemoteTaskCallback GUARDED_BY(mLock);
+ std::unique_ptr<grpc::ClientContext> mGetRemoteTasksContext GUARDED_BY(mLock);
+ // Associated with mCv to notify the task loop to stop waiting and exit.
+ bool mTaskWaitStopped GUARDED_BY(mLock);
+ // A mutex to make sure startTaskLoop does not overlap with stopTaskLoop.
+ std::mutex mStartStopTaskLoopLock;
+ bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock);
+ // Default wait time before retry connecting to remote access client is 10s.
+ size_t mRetryWaitInMs = 10'000;
+ std::shared_ptr<DebugRemoteTaskCallback> mDebugCallback;
+
+ void runTaskLoop();
+ void maybeStartTaskLoop();
+ void maybeStopTaskLoop();
+ ndk::ScopedAStatus getDeviceIdWithClient(
+ android::frameworks::automotive::vhal::IVhalClient& client, std::string* deviceId);
+
+ void setRetryWaitInMs(size_t retryWaitInMs) { mRetryWaitInMs = retryWaitInMs; }
+ void dumpHelp(int fd);
+};
+
+} // namespace remoteaccess
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/remoteaccess/hal/default/proto/Android.bp b/automotive/remoteaccess/hal/default/proto/Android.bp
new file mode 100644
index 0000000..3e0dba1
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/proto/Android.bp
@@ -0,0 +1,79 @@
+// 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"],
+}
+
+genrule {
+ name: "wakeup_client_pb_h",
+ tools: [
+ "aprotoc",
+ "protoc-gen-grpc-cpp-plugin",
+ ],
+ cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+ srcs: [
+ "wakeup_client.proto",
+ ],
+ out: [
+ "wakeup_client.pb.h",
+ "wakeup_client.grpc.pb.h",
+ ],
+}
+
+genrule {
+ name: "wakeup_client_pb_cc",
+ tools: [
+ "aprotoc",
+ "protoc-gen-grpc-cpp-plugin",
+ ],
+ cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+ srcs: [
+ "wakeup_client.proto",
+ ],
+ out: [
+ "wakeup_client.pb.cc",
+ "wakeup_client.grpc.pb.cc",
+ ],
+}
+
+cc_library_static {
+ name: "wakeup_client_protos",
+ vendor_available: true,
+ host_supported: true,
+ include_dirs: [
+ "external/protobuf/src",
+ ],
+ generated_headers: [
+ "wakeup_client_pb_h",
+ ],
+ export_generated_headers: [
+ "wakeup_client_pb_h",
+ ],
+ generated_sources: [
+ "wakeup_client_pb_cc",
+ ],
+ shared_libs: [
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+}
diff --git a/automotive/remoteaccess/hal/default/proto/wakeup_client.proto b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
new file mode 100644
index 0000000..4fe0d01
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.hardware.automotive.remoteaccess;
+
+/**
+ * Service provided by a wakeup client running on TCU.
+ */
+service WakeupClient {
+ /**
+ * Establish a long-live connection to receive remote tasks.
+ *
+ * <p>For the server, whenever a remote task arrives, if the connection is
+ * alive, it will use the return stream to return a task's information.
+ *
+ * <p>If the connection is not alive, the server must stores the remote task
+ * until a new connection is established (which means AP is ready to
+ * receive remote task again) and send the stored tasks.
+ *
+ * <p>If the server closes the connection, the client will try to
+ * reestablish the connection.
+ */
+ rpc GetRemoteTasks(GetRemoteTasksRequest) returns (stream GetRemoteTasksResponse) {}
+
+ /**
+ * Notifies whether AP is required to be waken up when remote task arrives.
+ *
+ * <p>Wakeup client should store and use this state until a new call with a
+ * different state arrives.
+ *
+ * <p>If {@code isWakeupRequired} in the request is true, it must wake up AP
+ * when a remote task arrives.
+ *
+ * <p>If {@code isWakeupRequired} in the request is false, it must not try
+ * to wake up AP.
+ */
+ rpc NotifyWakeupRequired(NotifyWakeupRequiredRequest) returns (NotifyWakeupRequiredResponse) {}
+}
+
+message GetRemoteTasksRequest {}
+
+message GetRemoteTasksResponse {
+ string clientId = 1;
+ bytes data = 2;
+}
+
+message NotifyWakeupRequiredRequest {
+ bool isWakeupRequired = 1;
+}
+
+message NotifyWakeupRequiredResponse {}
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc b/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc
new file mode 100644
index 0000000..b7a9cdc
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc
@@ -0,0 +1,4 @@
+service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-default-service
+ class hal
+ user vehicle_network
+ group system inet
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml b/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml
new file mode 100644
index 0000000..d050a1b
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.automotive.remoteaccess</name>
+ <version>1</version>
+ <fqname>IRemoteAccess/default</fqname>
+ </hal>
+</manifest>
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
new file mode 100644
index 0000000..8720c2f
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
@@ -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.
+ */
+
+#define LOG_TAG "RemoteAccessImpl"
+
+#include "RemoteAccessService.h"
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <grpcpp/create_channel.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+
+constexpr char SERVICE_NAME[] = "android.hardware.automotive.remoteaccess.IRemoteAccess/default";
+
+int main(int /* argc */, char* /* argv */[]) {
+ ALOGI("Registering RemoteAccessService as service...");
+
+#ifndef GRPC_SERVICE_ADDRESS
+ ALOGE("GRPC_SERVICE_ADDRESS is not defined, exiting");
+ exit(1);
+#endif
+ auto channel = grpc::CreateChannel(GRPC_SERVICE_ADDRESS, grpc::InsecureChannelCredentials());
+ auto clientStub = android::hardware::automotive::remoteaccess::WakeupClient::NewStub(channel);
+ auto service = ndk::SharedRefBase::make<
+ android::hardware::automotive::remoteaccess::RemoteAccessService>(clientStub.get());
+
+ binder_exception_t err = AServiceManager_addService(service->asBinder().get(), SERVICE_NAME);
+ if (err != EX_NONE) {
+ ALOGE("failed to register android.hardware.automotive.remote.IRemoteAccess service, "
+ "exception: %d",
+ err);
+ exit(1);
+ }
+
+ if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
+ ALOGE("%s", "failed to set thread pool max thread count");
+ exit(1);
+ }
+ ABinderProcess_startThreadPool();
+
+ ALOGI("RemoteAccess service Ready");
+
+ ABinderProcess_joinThreadPool();
+
+ ALOGW("Should not reach here");
+
+ return 0;
+}
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
new file mode 100644
index 0000000..5cd58d3
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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 "RemoteAccessService.h"
+
+#include <VehicleUtils.h>
+#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
+#include <android-base/stringprintf.h>
+#include <android/binder_status.h>
+#include <grpc++/grpc++.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::remoteaccess::ApState;
+using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
+using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
+using ::android::base::ScopedLockAssertion;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+using ::android::frameworks::automotive::vhal::IVhalClient;
+using ::android::hardware::automotive::vehicle::toInt;
+using ::grpc::ClientContext;
+using ::grpc::ClientReaderInterface;
+using ::grpc::Status;
+using ::grpc::StatusCode;
+using ::ndk::ScopedAStatus;
+
+const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup";
+constexpr char COMMAND_SET_AP_STATE[] = "--set-ap-state";
+constexpr char COMMAND_START_DEBUG_CALLBACK[] = "--start-debug-callback";
+constexpr char COMMAND_STOP_DEBUG_CALLBACK[] = "--stop-debug-callback";
+constexpr char COMMAND_SHOW_TASK[] = "--show-task";
+constexpr char COMMAND_GET_DEVICE_ID[] = "--get-device-id";
+
+std::vector<uint8_t> stringToBytes(const std::string& s) {
+ const char* data = s.data();
+ return std::vector<uint8_t>(data, data + s.size());
+}
+
+ScopedAStatus rpcStatusToScopedAStatus(const Status& status, const std::string& errorMsg) {
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ status.error_code(), (errorMsg + ", error: " + status.error_message()).c_str());
+}
+
+std::string printBytes(const std::vector<uint8_t>& bytes) {
+ std::string s;
+ for (size_t i = 0; i < bytes.size(); i++) {
+ StringAppendF(&s, "%02x", bytes[i]);
+ }
+ return s;
+}
+
+bool checkBoolFlag(const char* flag) {
+ return !strcmp(flag, "1") || !strcmp(flag, "0");
+}
+
+void dprintErrorStatus(int fd, const char* detail, const ScopedAStatus& status) {
+ dprintf(fd, "%s, code: %d, error: %s\n", detail, status.getStatus(), status.getMessage());
+}
+
+} // namespace
+
+RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub)
+ : mGrpcStub(grpcStub){};
+
+RemoteAccessService::~RemoteAccessService() {
+ maybeStopTaskLoop();
+}
+
+void RemoteAccessService::maybeStartTaskLoop() {
+ std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
+ if (mTaskLoopRunning) {
+ return;
+ }
+
+ mThread = std::thread([this]() { runTaskLoop(); });
+
+ mTaskLoopRunning = true;
+}
+
+void RemoteAccessService::maybeStopTaskLoop() {
+ std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
+ if (!mTaskLoopRunning) {
+ return;
+ }
+
+ {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ // Try to stop the reading stream.
+ if (mGetRemoteTasksContext) {
+ mGetRemoteTasksContext->TryCancel();
+ mGetRemoteTasksContext.reset();
+ }
+ mTaskWaitStopped = true;
+ mCv.notify_all();
+ }
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+
+ mTaskLoopRunning = false;
+}
+
+void RemoteAccessService::runTaskLoop() {
+ GetRemoteTasksRequest request = {};
+ std::unique_ptr<ClientReaderInterface<GetRemoteTasksResponse>> reader;
+ while (true) {
+ {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mGetRemoteTasksContext.reset(new ClientContext());
+ reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request);
+ }
+ GetRemoteTasksResponse response;
+ while (reader->Read(&response)) {
+ ALOGI("Receiving one task from remote task client");
+
+ std::shared_ptr<IRemoteTaskCallback> callback;
+ {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ callback = mRemoteTaskCallback;
+ }
+ if (callback == nullptr) {
+ ALOGD("No callback registered, task ignored");
+ continue;
+ }
+ ALOGD("Calling onRemoteTaskRequested callback for client ID: %s",
+ response.clientid().c_str());
+ ScopedAStatus callbackStatus = callback->onRemoteTaskRequested(
+ response.clientid(), stringToBytes(response.data()));
+ if (!callbackStatus.isOk()) {
+ ALOGE("Failed to call onRemoteTaskRequested callback, status: %d, message: %s",
+ callbackStatus.getStatus(), callbackStatus.getMessage());
+ }
+ }
+ Status status = reader->Finish();
+
+ ALOGE("GetRemoteTasks stream breaks, code: %d, message: %s, sleeping for 10s and retry",
+ status.error_code(), status.error_message().c_str());
+ // The long lasting connection should not return. But if the server returns, retry after
+ // 10s.
+ {
+ std::unique_lock lk(mLock);
+ if (mCv.wait_for(lk, std::chrono::milliseconds(mRetryWaitInMs), [this] {
+ ScopedLockAssertion lockAssertion(mLock);
+ return mTaskWaitStopped;
+ })) {
+ // If the stopped flag is set, we are quitting, exit the loop.
+ break;
+ }
+ }
+ }
+}
+
+ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) {
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ auto vhalClient = IVhalClient::tryCreate();
+ if (vhalClient == nullptr) {
+ ALOGE("Failed to connect to VHAL");
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ /*errorCode=*/0, "Failed to connect to VHAL to get device ID");
+ }
+ return getDeviceIdWithClient(*vhalClient.get(), deviceId);
+#else
+ // Don't use VHAL client in fuzzing since IPC is not allowed.
+ return ScopedAStatus::ok();
+#endif
+}
+
+ScopedAStatus RemoteAccessService::getDeviceIdWithClient(IVhalClient& vhalClient,
+ std::string* deviceId) {
+ auto result = vhalClient.getValueSync(
+ *vhalClient.createHalPropValue(toInt(VehicleProperty::INFO_VIN)));
+ if (!result.ok()) {
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ /*errorCode=*/0,
+ ("failed to get INFO_VIN from VHAL: " + result.error().message()).c_str());
+ }
+ *deviceId = (*result)->getStringValue();
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::getWakeupServiceName(std::string* wakeupServiceName) {
+ *wakeupServiceName = WAKEUP_SERVICE_NAME;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::setRemoteTaskCallback(
+ const std::shared_ptr<IRemoteTaskCallback>& callback) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mRemoteTaskCallback = callback;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mRemoteTaskCallback.reset();
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::notifyApStateChange(const ApState& newState) {
+ ClientContext context;
+ NotifyWakeupRequiredRequest request = {};
+ request.set_iswakeuprequired(newState.isWakeupRequired);
+ NotifyWakeupRequiredResponse response = {};
+ Status status = mGrpcStub->NotifyWakeupRequired(&context, request, &response);
+ if (!status.ok()) {
+ return rpcStatusToScopedAStatus(status, "Failed to notify isWakeupRequired");
+ }
+
+ if (newState.isReadyForRemoteTask) {
+ maybeStartTaskLoop();
+ } else {
+ maybeStopTaskLoop();
+ }
+ return ScopedAStatus::ok();
+}
+
+bool RemoteAccessService::checkDumpPermission() {
+ uid_t uid = AIBinder_getCallingUid();
+ return uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM;
+}
+
+void RemoteAccessService::dumpHelp(int fd) {
+ dprintf(fd, "%s",
+ (std::string("RemoteAccess HAL debug interface, Usage: \n") + COMMAND_SET_AP_STATE +
+ " [0/1](isReadyForRemoteTask) [0/1](isWakeupRequired) Set the new AP state\n" +
+ COMMAND_START_DEBUG_CALLBACK +
+ " Start a debug callback that will record the received tasks\n" +
+ COMMAND_STOP_DEBUG_CALLBACK + " Stop the debug callback\n" + COMMAND_SHOW_TASK +
+ " Show tasks received by debug callback\n" + COMMAND_GET_DEVICE_ID +
+ " Get device id\n")
+ .c_str());
+}
+
+binder_status_t RemoteAccessService::dump(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpPermission()) {
+ dprintf(fd, "Caller must be root, system or shell\n");
+ return STATUS_PERMISSION_DENIED;
+ }
+
+ if (numArgs == 0) {
+ dumpHelp(fd);
+ return STATUS_OK;
+ }
+
+ if (!strcmp(args[0], COMMAND_SET_AP_STATE)) {
+ if (numArgs < 3) {
+ dumpHelp(fd);
+ return STATUS_OK;
+ }
+ ApState apState = {};
+ const char* remoteTaskFlag = args[1];
+ if (!strcmp(remoteTaskFlag, "1") && !strcmp(remoteTaskFlag, "0")) {
+ dumpHelp(fd);
+ return STATUS_OK;
+ }
+ if (!checkBoolFlag(args[1])) {
+ dumpHelp(fd);
+ return STATUS_OK;
+ }
+ if (!strcmp(args[1], "1")) {
+ apState.isReadyForRemoteTask = true;
+ }
+ if (!checkBoolFlag(args[2])) {
+ dumpHelp(fd);
+ return STATUS_OK;
+ }
+ if (!strcmp(args[2], "1")) {
+ apState.isWakeupRequired = true;
+ }
+ auto status = notifyApStateChange(apState);
+ if (!status.isOk()) {
+ dprintErrorStatus(fd, "Failed to set AP state", status);
+ } else {
+ dprintf(fd, "successfully set the new AP state\n");
+ }
+ } else if (!strcmp(args[0], COMMAND_START_DEBUG_CALLBACK)) {
+ mDebugCallback = ndk::SharedRefBase::make<DebugRemoteTaskCallback>();
+ setRemoteTaskCallback(mDebugCallback);
+ dprintf(fd, "Debug callback registered\n");
+ } else if (!strcmp(args[0], COMMAND_STOP_DEBUG_CALLBACK)) {
+ if (mDebugCallback) {
+ mDebugCallback.reset();
+ }
+ clearRemoteTaskCallback();
+ dprintf(fd, "Debug callback unregistered\n");
+ } else if (!strcmp(args[0], COMMAND_SHOW_TASK)) {
+ if (mDebugCallback) {
+ dprintf(fd, "%s", mDebugCallback->printTasks().c_str());
+ } else {
+ dprintf(fd, "Debug callback is not currently used, use \"%s\" first.\n",
+ COMMAND_START_DEBUG_CALLBACK);
+ }
+ } else if (!strcmp(args[0], COMMAND_GET_DEVICE_ID)) {
+ std::string deviceId;
+ auto status = getDeviceId(&deviceId);
+ if (!status.isOk()) {
+ dprintErrorStatus(fd, "Failed to get device ID", status);
+ } else {
+ dprintf(fd, "Device Id: %s\n", deviceId.c_str());
+ }
+ } else {
+ dumpHelp(fd);
+ }
+
+ return STATUS_OK;
+}
+
+ScopedAStatus DebugRemoteTaskCallback::onRemoteTaskRequested(const std::string& clientId,
+ const std::vector<uint8_t>& data) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mTasks.push_back({
+ .clientId = clientId,
+ .data = data,
+ });
+ return ScopedAStatus::ok();
+}
+
+std::string DebugRemoteTaskCallback::printTasks() {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ std::string s = StringPrintf("Received %zu tasks in %f seconds", mTasks.size(),
+ (android::uptimeMillis() - mStartTimeMillis) / 1000.);
+ for (size_t i = 0; i < mTasks.size(); i++) {
+ StringAppendF(&s, "Client Id: %s, Data: %s\n", mTasks[i].clientId.c_str(),
+ printBytes(mTasks[i].data).c_str());
+ }
+ return s;
+}
+
+} // namespace remoteaccess
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/remoteaccess/hal/default/test/Android.bp b/automotive/remoteaccess/hal/default/test/Android.bp
new file mode 100644
index 0000000..227175a
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/test/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "RemoteAccessServiceUnitTest",
+ vendor: true,
+ srcs: ["*.cpp"],
+ whole_static_libs: [
+ "RemoteAccessService",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "liblog",
+ "libutils",
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ // libgrpc++.so is installed as root, require root to access it.
+ require_root: true,
+ static_libs: [
+ "libgtest",
+ "libgmock",
+ ],
+ defaults: [
+ "vhalclient_defaults",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
new file mode 100644
index 0000000..8c4fa08
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.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 "RemoteAccessService.h"
+
+#include <AidlHalPropValue.h>
+#include <IVhalClient.h>
+#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
+#include <aidl/android/hardware/automotive/vehicle/VehiclePropValue.h>
+#include <gmock/gmock.h>
+#include <grpcpp/test/mock_stream.h>
+#include <gtest/gtest.h>
+#include <wakeup_client.grpc.pb.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+namespace {
+
+using ::android::base::ScopedLockAssertion;
+using ::android::frameworks::automotive::vhal::AidlHalPropValue;
+using ::android::frameworks::automotive::vhal::IHalPropConfig;
+using ::android::frameworks::automotive::vhal::IHalPropValue;
+using ::android::frameworks::automotive::vhal::ISubscriptionCallback;
+using ::android::frameworks::automotive::vhal::ISubscriptionClient;
+using ::android::frameworks::automotive::vhal::IVhalClient;
+using ::android::frameworks::automotive::vhal::VhalClientResult;
+
+using ::aidl::android::hardware::automotive::remoteaccess::ApState;
+using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
+using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+
+using ::grpc::ClientAsyncReaderInterface;
+using ::grpc::ClientAsyncResponseReaderInterface;
+using ::grpc::ClientContext;
+using ::grpc::ClientReader;
+using ::grpc::ClientReaderInterface;
+using ::grpc::CompletionQueue;
+using ::grpc::Status;
+using ::grpc::testing::MockClientReader;
+using ::ndk::ScopedAStatus;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+constexpr char kTestVin[] = "test_VIN";
+
+} // namespace
+
+class MockGrpcClientStub : public WakeupClient::StubInterface {
+ public:
+ MOCK_METHOD(ClientReaderInterface<GetRemoteTasksResponse>*, GetRemoteTasksRaw,
+ (ClientContext * context, const GetRemoteTasksRequest& request));
+ MOCK_METHOD(Status, NotifyWakeupRequired,
+ (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+ NotifyWakeupRequiredResponse* response));
+ // Async methods which we do not care.
+ MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, AsyncGetRemoteTasksRaw,
+ (ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq,
+ void* tag));
+ MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, PrepareAsyncGetRemoteTasksRaw,
+ (ClientContext * context, const GetRemoteTasksRequest& request,
+ CompletionQueue* cq));
+ MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
+ AsyncNotifyWakeupRequiredRaw,
+ (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+ CompletionQueue* cq));
+ MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
+ PrepareAsyncNotifyWakeupRequiredRaw,
+ (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+ CompletionQueue* cq));
+};
+
+class FakeVhalClient final : public android::frameworks::automotive::vhal::IVhalClient {
+ public:
+ inline bool isAidlVhal() { return true; }
+
+ VhalClientResult<std::unique_ptr<IHalPropValue>> getValueSync(
+ const IHalPropValue& requestValue) override {
+ auto propValue = std::make_unique<AidlHalPropValue>(requestValue.getPropId());
+ propValue->setStringValue(kTestVin);
+ return propValue;
+ }
+
+ std::unique_ptr<IHalPropValue> createHalPropValue(int32_t propId) override {
+ return std::make_unique<AidlHalPropValue>(propId);
+ }
+
+ // Functions we do not care.
+ std::unique_ptr<IHalPropValue> createHalPropValue([[maybe_unused]] int32_t propId,
+ [[maybe_unused]] int32_t areaId) override {
+ return nullptr;
+ }
+
+ void getValue([[maybe_unused]] const IHalPropValue& requestValue,
+ [[maybe_unused]] std::shared_ptr<GetValueCallbackFunc> callback) override {}
+
+ void setValue([[maybe_unused]] const IHalPropValue& requestValue,
+ [[maybe_unused]] std::shared_ptr<SetValueCallbackFunc> callback) override {}
+
+ VhalClientResult<void> setValueSync([[maybe_unused]] const IHalPropValue& requestValue) {
+ return {};
+ }
+
+ VhalClientResult<void> addOnBinderDiedCallback(
+ [[maybe_unused]] std::shared_ptr<OnBinderDiedCallbackFunc> callback) override {
+ return {};
+ }
+
+ VhalClientResult<void> removeOnBinderDiedCallback(
+ [[maybe_unused]] std::shared_ptr<OnBinderDiedCallbackFunc> callback) override {
+ return {};
+ }
+
+ VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getAllPropConfigs() override {
+ return std::vector<std::unique_ptr<IHalPropConfig>>();
+ }
+
+ VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getPropConfigs(
+ [[maybe_unused]] std::vector<int32_t> propIds) override {
+ return std::vector<std::unique_ptr<IHalPropConfig>>();
+ }
+
+ std::unique_ptr<ISubscriptionClient> getSubscriptionClient(
+ [[maybe_unused]] std::shared_ptr<ISubscriptionCallback> callback) override {
+ return nullptr;
+ }
+};
+
+class FakeRemoteTaskCallback : public BnRemoteTaskCallback {
+ public:
+ ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
+ const std::vector<uint8_t>& data) override {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mDataByClientId[clientId] = data;
+ mTaskCount++;
+ mCv.notify_all();
+ return ScopedAStatus::ok();
+ }
+
+ std::vector<uint8_t> getData(const std::string& clientId) { return mDataByClientId[clientId]; }
+
+ bool wait(size_t taskCount, size_t timeoutInSec) {
+ std::unique_lock<std::mutex> lock(mLock);
+ return mCv.wait_for(lock, std::chrono::seconds(timeoutInSec), [taskCount, this] {
+ ScopedLockAssertion lockAssertion(mLock);
+ return mTaskCount >= taskCount;
+ });
+ }
+
+ private:
+ std::mutex mLock;
+ std::unordered_map<std::string, std::vector<uint8_t>> mDataByClientId GUARDED_BY(mLock);
+ size_t mTaskCount GUARDED_BY(mLock) = 0;
+ std::condition_variable mCv;
+};
+
+class RemoteAccessServiceUnitTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ mGrpcWakeupClientStub = std::make_unique<MockGrpcClientStub>();
+ mService = ndk::SharedRefBase::make<RemoteAccessService>(mGrpcWakeupClientStub.get());
+ }
+
+ MockGrpcClientStub* getGrpcWakeupClientStub() { return mGrpcWakeupClientStub.get(); }
+
+ RemoteAccessService* getService() { return mService.get(); }
+
+ void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); }
+
+ ScopedAStatus getDeviceIdWithClient(IVhalClient& vhalClient, std::string* deviceId) {
+ return mService->getDeviceIdWithClient(vhalClient, deviceId);
+ }
+
+ private:
+ std::unique_ptr<MockGrpcClientStub> mGrpcWakeupClientStub;
+ std::shared_ptr<RemoteAccessService> mService;
+};
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) {
+ std::string serviceName;
+
+ ScopedAStatus status = getService()->getWakeupServiceName(&serviceName);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(serviceName, "com.google.vehicle.wakeup");
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestNotifyApStateChangeWakeupRequired) {
+ bool isWakeupRequired = false;
+ EXPECT_CALL(*getGrpcWakeupClientStub(), NotifyWakeupRequired)
+ .WillOnce([&isWakeupRequired]([[maybe_unused]] ClientContext* context,
+ const NotifyWakeupRequiredRequest& request,
+ [[maybe_unused]] NotifyWakeupRequiredResponse* response) {
+ isWakeupRequired = request.iswakeuprequired();
+ return Status();
+ });
+
+ ApState newState = {
+ .isWakeupRequired = true,
+ };
+ ScopedAStatus status = getService()->notifyApStateChange(newState);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_TRUE(isWakeupRequired);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasks) {
+ GetRemoteTasksResponse response1;
+ std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+ response1.set_clientid("1");
+ response1.set_data(testData.data(), testData.size());
+ GetRemoteTasksResponse response2;
+ response2.set_clientid("2");
+ std::shared_ptr<FakeRemoteTaskCallback> callback =
+ ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+ ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+ .WillByDefault(
+ [response1, response2]([[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const GetRemoteTasksRequest& request) {
+ // mockReader ownership will be transferred to the client so we don't own it
+ // here.
+ MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+ new MockClientReader<GetRemoteTasksResponse>();
+ EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+ EXPECT_CALL(*mockClientReader, Read(_))
+ .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
+ .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
+ .WillRepeatedly(Return(false));
+ return mockClientReader;
+ });
+
+ getService()->setRemoteTaskCallback(callback);
+ // Start the long live connection to receive tasks.
+ ApState newState = {
+ .isReadyForRemoteTask = true,
+ };
+ ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+ ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+ << "Did not receive enough tasks";
+ EXPECT_EQ(callback->getData("1"), testData);
+ EXPECT_EQ(callback->getData("2"), std::vector<uint8_t>());
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksRetryConnection) {
+ GetRemoteTasksResponse response;
+ std::shared_ptr<FakeRemoteTaskCallback> callback =
+ ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+ ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+ .WillByDefault([response]([[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const GetRemoteTasksRequest& request) {
+ // mockReader ownership will be transferred to the client so we don't own it here.
+ MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+ new MockClientReader<GetRemoteTasksResponse>();
+ EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+ // Connection fails after receiving one task. Should retry after some time.
+ EXPECT_CALL(*mockClientReader, Read(_))
+ .WillOnce(DoAll(SetArgPointee<0>(response), Return(true)))
+ .WillRepeatedly(Return(false));
+ return mockClientReader;
+ });
+
+ getService()->setRemoteTaskCallback(callback);
+ setRetryWaitInMs(100);
+ // Start the long live connection to receive tasks.
+ ApState newState = {
+ .isReadyForRemoteTask = true,
+ };
+ ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+ ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+ << "Did not receive enough tasks";
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksDefaultNotReady) {
+ GetRemoteTasksResponse response1;
+ std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+ response1.set_clientid("1");
+ response1.set_data(testData.data(), testData.size());
+ GetRemoteTasksResponse response2;
+ response2.set_clientid("2");
+ std::shared_ptr<FakeRemoteTaskCallback> callback =
+ ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+ EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(0);
+
+ // Default state is not ready for remote tasks, so no callback will be called.
+ getService()->setRemoteTaskCallback(callback);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksNotReadyAfterReady) {
+ GetRemoteTasksResponse response1;
+ std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+ response1.set_clientid("1");
+ response1.set_data(testData.data(), testData.size());
+ GetRemoteTasksResponse response2;
+ response2.set_clientid("2");
+ std::shared_ptr<FakeRemoteTaskCallback> callback =
+ ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+ ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+ .WillByDefault(
+ [response1, response2]([[maybe_unused]] ClientContext* context,
+ [[maybe_unused]] const GetRemoteTasksRequest& request) {
+ // mockReader ownership will be transferred to the client so we don't own it
+ // here.
+ MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+ new MockClientReader<GetRemoteTasksResponse>();
+ EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+ EXPECT_CALL(*mockClientReader, Read(_))
+ .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
+ .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
+ .WillRepeatedly(Return(false));
+ return mockClientReader;
+ });
+ // Should only be called once when is is ready for remote task.
+ EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(1);
+
+ getService()->setRemoteTaskCallback(callback);
+ setRetryWaitInMs(100);
+ // Start the long live connection to receive tasks.
+ ApState newState = {
+ .isReadyForRemoteTask = true,
+ };
+ ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+ ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+ << "Did not receive enough tasks";
+
+ // Stop the long live connection.
+ newState.isReadyForRemoteTask = false;
+ ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+ // Wait for the retry delay, but the loop should already exit.
+ std::this_thread::sleep_for(std::chrono::milliseconds(150));
+}
+
+TEST_F(RemoteAccessServiceUnitTest, testGetDeviceId) {
+ std::string deviceId;
+
+ FakeVhalClient vhalClient;
+
+ ASSERT_TRUE(getDeviceIdWithClient(vhalClient, &deviceId).isOk());
+ ASSERT_EQ(deviceId, kTestVin);
+}
+
+} // namespace remoteaccess
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/remoteaccess/test_grpc_server/README.md b/automotive/remoteaccess/test_grpc_server/README.md
new file mode 100644
index 0000000..deea764
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/README.md
@@ -0,0 +1,7 @@
+# Test GRPC Server.
+
+A test GRPC server that implements wakeup_client.proto. This test server acts
+as a reference implementation for a remote wakeup client running on TCU. This
+reference server also implements wakeup_client_debug.proto which is the
+debugging interface. It is recommended that the actual implementation also
+implements this test interface for easier end-to-end testing.
diff --git a/automotive/remoteaccess/test_grpc_server/impl/Android.bp b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
new file mode 100644
index 0000000..8aab2e3
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+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_binary {
+ name: "TestWakeupClientServer",
+ vendor: true,
+ srcs: ["src/*.cpp"],
+ local_include_dirs: ["include"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libutils",
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ whole_static_libs: [
+ "wakeup_client_protos",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+ ],
+}
diff --git a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
new file mode 100644
index 0000000..61c1ad3
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
@@ -0,0 +1,134 @@
+/*
+ * 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-base/thread_annotations.h>
+#include <utils/Looper.h>
+#include <wakeup_client.grpc.pb.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+// A class to generate fake task for testing. Not required for real implementation. In real
+// implementation, the task should come from remote task server. This class is thread-safe.
+class FakeTaskGenerator final {
+ public:
+ GetRemoteTasksResponse generateTask();
+
+ private:
+ // Simulates the client ID for each task.
+ std::atomic<int> mCurrentClientId = 0;
+ constexpr static uint8_t DATA[] = {0xde, 0xad, 0xbe, 0xef};
+};
+
+struct TaskInfo {
+ // This is unique per-task. Note that a task might be popped and put back into the task queue,
+ // it will have a new task ID but the same clientId in the task data.
+ int taskId;
+ int64_t timestampInMs;
+ GetRemoteTasksResponse taskData;
+};
+
+struct TaskInfoComparator {
+ // We want the smallest timestamp and smallest task ID on top.
+ bool operator()(const TaskInfo& l, const TaskInfo& r) {
+ return l.timestampInMs > r.timestampInMs ||
+ (l.timestampInMs == r.timestampInMs && l.taskId > r.taskId);
+ }
+};
+
+// forward-declaration.
+class TaskQueue;
+
+class TaskTimeoutMessageHandler final : public android::MessageHandler {
+ public:
+ TaskTimeoutMessageHandler(TaskQueue* taskQueue);
+ void handleMessage(const android::Message& message) override;
+
+ private:
+ TaskQueue* mTaskQueue;
+};
+
+// TaskQueue is thread-safe.
+class TaskQueue final {
+ public:
+ TaskQueue();
+ ~TaskQueue();
+
+ void add(const GetRemoteTasksResponse& response);
+ std::optional<GetRemoteTasksResponse> maybePopOne();
+ void waitForTask();
+ void stopWait();
+ void handleTaskTimeout();
+
+ private:
+ std::thread mCheckTaskTimeoutThread;
+ std::mutex mLock;
+ std::priority_queue<TaskInfo, std::vector<TaskInfo>, TaskInfoComparator> mTasks
+ GUARDED_BY(mLock);
+ // A variable to notify mTasks is not empty.
+ std::condition_variable mTasksNotEmptyCv;
+ bool mStopped GUARDED_BY(mLock);
+ android::sp<Looper> mLooper;
+ android::sp<TaskTimeoutMessageHandler> mTaskTimeoutMessageHandler;
+ std::atomic<int> mTaskIdCounter = 0;
+
+ void checkForTestTimeoutLoop();
+ void waitForTaskWithLock(std::unique_lock<std::mutex>& lock);
+};
+
+class TestWakeupClientServiceImpl final : public WakeupClient::Service {
+ public:
+ TestWakeupClientServiceImpl();
+
+ ~TestWakeupClientServiceImpl();
+
+ grpc::Status GetRemoteTasks(grpc::ServerContext* context, const GetRemoteTasksRequest* request,
+ grpc::ServerWriter<GetRemoteTasksResponse>* writer) override;
+
+ grpc::Status NotifyWakeupRequired(grpc::ServerContext* context,
+ const NotifyWakeupRequiredRequest* request,
+ NotifyWakeupRequiredResponse* response) override;
+
+ private:
+ // This is a thread for communicating with remote wakeup server (via network) and receive tasks
+ // from it.
+ std::thread mThread;
+ // A variable to notify server is stopping.
+ std::condition_variable mServerStoppedCv;
+ std::mutex mLock;
+ bool mServerStopped GUARDED_BY(mLock);
+
+ // Thread-safe. For test impl only.
+ FakeTaskGenerator mFakeTaskGenerator;
+ // Thread-sfae.
+ TaskQueue mTaskQueue;
+
+ void fakeTaskGenerateLoop();
+};
+
+} // namespace remoteaccess
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
new file mode 100644
index 0000000..2cdf68b
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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 "TestWakeupClientServiceImpl.h"
+
+#include <android-base/stringprintf.h>
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/SystemClock.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+namespace {
+
+using ::android::uptimeMillis;
+using ::android::base::ScopedLockAssertion;
+using ::android::base::StringPrintf;
+using ::grpc::ServerContext;
+using ::grpc::ServerWriter;
+using ::grpc::Status;
+
+constexpr int kTaskIntervalInMs = 5'000;
+constexpr int64_t KTaskTimeoutInMs = 20'000;
+
+} // namespace
+
+GetRemoteTasksResponse FakeTaskGenerator::generateTask() {
+ int clientId = mCurrentClientId++;
+ GetRemoteTasksResponse response;
+ response.set_data(std::string(reinterpret_cast<const char*>(DATA), sizeof(DATA)));
+ std::string clientIdStr = StringPrintf("%d", clientId);
+ response.set_clientid(clientIdStr);
+ return response;
+}
+
+TaskTimeoutMessageHandler::TaskTimeoutMessageHandler(TaskQueue* taskQueue)
+ : mTaskQueue(taskQueue) {}
+
+void TaskTimeoutMessageHandler::handleMessage(const android::Message& message) {
+ mTaskQueue->handleTaskTimeout();
+}
+
+TaskQueue::TaskQueue() {
+ mTaskTimeoutMessageHandler = android::sp<TaskTimeoutMessageHandler>::make(this);
+ mLooper = Looper::prepare(/*opts=*/0);
+ mCheckTaskTimeoutThread = std::thread([this] { checkForTestTimeoutLoop(); });
+}
+
+TaskQueue::~TaskQueue() {
+ {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mStopped = true;
+ }
+ while (true) {
+ // Remove all pending timeout handlers from queue.
+ if (!maybePopOne().has_value()) {
+ break;
+ }
+ }
+ if (mCheckTaskTimeoutThread.joinable()) {
+ mCheckTaskTimeoutThread.join();
+ }
+}
+
+std::optional<GetRemoteTasksResponse> TaskQueue::maybePopOne() {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ if (mTasks.size() == 0) {
+ return std::nullopt;
+ }
+ TaskInfo response = std::move(mTasks.top());
+ mTasks.pop();
+ mLooper->removeMessages(mTaskTimeoutMessageHandler, response.taskId);
+ return std::move(response.taskData);
+}
+
+void TaskQueue::add(const GetRemoteTasksResponse& task) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ if (mStopped) {
+ return;
+ }
+ int taskId = mTaskIdCounter++;
+ mTasks.push(TaskInfo{
+ .taskId = taskId,
+ .timestampInMs = uptimeMillis(),
+ .taskData = task,
+ });
+ android::Message message(taskId);
+ mLooper->sendMessageDelayed(KTaskTimeoutInMs * 1000, mTaskTimeoutMessageHandler, message);
+ mTasksNotEmptyCv.notify_all();
+}
+
+void TaskQueue::waitForTask() {
+ std::unique_lock<std::mutex> lock(mLock);
+ waitForTaskWithLock(lock);
+}
+
+void TaskQueue::waitForTaskWithLock(std::unique_lock<std::mutex>& lock) {
+ mTasksNotEmptyCv.wait(lock, [this] {
+ ScopedLockAssertion lockAssertion(mLock);
+ return mTasks.size() > 0 || mStopped;
+ });
+}
+
+void TaskQueue::stopWait() {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mStopped = true;
+ mTasksNotEmptyCv.notify_all();
+}
+
+void TaskQueue::checkForTestTimeoutLoop() {
+ Looper::setForThread(mLooper);
+
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(mLock);
+ if (mStopped) {
+ ALOGW("The TestWakeupClientServiceImpl is stopping, "
+ "exiting checkForTestTimeoutLoop");
+ return;
+ }
+ }
+
+ mLooper->pollAll(/*timeoutMillis=*/-1);
+ }
+}
+
+void TaskQueue::handleTaskTimeout() {
+ // We know which task timed-out from the taskId in the message. However, there is no easy way
+ // to remove a specific task with the task ID from the priority_queue, so we just check from
+ // the top of the queue (which have the oldest tasks).
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ int64_t now = uptimeMillis();
+ while (mTasks.size() > 0) {
+ const TaskInfo& taskInfo = mTasks.top();
+ if (taskInfo.timestampInMs + KTaskTimeoutInMs > now) {
+ break;
+ }
+ // In real implementation, this should report task failure to remote wakeup server.
+ ALOGW("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms",
+ taskInfo.taskData.clientid().c_str(), taskInfo.timestampInMs, now);
+ mTasks.pop();
+ }
+}
+
+TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {
+ mThread = std::thread([this] { fakeTaskGenerateLoop(); });
+}
+
+TestWakeupClientServiceImpl::~TestWakeupClientServiceImpl() {
+ {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ mServerStopped = true;
+ mServerStoppedCv.notify_all();
+ }
+ mTaskQueue.stopWait();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void TestWakeupClientServiceImpl::fakeTaskGenerateLoop() {
+ // In actual implementation, this should communicate with the remote server and receives tasks
+ // from it. Here we simulate receiving one remote task every {kTaskIntervalInMs}ms.
+ while (true) {
+ mTaskQueue.add(mFakeTaskGenerator.generateTask());
+ ALOGI("Sleeping for %d seconds until next task", kTaskIntervalInMs);
+
+ std::unique_lock lk(mLock);
+ if (mServerStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
+ ScopedLockAssertion lockAssertion(mLock);
+ return mServerStopped;
+ })) {
+ // If the stopped flag is set, we are quitting, exit the loop.
+ return;
+ }
+ }
+}
+
+Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
+ const GetRemoteTasksRequest* request,
+ ServerWriter<GetRemoteTasksResponse>* writer) {
+ ALOGD("GetRemoteTasks called");
+ while (true) {
+ mTaskQueue.waitForTask();
+
+ while (true) {
+ auto maybeTask = mTaskQueue.maybePopOne();
+ if (!maybeTask.has_value()) {
+ // No task left, loop again and wait for another task(s).
+ break;
+ }
+ // Loop through all the task in the queue but obtain lock for each element so we don't
+ // hold lock while writing the response.
+ const GetRemoteTasksResponse& response = maybeTask.value();
+ if (!writer->Write(response)) {
+ // Broken stream, maybe the client is shutting down.
+ ALOGW("Failed to deliver remote task to remote access HAL");
+ // The task failed to be sent, add it back to the queue. The order might change, but
+ // it is okay.
+ mTaskQueue.add(response);
+ return Status::CANCELLED;
+ }
+ }
+ }
+ return Status::OK;
+}
+
+Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
+ const NotifyWakeupRequiredRequest* request,
+ NotifyWakeupRequiredResponse* response) {
+ return Status::OK;
+}
+
+} // namespace remoteaccess
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
new file mode 100644
index 0000000..bb03e70
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 <string>
+
+#include "TestWakeupClientServiceImpl.h"
+
+#include <grpc/grpc.h>
+#include <grpcpp/security/server_credentials.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <utils/Log.h>
+
+using ::android::hardware::automotive::remoteaccess::TestWakeupClientServiceImpl;
+using ::grpc::Server;
+using ::grpc::ServerBuilder;
+using ::grpc::ServerWriter;
+
+void RunServer() {
+ std::string serverAddress(GRPC_SERVICE_ADDRESS);
+ std::shared_ptr<TestWakeupClientServiceImpl> service =
+ std::make_unique<TestWakeupClientServiceImpl>();
+
+ ServerBuilder builder;
+ builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials());
+ builder.RegisterService(service.get());
+ std::unique_ptr<Server> server(builder.BuildAndStart());
+ ALOGI("Test Remote Access GRPC Server listening on %s", serverAddress.c_str());
+ server->Wait();
+}
+
+int main(int argc, char** argv) {
+ RunServer();
+ return 0;
+}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 11cda3d..1852673 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -457,6 +457,9 @@
/**
* Parking brake state.
*
+ * This property is true indicates that the car's parking brake is currently engaged. False
+ * implies that the car's parking brake is currently disengaged.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
*/
@@ -465,6 +468,15 @@
/**
* Auto-apply parking brake.
*
+ * This property is true indicates that the car's automatic parking brake feature is currently
+ * enabled. False indicates that the car's automatic parking brake feature is currently
+ * disabled.
+ *
+ * This property is often confused with PARKING_BRAKE_ON. The difference is that
+ * PARKING_BRAKE_ON describes whether the actual parking brake is currently on/off, whereas
+ * PARKING_BRAKE_AUTO_APPLY describes whether the feature of automatic parking brake is enabled/
+ * disabled, and does not describe the current state of the actual parking brake.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ
*/
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 0c949d6..7b471e1 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -110,6 +110,16 @@
"minSampleRate": 1.0
},
{
+ "property": "VehicleProperty::PERF_VEHICLE_SPEED_DISPLAY",
+ "defaultValue": {
+ "floatValues": [
+ 0.0
+ ]
+ },
+ "maxSampleRate": 10.0,
+ "minSampleRate": 1.0
+ },
+ {
"property": "VehicleProperty::VEHICLE_SPEED_DISPLAY_UNITS",
"defaultValue": {
"int32Values": [
@@ -1954,6 +1964,36 @@
]
},
{
+ "property": "VehicleProperty::DOOR_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::DOOR_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::DOOR_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::DOOR_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::DOOR_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
"property": "VehicleProperty::MIRROR_Z_POS",
"defaultValue": {
"int32Values": [
diff --git a/automotive/vehicle/vts/Android.bp b/automotive/vehicle/vts/Android.bp
index 1cfd542..736787b 100644
--- a/automotive/vehicle/vts/Android.bp
+++ b/automotive/vehicle/vts/Android.bp
@@ -45,6 +45,7 @@
"general-tests",
"vts",
"automotive-tests",
+ "automotive-general-tests",
],
require_root: true,
}
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 68a1f26..651c9dc 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -56,43 +56,58 @@
return;
}
- if (FingerprintHalProperties::operation_enroll_fails().value_or(false)) {
- LOG(ERROR) << "Fail: operation_enroll_fails";
- cb->onError(Error::VENDOR, 0 /* vendorError */);
+ // Force error-out
+ auto err = FingerprintHalProperties::operation_enroll_error().value_or(0);
+ if (err != 0) {
+ LOG(ERROR) << "Fail: operation_enroll_error";
+ auto ec = convertError(err);
+ cb->onError(ec.first, ec.second);
return;
}
- // format is "<id>:<progress_ms>,<progress_ms>,...:<result>
+ // Format is "<id>:<progress_ms-[acquiredInfo..]>,...:<result>
auto nextEnroll = FingerprintHalProperties::next_enrollment().value_or("");
auto parts = Util::split(nextEnroll, ":");
if (parts.size() != 3) {
- LOG(ERROR) << "Fail: invalid next_enrollment";
+ LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll;
cb->onError(Error::VENDOR, 0 /* vendorError */);
return;
}
auto enrollmentId = std::stoi(parts[0]);
- auto progress = Util::split(parts[1], ",");
- for (size_t i = 0; i < progress.size(); i++) {
- auto left = progress.size() - i - 1;
- SLEEP_MS(std::stoi(progress[i]));
+ auto progress = parseEnrollmentCapture(parts[1]);
+ for (size_t i = 0; i < progress.size(); i += 2) {
+ auto left = (progress.size() - i) / 2 - 1;
+ auto duration = progress[i][0];
+ auto acquired = progress[i + 1];
+ auto N = acquired.size();
- if (shouldCancel(cancel)) {
- LOG(ERROR) << "Fail: cancel";
- cb->onError(Error::CANCELED, 0 /* vendorCode */);
- return;
+ for (int j = 0; j < N; j++) {
+ SLEEP_MS(duration / N);
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+ auto ac = convertAcquiredInfo(acquired[j]);
+ cb->onAcquired(ac.first, ac.second);
}
- cb->onAcquired(AcquiredInfo::GOOD, 0 /* vendorCode */);
if (left == 0 && !IS_TRUE(parts[2])) { // end and failed
LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
FingerprintHalProperties::next_enrollment({});
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
} else { // progress and update props if last time
+ LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left;
if (left == 0) {
auto enrollments = FingerprintHalProperties::enrollments();
enrollments.emplace_back(enrollmentId);
FingerprintHalProperties::enrollments(enrollments);
FingerprintHalProperties::next_enrollment({});
+ // change authenticatorId after new enrollment
+ auto id = FingerprintHalProperties::authenticator_id().value_or(0);
+ auto newId = id + 1;
+ FingerprintHalProperties::authenticator_id(newId);
LOG(INFO) << "Enrolled: " << enrollmentId;
}
cb->onEnrollmentProgress(enrollmentId, left);
@@ -104,12 +119,31 @@
const std::future<void>& cancel) {
BEGIN_OP(FingerprintHalProperties::operation_authenticate_latency().value_or(DEFAULT_LATENCY));
- auto now = Util::getSystemNanoTime();
- int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(0);
+ int64_t now = Util::getSystemNanoTime();
+ int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(10);
+ auto acquired = FingerprintHalProperties::operation_authenticate_acquired().value_or("1");
+ auto acquiredInfos = parseIntSequence(acquired);
+ int N = acquiredInfos.size();
+
+ if (N == 0) {
+ LOG(ERROR) << "Fail to parse authentiate acquired info: " + acquired;
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
+ int i = 0;
do {
if (FingerprintHalProperties::operation_authenticate_fails().value_or(false)) {
LOG(ERROR) << "Fail: operation_authenticate_fails";
- cb->onError(Error::VENDOR, 0 /* vendorError */);
+ cb->onAuthenticationFailed();
+ return;
+ }
+
+ auto err = FingerprintHalProperties::operation_authenticate_error().value_or(0);
+ if (err != 0) {
+ LOG(ERROR) << "Fail: operation_authenticate_error";
+ auto ec = convertError(err);
+ cb->onError(ec.first, ec.second);
return;
}
@@ -126,20 +160,25 @@
return;
}
- auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
- auto enrolls = FingerprintHalProperties::enrollments();
- auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
- if (id > 0 && isEnrolled) {
- cb->onAuthenticationSucceeded(id, {} /* hat */);
- return;
+ if (i < N) {
+ auto ac = convertAcquiredInfo(acquiredInfos[i]);
+ cb->onAcquired(ac.first, ac.second);
+ i++;
}
- SLEEP_MS(100);
+ SLEEP_MS(duration / N);
} while (!Util::hasElapsed(now, duration));
- LOG(ERROR) << "Fail: not enrolled";
- cb->onAuthenticationFailed();
- cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
+ auto enrolls = FingerprintHalProperties::enrollments();
+ auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
+ if (id > 0 && isEnrolled) {
+ cb->onAuthenticationSucceeded(id, {} /* hat */);
+ return;
+ } else {
+ LOG(ERROR) << "Fail: fingerprint not enrolled";
+ cb->onAuthenticationFailed();
+ }
}
void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb,
@@ -147,17 +186,42 @@
BEGIN_OP(FingerprintHalProperties::operation_detect_interaction_latency().value_or(
DEFAULT_LATENCY));
- if (FingerprintHalProperties::operation_detect_interaction_fails().value_or(false)) {
- LOG(ERROR) << "Fail: operation_detect_interaction_fails";
- cb->onError(Error::VENDOR, 0 /* vendorError */);
+ int64_t duration =
+ FingerprintHalProperties::operation_detect_interaction_duration().value_or(10);
+ auto acquired = FingerprintHalProperties::operation_detect_interaction_acquired().value_or("1");
+ auto acquiredInfos = parseIntSequence(acquired);
+ int N = acquiredInfos.size();
+ int64_t now = Util::getSystemNanoTime();
+
+ if (N == 0) {
+ LOG(ERROR) << "Fail to parse detect interaction acquired info: " + acquired;
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
return;
}
- if (shouldCancel(cancel)) {
- LOG(ERROR) << "Fail: cancel";
- cb->onError(Error::CANCELED, 0 /* vendorCode */);
- return;
- }
+ int i = 0;
+ do {
+ auto err = FingerprintHalProperties::operation_detect_interaction_error().value_or(0);
+ if (err != 0) {
+ LOG(ERROR) << "Fail: operation_detect_interaction_error";
+ auto ec = convertError(err);
+ cb->onError(ec.first, ec.second);
+ return;
+ }
+
+ if (shouldCancel(cancel)) {
+ LOG(ERROR) << "Fail: cancel";
+ cb->onError(Error::CANCELED, 0 /* vendorCode */);
+ return;
+ }
+
+ if (i < N) {
+ auto ac = convertAcquiredInfo(acquiredInfos[i]);
+ cb->onAcquired(ac.first, ac.second);
+ i++;
+ }
+ SLEEP_MS(duration / N);
+ } while (!Util::hasElapsed(now, duration));
auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
auto enrolls = FingerprintHalProperties::enrollments();
@@ -211,24 +275,37 @@
void FakeFingerprintEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
BEGIN_OP(0);
- int64_t authenticatorId = FingerprintHalProperties::authenticator_id().value_or(0);
- if (FingerprintHalProperties::enrollments().size() > 0 && authenticatorId == 0) {
- authenticatorId = 99999999; // default authenticatorId, TODO(b/230515082)
+ int64_t authenticatorId;
+ if (FingerprintHalProperties::enrollments().size() == 0) {
+ authenticatorId = 0;
+ } else {
+ authenticatorId = FingerprintHalProperties::authenticator_id().value_or(0);
+ if (authenticatorId == 0) authenticatorId = 1;
}
cb->onAuthenticatorIdRetrieved(authenticatorId);
}
void FakeFingerprintEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
BEGIN_OP(0);
- auto id = FingerprintHalProperties::authenticator_id().value_or(0);
- auto newId = id + 1;
+ int64_t newId;
+ if (FingerprintHalProperties::enrollments().size() == 0) {
+ newId = 0;
+ } else {
+ auto id = FingerprintHalProperties::authenticator_id().value_or(0);
+ newId = id + 1;
+ }
FingerprintHalProperties::authenticator_id(newId);
cb->onAuthenticatorIdInvalidated(newId);
}
void FakeFingerprintEngine::resetLockoutImpl(ISessionCallback* cb,
- const keymaster::HardwareAuthToken& /*hat*/) {
+ const keymaster::HardwareAuthToken& hat) {
BEGIN_OP(0);
+ if (hat.mac.empty()) {
+ LOG(ERROR) << "Fail: hat in resetLockout()";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
FingerprintHalProperties::lockout(false);
cb->onLockoutCleared();
}
@@ -286,4 +363,96 @@
return {0 /* displayId (not used) */, 0 /* sensorLocationX */, 0 /* sensorLocationY */,
0 /* sensorRadius */, "" /* display */};
}
+
+std::vector<int32_t> FakeFingerprintEngine::parseIntSequence(const std::string& str,
+ const std::string& sep) {
+ std::vector<std::string> seqs = Util::split(str, sep);
+ std::vector<int32_t> res;
+
+ for (const auto& seq : seqs) {
+ int32_t val;
+ if (ParseInt(seq, &val)) {
+ res.push_back(val);
+ } else {
+ LOG(WARNING) << "Invalid int sequence:" + str;
+ res.clear();
+ break;
+ }
+ }
+
+ return res;
+}
+
+std::vector<std::vector<int32_t>> FakeFingerprintEngine::parseEnrollmentCapture(
+ const std::string& str) {
+ 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);
+ } else
+ res.push_back(defaultAcquiredInfo);
+
+ i = found + 1;
+ if (found == std::string::npos || found == N - 1) aborted = false;
+ }
+
+ if (aborted) {
+ LOG(ERROR) << "Failed to parse enrollment captures:" + str;
+ res.clear();
+ }
+
+ return res;
+}
+
+std::pair<AcquiredInfo, int32_t> FakeFingerprintEngine::convertAcquiredInfo(int32_t code) {
+ std::pair<AcquiredInfo, int32_t> res;
+ if (code > FINGERPRINT_ACQUIRED_VENDOR_BASE) {
+ res.first = AcquiredInfo::VENDOR;
+ res.second = code - FINGERPRINT_ACQUIRED_VENDOR_BASE;
+ } else {
+ res.first = (AcquiredInfo)code;
+ res.second = 0;
+ }
+ return res;
+}
+
+std::pair<Error, int32_t> FakeFingerprintEngine::convertError(int32_t code) {
+ std::pair<Error, int32_t> res;
+ if (code > FINGERPRINT_ERROR_VENDOR_BASE) {
+ res.first = Error::VENDOR;
+ res.second = code - FINGERPRINT_ERROR_VENDOR_BASE;
+ } else {
+ res.first = (Error)code;
+ res.second = 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 922781c..74e7caf 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -65,8 +65,18 @@
{SW_COMPONENT_ID, "" /* hardwareVersion */, "" /* firmwareVersion */,
"" /* serialNumber */, SW_VERSION}};
- common::CommonProps commonProps = {SENSOR_ID, SENSOR_STRENGTH, MAX_ENROLLMENTS_PER_USER,
- componentInfo};
+ auto sensorId = FingerprintHalProperties::sensor_id().value_or(SENSOR_ID);
+ auto sensorStrength =
+ FingerprintHalProperties::sensor_strength().value_or((int)SENSOR_STRENGTH);
+ auto maxEnrollments =
+ FingerprintHalProperties::max_enrollments().value_or(MAX_ENROLLMENTS_PER_USER);
+ auto navigationGuesture = FingerprintHalProperties::navigation_guesture().value_or(false);
+ auto detectInteraction = FingerprintHalProperties::detect_interaction().value_or(false);
+ auto displayTouch = FingerprintHalProperties::display_touch().value_or(true);
+ auto controlIllumination = FingerprintHalProperties::control_illumination().value_or(false);
+
+ common::CommonProps commonProps = {sensorId, (common::SensorStrength)sensorStrength,
+ maxEnrollments, componentInfo};
SensorLocation sensorLocation = mEngine->getSensorLocation();
@@ -75,8 +85,10 @@
*out = {{commonProps,
mSensorType,
{sensorLocation},
- SUPPORTS_NAVIGATION_GESTURES,
- false /* supportsDetectInteraction */}};
+ navigationGuesture,
+ detectInteraction,
+ displayTouch,
+ controlIllumination}};
return ndk::ScopedAStatus::ok();
}
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
index bb6bb48..ad471f7 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -65,7 +65,7 @@
$ adb shell getprop persist.vendor.fingerprint.virtual.enrollments
```
-### Authenticate
+## Authenticate
To authenticate successfully set the enrolled id that should succeed. Unset it
or change the value to make authenticate operations fail:
@@ -74,7 +74,52 @@
$ adb shell setprop vendor.fingerprint.virtual.enrollment_hit 1
````
-### View HAL State
+## Acquired Info Insertion
+
+Fingerprint image acquisition states at HAL are reported to framework via onAcquired() callback. The valid acquired state info for AIDL HAL include
+
+{UNKNOWN(0), GOOD(1), PARTIAL(2), INSUFFICIENT(3), SENSOR_DIRTY(4), TOO_SLOW(5), TOO_FAST(6), VENDOR(7), START(8), TOO_DARK(9), TOO_BRIGHT(10), IMMOBILE(11), RETRYING_CAPTURE(12)}
+
+Refer to [AcquiredInfo.aidl](../android/hardware/biometrics/fingerprint/AcquiredInfo.aidl) for details
+
+
+The states can be specified in sequence for the HAL operations involving fingerprint image captures, namely authenticate, enrollment and detectInteraction
+
+```shell
+$ adb shell setprop vendor.fingerprint.virtual.operation_authenticate_acquired 6,9,1
+$ adb shell setprop vendor.fingerprint.virtual.operation_detect_interaction_acquired 6,1
+$ adb shell setprop vendor.fingerprint.virtual.next_enrollment 2:1000-[5,1],500:true
+
+#next_enrollment format example:
+.---------------------- enrollment id (2)
+| .------------------ the image capture 1 duration (1000ms)
+| | .-------------- acquired info first (TOO_SLOW)
+| | | .------------ acquired info second (GOOD)
+| | | | .-------- the image capture 2 duration (500ms)
+| | | | | .---- enrollment end status (success)
+| | | | | |
+| | | | | |
+| | | | | |
+2:1000-[5,1],500:true
+```
+For vendor specific acquired info, acquiredInfo = 1000 + vendorAcquiredInfo
+
+## Error Insertion
+The valid error codes for AIDL HAL include
+
+{UNKNOWN(0), HW_UNAVAILABLE(1), UNABLE_TO_PROCESS(2), TIMEOUT(3), NO_SPACE(4), CANCELED(5), UNABLE_TO_REMOVE(6), VENDOR(7), BAD_CALIBRATION(8)}
+
+Refer to [Error.aidl](../android/hardware/biometrics/fingerprint/Error.aidl) for details
+
+
+There are many HAL operations which can result in errors, refer to [here](fingerprint.sysprop) file for details.
+
+```shell
+$ adb shell setprop vendor.fingerprint.virtual.operation_authenticate_error 8
+```
+For vendor specific error, errorCode = 1000 + vendorErrorCode
+
+## View HAL State
To view all the properties of the HAL (see `fingerprint.sysprop` file for the API):
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 9dfb74d..fa21663 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
@@ -5,7 +5,7 @@
api_name: "authenticator_id"
type: Long
access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.authenticator_id"
+ prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
}
prop {
api_name: "challenge"
@@ -14,6 +14,21 @@
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
@@ -28,7 +43,18 @@
prop {
api_name: "lockout"
access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.lockout"
+ 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"
@@ -37,12 +63,24 @@
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"
@@ -54,9 +92,22 @@
prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
}
prop {
- api_name: "operation_detect_interaction_fails"
+ api_name: "operation_detect_interaction_acquired"
+ type: String
access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails"
+ 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"
@@ -65,9 +116,10 @@
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
}
prop {
- api_name: "operation_enroll_fails"
+ api_name: "operation_enroll_error"
+ type: Integer
access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_enroll_fails"
+ prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
}
prop {
api_name: "operation_enroll_latency"
@@ -76,12 +128,24 @@
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
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index 85e93b0..9b8fada 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -32,8 +32,12 @@
api_name: "enrollment_hit"
}
-# the next enrollment in the format: "<id>:<delay>,<delay>,...:<result>"
-# for example: "2:0:true"
+# the next enrollment in the format of:
+# "<id>:<delay>,<delay>,...:<result>"
+# <delay> = <duration-[acquiredInfos]>
+# [acquiredInfos] = [acquiredInfo1, acquiredInfo2, ...]
+# (refer to README.md file for acquiredInfo values)
+# e.g. "2:100,20:true", "2:100-[5,1],20:true"
# this property is reset after enroll completes
prop {
prop_name: "vendor.fingerprint.virtual.next_enrollment"
@@ -45,7 +49,7 @@
# value for getAuthenticatorId or 0
prop {
- prop_name: "vendor.fingerprint.virtual.authenticator_id"
+ prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
type: Long
scope: Public
access: ReadWrite
@@ -63,7 +67,7 @@
# if locked out
prop {
- prop_name: "vendor.fingerprint.virtual.lockout"
+ prop_name: "persist.vendor.fingerprint.virtual.lockout"
type: Boolean
scope: Public
access: ReadWrite
@@ -79,22 +83,26 @@
api_name: "operation_authenticate_fails"
}
-# force all detectInteraction operations to fail
+# force all detectInteraction operations to error out
+# error consists of errorCode and vendorErrorCode
+# valid errorCodes are listed in README.md file
+# vendorErrorCode = (error>1000) ? error-1000 : 0
+# e.g. error(1002) --> errorCode(7) and vendorErrorCode(2)
prop {
- prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails"
- type: Boolean
+ prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
+ type: Integer
scope: Public
access: ReadWrite
- api_name: "operation_detect_interaction_fails"
+ api_name: "operation_detect_interaction_error"
}
-# force all enroll operations to fail
+# force all enroll operations to result in error
prop {
- prop_name: "vendor.fingerprint.virtual.operation_enroll_fails"
- type: Boolean
+ prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
+ type: Integer
scope: Public
access: ReadWrite
- api_name: "operation_enroll_fails"
+ api_name: "operation_enroll_error"
}
# add a latency to authentication operations
@@ -134,6 +142,15 @@
api_name: "operation_authenticate_duration"
}
+# insert error for authenticate operations
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_error"
+}
+
# sensor location
# <x>:<y>:<radius> in pixel
prop {
@@ -143,3 +160,99 @@
access: ReadWrite
api_name: "sensor_location"
}
+
+# acquired info during authentication in format of sequence
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
+ type: String
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_authenticate_acquired"
+}
+
+# millisecond duration for detect interaction operations
+# (waits for changes to enrollment_hit)
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_detect_interaction_duration"
+}
+
+# acquired info during detect interaction operation in format of sequence
+# e.g. 5,6,1 (TOO_SLOW, TOO_FAST, GOOD)
+# onAcquired() callback will be invoked in sequence
+# vendorAcquiredCode = (acquired>1000) ? acquired-1000 : 0
+prop {
+ prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
+ type: String
+ scope: Public
+ access: ReadWrite
+ api_name: "operation_detect_interaction_acquired"
+}
+
+# sensor id (default: 5)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "sensor_id"
+}
+
+# sensor strength (default: 2)
+# [0=CONVENECE, 1=WEAK, 2=STRONG]
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "sensor_strength"
+}
+
+# max enrollments per user (default: 5)
+#
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
+ type: Integer
+ scope: Public
+ access: ReadWrite
+ api_name: "max_enrollments"
+}
+
+# whether support navigation guestures (default: false)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "navigation_guesture"
+}
+
+# whether support detect interaction (default: false)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "detect_interaction"
+}
+
+# whether support display touch by hal (default: true)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "display_touch"
+}
+
+# whether support illumination control by hal (default: false)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ api_name: "control_illumination"
+}
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index d7df818..22b1744 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -59,7 +59,17 @@
virtual SensorLocation defaultSensorLocation();
+ std::vector<int32_t> parseIntSequence(const std::string& str, const std::string& sep = ",");
+
+ std::vector<std::vector<int32_t>> parseEnrollmentCapture(const std::string& str);
+
std::mt19937 mRandom;
+
+ 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);
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
index 8696d26..32d01f4 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -38,8 +38,9 @@
mLastChallengeRevoked = challenge;
return ndk::ScopedAStatus::ok();
};
- ::ndk::ScopedAStatus onError(fingerprint::Error error, int32_t) override {
+ ::ndk::ScopedAStatus onError(fingerprint::Error error, int32_t vendorCode) override {
mError = error;
+ mErrorVendorCode = vendorCode;
return ndk::ScopedAStatus::ok();
};
::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override {
@@ -62,7 +63,10 @@
mInteractionDetectedCount++;
return ndk::ScopedAStatus::ok();
};
- ndk::ScopedAStatus onAcquired(AcquiredInfo /*info*/, int32_t /*vendorCode*/) override {
+ ndk::ScopedAStatus onAcquired(AcquiredInfo info, int32_t vendorCode) override {
+ mLastAcquiredInfo = (int32_t)info;
+ mLastAcquiredVendorCode = vendorCode;
+ mLastAcquiredCount++;
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus onEnrollmentsEnumerated(
@@ -94,6 +98,7 @@
ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
Error mError = Error::UNKNOWN;
+ int32_t mErrorVendorCode = 0;
int64_t mLastChallenge = -1;
int64_t mLastChallengeRevoked = -1;
int32_t mLastEnrolled = -1;
@@ -105,6 +110,9 @@
bool mAuthenticatorIdInvalidated = false;
bool mLockoutPermanent = false;
int mInteractionDetectedCount = 0;
+ int32_t mLastAcquiredInfo = -1;
+ int32_t mLastAcquiredVendorCode = -1;
+ int32_t mLastAcquiredCount = 0;
};
class FakeFingerprintEngineTest : public ::testing::Test {
@@ -116,6 +124,12 @@
mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
}
+ void TearDown() override {
+ FingerprintHalProperties::operation_authenticate_error(0);
+ FingerprintHalProperties::operation_detect_interaction_error(0);
+ FingerprintHalProperties::operation_authenticate_acquired("");
+ }
+
FakeFingerprintEngine mEngine;
std::shared_ptr<TestSessionCallback> mCallback;
std::promise<void> mCancel;
@@ -135,11 +149,13 @@
TEST_F(FakeFingerprintEngineTest, ResetLockout) {
FingerprintHalProperties::lockout(true);
- mEngine.resetLockoutImpl(mCallback.get(), {});
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ mEngine.resetLockoutImpl(mCallback.get(), hat);
ASSERT_FALSE(FingerprintHalProperties::lockout().value_or(true));
}
TEST_F(FakeFingerprintEngineTest, AuthenticatorId) {
+ FingerprintHalProperties::enrollments({1});
FingerprintHalProperties::authenticator_id(50);
mEngine.getAuthenticatorIdImpl(mCallback.get());
ASSERT_EQ(50, mCallback->mLastAuthenticatorId);
@@ -162,6 +178,7 @@
ASSERT_EQ(1, FingerprintHalProperties::enrollments().size());
ASSERT_EQ(4, FingerprintHalProperties::enrollments()[0].value());
ASSERT_EQ(4, mCallback->mLastEnrolled);
+ ASSERT_EQ(1, mCallback->mLastAcquiredInfo);
}
TEST_F(FakeFingerprintEngineTest, EnrollCancel) {
@@ -189,12 +206,28 @@
ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value());
}
+TEST_F(FakeFingerprintEngineTest, EnrollAcquired) {
+ FingerprintHalProperties::enrollments({});
+ FingerprintHalProperties::next_enrollment("4:0,5-[12,1013]:true");
+ keymaster::HardwareAuthToken hat{.mac = {2, 4}};
+ int32_t prevCnt = mCallback->mLastAcquiredCount;
+ mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future());
+ ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value());
+ ASSERT_EQ(1, FingerprintHalProperties::enrollments().size());
+ ASSERT_EQ(4, FingerprintHalProperties::enrollments()[0].value());
+ ASSERT_EQ(4, mCallback->mLastEnrolled);
+ ASSERT_EQ(prevCnt + 3, mCallback->mLastAcquiredCount);
+ ASSERT_EQ(7, mCallback->mLastAcquiredInfo);
+ ASSERT_EQ(13, mCallback->mLastAcquiredVendorCode);
+}
+
TEST_F(FakeFingerprintEngineTest, Authenticate) {
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
ASSERT_FALSE(mCallback->mAuthenticateFailed);
ASSERT_EQ(2, mCallback->mLastAuthenticated);
+ ASSERT_EQ(1, mCallback->mLastAcquiredInfo);
}
TEST_F(FakeFingerprintEngineTest, AuthenticateCancel) {
@@ -211,7 +244,6 @@
FingerprintHalProperties::enrollment_hit({});
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
ASSERT_TRUE(mCallback->mAuthenticateFailed);
- ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS);
}
TEST_F(FakeFingerprintEngineTest, AuthenticateNotEnrolled) {
@@ -219,7 +251,6 @@
FingerprintHalProperties::enrollment_hit(3);
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
ASSERT_TRUE(mCallback->mAuthenticateFailed);
- ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS);
}
TEST_F(FakeFingerprintEngineTest, AuthenticateLockout) {
@@ -231,11 +262,41 @@
ASSERT_NE(mCallback->mError, Error::UNKNOWN);
}
+TEST_F(FakeFingerprintEngineTest, AuthenticateError8) {
+ FingerprintHalProperties::operation_authenticate_error(8);
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_EQ(mCallback->mError, (Error)8);
+ ASSERT_EQ(mCallback->mErrorVendorCode, 0);
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticateError9) {
+ FingerprintHalProperties::operation_authenticate_error(1009);
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_EQ(mCallback->mError, (Error)7);
+ ASSERT_EQ(mCallback->mErrorVendorCode, 9);
+}
+
+TEST_F(FakeFingerprintEngineTest, AuthenticateAcquired) {
+ FingerprintHalProperties::lockout(false);
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ FingerprintHalProperties::operation_authenticate_acquired("4,1009");
+ int32_t prevCount = mCallback->mLastAcquiredCount;
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ ASSERT_FALSE(mCallback->mAuthenticateFailed);
+ ASSERT_EQ(2, mCallback->mLastAuthenticated);
+ ASSERT_EQ(prevCount + 2, mCallback->mLastAcquiredCount);
+ ASSERT_EQ(7, mCallback->mLastAcquiredInfo);
+ ASSERT_EQ(9, mCallback->mLastAcquiredVendorCode);
+}
+
TEST_F(FakeFingerprintEngineTest, InteractionDetect) {
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
+ FingerprintHalProperties::operation_detect_interaction_acquired("");
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
+ ASSERT_EQ(1, mCallback->mLastAcquiredInfo);
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectCancel) {
@@ -261,6 +322,26 @@
ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
}
+TEST_F(FakeFingerprintEngineTest, InteractionDetectError) {
+ FingerprintHalProperties::operation_detect_interaction_error(8);
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
+ ASSERT_EQ(mCallback->mError, (Error)8);
+ ASSERT_EQ(mCallback->mErrorVendorCode, 0);
+}
+
+TEST_F(FakeFingerprintEngineTest, InteractionDetectAcquired) {
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ FingerprintHalProperties::operation_detect_interaction_acquired("4,1013");
+ int32_t prevCount = mCallback->mLastAcquiredCount;
+ mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
+ ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
+ ASSERT_EQ(prevCount + 2, mCallback->mLastAcquiredCount);
+ ASSERT_EQ(7, mCallback->mLastAcquiredInfo);
+ ASSERT_EQ(13, mCallback->mLastAcquiredVendorCode);
+}
+
TEST_F(FakeFingerprintEngineTest, EnumerateEnrolled) {
FingerprintHalProperties::enrollments({2, 4, 8});
mEngine.enumerateEnrollmentsImpl(mCallback.get());
@@ -290,6 +371,76 @@
}
}
+TEST_F(FakeFingerprintEngineTest, parseIntSequence) {
+ std::vector<int32_t> seqV;
+ seqV = mEngine.parseIntSequence("");
+ ASSERT_EQ(0, seqV.size());
+ seqV = mEngine.parseIntSequence("2");
+ ASSERT_EQ(1, seqV.size());
+ ASSERT_EQ(2, seqV[0]);
+ seqV = mEngine.parseIntSequence("2,3,4");
+ std::vector<int32_t> expV{2, 3, 4};
+ ASSERT_EQ(expV, seqV);
+ seqV = mEngine.parseIntSequence("2,3,a");
+ ASSERT_EQ(0, seqV.size());
+ seqV = mEngine.parseIntSequence("2, 3, 4");
+ ASSERT_EQ(expV, seqV);
+ seqV = mEngine.parseIntSequence("123,456");
+ ASSERT_EQ(2, seqV.size());
+ std::vector<int32_t> expV1{123, 456};
+ ASSERT_EQ(expV1, seqV);
+ seqV = mEngine.parseIntSequence("12f3,456");
+ ASSERT_EQ(0, seqV.size());
+}
+
+TEST_F(FakeFingerprintEngineTest, parseEnrollmentCaptureOk) {
+ std::vector<std::vector<int32_t>> ecV;
+ ecV = mEngine.parseEnrollmentCapture("100,200,300");
+ ASSERT_EQ(6, ecV.size());
+ std::vector<std::vector<int32_t>> expE{{100}, {200}, {300}};
+ std::vector<int32_t> defC{1};
+ for (int i = 0; i < ecV.size(); i += 2) {
+ ASSERT_EQ(expE[i / 2], ecV[i]);
+ ASSERT_EQ(defC, ecV[i + 1]);
+ }
+ ecV = mEngine.parseEnrollmentCapture("100");
+ ASSERT_EQ(2, ecV.size());
+ ASSERT_EQ(expE[0], ecV[0]);
+ ASSERT_EQ(defC, ecV[1]);
+
+ ecV = mEngine.parseEnrollmentCapture("100-[5,6,7]");
+ std::vector<int32_t> expC{5, 6, 7};
+ ASSERT_EQ(2, ecV.size());
+ for (int i = 0; i < ecV.size(); i += 2) {
+ ASSERT_EQ(expE[i / 2], ecV[i]);
+ ASSERT_EQ(expC, ecV[i + 1]);
+ }
+ ecV = mEngine.parseEnrollmentCapture("100-[5,6,7], 200, 300-[9,10]");
+ std::vector<std::vector<int32_t>> expC1{{5, 6, 7}, {1}, {9, 10}};
+ ASSERT_EQ(6, ecV.size());
+ for (int i = 0; i < ecV.size(); i += 2) {
+ ASSERT_EQ(expE[i / 2], ecV[i]);
+ ASSERT_EQ(expC1[i / 2], ecV[i + 1]);
+ }
+ ecV = mEngine.parseEnrollmentCapture("100-[5,6,7], 200-[2,1], 300-[9]");
+ std::vector<std::vector<int32_t>> expC2{{5, 6, 7}, {2, 1}, {9}};
+ ASSERT_EQ(ecV.size(), 6);
+ for (int i = 0; i < ecV.size(); i += 2) {
+ ASSERT_EQ(expE[i / 2], ecV[i]);
+ ASSERT_EQ(expC2[i / 2], ecV[i + 1]);
+ }
+}
+
+TEST_F(FakeFingerprintEngineTest, parseEnrollmentCaptureFail) {
+ 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) {
+ ecV = mEngine.parseEnrollmentCapture(s);
+ ASSERT_EQ(ecV.size(), 0);
+ }
+}
+
} // 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 485f401..7c0021f 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
@@ -47,14 +47,10 @@
sc.sensorRadius == FakeFingerprintEngineUdfps::defaultSensorRadius && sc.display == "");
}
-TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocation) {
- FingerprintHalProperties::sensor_location("");
- SensorLocation sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
-
+TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocationOk) {
auto loc = "100:200:30";
FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
+ SensorLocation sc = mEngine.getSensorLocation();
ASSERT_TRUE(sc.sensorLocationX == 100);
ASSERT_TRUE(sc.sensorLocationY == 200);
ASSERT_TRUE(sc.sensorRadius == 30);
@@ -66,8 +62,14 @@
ASSERT_TRUE(sc.sensorLocationY == 200);
ASSERT_TRUE(sc.sensorRadius == 30);
ASSERT_TRUE(sc.display == "screen1");
+}
- loc = "100";
+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));
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index ebd728d..e9b74b7 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -23,6 +23,7 @@
#include <android/binder_process.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <cutils/properties.h>
#include <fmq/AidlMessageQueue.h>
#include <cstdint>
@@ -248,7 +249,8 @@
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
case SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH: {
- ASSERT_FALSE(temp_provider_capabilities_.empty());
+ // empty capability means offload is unsupported since capabilities are
+ // not hardcoded
for (auto audio_capability : temp_provider_capabilities_) {
ASSERT_EQ(audio_capability.getTag(),
AudioCapabilities::leAudioCapabilities);
@@ -513,8 +515,9 @@
for (auto channel_mode : opus_capability->channelMode) {
OpusConfiguration opus_data{
.samplingFrequencyHz = samplingFrequencyHz,
+ .frameDurationUs = frameDurationUs,
.channelMode = channel_mode,
- .frameDurationUs = frameDurationUs};
+ };
opus_codec_specifics.push_back(
CodecConfiguration::CodecSpecific(opus_data));
}
@@ -623,8 +626,8 @@
for (auto channel_mode : a2dp_channel_modes) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
};
bool is_codec_config_valid = IsPcmConfigSupported(pcm_config);
DataMQDesc mq_desc;
@@ -937,8 +940,8 @@
for (auto channel_mode : hearing_aid_channel_modes_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
};
bool is_codec_config_valid = IsPcmConfigSupported(pcm_config);
DataMQDesc mq_desc;
@@ -1008,8 +1011,8 @@
for (auto data_interval_us : le_audio_output_data_interval_us_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
.dataIntervalUs = data_interval_us,
};
bool is_codec_config_valid =
@@ -1081,8 +1084,8 @@
for (auto data_interval_us : le_audio_input_data_interval_us_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
.dataIntervalUs = data_interval_us,
};
bool is_codec_config_valid =
@@ -1144,7 +1147,7 @@
bool supported) {
std::vector<Lc3Configuration> le_audio_codec_configs;
if (!supported) {
- Lc3Configuration lc3_config{.samplingFrequencyHz = 0, .pcmBitDepth = 0};
+ Lc3Configuration lc3_config{.pcmBitDepth = 0, .samplingFrequencyHz = 0};
le_audio_codec_configs.push_back(lc3_config);
return le_audio_codec_configs;
}
@@ -1428,8 +1431,8 @@
for (auto data_interval_us : le_audio_output_data_interval_us_) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
.dataIntervalUs = data_interval_us,
};
bool is_codec_config_valid =
@@ -1490,7 +1493,7 @@
std::vector<Lc3Configuration> GetBroadcastLc3SupportedList(bool supported) {
std::vector<Lc3Configuration> le_audio_codec_configs;
if (!supported) {
- Lc3Configuration lc3_config{.samplingFrequencyHz = 0, .pcmBitDepth = 0};
+ Lc3Configuration lc3_config{.pcmBitDepth = 0, .samplingFrequencyHz = 0};
le_audio_codec_configs.push_back(lc3_config);
return le_audio_codec_configs;
}
@@ -1650,8 +1653,8 @@
for (auto channel_mode : a2dp_channel_modes) {
PcmConfiguration pcm_config{
.sampleRateHz = sample_rate,
- .bitsPerSample = bits_per_sample,
.channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample,
};
bool is_codec_config_valid = IsPcmConfigSupported(pcm_config);
DataMQDesc mq_desc;
diff --git a/broadcastradio/aidl/Android.bp b/broadcastradio/aidl/Android.bp
new file mode 100644
index 0000000..2d8078b
--- /dev/null
+++ b/broadcastradio/aidl/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // 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.broadcastradio",
+ vendor_available: true,
+ srcs: ["android/hardware/broadcastradio/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "module_current",
+ min_sdk_version: "Tiramisu",
+ },
+ },
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmBandRange.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmBandRange.aidl
new file mode 100644
index 0000000..ca511aa
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmBandRange.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AmFmBandRange {
+ int lowerBound;
+ int upperBound;
+ int spacing;
+ int seekSpacing;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
new file mode 100644
index 0000000..f3aecdf
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AmFmRegionConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AmFmRegionConfig {
+ android.hardware.broadcastradio.AmFmBandRange[] ranges;
+ int fmDeemphasis;
+ int fmRds;
+ const int DEEMPHASIS_D50 = 1;
+ const int DEEMPHASIS_D75 = 2;
+ const int RDS = 1;
+ const int RBDS = 2;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Announcement.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Announcement.aidl
new file mode 100644
index 0000000..bbdd86f
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Announcement.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Announcement {
+ android.hardware.broadcastradio.ProgramSelector selector;
+ android.hardware.broadcastradio.AnnouncementType type = android.hardware.broadcastradio.AnnouncementType.INVALID;
+ android.hardware.broadcastradio.VendorKeyValue[] vendorInfo;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AnnouncementType.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AnnouncementType.aidl
new file mode 100644
index 0000000..237b868
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/AnnouncementType.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.broadcastradio;
+@Backing(type="byte") @JavaDerive(equals=true, toString=true) @VintfStability
+enum AnnouncementType {
+ INVALID = 0,
+ EMERGENCY = 1,
+ WARNING = 2,
+ TRAFFIC = 3,
+ WEATHER = 4,
+ NEWS = 5,
+ EVENT = 6,
+ SPORT = 7,
+ MISC = 8,
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
new file mode 100644
index 0000000..6fb9a62
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ConfigFlag.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.broadcastradio;
+@Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
+enum ConfigFlag {
+ FORCE_MONO = 1,
+ FORCE_ANALOG = 2,
+ FORCE_DIGITAL = 3,
+ RDS_AF = 4,
+ RDS_REG = 5,
+ DAB_DAB_LINKING = 6,
+ DAB_FM_LINKING = 7,
+ DAB_DAB_SOFT_LINKING = 8,
+ DAB_FM_SOFT_LINKING = 9,
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/DabTableEntry.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/DabTableEntry.aidl
new file mode 100644
index 0000000..162f4abd
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/DabTableEntry.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.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable DabTableEntry {
+ String label;
+ int frequencyKhz;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IAnnouncementListener.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IAnnouncementListener.aidl
new file mode 100644
index 0000000..346af58
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IAnnouncementListener.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@VintfStability
+interface IAnnouncementListener {
+ oneway void onListUpdated(in android.hardware.broadcastradio.Announcement[] announcements);
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IBroadcastRadio.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IBroadcastRadio.aidl
new file mode 100644
index 0000000..39eb04c
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IBroadcastRadio.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.broadcastradio;
+@VintfStability
+interface IBroadcastRadio {
+ android.hardware.broadcastradio.Properties getProperties();
+ android.hardware.broadcastradio.AmFmRegionConfig getAmFmRegionConfig(in boolean full);
+ android.hardware.broadcastradio.DabTableEntry[] getDabRegionConfig();
+ void setTunerCallback(in android.hardware.broadcastradio.ITunerCallback callback);
+ void unsetTunerCallback();
+ void tune(in android.hardware.broadcastradio.ProgramSelector program);
+ void seek(in boolean directionUp, in boolean skipSubChannel);
+ void step(in boolean directionUp);
+ void cancel();
+ void startProgramListUpdates(in android.hardware.broadcastradio.ProgramFilter filter);
+ void stopProgramListUpdates();
+ boolean isConfigFlagSet(in android.hardware.broadcastradio.ConfigFlag flag);
+ void setConfigFlag(in android.hardware.broadcastradio.ConfigFlag flag, in boolean value);
+ android.hardware.broadcastradio.VendorKeyValue[] setParameters(in android.hardware.broadcastradio.VendorKeyValue[] parameters);
+ android.hardware.broadcastradio.VendorKeyValue[] getParameters(in String[] keys);
+ byte[] getImage(in int id);
+ android.hardware.broadcastradio.ICloseHandle registerAnnouncementListener(in android.hardware.broadcastradio.IAnnouncementListener listener, in android.hardware.broadcastradio.AnnouncementType[] enabled);
+ const int INVALID_IMAGE = 0;
+ const int ANTENNA_STATE_CHANGE_TIMEOUT_MS = 100;
+ const int LIST_COMPLETE_TIMEOUT_MS = 300000;
+ const int TUNER_TIMEOUT_MS = 30000;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ICloseHandle.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ICloseHandle.aidl
new file mode 100644
index 0000000..75e7f2a
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ICloseHandle.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@VintfStability
+interface ICloseHandle {
+ void close();
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ITunerCallback.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ITunerCallback.aidl
new file mode 100644
index 0000000..f5badad
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ITunerCallback.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@VintfStability
+interface ITunerCallback {
+ oneway void onTuneFailed(in android.hardware.broadcastradio.Result result, in android.hardware.broadcastradio.ProgramSelector selector);
+ oneway void onCurrentProgramInfoChanged(in android.hardware.broadcastradio.ProgramInfo info);
+ oneway void onProgramListUpdated(in android.hardware.broadcastradio.ProgramListChunk chunk);
+ oneway void onAntennaStateChange(in boolean connected);
+ oneway void onConfigFlagUpdated(in android.hardware.broadcastradio.ConfigFlag flag, in boolean value);
+ oneway void onParametersUpdated(in android.hardware.broadcastradio.VendorKeyValue[] parameters);
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
new file mode 100644
index 0000000..4e8296a
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/IdentifierType.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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.broadcastradio;
+@Backing(type="int") @JavaDerive(equals=true, toString=true) @VintfStability
+enum IdentifierType {
+ VENDOR_START = 1000,
+ VENDOR_END = 1999,
+ INVALID = 0,
+ AMFM_FREQUENCY_KHZ = 1,
+ RDS_PI = 2,
+ HD_STATION_ID_EXT = 3,
+ HD_STATION_NAME = 4,
+ DAB_SID_EXT = 5,
+ DAB_ENSEMBLE = 6,
+ DAB_SCID = 7,
+ DAB_FREQUENCY_KHZ = 8,
+ DRMO_SERVICE_ID = 9,
+ DRMO_FREQUENCY_KHZ = 10,
+ SXM_SERVICE_ID = 12,
+ SXM_CHANNEL = 13,
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
new file mode 100644
index 0000000..e02b6b1
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Metadata.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+union Metadata {
+ String rdsPs;
+ int rdsPty;
+ int rbdsPty;
+ String rdsRt;
+ String songTitle;
+ String songArtist;
+ String songAlbum;
+ int stationIcon;
+ int albumArt;
+ String programName;
+ String dabEnsembleName;
+ String dabEnsembleNameShort;
+ String dabServiceName;
+ String dabServiceNameShort;
+ String dabComponentName;
+ String dabComponentNameShort;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramFilter.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramFilter.aidl
new file mode 100644
index 0000000..9edeb8d
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramFilter.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ProgramFilter {
+ android.hardware.broadcastradio.IdentifierType[] identifierTypes;
+ android.hardware.broadcastradio.ProgramIdentifier[] identifiers;
+ boolean includeCategories;
+ boolean excludeModifications;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramIdentifier.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramIdentifier.aidl
new file mode 100644
index 0000000..6676350
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramIdentifier.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.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ProgramIdentifier {
+ android.hardware.broadcastradio.IdentifierType type = android.hardware.broadcastradio.IdentifierType.INVALID;
+ long value;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
new file mode 100644
index 0000000..5e662d2
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ProgramInfo {
+ android.hardware.broadcastradio.ProgramSelector selector;
+ android.hardware.broadcastradio.ProgramIdentifier logicallyTunedTo;
+ android.hardware.broadcastradio.ProgramIdentifier physicallyTunedTo;
+ @nullable android.hardware.broadcastradio.ProgramIdentifier[] relatedContent;
+ int infoFlags;
+ int signalQuality;
+ android.hardware.broadcastradio.Metadata[] metadata;
+ android.hardware.broadcastradio.VendorKeyValue[] vendorInfo;
+ const int FLAG_LIVE = 1;
+ const int FLAG_MUTED = 2;
+ const int FLAG_TRAFFIC_PROGRAM = 4;
+ const int FLAG_TRAFFIC_ANNOUNCEMENT = 8;
+ const int FLAG_TUNABLE = 16;
+ const int FLAG_STEREO = 32;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramListChunk.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramListChunk.aidl
new file mode 100644
index 0000000..5d53b99
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramListChunk.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ProgramListChunk {
+ boolean purge;
+ boolean complete;
+ android.hardware.broadcastradio.ProgramInfo[] modified;
+ @nullable android.hardware.broadcastradio.ProgramIdentifier[] removed;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramSelector.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramSelector.aidl
new file mode 100644
index 0000000..9af1dc8
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/ProgramSelector.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.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ProgramSelector {
+ android.hardware.broadcastradio.ProgramIdentifier primaryId;
+ android.hardware.broadcastradio.ProgramIdentifier[] secondaryIds;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Properties.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Properties.aidl
new file mode 100644
index 0000000..643b819
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Properties.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Properties {
+ String maker;
+ String product;
+ String version;
+ String serial;
+ android.hardware.broadcastradio.IdentifierType[] supportedIdentifierTypes;
+ android.hardware.broadcastradio.VendorKeyValue[] vendorInfo;
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
new file mode 100644
index 0000000..07edae8
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/Result.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.broadcastradio;
+@Backing(type="int") @VintfStability
+enum Result {
+ OK = 0,
+ INTERNAL_ERROR = 1,
+ INVALID_ARGUMENTS = 2,
+ INVALID_STATE = 3,
+ NOT_SUPPORTED = 4,
+ TIMEOUT = 5,
+ UNKNOWN_ERROR = 6,
+}
diff --git a/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/VendorKeyValue.aidl b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/VendorKeyValue.aidl
new file mode 100644
index 0000000..3c6b194
--- /dev/null
+++ b/broadcastradio/aidl/aidl_api/android.hardware.broadcastradio/current/android/hardware/broadcastradio/VendorKeyValue.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.broadcastradio;
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable VendorKeyValue {
+ String key;
+ String value;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/AmFmBandRange.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmBandRange.aidl
new file mode 100644
index 0000000..562631f
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmBandRange.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.broadcastradio;
+
+/**
+ * Defines the AM/FM band range for configuring different regions.
+ *
+ * <p>Channel grid is defined as: each possible channel is set at
+ * lowerBound + channelNumber * spacing, up to upperBound.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable AmFmBandRange {
+ /**
+ * The frequency (in kHz) of the first channel within the range.
+ *
+ * Lower bound must be a tunable frequency.
+ */
+ int lowerBound;
+
+ /**
+ * The frequency (in kHz) of the last channel within the range.
+ */
+ int upperBound;
+
+ /**
+ * Channel grid resolution (in kHz), telling how far the channels are apart.
+ */
+ int spacing;
+
+ /**
+ * Channel spacing (in kHz) used to speed up seeking to the next station
+ * via the {@link IBroadcastRadio#seek} operation.
+ *
+ * It must be a multiple of channel grid resolution.
+ *
+ * Tuner may first quickly check every n-th channel and if it detects echo
+ * from a station, it fine-tunes to find the exact frequency.
+ *
+ * It's ignored for capabilities check (with full=true when calling
+ * getAmFmRegionConfig).
+ */
+ int seekSpacing;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
new file mode 100644
index 0000000..a3086c6
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/AmFmRegionConfig.aidl
@@ -0,0 +1,86 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.AmFmBandRange;
+
+/**
+ * Regional configuration for AM/FM.
+ *
+ * <p>For hardware capabilities check (with full=true when calling
+ * {@link IBroadcastRadio#getAmFmRegionConfig}), HAL implementation fills
+ * entire supported range of frequencies and features.
+ *
+ * When checking current configuration, at most one bit in each bitset
+ * can be set.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable AmFmRegionConfig {
+ /**
+ * Noth D50 and D75 are FM de-emphasis filter supported or configured.
+ *
+ * Both might be set for hardware capabilities check (with full={@code true}
+ * when calling getAmFmRegionConfig), but exactly one for specific region
+ * settings.
+ */
+ const int DEEMPHASIS_D50 = 1 << 0;
+
+ const int DEEMPHASIS_D75 = 1 << 1;
+
+ /**
+ * Both RDS and RBDS are supported or configured RDS variants.
+ *
+ * Both might be set for hardware capabilities check (with full={@code true}
+ * when calling getAmFmRegionConfig), but only one (or none) for specific
+ * region settings.
+ *
+ * RDS is Standard variant, used everywhere except North America.
+ */
+ const int RDS = 1 << 0;
+
+ /**
+ * Variant used in North America (see RDS).
+ */
+ const int RBDS = 1 << 1;
+
+ /**
+ * All supported or configured AM/FM bands.
+ *
+ * AM/FM bands are identified by frequency value
+ * (see {@link IdentifierType#AMFM_FREQUENCY_KHZ}).
+ *
+ * With typical configuration, it's expected to have two frequency ranges
+ * for capabilities check (AM and FM) and four ranges for specific region
+ * configuration (AM LW, AM MW, AM SW, FM).
+ */
+ AmFmBandRange[] ranges;
+
+ /**
+ * De-emphasis filter supported/configured.
+ *
+ * It is a bitset of de-emphasis values (DEEMPHASIS_D50 and DEEMPHASIS_D75).
+ */
+ int fmDeemphasis;
+
+ /**
+ * RDS/RBDS variant supported/configured.
+ *
+ * It is a bitset of RDS values (RDS and RBDS).
+ */
+ int fmRds;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/Announcement.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/Announcement.aidl
new file mode 100644
index 0000000..a972d4d
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/Announcement.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.AnnouncementType;
+import android.hardware.broadcastradio.ProgramSelector;
+import android.hardware.broadcastradio.VendorKeyValue;
+
+/**
+ * Station broadcasting active announcement.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable Announcement {
+ /**
+ * Program selector to tune to the announcement.
+ */
+ ProgramSelector selector;
+
+ /**
+ * Announcement type.
+ */
+ AnnouncementType type = AnnouncementType.INVALID;
+
+ /**
+ * Vendor-specific information.
+ *
+ * It may be used for extra features, not supported by the platform,
+ * for example: com.me.hdradio.urgency=100; com.me.hdradio.certainity=50.
+ */
+ VendorKeyValue[] vendorInfo;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/AnnouncementType.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/AnnouncementType.aidl
new file mode 100644
index 0000000..8df8025
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/AnnouncementType.aidl
@@ -0,0 +1,72 @@
+/*
+ * 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.broadcastradio;
+
+/**
+ * Type of an announcement.
+ *
+ * <p>It maps to different announcement types for each radio technology.
+ */
+@VintfStability
+@Backing(type="byte")
+@JavaDerive(equals=true, toString=true)
+enum AnnouncementType {
+ /**
+ * Undefined announcement type
+ */
+ INVALID = 0,
+
+ /**
+ * DAB alarm, RDS emergency program type (PTY 31).
+ */
+ EMERGENCY = 1,
+
+ /**
+ * DAB warning.
+ */
+ WARNING,
+
+ /**
+ * DAB road traffic, RDS TA, HD Radio transportation.
+ */
+ TRAFFIC,
+
+ /**
+ * Weather.
+ */
+ WEATHER,
+
+ /**
+ * News.
+ */
+ NEWS,
+
+ /**
+ * DAB event, special event.
+ */
+ EVENT,
+
+ /**
+ * DAB sport report, RDS sports.
+ */
+ SPORT,
+
+ /**
+ * All others.
+ */
+ MISC,
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
new file mode 100644
index 0000000..11da39c
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ConfigFlag.aidl
@@ -0,0 +1,92 @@
+/*
+ * 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.broadcastradio;
+
+/**
+ * Configuration flags to be used with isConfigFlagSet and setConfigFlag methods
+ * of IBroadcastRadio.
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(equals=true, toString=true)
+enum ConfigFlag {
+ /**
+ * Forces mono audio stream reception.
+ *
+ * Analog broadcasts can recover poor reception conditions by jointing
+ * stereo channels into one. Mainly for, but not limited to AM/FM.
+ */
+ FORCE_MONO = 1,
+
+ /**
+ * Forces the analog playback for the supporting radio technology.
+ *
+ * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
+ * this option. This is purely user choice, ie. does not reflect digital-
+ * analog handover state managed from the HAL implementation side.
+ *
+ * Some radio technologies may not support this, ie. DAB.
+ */
+ FORCE_ANALOG,
+
+ /**
+ * Forces the digital playback for the supporting radio technology.
+ *
+ * User may disable digital-analog handover that happens with poor
+ * reception conditions. With digital forced, the radio will remain silent
+ * instead of switching to analog channel if it's available. This is purely
+ * user choice, it does not reflect the actual state of handover.
+ */
+ FORCE_DIGITAL,
+
+ /**
+ * RDS Alternative Frequencies.
+ *
+ * If set and the currently tuned RDS station broadcasts on multiple
+ * channels, radio tuner automatically switches to the best available
+ * alternative.
+ */
+ RDS_AF,
+
+ /**
+ * RDS region-specific program lock-down.
+ *
+ * Allows user to lock to the current region as they move into the
+ * other region.
+ */
+ RDS_REG,
+
+ /**
+ * Enables DAB-DAB hard- and implicit-linking (the same content).
+ */
+ DAB_DAB_LINKING,
+
+ /**
+ * Enables DAB-FM hard- and implicit-linking (the same content).
+ */
+ DAB_FM_LINKING,
+
+ /**
+ * Enables DAB-DAB soft-linking (related content).
+ */
+ DAB_DAB_SOFT_LINKING,
+
+ /**
+ * Enables DAB-FM soft-linking (related content).
+ */
+ DAB_FM_SOFT_LINKING,
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/DabTableEntry.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/DabTableEntry.aidl
new file mode 100644
index 0000000..13c20b0
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/DabTableEntry.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.broadcastradio;
+
+/**
+ * An entry in regional configuration for DAB.
+ *
+ * <p>This defines a frequency table row for ensembles.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable DabTableEntry {
+ /**
+ * Channel name, i.e. 5A, 7B.
+ *
+ * It must match the following regular expression:
+ * /^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$/ (2-7 uppercase alphanumeric characters
+ * without spaces allowed at the beginning nor end).
+ */
+ String label;
+
+ /**
+ * Frequency, in kHz.
+ */
+ int frequencyKhz;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/IAnnouncementListener.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/IAnnouncementListener.aidl
new file mode 100644
index 0000000..f6021c1
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/IAnnouncementListener.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.broadcastradio;
+
+import android.hardware.broadcastradio.Announcement;
+
+/**
+ * Callback interface for announcement listener.
+ *
+ * For typical configuration, the listener is a broadcast radio service.
+ */
+@VintfStability
+interface IAnnouncementListener {
+ /**
+ * Called whenever announcement list has changed.
+ *
+ * @param announcements The complete list of currently active announcements.
+ */
+ oneway void onListUpdated(in Announcement[] announcements);
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/IBroadcastRadio.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/IBroadcastRadio.aidl
new file mode 100644
index 0000000..0f88fc0
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/IBroadcastRadio.aidl
@@ -0,0 +1,353 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.AmFmRegionConfig;
+import android.hardware.broadcastradio.AnnouncementType;
+import android.hardware.broadcastradio.ConfigFlag;
+import android.hardware.broadcastradio.DabTableEntry;
+import android.hardware.broadcastradio.IAnnouncementListener;
+import android.hardware.broadcastradio.ICloseHandle;
+import android.hardware.broadcastradio.ITunerCallback;
+import android.hardware.broadcastradio.ProgramFilter;
+import android.hardware.broadcastradio.ProgramSelector;
+import android.hardware.broadcastradio.Properties;
+import android.hardware.broadcastradio.VendorKeyValue;
+
+/**
+ * Represents a hardware broadcast radio module. A single module may contain
+ * multiple hardware tuners (i.e. with an additional background tuner), but the
+ * layers above the HAL see them as a single logical unit.
+ */
+@VintfStability
+interface IBroadcastRadio {
+ /**
+ * Invalid identifier for {@link IBroadcastRadio#getImage}.
+ */
+ const int INVALID_IMAGE = 0;
+
+ /**
+ * If the antenna is disconnected from the beginning, the
+ * {@link ITunerCallback#onAntennaStateChange} callback must be
+ * called within this time.
+ */
+ const int ANTENNA_STATE_CHANGE_TIMEOUT_MS = 100;
+
+ /**
+ * All chunks of a signal program list update must be transmitted
+ * within this time.
+ */
+ const int LIST_COMPLETE_TIMEOUT_MS = 300000;
+
+ /**
+ * All tune, seek and step operations must be completed within
+ * this time.
+ */
+ const int TUNER_TIMEOUT_MS = 30000;
+
+ /**
+ * Returns module properties: a description of a module and its
+ * capabilities. This method must not fail.
+ *
+ * @return Module description.
+ */
+ Properties getProperties();
+
+ /**
+ * Fetches current or possible AM/FM region configuration.
+ *
+ * If the tuner doesn't support AM/FM, a service-specific error
+ * {@link Result#NOT_SUPPORTED} will be returned.
+ *
+ * @param full If {@code true}, returns full hardware capabilities.
+ * If {@code false}, returns current regional configuration.
+ * @return config Hardware capabilities (full={@code true}) or current configuration
+ * (full={@code false}).
+ */
+ AmFmRegionConfig getAmFmRegionConfig(in boolean full);
+
+ /**
+ * Fetches current DAB region configuration.
+ *
+ * If tuner doesn't support DAB, a service-specific error
+ * {@link Result#NOT_SUPPORTED} wiil be returned.
+ *
+ * @return config Current configuration.
+ */
+ DabTableEntry[] getDabRegionConfig();
+
+ /**
+ * Sets callback interface.
+ *
+ * It is expected that there will only ever be a single callback set.
+ * If called when a callback is already set, the existing one should be
+ * replaced with the new callback.
+ *
+ * If the callback to be set is null, a service-specific error
+ * {@link Result#INVALID_ARGUMENTS} will be returned; if the callback
+ * is not set successfully, a service-specific error
+ * {@link Result#NOT_SUPPORTED} should be returned.
+ *
+ * @param callback The callback interface used for BroadcastRadio HAL.
+ */
+ void setTunerCallback(in ITunerCallback callback);
+
+ /**
+ * Unsets callback interface.
+ *
+ * The existing callback is set to null.
+ */
+ void unsetTunerCallback();
+
+ /**
+ * Tunes to a specified program.
+ *
+ * Automatically cancels pending tune(), seek() or step().
+ * The method should first check whether tune can be processed by the status
+ * of tuner and inputs, schedule tune task, and then return status
+ * immediately. If a non-null callback is not set, a service-specific
+ * error {@link Result#INVALID_STATE} will be returned; if the program
+ * selector doesn't contain any supported identifier, a service-specific error
+ * {@link Result#NOT_SUPPORTED} will be returned; if the program selector
+ * contains identifiers in invalid format (i.e. out of range), a
+ * service-specific error {@link Result#INVALID_ARGUMENTS} will be returned;
+ * otherwise, OK will be returned as status. Tune task should be processed
+ * asynchronously after the method returns status. If the method returns OK,
+ * {@link ITunerCallback#tuneFailed} or
+ * {@link ITunerCallback#currentProgramInfoChanged} callback must be called
+ * after the tune task completes.
+ *
+ * @param program Program to tune to.
+ */
+ void tune(in ProgramSelector program);
+
+ /**
+ * Seeks the next valid program on the "air".
+ *
+ * Advance to the next detected program and stay there.
+ *
+ * Automatically cancels pending tune(), seek() or step().
+ * The method should first check whether seek can be processed by the status
+ * of tuner and inputs, schedule seek task, and then return status
+ * immediately. If a non-null callback is not set, a service-specific
+ * error {@link Result#INVALID_STATE} will be returned; otherwise, OK will
+ * be returned as status. Seek task should be processed asynchronously
+ * after the method returns status. If the method returns OK,
+ * {@link ITunerCallback#tuneFailed} or
+ * {@link ITunerCallback#currentProgramInfoChanged} callback must be called
+ * after the seek task completes.
+ *
+ * The skipSubChannel parameter is used to skip digital radio subchannels:
+ * - HD Radio SPS;
+ * - DAB secondary service.
+ *
+ * As an implementation detail, the HAL has the option to perform an actual
+ * seek or select the next program from the list retrieved in the
+ * background.
+ *
+ * @param directionUp {@code true} to change towards higher numeric values
+ * (frequency, channel number), {@code false} towards
+ * lower.
+ * @param skipSubChannel Don't tune to subchannels.
+ */
+ void seek(in boolean directionUp, in boolean skipSubChannel);
+
+ /**
+ * Steps to the adjacent channel, which may not be occupied by any program.
+ *
+ * Automatically cancels pending tune(), seek() or step().
+ * The method should first check whether step can be processed by the status
+ * of tuner and inputs, schedule step task, and then return status
+ * immediately. If a non-null callback is not set, service-specific
+ * error {@link Result#INVALID_STATE} will be returned; if tuning to an
+ * unoccupied channel is not supported (i.e. for satellite radio), a
+ * service-specific error {@link Result#NOT_SUPPORTED} will be returned;
+ * otherwise, OK should be returned as status. Step task should be
+ * processed asynchronously after the method returns status. If the
+ * method returns OK, {@link ITunerCallback#tuneFailed} or
+ * {@link currentProgramInfoChanged} callback must be called after the
+ * step task completes.
+ *
+ * @param directionUp {@code true} to change towards higher numeric values
+ * (frequency, channel number), {@code false} towards lower.
+ */
+ void step(in boolean directionUp);
+
+ /**
+ * Cancels pending tune(), seek() or step().
+ *
+ * If there is no such operation running, the call can be ignored.
+ * If cancel is called after the HAL completes an operation (tune, seek, and step)
+ * and before the callback completions, the cancel can be ignored and the callback
+ * should complete.
+ */
+ void cancel();
+
+ /**
+ * Applies a filter to the program list and starts sending program list
+ * update over {@link ITunerCallback#onProgramListUpdated} callback.
+ *
+ * There may be only one updates stream active at the moment. Calling this
+ * method again must result in cancelling the pending update request.
+ *
+ * This call clears the program list on the client side, the HAL must send
+ * the whole list again.
+ *
+ * If the program list scanning hardware (i.e. background tuner) is
+ * unavailable at the moment, the call must succeed and start updates
+ * when it becomes available.
+ *
+ * If the program list scanning is not supported by the hardware, a
+ * service-specific error {@link Result#NOT_SUPPORTED} will be returned.
+ *
+ * @param filter Filter to apply on the fetched program list.
+ */
+ void startProgramListUpdates(in ProgramFilter filter);
+
+ /**
+ * Stops sending program list updates.
+ *
+ * If stopProgramListUpdates is called after the HAL completes a program list update
+ * and before the onCurrentProgramInfoChanged callback completions,
+ * stopProgramListUpdates can be ignored and the callback should complete.
+ */
+ void stopProgramListUpdates();
+
+ /**
+ * Fetches the current setting of a given config flag.
+ *
+ * The success/failure result must be consistent with setConfigFlag.
+ *
+ * If the flag is not applicable, a service-specific error
+ * {@link Result#INVALID_STATE} will be returned. If the flag is not
+ * supported at all, a service-specific error {@link Result#NOT_SUPPORTED}
+ * will be returned.
+ *
+ * @return the current value of the flag, if succeed.
+ */
+ boolean isConfigFlagSet(in ConfigFlag flag);
+
+ /**
+ * Sets the config flag.
+ *
+ * The success/failure result must be consistent with isConfigFlagSet.
+ *
+ * If the flag is not applicable, a service-specific error
+ * {@link Result#INVALID_STATE} will be returned. If the flag is not
+ * supported at all, a service-specific error {@link Result#NOT_SUPPORTED}
+ * will be returned.
+ *
+ * @param flag Flag to set.
+ * @param value The new value of a given flag.
+ */
+ void setConfigFlag(in ConfigFlag flag, in boolean value);
+
+ /**
+ * Generic method for setting vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * Framework does not make any assumptions on the keys or values, other than
+ * ones stated in VendorKeyValue documentation (a requirement of key
+ * prefixes).
+ *
+ * For each pair in the result array, the key must be one of the keys
+ * contained in the input (possibly with wildcards expanded), and the value
+ * must be a vendor-specific result status (i.e. the string "OK" or an error
+ * code). The implementation may choose to return an empty array, or only
+ * return a status for a subset of the provided inputs, at its discretion.
+ *
+ * Application and HAL must not use keys with unknown prefix. In particular,
+ * it must not place a key-value pair in results array for unknown key from
+ * parameters array - instead, an unknown key should simply be ignored.
+ * In other words, results array may contain a subset of parameter keys
+ * (however, the framework doesn't enforce a strict subset - the only
+ * formal requirement is vendor domain prefix for keys).
+ *
+ * @param parameters Vendor-specific key-value pairs.
+ * @return Operation completion status for parameters being set.
+ */
+ VendorKeyValue[] setParameters(in VendorKeyValue[] parameters);
+
+ /**
+ * Generic method for retrieving vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * Framework does not cache set/get requests, so it's allowed for
+ * getParameter to return a different value than previous setParameter call.
+ *
+ * The syntax and semantics of keys are up to the vendor (as long as prefix
+ * rules are obeyed). For instance, vendors may include some form of
+ * wildcard support. In such case, result array may be of different size
+ * than requested keys array. However, wildcards are not recognized by
+ * framework and they are passed as-is to the HAL implementation.
+ *
+ * Unknown keys must be ignored and not placed into results array.
+ *
+ * @param keys Parameter keys to fetch.
+ * @return Vendor-specific key-value pairs.
+ */
+ VendorKeyValue[] getParameters(in String[] keys);
+
+ /**
+ * Fetches image from radio module cache.
+ *
+ * This is out-of-band transport mechanism for images carried with metadata.
+ * The metadata array only passes the identifier, so the client may cache
+ * images or even not fetch them.
+ *
+ * The identifier may be any arbitrary number (i.e. sha256 prefix) selected
+ * by the vendor. It must be stable so the application may cache it.
+ *
+ * The data must be a valid PNG, JPEG, GIF or BMP file, and must be less
+ * than 1MB, due to hard limit on binder transaction buffer.
+ *
+ * Image data with an invalid format must be handled gracefully in the same
+ * way as a missing image.
+ *
+ * The image identifier may become invalid after some time from passing it
+ * with metadata struct (due to resource cleanup at the HAL implementation).
+ * However, it must remain valid for a currently tuned program at least
+ * until onCurrentProgramInfoChanged is called.
+ *
+ * @param id Identifier of an image (value of {@link IBroadcastRadio#INVALID_IMAGE}
+ * is reserved and must be treated as invalid image).
+ * @return A binary blob with image data
+ * or a zero-length array if identifier doesn't exist.
+ */
+ byte[] getImage(in int id);
+
+ /**
+ * Registers announcement listener.
+ *
+ * If there is at least one observer registered, HAL implementation must
+ * notify about announcements.
+ *
+ * If the observer dies, the HAL implementation must unregister observer
+ * automatically.
+ *
+ * If the tuner doesn't support announcements, a service-specific error
+ * {@link Result#NOT_SUPPORTED} will be returned.
+ *
+ * @param listener The listener interface.
+ * @param enabled The list of announcement types to watch for.
+ * @return a handle to unregister observer.
+ */
+ ICloseHandle registerAnnouncementListener(
+ in IAnnouncementListener listener, in AnnouncementType[] enabled);
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ICloseHandle.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ICloseHandle.aidl
new file mode 100644
index 0000000..4a3240b
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ICloseHandle.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.broadcastradio;
+
+/**
+ * Represents a generic close handle to remove a callback that doesn't need
+ * active interface.
+ */
+@VintfStability
+interface ICloseHandle {
+ /**
+ * Closes the handle.
+ *
+ * The call must not fail and must only be issued once.
+ *
+ * After the close call is executed, no other calls to this interface
+ * are allowed. If the call is issued second time, a service-specific
+ * error {@link Result#INVALID_STATE} will be returned.
+ */
+ void close();
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ITunerCallback.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ITunerCallback.aidl
new file mode 100644
index 0000000..1f0221c
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ITunerCallback.aidl
@@ -0,0 +1,113 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.ConfigFlag;
+import android.hardware.broadcastradio.ProgramInfo;
+import android.hardware.broadcastradio.ProgramListChunk;
+import android.hardware.broadcastradio.ProgramSelector;
+import android.hardware.broadcastradio.Result;
+import android.hardware.broadcastradio.VendorKeyValue;
+
+@VintfStability
+oneway interface ITunerCallback {
+ /**
+ * Method called by the HAL when a tuning operation fails asynchronously
+ * following {@link IBroadcastRadio#tune}, {@link IBroadcastRadio#seek}
+ * or {@link IBroadcastRadio#step}.
+ *
+ * This callback is only called when the tune(), seek() or step() command
+ * succeeds without returning any error at first.
+ *
+ * @param result {@link Result#TIMEOUT} in case that tune(), seek() or
+ * step() is not completed within
+ * @link IBroadcastRadio#TUNER_TIMEOUT_MS}
+ * @param selector A ProgramSelector structure passed from tune() call;
+ * empty for step() and seek().
+ */
+ void onTuneFailed(in Result result, in ProgramSelector selector);
+
+ /**
+ * Method called by the HAL when current program information (including
+ * metadata) is updated. It must be called when {@link IBroadcastRadio#tune}
+ * {@link IBroadcastRadio#seek} or {@link IBroadcastRadio#step} command
+ * succeeds.
+ *
+ * This is also called when the radio tuned to the static (not a valid
+ * station), see {@link ProgramInfo#FLAG_TUNABLE} flag.
+ *
+ * @param info Current program information.
+ */
+ void onCurrentProgramInfoChanged(in ProgramInfo info);
+
+ /**
+ * A delta update of the program list, called whenever there's a change in
+ * the list.
+ *
+ * If there are frequent changes, HAL implementation must throttle the rate
+ * of the updates.
+ *
+ * There is a hard limit on binder transaction buffer, and the list must
+ * not exceed it. For large lists, HAL implementation must split them to
+ * multiple chunks, no larger than 500kiB each, and call this program list
+ * update callback method separately.
+ *
+ * @param chunk A chunk of the program list update.
+ */
+ void onProgramListUpdated(in ProgramListChunk chunk);
+
+ /**
+ * Method called by the HAL when the antenna gets connected or disconnected.
+ *
+ * For broadcast radio service, client must assume the antenna is connected.
+ * If it's not, then antennaStateChange must be called within
+ * {@link IBroadcastRadio#ANTENNA_STATE_CHANGE_TIMEOUT_MS} to indicate that.
+ *
+ * @param connected {@code true} if the antenna is now connected, {@code false}
+ * otherwise.
+ */
+ void onAntennaStateChange(in boolean connected);
+
+ /**
+ * Generic callback for passing updates to config flags.
+ *
+ * It's up to the HAL implementation if and how to implement this callback,
+ * as long as it obeys the prefix rule. However, setConfigFlag must not
+ * trigger this callback, while an internal event can change config flag
+ * asynchronously at the HAL layer.
+ *
+ * @param flag Flag that has changed.
+ * @param value The new value of the given flag.
+ */
+ void onConfigFlagUpdated(in ConfigFlag flag, in boolean value);
+
+ /**
+ * Generic callback for passing updates to vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * It's up to the HAL implementation if and how to implement this callback,
+ * as long as it obeys the prefix rule. In particular, only selected keys
+ * may be notified this way. However, setParameters must not trigger
+ * this callback, while an internal event can change parameters
+ * asynchronously at the HAL layer.
+ *
+ * @param parameters Vendor-specific key-value pairs,
+ * opaque to Android framework.
+ */
+ void onParametersUpdated(in VendorKeyValue[] parameters);
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
new file mode 100644
index 0000000..0484d02
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl
@@ -0,0 +1,163 @@
+/*
+ * 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.broadcastradio;
+
+/**
+ * Type of program identifier component.
+ *
+ * Each identifier type corresponds to exactly one radio technology,
+ * i.e. DAB_ENSEMBLE is specifically for DAB.
+ *
+ * VENDOR identifier types must be opaque to the framework.
+ *
+ * The value format for each (but VENDOR_*) identifier is strictly defined
+ * to maintain interoperability between devices made by different vendors.
+ *
+ * All other values are reserved for future use.
+ * Values not matching any enumerated constant must be ignored.
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(equals=true, toString=true)
+enum IdentifierType {
+ /**
+ * Primary/secondary identifier for vendor-specific radio technology.
+ * The value format is determined by a vendor.
+ *
+ * The vendor identifiers have limited serialization capabilities - see
+ * ProgramSelector description.
+ */
+ VENDOR_START = 1000,
+
+ /**
+ * See VENDOR_START
+ */
+ VENDOR_END = 1999,
+
+ /**
+ * Undefined identifier type.
+ */
+ INVALID = 0,
+
+ /**
+ * Primary identifier for analogue (without RDS) AM/FM stations:
+ * frequency in kHz.
+ *
+ * This identifier also contains band information:
+ * - <500kHz: AM LW;
+ * - 500kHz - 1705kHz: AM MW;
+ * - 1.71MHz - 30MHz: AM SW;
+ * - >60MHz: FM.
+ */
+ AMFM_FREQUENCY_KHZ,
+
+ /**
+ * 16bit primary identifier for FM RDS station.
+ */
+ RDS_PI,
+
+ /**
+ * 64bit compound primary identifier for HD Radio.
+ *
+ * Consists of (from the LSB):
+ * - 32bit: Station ID number;
+ * - 4bit: HD Radio subchannel;
+ * - 18bit: AMFM_FREQUENCY_KHZ.
+ *
+ * While station ID number should be unique globally, it sometimes get
+ * abused by broadcasters (i.e. not being set at all). To ensure local
+ * uniqueness, AMFM_FREQUENCY_KHZ was added here. Global uniqueness is
+ * a best-effort - see HD_STATION_NAME.
+ *
+ * HD Radio subchannel is a value in range 0-7.
+ * This index is 0-based (where 0 is MPS and 1..7 are SPS),
+ * as opposed to HD Radio standard (where it's 1-based).
+ *
+ * The remaining bits should be set to zeros when writing on the chip side
+ * and ignored when read.
+ */
+
+ HD_STATION_ID_EXT,
+
+ /**
+ * 64bit additional identifier for HD Radio.
+ *
+ * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
+ * globally unique. To provide a best-effort solution, a short version of
+ * station name may be carried as additional identifier and may be used
+ * by the tuner hardware to double-check tuning.
+ *
+ * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
+ * must be converted to uppercase). Encoded in little-endian ASCII:
+ * the first character of the name is the LSB.
+ *
+ * For example: "Abc" is encoded as 0x434241.
+ */
+ HD_STATION_NAME,
+
+ /**
+ * 28bit compound primary identifier for Digital Audio Broadcasting.
+ *
+ * Consists of (from the LSB):
+ * - 16bit: SId;
+ * - 8bit: ECC code;
+ * - 4bit: SCIdS.
+ *
+ * SCIdS (Service Component Identifier within the Service) value
+ * of 0 represents the main service, while 1 and above represents
+ * secondary services.
+ *
+ * The remaining bits should be set to zeros when writing on the chip side
+ * and ignored when read.
+ */
+ DAB_SID_EXT,
+
+ /**
+ * 16bit
+ */
+ DAB_ENSEMBLE,
+
+ /**
+ * 12bit
+ */
+ DAB_SCID,
+
+ /**
+ * kHz (see AMFM_FREQUENCY_KHZ)
+ */
+ DAB_FREQUENCY_KHZ,
+
+ /**
+ * 24bit primary identifier for Digital Radio Mondiale.
+ */
+ DRMO_SERVICE_ID,
+
+ /**
+ * kHz (see AMFM_FREQUENCY_KHZ)
+ */
+ DRMO_FREQUENCY_KHZ,
+
+ /**
+ * 32bit primary identifier for SiriusXM Satellite Radio.
+ */
+ SXM_SERVICE_ID = DRMO_FREQUENCY_KHZ + 2,
+
+ /**
+ * 0-999 range
+ */
+ SXM_CHANNEL,
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
new file mode 100644
index 0000000..3298cac
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/Metadata.aidl
@@ -0,0 +1,115 @@
+/*
+ * 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.broadcastradio;
+
+/**
+ * An element of metadata array.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+union Metadata {
+ /**
+ * RDS PS (string)
+ */
+ String rdsPs;
+
+ /**
+ * RDS PTY (uint8_t)
+ */
+ int rdsPty;
+
+ /**
+ * RBDS PTY (uint8_t)
+ */
+ int rbdsPty;
+
+ /**
+ * RDS RT (string)
+ */
+ String rdsRt;
+
+ /**
+ * Song title (string)
+ */
+ String songTitle;
+
+ /**
+ * Artist name (string)
+ */
+ String songArtist;
+
+ /**
+ * Album name (string)
+ */
+ String songAlbum;
+
+ /**
+ * Station icon (uint32_t, see {@link IBroadcastRadio#getImage})
+ */
+ int stationIcon;
+
+ /**
+ * Album art (uint32_t, see {@link IBroadcastRadio#getImage})
+ */
+ int albumArt;
+
+ /**
+ * Station name.
+ *
+ * This is a generic field to cover any radio technology.
+ *
+ * If the PROGRAM_NAME has the same content as DAB_*_NAME or RDS_PS,
+ * it may not be present, to preserve space - framework must repopulate
+ * it on the client side.
+ */
+ String programName;
+
+ /**
+ * DAB ensemble name (string)
+ */
+ String dabEnsembleName;
+
+ /**
+ * DAB ensemble name abbreviated (string).
+ *
+ * The string must be up to 8 characters long.
+ *
+ * If the short variant is present, the long (DAB_ENSEMBLE_NAME) one must be
+ * present as well.
+ */
+ String dabEnsembleNameShort;
+
+ /**
+ * DAB service name (string)
+ */
+ String dabServiceName;
+
+ /**
+ * DAB service name abbreviated (see DAB_ENSEMBLE_NAME_SHORT) (string)
+ */
+ String dabServiceNameShort;
+
+ /**
+ * DAB component name (string)
+ */
+ String dabComponentName;
+
+ /**
+ * DAB component name abbreviated (see DAB_ENSEMBLE_NAME_SHORT) (string)
+ */
+ String dabComponentNameShort;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramFilter.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramFilter.aidl
new file mode 100644
index 0000000..3dd10eb
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramFilter.aidl
@@ -0,0 +1,70 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.IdentifierType;
+import android.hardware.broadcastradio.ProgramIdentifier;
+
+/**
+ * Large-grain filter to the program list.
+ *
+ * This is meant to reduce binder transaction bandwidth, not for fine-grained
+ * filtering user might expect.
+ *
+ * The filter is designed as conjunctive normal form: the entry that passes the
+ * filter must satisfy all the clauses (members of this struct). Vector clauses
+ * are disjunctions of literals. In other words, there is AND between each
+ * high-level group and OR inside it.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable ProgramFilter {
+ /**
+ * List of identifier types that are filtered by the filter.
+ *
+ * If the program list entry contains at least one identifier of the type
+ * listed, it satisfies this condition.
+ *
+ * Empty list means no filtering on identifier type.
+ */
+ IdentifierType[] identifierTypes;
+
+ /**
+ * List of identifiers that are filtered by the filter.
+ *
+ * If the program list entry contains at least one listed identifier,
+ * it satisfies this condition.
+ *
+ * Empty list means no filtering on identifier.
+ */
+ ProgramIdentifier[] identifiers;
+
+ /**
+ * Includes non-tunable entries that define tree structure on the
+ * program list (i.e. DAB ensembles).
+ */
+ boolean includeCategories;
+
+ /**
+ * Disables updates on entry modifications.
+ *
+ * If {@code true}, 'modified' vector of {@link ProgramListChunk} must contain
+ * list additions only. Once the program is added to the list, it's not
+ * updated anymore.
+ */
+ boolean excludeModifications;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramIdentifier.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramIdentifier.aidl
new file mode 100644
index 0000000..2057d97
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramIdentifier.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.IdentifierType;
+
+/**
+ * A single program identifier component, i.e. frequency or channel ID.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable ProgramIdentifier {
+ /**
+ * Maps to IdentifierType enum.
+ */
+ IdentifierType type = IdentifierType.INVALID;
+
+ /**
+ * The uint64_t value field holds the value in format described in comments
+ * for IdentifierType enum.
+ */
+ long value;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
new file mode 100644
index 0000000..3e2c9cc
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl
@@ -0,0 +1,173 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.Metadata;
+import android.hardware.broadcastradio.ProgramIdentifier;
+import android.hardware.broadcastradio.ProgramSelector;
+import android.hardware.broadcastradio.VendorKeyValue;
+
+/**
+ * Program (channel, station) information.
+ *
+ * Carries both user-visible information (like station name) and technical
+ * details (tuning selector).
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable ProgramInfo {
+ /**
+ * Set when the program is currently playing live stream.
+ * This may result in a slightly altered reception parameters,
+ * usually targeted at reduced latency.
+ */
+ const int FLAG_LIVE = 1 << 0;
+
+ /**
+ * Radio stream is not playing, ie. due to bad reception conditions or
+ * buffering. In this state volume knob MAY be disabled to prevent user
+ * increasing volume too much.
+ */
+ const int FLAG_MUTED = 1 << 1;
+
+ /**
+ * Station broadcasts traffic information regularly,
+ * but not necessarily right now.
+ */
+ const int FLAG_TRAFFIC_PROGRAM = 1 << 2;
+
+ /**
+ * Station is broadcasting traffic information at the very moment.
+ */
+ const int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
+
+ /**
+ * Station can be tuned to (not playing static).
+ *
+ * It's the same condition that would stop a seek operation
+ * (i.e. {@link IBroadcastRadio#seek}).
+ *
+ * By definition, this flag must be set for all items on the program list.
+ */
+ const int FLAG_TUNABLE = 1 << 4;
+
+ /**
+ * Audio stream is MONO if this bit is not set.
+ */
+ const int FLAG_STEREO = 1 << 5;
+
+ /**
+ * An identifier used to point at the program (primarily to tune to it).
+ *
+ * This field is required - its type field must not be set to
+ * {@link IdentifierType#INVALID}.
+ */
+ ProgramSelector selector;
+
+ /**
+ * Identifier currently used for program selection.
+ *
+ * It allows to determine which technology is currently used for reception.
+ *
+ * Some program selectors contain tuning information for different radio
+ * technologies (i.e. FM RDS and DAB). For example, user may tune using
+ * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware
+ * may choose to use DAB technology to make actual tuning. This identifier
+ * must reflect that.
+ *
+ * This field is required for currently tuned program only.
+ * For all other items on the program list, its type field must be
+ * initialized to {@link IdentifierType#INVALID}.
+ *
+ * Only primary identifiers for a given radio technology are valid:
+ * - AMFM_FREQUENCY_KHZ for analog AM/FM;
+ * - RDS_PI for FM RDS;
+ * - HD_STATION_ID_EXT;
+ * - DAB_SID_EXT;
+ * - DRMO_SERVICE_ID;
+ * - SXM_SERVICE_ID;
+ * - VENDOR_*;
+ * - more might come in next minor versions of this HAL.
+ */
+ ProgramIdentifier logicallyTunedTo;
+
+ /**
+ * Identifier currently used by hardware to physically tune to a channel.
+ *
+ * Some radio technologies broadcast the same program on multiple channels,
+ * i.e. with RDS AF the same program may be broadcasted on multiple
+ * alternative frequencies; the same DAB program may be broadcast on
+ * multiple ensembles. This identifier points to the channel to which the
+ * radio hardware is physically tuned to.
+ *
+ * This field is required for currently tuned program only.
+ * For all other items on the program list, its type field must be
+ * initialized to {@link IdentifierType#INVALID}.
+ *
+ * Only physical identifiers are valid:
+ * - AMFM_FREQUENCY_KHZ;
+ * - DAB_ENSEMBLE;
+ * - DRMO_FREQUENCY_KHZ;
+ * - SXM_CHANNEL;
+ * - VENDOR_*;
+ * - more might come in next minor versions of this HAL.
+ */
+ ProgramIdentifier physicallyTunedTo;
+
+ /**
+ * Primary identifiers of related contents.
+ *
+ * Some radio technologies provide pointers to other programs that carry
+ * related content (i.e. DAB soft-links). This field is a list of pointers
+ * to other programs on the program list.
+ *
+ * This is not a list of programs that carry the same content (i.e.
+ * DAB hard-links, RDS AF). Switching to programs from this list usually
+ * require user action.
+ *
+ * Please note, that these identifiers do not have to exist on the program
+ * list - i.e. DAB tuner may provide information on FM RDS alternatives
+ * despite not supporting FM RDS. If the system has multiple tuners, another
+ * one may have it on its list.
+ *
+ * This field is optional.
+ */
+ @nullable ProgramIdentifier[] relatedContent;
+
+ /**
+ * Program flags.
+ */
+ int infoFlags;
+
+ /**
+ * Signal quality measured in 0% to 100% range to be shown in the UI.
+ */
+ int signalQuality;
+
+ /**
+ * Program metadata (station name, PTY, song title).
+ */
+ Metadata[] metadata;
+
+ /**
+ * Vendor-specific information.
+ *
+ * It may be used for extra features, not supported by the platform,
+ * for example: paid-service=true; bitrate=320kbps.
+ */
+ VendorKeyValue[] vendorInfo;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramListChunk.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramListChunk.aidl
new file mode 100644
index 0000000..a62d461
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramListChunk.aidl
@@ -0,0 +1,73 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.ProgramIdentifier;
+import android.hardware.broadcastradio.ProgramInfo;
+
+/**
+ * An update packet of the program list.
+ *
+ * The order of entries in the arrays is unspecified.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable ProgramListChunk {
+ /**
+ * Treats all previously added entries as removed.
+ *
+ * This is meant to save binder transaction bandwidth on 'removed' array
+ * and provide a clear empty state.
+ *
+ * If set, 'removed' array must be null.
+ *
+ * The client may wait with taking action on this until it received the
+ * chunk with complete flag set (to avoid part of stations temporarily
+ * disappearing from the list).
+ */
+ boolean purge;
+
+ /**
+ * If false, it means there are still programs not transmitted,
+ * due for transmission in following updates.
+ *
+ * Used by UIs that wait for complete list instead of displaying
+ * programs while scanning.
+ *
+ * After the whole channel range was scanned and all discovered programs
+ * were transmitted, the last chunk must have set this flag to {@code true}.
+ * This must happen within {@link IBroadcastRadio#LIST_COMPLETE_TIMEOUT_MS}
+ * from the startProgramListUpdates call. If it doesn't, client may assume
+ * the tuner came into a bad state and display error message.
+ */
+ boolean complete;
+
+ /**
+ * Added or modified program list entries.
+ *
+ * Two entries with the same primaryId (ProgramSelector member)
+ * are considered the same.
+ */
+ ProgramInfo[] modified;
+
+ /**
+ * Removed program list entries.
+ *
+ * Contains primaryId (ProgramSelector member) of a program to remove.
+ */
+ @nullable ProgramIdentifier[] removed;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
new file mode 100644
index 0000000..8bd3fd4
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/ProgramSelector.aidl
@@ -0,0 +1,74 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.ProgramIdentifier;
+
+/**
+ * A set of identifiers necessary to tune to a given station.
+ *
+ * This can hold a combination of various identifiers, like:
+ * - AM/FM frequency,
+ * - HD Radio subchannel,
+ * - DAB service ID.
+ *
+ * The type of radio technology is determined by the primary identifier - if the
+ * primary identifier is for DAB, the program is DAB. However, a program of a
+ * specific radio technology may have additional secondary identifiers for other
+ * technologies, i.e. a satellite program may have FM fallback frequency,
+ * if a station broadcasts both via satellite and FM.
+ *
+ * The identifiers from VENDOR_START..VENDOR_END range have limited
+ * serialization capabilities: they are serialized locally, but ignored by the
+ * cloud services. If a program has primary id from vendor range, it's not
+ * synchronized with other devices at all.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable ProgramSelector {
+ /**
+ * Primary program identifier.
+ *
+ * This identifier uniquely identifies a station and can be used for
+ * equality check.
+ *
+ * It can hold only a subset of identifier types, one per each
+ * radio technology:
+ * - analogue AM/FM: AMFM_FREQUENCY_KHZ;
+ * - FM RDS: RDS_PI;
+ * - HD Radio: HD_STATION_ID_EXT;
+ * - DAB: DAB_SID_EXT;
+ * - Digital Radio Mondiale: DRMO_SERVICE_ID;
+ * - SiriusXM: SXM_SERVICE_ID;
+ * - vendor-specific: VENDOR_START..VENDOR_END.
+ */
+ ProgramIdentifier primaryId;
+
+ /**
+ * Secondary program identifiers.
+ *
+ * These identifiers are supplementary and can speed up tuning process,
+ * but the primary ID must be sufficient (i.e. RDS PI is enough to select
+ * a station from the list after a full band scan).
+ *
+ * Two selectors with different secondary IDs, but the same primary ID are
+ * considered equal. In particular, secondary IDs array may get updated for
+ * an entry on the program list (ie. when a better frequency for a given
+ * station is found).
+ */
+ ProgramIdentifier[] secondaryIds;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/Properties.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/Properties.aidl
new file mode 100644
index 0000000..36cbaff
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/Properties.aidl
@@ -0,0 +1,76 @@
+/*
+ * 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.broadcastradio;
+
+import android.hardware.broadcastradio.IdentifierType;
+import android.hardware.broadcastradio.VendorKeyValue;
+
+/**
+ * Properties of a given broadcast radio module.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable Properties {
+ /**
+ * A company name who made the radio module. Must be a valid, registered
+ * name of the company itself.
+ *
+ * It must be opaque to the Android framework.
+ */
+ String maker;
+
+ /**
+ * A product name. Must be unique within the company.
+ *
+ * It must be opaque to the Android framework.
+ */
+ String product;
+
+ /**
+ * Version of the hardware module.
+ *
+ * It must be opaque to the Android framework.
+ */
+ String version;
+
+ /**
+ * Hardware serial number (for subscription services).
+ *
+ * It must be opaque to the Android framework.
+ */
+ String serial;
+
+ /**
+ * A list of supported {@link IdentifierType} values.
+ *
+ * If an identifier is supported by radio module, it means it can use it for
+ * tuning to ProgramSelector with either primary or secondary Identifier of
+ * a given type.
+ *
+ * Support for VENDOR identifier type does not guarantee compatibility, as
+ * other module properties (implementor, product, version) must be checked.
+ */
+ IdentifierType[] supportedIdentifierTypes;
+
+ /**
+ * Vendor-specific information.
+ *
+ * It may be used for extra features, not supported by the platform,
+ * for example: com.me.preset-slots=6; com.me.ultra-hd-capable={@code false}.
+ */
+ VendorKeyValue[] vendorInfo;
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/Result.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/Result.aidl
new file mode 100644
index 0000000..9985ccb
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/Result.aidl
@@ -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 android.hardware.broadcastradio;
+
+/**
+ * Result for methods of BroadcastRadio AIDL HAL interfaces.
+ */
+@VintfStability
+@Backing(type="int")
+enum Result {
+ /**
+ * Methods run without error.
+ */
+ OK,
+
+ /**
+ * Internal error in HAL.
+ */
+ INTERNAL_ERROR,
+
+ /**
+ * Error used when the input argument for the method is invalid.
+ */
+ INVALID_ARGUMENTS,
+
+ /**
+ * Error used when the service is of invalid state (i.e. callback
+ * is not registered for IBroadcastRadio).
+ */
+ INVALID_STATE,
+
+ /**
+ * Error used when an operation is not supported.
+ */
+ NOT_SUPPORTED,
+
+ /**
+ * Error used when a tune, seek, step or operation is not completed
+ * within {@link IBroadcastRadio#LIST_COMPLETE_TIMEOUT_MS}.
+ */
+ TIMEOUT,
+
+ /**
+ * Error that does not follow into the error categories above.
+ */
+ UNKNOWN_ERROR,
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/VendorKeyValue.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/VendorKeyValue.aidl
new file mode 100644
index 0000000..c923e92
--- /dev/null
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/VendorKeyValue.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.broadcastradio;
+
+/**
+ * A key-value pair for vendor-specific information to be passed as-is through
+ * Android framework to the front-end application.
+ */
+@VintfStability
+@JavaDerive(equals=true, toString=true)
+parcelable VendorKeyValue {
+ /**
+ * Key must start with unique vendor Java-style namespace,
+ * eg. 'com.somecompany.parameter1'.
+ */
+ String key;
+
+ /**
+ * Value must be passed through the framework without any changes.
+ * Format of this string can vary across vendors.
+ */
+ String value;
+}
diff --git a/broadcastradio/aidl/default/Android.bp b/broadcastradio/aidl/default/Android.bp
new file mode 100644
index 0000000..720aa8a
--- /dev/null
+++ b/broadcastradio/aidl/default/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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_binary {
+ name: "android.hardware.broadcastradio-service.default",
+ relative_install_path: "hw",
+ init_rc: ["broadcastradio-default.rc"],
+ vintf_fragments: ["broadcastradio-default.xml"],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "BroadcastRadio.cpp",
+ "main.cpp",
+ "VirtualProgram.cpp",
+ "VirtualRadio.cpp",
+ ],
+ static_libs: [
+ "android.hardware.broadcastradio@common-utils-aidl-lib",
+ "android.hardware.broadcastradio@common-utils-lib",
+ ],
+ shared_libs: [
+ "android.hardware.broadcastradio-V1-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "liblog",
+ "libcutils",
+ ],
+}
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
new file mode 100644
index 0000000..0209a0e
--- /dev/null
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -0,0 +1,788 @@
+/*
+ * 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 "BroadcastRadio.h"
+#include <broadcastradio-utils-aidl/Utils.h>
+#include "resources.h"
+
+#include <aidl/android/hardware/broadcastradio/IdentifierType.h>
+#include <aidl/android/hardware/broadcastradio/Result.h>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
+using ::aidl::android::hardware::broadcastradio::utils::tunesTo;
+using ::android::base::EqualsIgnoreCase;
+using ::ndk::ScopedAStatus;
+using ::std::literals::chrono_literals::operator""ms;
+using ::std::literals::chrono_literals::operator""s;
+using ::std::lock_guard;
+using ::std::mutex;
+using ::std::string;
+using ::std::vector;
+
+namespace {
+
+inline constexpr std::chrono::milliseconds kSeekDelayTimeMs = 200ms;
+inline constexpr std::chrono::milliseconds kStepDelayTimeMs = 100ms;
+inline constexpr std::chrono::milliseconds kTuneDelayTimeMs = 150ms;
+inline constexpr std::chrono::seconds kListDelayTimeS = 1s;
+
+// clang-format off
+const AmFmRegionConfig kDefaultAmFmConfig = {
+ {
+ {87500, 108000, 100, 100}, // FM
+ {153, 282, 3, 9}, // AM LW
+ {531, 1620, 9, 9}, // AM MW
+ {1600, 30000, 1, 5}, // AM SW
+ },
+ AmFmRegionConfig::DEEMPHASIS_D50,
+ AmFmRegionConfig::RDS};
+// clang-format on
+
+Properties initProperties(const VirtualRadio& virtualRadio) {
+ Properties prop = {};
+
+ prop.maker = "Android";
+ prop.product = virtualRadio.getName();
+ prop.supportedIdentifierTypes = vector<IdentifierType>({
+ IdentifierType::AMFM_FREQUENCY_KHZ,
+ IdentifierType::RDS_PI,
+ IdentifierType::HD_STATION_ID_EXT,
+ IdentifierType::DAB_SID_EXT,
+ });
+ prop.vendorInfo = vector<VendorKeyValue>({
+ {"com.android.sample", "sample"},
+ });
+
+ return prop;
+}
+
+// Makes ProgramInfo that does not point to any particular program
+ProgramInfo makeSampleProgramInfo(const ProgramSelector& selector) {
+ ProgramInfo info = {};
+ info.selector = selector;
+ info.logicallyTunedTo =
+ utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
+ utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
+ info.physicallyTunedTo = info.logicallyTunedTo;
+ return info;
+}
+
+static bool checkDumpCallerHasWritePermissions(int fd) {
+ uid_t uid = AIBinder_getCallingUid();
+ if (uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM) {
+ return true;
+ }
+ dprintf(fd, "BroadcastRadio HAL dump must be root, shell or system\n");
+ return false;
+}
+
+} // namespace
+
+BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
+ : mVirtualRadio(virtualRadio),
+ mAmFmConfig(kDefaultAmFmConfig),
+ mProperties(initProperties(virtualRadio)) {
+ const auto& ranges = kDefaultAmFmConfig.ranges;
+ if (ranges.size() > 0) {
+ ProgramSelector sel = utils::makeSelectorAmfm(ranges[0].lowerBound);
+ VirtualProgram virtualProgram = {};
+ if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
+ mCurrentProgram = virtualProgram.selector;
+ } else {
+ mCurrentProgram = sel;
+ }
+ }
+}
+
+BroadcastRadio::~BroadcastRadio() {
+ mThread.reset();
+}
+
+ScopedAStatus BroadcastRadio::getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs) {
+ if (full) {
+ *returnConfigs = {};
+ returnConfigs->ranges = vector<AmFmBandRange>({
+ {65000, 108000, 10, 0}, // FM
+ {150, 30000, 1, 0}, // AM
+ });
+ returnConfigs->fmDeemphasis =
+ AmFmRegionConfig::DEEMPHASIS_D50 | AmFmRegionConfig::DEEMPHASIS_D75;
+ returnConfigs->fmRds = AmFmRegionConfig::RDS | AmFmRegionConfig::RBDS;
+ return ScopedAStatus::ok();
+ }
+ lock_guard<mutex> lk(mMutex);
+ *returnConfigs = mAmFmConfig;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::getDabRegionConfig(vector<DabTableEntry>* returnConfigs) {
+ *returnConfigs = {
+ {"5A", 174928}, {"7D", 194064}, {"8A", 195936}, {"8B", 197648}, {"9A", 202928},
+ {"9B", 204640}, {"9C", 206352}, {"10B", 211648}, {"10C", 213360}, {"10D", 215072},
+ {"11A", 216928}, {"11B", 218640}, {"11C", 220352}, {"11D", 222064}, {"12A", 223936},
+ {"12B", 225648}, {"12C", 227360}, {"12D", 229072},
+ };
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::getImage(int32_t id, vector<uint8_t>* returnImage) {
+ LOG(DEBUG) << __func__ << ": fetching image " << std::hex << id;
+
+ if (id == resources::kDemoPngId) {
+ *returnImage = vector<uint8_t>(resources::kDemoPng, std::end(resources::kDemoPng));
+ return ScopedAStatus::ok();
+ }
+
+ LOG(WARNING) << __func__ << ": image of id " << std::hex << id << " doesn't exist";
+ *returnImage = {};
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::getProperties(Properties* returnProperties) {
+ lock_guard<mutex> lk(mMutex);
+ *returnProperties = mProperties;
+ return ScopedAStatus::ok();
+}
+
+ProgramInfo BroadcastRadio::tuneInternalLocked(const ProgramSelector& sel) {
+ LOG(DEBUG) << __func__ << ": tune (internal) to " << sel.toString();
+
+ VirtualProgram virtualProgram = {};
+ ProgramInfo programInfo;
+ if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
+ mCurrentProgram = virtualProgram.selector;
+ programInfo = virtualProgram;
+ } else {
+ mCurrentProgram = sel;
+ programInfo = makeSampleProgramInfo(sel);
+ }
+ mIsTuneCompleted = true;
+
+ return programInfo;
+}
+
+ScopedAStatus BroadcastRadio::setTunerCallback(const std::shared_ptr<ITunerCallback>& callback) {
+ LOG(DEBUG) << __func__ << ": setTunerCallback";
+
+ if (callback == nullptr) {
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::INVALID_ARGUMENTS), "cannot set tuner callback to null");
+ }
+
+ lock_guard<mutex> lk(mMutex);
+ mCallback = callback;
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::unsetTunerCallback() {
+ LOG(DEBUG) << __func__ << ": unsetTunerCallback";
+
+ lock_guard<mutex> lk(mMutex);
+ mCallback = nullptr;
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::tune(const ProgramSelector& program) {
+ LOG(DEBUG) << __func__ << ": tune to " << program.toString() << "...";
+
+ lock_guard<mutex> lk(mMutex);
+ if (mCallback == nullptr) {
+ LOG(ERROR) << __func__ << ": callback is not registered.";
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::INVALID_STATE), "callback is not registered");
+ }
+
+ if (!utils::isSupported(mProperties, program)) {
+ LOG(WARNING) << __func__ << ": selector not supported: " << program.toString();
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::NOT_SUPPORTED), "selector is not supported");
+ }
+
+ if (!utils::isValid(program)) {
+ LOG(ERROR) << __func__ << ": selector is not valid: " << program.toString();
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::INVALID_ARGUMENTS), "selector is not valid");
+ }
+
+ cancelLocked();
+
+ mIsTuneCompleted = false;
+ std::shared_ptr<ITunerCallback> callback = mCallback;
+ auto task = [this, program, callback]() {
+ ProgramInfo programInfo = {};
+ {
+ lock_guard<mutex> lk(mMutex);
+ programInfo = tuneInternalLocked(program);
+ }
+ callback->onCurrentProgramInfoChanged(programInfo);
+ };
+ mThread->schedule(task, kTuneDelayTimeMs);
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
+ LOG(DEBUG) << __func__ << ": seek " << (directionUp ? "up" : "down") << " with skipSubChannel? "
+ << (skipSubChannel ? "yes" : "no") << "...";
+
+ lock_guard<mutex> lk(mMutex);
+ if (mCallback == nullptr) {
+ LOG(ERROR) << __func__ << ": callback is not registered.";
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::INVALID_STATE), "callback is not registered");
+ }
+
+ cancelLocked();
+
+ const auto& list = mVirtualRadio.getProgramList();
+ std::shared_ptr<ITunerCallback> callback = mCallback;
+ if (list.empty()) {
+ mIsTuneCompleted = false;
+ auto task = [callback]() {
+ LOG(DEBUG) << "seek: program list is empty, seek couldn't stop";
+
+ callback->onTuneFailed(Result::TIMEOUT, {});
+ };
+ mThread->schedule(task, kSeekDelayTimeMs);
+
+ return ScopedAStatus::ok();
+ }
+
+ // The list is not sorted here since it has already stored in VirtualRadio.
+ // If the list is not sorted in advance, it should be sorted here.
+ const auto& current = mCurrentProgram;
+ auto found = std::lower_bound(list.begin(), list.end(), VirtualProgram({current}));
+ if (directionUp) {
+ if (found < list.end() - 1) {
+ if (tunesTo(current, found->selector)) found++;
+ } else {
+ found = list.begin();
+ }
+ } else {
+ if (found > list.begin() && found != list.end()) {
+ found--;
+ } else {
+ found = list.end() - 1;
+ }
+ }
+ const ProgramSelector tuneTo = found->selector;
+
+ mIsTuneCompleted = false;
+ auto task = [this, tuneTo, callback]() {
+ ProgramInfo programInfo = {};
+ {
+ lock_guard<mutex> lk(mMutex);
+ programInfo = tuneInternalLocked(tuneTo);
+ }
+ callback->onCurrentProgramInfoChanged(programInfo);
+ };
+ mThread->schedule(task, kSeekDelayTimeMs);
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::step(bool directionUp) {
+ LOG(DEBUG) << __func__ << ": step " << (directionUp ? "up" : "down") << "...";
+
+ lock_guard<mutex> lk(mMutex);
+ if (mCallback == nullptr) {
+ LOG(ERROR) << __func__ << ": callback is not registered.";
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::INVALID_STATE), "callback is not registered");
+ }
+
+ cancelLocked();
+
+ if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+ LOG(WARNING) << __func__ << ": can't step in anything else than AM/FM";
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::NOT_SUPPORTED), "cannot step in anything else than AM/FM");
+ }
+
+ int64_t stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
+ std::optional<AmFmBandRange> range = getAmFmRangeLocked();
+ if (!range) {
+ LOG(ERROR) << __func__ << ": can't find current band or tune operation is in process";
+ ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::INTERNAL_ERROR),
+ "can't find current band or tune operation is in process");
+ }
+
+ if (directionUp) {
+ stepTo += range->spacing;
+ } else {
+ stepTo -= range->spacing;
+ }
+ if (stepTo > range->upperBound) {
+ stepTo = range->lowerBound;
+ }
+ if (stepTo < range->lowerBound) {
+ stepTo = range->upperBound;
+ }
+
+ mIsTuneCompleted = false;
+ std::shared_ptr<ITunerCallback> callback = mCallback;
+ auto task = [this, stepTo, callback]() {
+ ProgramInfo programInfo;
+ {
+ lock_guard<mutex> lk(mMutex);
+ programInfo = tuneInternalLocked(utils::makeSelectorAmfm(stepTo));
+ }
+ callback->onCurrentProgramInfoChanged(programInfo);
+ };
+ mThread->schedule(task, kStepDelayTimeMs);
+
+ return ScopedAStatus::ok();
+}
+
+void BroadcastRadio::cancelLocked() {
+ LOG(DEBUG) << __func__ << ": cancelling current operations...";
+
+ mThread->cancelAll();
+ if (mCurrentProgram.primaryId.type != IdentifierType::INVALID) {
+ mIsTuneCompleted = true;
+ }
+}
+
+ScopedAStatus BroadcastRadio::cancel() {
+ LOG(DEBUG) << __func__ << ": cancel pending tune, seek and step...";
+
+ lock_guard<mutex> lk(mMutex);
+ cancelLocked();
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::startProgramListUpdates(const ProgramFilter& filter) {
+ LOG(DEBUG) << __func__ << ": requested program list updates, filter = " << filter.toString()
+ << "...";
+
+ auto filterCb = [&filter](const VirtualProgram& program) {
+ return utils::satisfies(filter, program.selector);
+ };
+
+ lock_guard<mutex> lk(mMutex);
+
+ const auto& list = mVirtualRadio.getProgramList();
+ vector<VirtualProgram> filteredList;
+ std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
+
+ auto task = [this, filteredList]() {
+ std::shared_ptr<ITunerCallback> callback;
+ {
+ lock_guard<mutex> lk(mMutex);
+ if (mCallback == nullptr) {
+ LOG(WARNING) << "Callback is null when updating program List";
+ return;
+ }
+ callback = mCallback;
+ }
+
+ ProgramListChunk chunk = {};
+ chunk.purge = true;
+ chunk.complete = true;
+ chunk.modified = vector<ProgramInfo>(filteredList.begin(), filteredList.end());
+
+ callback->onProgramListUpdated(chunk);
+ };
+ mThread->schedule(task, kListDelayTimeS);
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::stopProgramListUpdates() {
+ LOG(DEBUG) << __func__ << ": requested program list updates to stop...";
+ // TODO(b/243681584) Implement stop program list updates method
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, [[maybe_unused]] bool* returnIsSet) {
+ LOG(DEBUG) << __func__ << ": flag = " << toString(flag);
+
+ LOG(INFO) << __func__ << ": getting ConfigFlag is not supported";
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::NOT_SUPPORTED), "getting ConfigFlag is not supported");
+}
+
+ScopedAStatus BroadcastRadio::setConfigFlag(ConfigFlag flag, bool value) {
+ LOG(DEBUG) << __func__ << ": flag = " << toString(flag) << ", value = " << value;
+
+ LOG(INFO) << __func__ << ": setting ConfigFlag is not supported";
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::NOT_SUPPORTED), "setting ConfigFlag is not supported");
+}
+
+ScopedAStatus BroadcastRadio::setParameters(
+ [[maybe_unused]] const vector<VendorKeyValue>& parameters,
+ vector<VendorKeyValue>* returnParameters) {
+ // TODO(b/243682330) Support vendor parameter functionality
+ *returnParameters = {};
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus BroadcastRadio::getParameters([[maybe_unused]] const vector<string>& keys,
+ vector<VendorKeyValue>* returnParameters) {
+ // TODO(b/243682330) Support vendor parameter functionality
+ *returnParameters = {};
+ return ScopedAStatus::ok();
+}
+
+std::optional<AmFmBandRange> BroadcastRadio::getAmFmRangeLocked() const {
+ if (!mIsTuneCompleted) {
+ LOG(WARNING) << __func__ << ": tune operation is in process";
+ return {};
+ }
+ if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+ LOG(WARNING) << __func__ << ": current program does not has AMFM_FREQUENCY_KHZ identifier";
+ return {};
+ }
+
+ int64_t freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
+ for (const auto& range : mAmFmConfig.ranges) {
+ if (range.lowerBound <= freq && range.upperBound >= freq) {
+ return range;
+ }
+ }
+
+ return {};
+}
+
+ScopedAStatus BroadcastRadio::registerAnnouncementListener(
+ [[maybe_unused]] const std::shared_ptr<IAnnouncementListener>& listener,
+ const vector<AnnouncementType>& enabled, std::shared_ptr<ICloseHandle>* returnCloseHandle) {
+ LOG(DEBUG) << __func__ << ": registering announcement listener for "
+ << utils::vectorToString(enabled);
+
+ // TODO(b/243683842) Support announcement listener
+ *returnCloseHandle = nullptr;
+ LOG(INFO) << __func__ << ": registering announcementListener is not supported";
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ resultToInt(Result::NOT_SUPPORTED),
+ "registering announcementListener is not supported");
+}
+
+binder_status_t BroadcastRadio::dump(int fd, const char** args, uint32_t numArgs) {
+ if (numArgs == 0) {
+ return dumpsys(fd);
+ }
+
+ string option = string(args[0]);
+ if (EqualsIgnoreCase(option, "--help")) {
+ return cmdHelp(fd);
+ } else if (EqualsIgnoreCase(option, "--tune")) {
+ return cmdTune(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--seek")) {
+ return cmdSeek(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--step")) {
+ return cmdStep(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--cancel")) {
+ return cmdCancel(fd, numArgs);
+ } else if (EqualsIgnoreCase(option, "--startProgramListUpdates")) {
+ return cmdStartProgramListUpdates(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--stopProgramListUpdates")) {
+ return cmdStopProgramListUpdates(fd, numArgs);
+ }
+ dprintf(fd, "Invalid option: %s\n", option.c_str());
+ return STATUS_BAD_VALUE;
+}
+
+binder_status_t BroadcastRadio::dumpsys(int fd) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ lock_guard<mutex> lk(mMutex);
+ dprintf(fd, "AmFmRegionConfig: %s\n", mAmFmConfig.toString().c_str());
+ dprintf(fd, "Properties: %s \n", mProperties.toString().c_str());
+ if (mIsTuneCompleted) {
+ dprintf(fd, "Tune completed\n");
+ } else {
+ dprintf(fd, "Tune not completed\n");
+ }
+ if (mCallback == nullptr) {
+ dprintf(fd, "No ITunerCallback registered\n");
+ } else {
+ dprintf(fd, "ITunerCallback registered\n");
+ }
+ dprintf(fd, "CurrentProgram: %s \n", mCurrentProgram.toString().c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdHelp(int fd) const {
+ dprintf(fd, "Usage: \n\n");
+ dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n");
+ dprintf(fd, "--help: shows this help\n");
+ dprintf(fd,
+ "--tune amfm <FREQUENCY>: tunes amfm radio to frequency (in Hz) specified: "
+ "frequency (int) \n"
+ "--tune dab <SID> <ENSEMBLE>: tunes dab radio to sid and ensemble specified: "
+ "sidExt (int), ensemble (int) \n");
+ dprintf(fd,
+ "--seek [up|down] <SKIP_SUB_CHANNEL>: seek with direction (up or down) and "
+ "option whether skipping sub channel: "
+ "skipSubChannel (string, should be either \"true\" or \"false\")\n");
+ dprintf(fd, "--step [up|down]: step in direction (up or down) specified\n");
+ dprintf(fd, "--cancel: cancel current pending tune, step, and seek\n");
+ dprintf(fd,
+ "--startProgramListUpdates <IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
+ "<EXCLUDE_MODIFICATIONS>: start update program list with the filter specified: "
+ "identifier types (string, in format <TYPE>,<TYPE>,...,<TYPE> or \"null\" (if empty), "
+ "where TYPE is int), "
+ "program identifiers (string, in format "
+ "<TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE> or \"null\" (if empty), "
+ "where TYPE is int and VALUE is long), "
+ "includeCategories (string, should be either \"true\" or \"false\"), "
+ "excludeModifications (string, should be either \"true\" or \"false\")\n");
+ dprintf(fd, "--stopProgramListUpdates: stop current pending program list updates\n");
+ dprintf(fd,
+ "Note on <TYPE> for --startProgramList command: it is int for identifier type. "
+ "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
+ "for its definition.\n");
+ dprintf(fd,
+ "Note on <VALUE> for --startProgramList command: it is long type for identifier value. "
+ "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
+ "for its value.\n");
+
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdTune(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 3 && numArgs != 4) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --tune amfm <FREQUENCY> "
+ "or --tune dab <SID> <ENSEMBLE>\n");
+ return STATUS_BAD_VALUE;
+ }
+ bool isDab = false;
+ if (EqualsIgnoreCase(string(args[1]), "dab")) {
+ isDab = true;
+ } else if (!EqualsIgnoreCase(string(args[1]), "amfm")) {
+ dprintf(fd, "Unknown radio type provided with tune: %s\n", args[1]);
+ return STATUS_BAD_VALUE;
+ }
+ ProgramSelector sel = {};
+ if (isDab) {
+ if (numArgs != 4) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --tune dab <SID> <ENSEMBLE>\n");
+ return STATUS_BAD_VALUE;
+ }
+ int sid;
+ if (!utils::parseArgInt(string(args[2]), &sid)) {
+ dprintf(fd, "Non-integer sid provided with tune: %s\n", string(args[2]).c_str());
+ return STATUS_BAD_VALUE;
+ }
+ int ensemble;
+ if (!utils::parseArgInt(string(args[3]), &ensemble)) {
+ dprintf(fd, "Non-integer ensemble provided with tune: %s\n", string(args[3]).c_str());
+ return STATUS_BAD_VALUE;
+ }
+ sel = utils::makeSelectorDab(sid, ensemble);
+ } else {
+ if (numArgs != 3) {
+ dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
+ return STATUS_BAD_VALUE;
+ }
+ int freq;
+ if (!utils::parseArgInt(string(args[2]), &freq)) {
+ dprintf(fd, "Non-integer frequency provided with tune: %s\n", string(args[2]).c_str());
+ return STATUS_BAD_VALUE;
+ }
+ sel = utils::makeSelectorAmfm(freq);
+ }
+
+ auto tuneResult = tune(sel);
+ if (!tuneResult.isOk()) {
+ dprintf(fd, "Unable to tune %s radio to %s\n", args[1], sel.toString().c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Tune %s radio to %s \n", args[1], sel.toString().c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdSeek(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 3) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --seek <DIRECTION> "
+ "<SKIP_SUB_CHANNEL>\n");
+ return STATUS_BAD_VALUE;
+ }
+ string seekDirectionIn = string(args[1]);
+ bool seekDirectionUp;
+ if (!utils::parseArgDirection(seekDirectionIn, &seekDirectionUp)) {
+ dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with seek: %s\n",
+ seekDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string skipSubChannelIn = string(args[2]);
+ bool skipSubChannel;
+ if (!utils::parseArgBool(skipSubChannelIn, &skipSubChannel)) {
+ dprintf(fd, "Invalid skipSubChannel (\"true\" or \"false\") provided with seek: %s\n",
+ skipSubChannelIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ auto seekResult = seek(seekDirectionUp, skipSubChannel);
+ if (!seekResult.isOk()) {
+ dprintf(fd, "Unable to seek in %s direction\n", seekDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Seek in %s direction\n", seekDirectionIn.c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdStep(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 2) {
+ dprintf(fd, "Invalid number of arguments: please provide --step <DIRECTION>\n");
+ return STATUS_BAD_VALUE;
+ }
+ string stepDirectionIn = string(args[1]);
+ bool stepDirectionUp;
+ if (!utils::parseArgDirection(stepDirectionIn, &stepDirectionUp)) {
+ dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with step: %s\n",
+ stepDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ auto stepResult = step(stepDirectionUp);
+ if (!stepResult.isOk()) {
+ dprintf(fd, "Unable to step in %s direction\n", stepDirectionIn.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Step in %s direction\n", stepDirectionIn.c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdCancel(int fd, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 1) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --cancel "
+ "only and no more arguments\n");
+ return STATUS_BAD_VALUE;
+ }
+
+ auto cancelResult = cancel();
+ if (!cancelResult.isOk()) {
+ dprintf(fd, "Unable to cancel pending tune, seek, and step\n");
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Canceled pending tune, seek, and step\n");
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdStartProgramListUpdates(int fd, const char** args,
+ uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 5) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --startProgramListUpdates "
+ "<IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
+ "<EXCLUDE_MODIFICATIONS>\n");
+ return STATUS_BAD_VALUE;
+ }
+ string filterTypesStr = string(args[1]);
+ std::vector<IdentifierType> filterTypeList;
+ if (!EqualsIgnoreCase(filterTypesStr, "null") &&
+ !utils::parseArgIdentifierTypeArray(filterTypesStr, &filterTypeList)) {
+ dprintf(fd,
+ "Invalid identifier types provided with startProgramListUpdates: %s, "
+ "should be: <TYPE>,<TYPE>,...,<TYPE>\n",
+ filterTypesStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string filtersStr = string(args[2]);
+ std::vector<ProgramIdentifier> filterList;
+ if (!EqualsIgnoreCase(filtersStr, "null") &&
+ !utils::parseProgramIdentifierList(filtersStr, &filterList)) {
+ dprintf(fd,
+ "Invalid program identifiers provided with startProgramListUpdates: %s, "
+ "should be: <TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE>\n",
+ filtersStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string includeCategoriesStr = string(args[3]);
+ bool includeCategories;
+ if (!utils::parseArgBool(includeCategoriesStr, &includeCategories)) {
+ dprintf(fd,
+ "Invalid includeCategories (\"true\" or \"false\") "
+ "provided with startProgramListUpdates : %s\n",
+ includeCategoriesStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ string excludeModificationsStr = string(args[4]);
+ bool excludeModifications;
+ if (!utils::parseArgBool(excludeModificationsStr, &excludeModifications)) {
+ dprintf(fd,
+ "Invalid excludeModifications(\"true\" or \"false\") "
+ "provided with startProgramListUpdates : %s\n",
+ excludeModificationsStr.c_str());
+ return STATUS_BAD_VALUE;
+ }
+ ProgramFilter filter = {filterTypeList, filterList, includeCategories, excludeModifications};
+
+ auto updateResult = startProgramListUpdates(filter);
+ if (!updateResult.isOk()) {
+ dprintf(fd, "Unable to start program list update for filter %s \n",
+ filter.toString().c_str());
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Start program list update for filter %s\n", filter.toString().c_str());
+ return STATUS_OK;
+}
+
+binder_status_t BroadcastRadio::cmdStopProgramListUpdates(int fd, uint32_t numArgs) {
+ if (!checkDumpCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+ if (numArgs != 1) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide --stopProgramListUpdates "
+ "only and no more arguments\n");
+ return STATUS_BAD_VALUE;
+ }
+
+ auto stopResult = stopProgramListUpdates();
+ if (!stopResult.isOk()) {
+ dprintf(fd, "Unable to stop pending program list update\n");
+ return STATUS_BAD_VALUE;
+ }
+ dprintf(fd, "Stop pending program list update\n");
+ return STATUS_OK;
+}
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/BroadcastRadio.h b/broadcastradio/aidl/default/BroadcastRadio.h
new file mode 100644
index 0000000..1c85ddc
--- /dev/null
+++ b/broadcastradio/aidl/default/BroadcastRadio.h
@@ -0,0 +1,93 @@
+/*
+ * 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 "VirtualRadio.h"
+
+#include <aidl/android/hardware/broadcastradio/AmFmBandRange.h>
+#include <aidl/android/hardware/broadcastradio/AmFmRegionConfig.h>
+#include <aidl/android/hardware/broadcastradio/AnnouncementType.h>
+#include <aidl/android/hardware/broadcastradio/BnBroadcastRadio.h>
+#include <aidl/android/hardware/broadcastradio/DabTableEntry.h>
+#include <aidl/android/hardware/broadcastradio/IAnnouncementListener.h>
+#include <aidl/android/hardware/broadcastradio/ICloseHandle.h>
+#include <aidl/android/hardware/broadcastradio/ITunerCallback.h>
+#include <aidl/android/hardware/broadcastradio/Properties.h>
+#include <broadcastradio-utils/WorkerThread.h>
+
+#include <android-base/thread_annotations.h>
+
+#include <optional>
+
+namespace aidl::android::hardware::broadcastradio {
+
+class BroadcastRadio final : public BnBroadcastRadio {
+ public:
+ explicit BroadcastRadio(const VirtualRadio& virtualRadio);
+ ~BroadcastRadio();
+ ndk::ScopedAStatus getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs) override;
+ ndk::ScopedAStatus getDabRegionConfig(std::vector<DabTableEntry>* returnConfigs) override;
+ ndk::ScopedAStatus getImage(int32_t id, std::vector<uint8_t>* returnImage) override;
+ ndk::ScopedAStatus getProperties(Properties* returnProperties) override;
+
+ ndk::ScopedAStatus setTunerCallback(const std::shared_ptr<ITunerCallback>& callback) override;
+ ndk::ScopedAStatus unsetTunerCallback() override;
+ ndk::ScopedAStatus tune(const ProgramSelector& program) override;
+ ndk::ScopedAStatus seek(bool directionUp, bool skipSubChannel) override;
+ ndk::ScopedAStatus step(bool directionUp) override;
+ ndk::ScopedAStatus cancel() override;
+ ndk::ScopedAStatus startProgramListUpdates(const ProgramFilter& filter) override;
+ ndk::ScopedAStatus stopProgramListUpdates() override;
+ ndk::ScopedAStatus isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) override;
+ ndk::ScopedAStatus setConfigFlag(ConfigFlag flag, bool in_value) override;
+ ndk::ScopedAStatus setParameters(const std::vector<VendorKeyValue>& parameters,
+ std::vector<VendorKeyValue>* returnParameters) override;
+ ndk::ScopedAStatus getParameters(const std::vector<std::string>& keys,
+ std::vector<VendorKeyValue>* returnParameters) override;
+ ndk::ScopedAStatus registerAnnouncementListener(
+ const std::shared_ptr<IAnnouncementListener>& listener,
+ const std::vector<AnnouncementType>& enabled,
+ std::shared_ptr<ICloseHandle>* returnCloseHandle) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ private:
+ const VirtualRadio& mVirtualRadio;
+ std::mutex mMutex;
+ AmFmRegionConfig mAmFmConfig GUARDED_BY(mMutex);
+ std::unique_ptr<::android::WorkerThread> mThread GUARDED_BY(mMutex) =
+ std::unique_ptr<::android::WorkerThread>(new ::android::WorkerThread());
+ bool mIsTuneCompleted GUARDED_BY(mMutex) = true;
+ Properties mProperties GUARDED_BY(mMutex);
+ ProgramSelector mCurrentProgram GUARDED_BY(mMutex) = {};
+ std::shared_ptr<ITunerCallback> mCallback GUARDED_BY(mMutex);
+
+ std::optional<AmFmBandRange> getAmFmRangeLocked() const;
+ void cancelLocked();
+ ProgramInfo tuneInternalLocked(const ProgramSelector& sel);
+
+ binder_status_t cmdHelp(int fd) const;
+ binder_status_t cmdTune(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdSeek(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdStep(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdCancel(int fd, uint32_t numArgs);
+ binder_status_t cmdStartProgramListUpdates(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdStopProgramListUpdates(int fd, uint32_t numArgs);
+
+ binder_status_t dumpsys(int fd);
+};
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/VirtualProgram.cpp b/broadcastradio/aidl/default/VirtualProgram.cpp
new file mode 100644
index 0000000..0df0a82
--- /dev/null
+++ b/broadcastradio/aidl/default/VirtualProgram.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 "VirtualProgram.h"
+
+#include <broadcastradio-utils-aidl/Utils.h>
+#include "resources.h"
+
+#include <android-base/logging.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+using ::std::vector;
+
+VirtualProgram::operator ProgramInfo() const {
+ ProgramInfo info = {};
+
+ info.selector = selector;
+
+ IdentifierType programType = selector.primaryId.type;
+ bool isDigital = (programType != IdentifierType::AMFM_FREQUENCY_KHZ &&
+ programType != IdentifierType::RDS_PI);
+
+ auto selectId = [&info](const IdentifierType& type) {
+ return utils::makeIdentifier(type, utils::getId(info.selector, type));
+ };
+
+ switch (programType) {
+ case IdentifierType::AMFM_FREQUENCY_KHZ:
+ info.logicallyTunedTo = info.physicallyTunedTo =
+ selectId(IdentifierType::AMFM_FREQUENCY_KHZ);
+ break;
+ case IdentifierType::RDS_PI:
+ info.logicallyTunedTo = selectId(IdentifierType::RDS_PI);
+ info.physicallyTunedTo = selectId(IdentifierType::AMFM_FREQUENCY_KHZ);
+ break;
+ case IdentifierType::HD_STATION_ID_EXT:
+ info.logicallyTunedTo = selectId(IdentifierType::HD_STATION_ID_EXT);
+ info.physicallyTunedTo = selectId(IdentifierType::AMFM_FREQUENCY_KHZ);
+ break;
+ case IdentifierType::DAB_SID_EXT:
+ info.logicallyTunedTo = selectId(IdentifierType::DAB_SID_EXT);
+ info.physicallyTunedTo = selectId(IdentifierType::DAB_ENSEMBLE);
+ break;
+ case IdentifierType::DRMO_SERVICE_ID:
+ info.logicallyTunedTo = selectId(IdentifierType::DRMO_SERVICE_ID);
+ info.physicallyTunedTo = selectId(IdentifierType::DRMO_FREQUENCY_KHZ);
+ break;
+ case IdentifierType::SXM_SERVICE_ID:
+ info.logicallyTunedTo = selectId(IdentifierType::SXM_SERVICE_ID);
+ info.physicallyTunedTo = selectId(IdentifierType::SXM_CHANNEL);
+ break;
+ default:
+ LOG(FATAL) << "unsupported program type: " << toString(programType);
+ return {};
+ }
+
+ info.infoFlags |= (ProgramInfo::FLAG_TUNABLE | ProgramInfo::FLAG_STEREO);
+ info.signalQuality = isDigital ? kSignalQualityDigital : kSignalQualityNonDigital;
+
+ info.metadata = vector<Metadata>({
+ Metadata::make<Metadata::rdsPs>(programName),
+ Metadata::make<Metadata::songTitle>(songTitle),
+ Metadata::make<Metadata::songArtist>(songArtist),
+ Metadata::make<Metadata::stationIcon>(resources::kDemoPngId),
+ Metadata::make<Metadata::albumArt>(resources::kDemoPngId),
+ });
+
+ info.vendorInfo = vector<VendorKeyValue>({
+ {"com.android.sample", "sample"},
+ {"com.android.sample.VirtualProgram", "VirtualProgram"},
+ });
+
+ return info;
+}
+
+bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs) {
+ auto& l = lhs.selector;
+ auto& r = rhs.selector;
+
+ // Two programs with the same primaryId are considered the same.
+ if (l.primaryId.type != r.primaryId.type) return l.primaryId.type < r.primaryId.type;
+
+ return l.primaryId.value < r.primaryId.value;
+}
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/VirtualProgram.h b/broadcastradio/aidl/default/VirtualProgram.h
new file mode 100644
index 0000000..0c20721
--- /dev/null
+++ b/broadcastradio/aidl/default/VirtualProgram.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 <aidl/android/hardware/broadcastradio/IdentifierType.h>
+#include <aidl/android/hardware/broadcastradio/ProgramInfo.h>
+#include <aidl/android/hardware/broadcastradio/ProgramSelector.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+constexpr int kSignalQualityDigital = 100;
+constexpr int kSignalQualityNonDigital = 80;
+/**
+ * A radio program mock.
+ *
+ * This represents broadcast waves flying over the air,
+ * not an entry for a captured station in the radio tuner memory.
+ */
+struct VirtualProgram {
+ ProgramSelector selector;
+
+ std::string programName = "";
+ std::string songArtist = "";
+ std::string songTitle = "";
+
+ operator ProgramInfo() const;
+
+ /**
+ * Defines order in which virtual programs appear on the "air" with
+ * ITunerSession::scan().
+ *
+ * It's for default implementation purposes, may not be complete or correct.
+ */
+ friend bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs);
+};
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/VirtualRadio.cpp b/broadcastradio/aidl/default/VirtualRadio.cpp
new file mode 100644
index 0000000..851543b
--- /dev/null
+++ b/broadcastradio/aidl/default/VirtualRadio.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "VirtualRadio.h"
+#include <broadcastradio-utils-aidl/Utils.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+using ::aidl::android::hardware::broadcastradio::utils::makeSelectorAmfm;
+using ::aidl::android::hardware::broadcastradio::utils::makeSelectorDab;
+using ::std::string;
+using ::std::vector;
+
+VirtualRadio::VirtualRadio(const string& name, const vector<VirtualProgram>& initialList)
+ : mName(name), mPrograms(initialList) {
+ sort(mPrograms.begin(), mPrograms.end());
+}
+
+string VirtualRadio::getName() const {
+ return mName;
+}
+
+const vector<VirtualProgram>& VirtualRadio::getProgramList() const {
+ return mPrograms;
+}
+
+bool VirtualRadio::getProgram(const ProgramSelector& selector, VirtualProgram* programOut) const {
+ for (const auto& program : mPrograms) {
+ if (utils::tunesTo(selector, program.selector)) {
+ *programOut = program;
+ return true;
+ }
+ }
+ return false;
+}
+
+// get singleton of AMFM Virtual Radio
+const VirtualRadio& VirtualRadio::getAmFmRadio() {
+ // clang-format off
+ static VirtualRadio amFmRadioMock(
+ "AM/FM radio mock",
+ {
+ {makeSelectorAmfm(94900), "Wild 94.9", "Drake ft. Rihanna", "Too Good"},
+ {makeSelectorAmfm(96500), "KOIT", "Celine Dion", "All By Myself"},
+ {makeSelectorAmfm(97300), "Alice@97.3", "Drops of Jupiter", "Train"},
+ {makeSelectorAmfm(99700), "99.7 Now!", "The Chainsmokers", "Closer"},
+ {makeSelectorAmfm(101300), "101-3 KISS-FM", "Justin Timberlake", "Rock Your Body"},
+ {makeSelectorAmfm(103700), "iHeart80s @ 103.7", "Michael Jackson", "Billie Jean"},
+ {makeSelectorAmfm(106100), "106 KMEL", "Drake", "Marvins Room"},
+ {makeSelectorAmfm(700), "700 AM", "Artist700", "Title700"},
+ {makeSelectorAmfm(1700), "1700 AM", "Artist1700", "Title1700"},
+ });
+ // clang-format on
+ return amFmRadioMock;
+}
+
+// get singleton of DAB Virtual Radio
+const VirtualRadio& VirtualRadio::getDabRadio() {
+ // clang-format off
+ static VirtualRadio dabRadioMock(
+ "DAB radio mock",
+ {
+ {makeSelectorDab(0xA00001u, 0x0001u), "BBC Radio 1", "Khalid", "Talk"},
+ {makeSelectorDab(0xB00001u, 0x1001u), "Classic FM", "Jean Sibelius", "Andante Festivo"},
+ {makeSelectorDab(0xB00002u, 0x1001u), "Absolute Radio", "Coldplay", "Clocks"},
+ });
+ // clang-format on
+ return dabRadioMock;
+}
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/VirtualRadio.h b/broadcastradio/aidl/default/VirtualRadio.h
new file mode 100644
index 0000000..ae039c4
--- /dev/null
+++ b/broadcastradio/aidl/default/VirtualRadio.h
@@ -0,0 +1,48 @@
+/*
+ * 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 "VirtualProgram.h"
+
+#include <vector>
+
+namespace aidl::android::hardware::broadcastradio {
+
+/**
+ * A radio frequency space mock.
+ *
+ * This represents all broadcast waves in the air for a given radio technology,
+ * not a captured station list in the radio tuner memory.
+ *
+ * It's meant to abstract out radio content from default tuner implementation.
+ */
+class VirtualRadio final {
+ public:
+ VirtualRadio(const std::string& name, const std::vector<VirtualProgram>& initialList);
+ std::string getName() const;
+ const std::vector<VirtualProgram>& getProgramList() const;
+ bool getProgram(const ProgramSelector& selector, VirtualProgram* program) const;
+
+ static const VirtualRadio& getAmFmRadio();
+ static const VirtualRadio& getDabRadio();
+
+ private:
+ const std::string mName;
+ std::vector<VirtualProgram> mPrograms;
+};
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/aidl/default/broadcastradio-default.rc b/broadcastradio/aidl/default/broadcastradio-default.rc
new file mode 100644
index 0000000..49389e6
--- /dev/null
+++ b/broadcastradio/aidl/default/broadcastradio-default.rc
@@ -0,0 +1,6 @@
+service vendor.broadcastradio-default /vendor/bin/hw/android.hardware.broadcastradio-service.default
+ interface aidl android.hardware.broadcastradio.IBroadcastRadio/amfm
+ interface aidl android.hardware.broadcastradio.IBroadcastRadio/dab
+ class hal
+ user audioserver
+ group audio
diff --git a/broadcastradio/aidl/default/broadcastradio-default.xml b/broadcastradio/aidl/default/broadcastradio-default.xml
new file mode 100644
index 0000000..1555822
--- /dev/null
+++ b/broadcastradio/aidl/default/broadcastradio-default.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.broadcastradio</name>
+ <fqname>IBroadcastRadio/amfm</fqname>
+ <fqname>IBroadcastRadio/dab</fqname>
+ </hal>
+</manifest>
diff --git a/broadcastradio/aidl/default/main.cpp b/broadcastradio/aidl/default/main.cpp
new file mode 100644
index 0000000..6bf20d5
--- /dev/null
+++ b/broadcastradio/aidl/default/main.cpp
@@ -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.
+ */
+
+#include "BroadcastRadio.h"
+#include "VirtualRadio.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::broadcastradio::BroadcastRadio;
+using ::aidl::android::hardware::broadcastradio::VirtualRadio;
+
+int main() {
+ android::base::SetDefaultTag("BcRadioAidlDef");
+ ABinderProcess_setThreadPoolMaxThreadCount(4);
+ ABinderProcess_startThreadPool();
+
+ const VirtualRadio& amFmRadioMock = VirtualRadio::getAmFmRadio();
+ std::shared_ptr<BroadcastRadio> broadcastRadio =
+ ::ndk::SharedRefBase::make<BroadcastRadio>(amFmRadioMock);
+ const std::string instanceAmFm = std::string() + BroadcastRadio::descriptor + "/amfm";
+ binder_status_t statusAmFm =
+ AServiceManager_addService(broadcastRadio->asBinder().get(), instanceAmFm.c_str());
+ CHECK_EQ(statusAmFm, STATUS_OK)
+ << "Failed to register Broadcast Radio AM/FM HAL implementation";
+
+ const VirtualRadio& dabRadioMock = VirtualRadio::getDabRadio();
+ std::shared_ptr<BroadcastRadio> dabRadio =
+ ::ndk::SharedRefBase::make<BroadcastRadio>(dabRadioMock);
+ const std::string instanceDab = std::string() + BroadcastRadio::descriptor + "/dab";
+ binder_status_t statusDab =
+ AServiceManager_addService(dabRadio->asBinder().get(), instanceDab.c_str());
+ CHECK_EQ(statusDab, STATUS_OK) << "Failed to register Broadcast Radio DAB HAL implementation";
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/broadcastradio/aidl/default/resources.h b/broadcastradio/aidl/default/resources.h
new file mode 100644
index 0000000..beffc76
--- /dev/null
+++ b/broadcastradio/aidl/default/resources.h
@@ -0,0 +1,36 @@
+/*
+ * 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/broadcastradio/ProgramSelector.h>
+
+namespace aidl::android::hardware::broadcastradio::resources {
+
+constexpr int32_t kDemoPngId = 123456;
+constexpr uint8_t kDemoPng[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44,
+ 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, 0x00, 0x00, 0x25,
+ 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00, 0x5d, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0xd9,
+ 0xc1, 0x09, 0x00, 0x30, 0x08, 0x04, 0xc1, 0x33, 0xfd, 0xf7, 0x6c, 0x6a, 0xc8, 0x23, 0x04,
+ 0xc9, 0x6c, 0x01, 0xc2, 0x20, 0xbe, 0x4c, 0x86, 0x57, 0x49, 0xba, 0xfb, 0xd6, 0xf4, 0xba,
+ 0x3e, 0x7f, 0x4d, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x8f, 0x00, 0xbd, 0xce, 0x7f,
+ 0xc0, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xb8, 0x0d, 0x32, 0xd4, 0x0c, 0x77, 0xbd,
+ 0xfb, 0xc1, 0xce, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
+
+} // namespace aidl::android::hardware::broadcastradio::resources
diff --git a/broadcastradio/aidl/vts/Android.bp b/broadcastradio/aidl/vts/Android.bp
new file mode 100644
index 0000000..b60387e
--- /dev/null
+++ b/broadcastradio/aidl/vts/Android.bp
@@ -0,0 +1,47 @@
+// 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_test {
+ name: "VtsHalBroadcastradioAidlTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ tidy_timeout_srcs: ["src/*.cpp"],
+ srcs: ["src/*.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libbase",
+ "libxml2",
+ ],
+ static_libs: [
+ "android.hardware.broadcastradio-V1-ndk",
+ "android.hardware.broadcastradio@common-utils-aidl-lib",
+ "android.hardware.broadcastradio@vts-utils-lib",
+ "libgmock",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/broadcastradio/aidl/vts/OWNERS b/broadcastradio/aidl/vts/OWNERS
new file mode 100644
index 0000000..302fdd7
--- /dev/null
+++ b/broadcastradio/aidl/vts/OWNERS
@@ -0,0 +1,4 @@
+xuweilin@google.com
+oscarazu@google.com
+ericjeong@google.com
+keunyoung@google.com
diff --git a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
new file mode 100644
index 0000000..5a56846
--- /dev/null
+++ b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
@@ -0,0 +1,1103 @@
+/*
+ * 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 EGMOCK_VERBOSE 1
+
+#include <aidl/android/hardware/broadcastradio/BnAnnouncementListener.h>
+#include <aidl/android/hardware/broadcastradio/BnTunerCallback.h>
+#include <aidl/android/hardware/broadcastradio/ConfigFlag.h>
+#include <aidl/android/hardware/broadcastradio/IBroadcastRadio.h>
+#include <aidl/android/hardware/broadcastradio/ProgramListChunk.h>
+#include <aidl/android/hardware/broadcastradio/ProgramSelector.h>
+#include <aidl/android/hardware/broadcastradio/VendorKeyValue.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <broadcastradio-utils-aidl/Utils.h>
+#include <broadcastradio-vts-utils/mock-timeout.h>
+#include <cutils/bitops.h>
+#include <gmock/gmock.h>
+
+#include <chrono>
+#include <optional>
+#include <regex>
+
+namespace aidl::android::hardware::broadcastradio::vts {
+
+namespace {
+
+using ::aidl::android::hardware::broadcastradio::utils::makeIdentifier;
+using ::aidl::android::hardware::broadcastradio::utils::makeSelectorAmfm;
+using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+using ::std::string;
+using ::std::vector;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::ByMove;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::SaveArg;
+
+namespace bcutils = ::aidl::android::hardware::broadcastradio::utils;
+
+inline constexpr std::chrono::seconds kTuneTimeoutSec =
+ std::chrono::seconds(IBroadcastRadio::TUNER_TIMEOUT_MS * 1000);
+inline constexpr std::chrono::seconds kProgramListScanTimeoutSec =
+ std::chrono::seconds(IBroadcastRadio::LIST_COMPLETE_TIMEOUT_MS * 1000);
+
+const ConfigFlag kConfigFlagValues[] = {
+ ConfigFlag::FORCE_MONO,
+ ConfigFlag::FORCE_ANALOG,
+ ConfigFlag::FORCE_DIGITAL,
+ ConfigFlag::RDS_AF,
+ ConfigFlag::RDS_REG,
+ ConfigFlag::DAB_DAB_LINKING,
+ ConfigFlag::DAB_FM_LINKING,
+ ConfigFlag::DAB_DAB_SOFT_LINKING,
+ ConfigFlag::DAB_FM_SOFT_LINKING,
+};
+
+void printSkipped(const string& msg) {
+ const auto testInfo = testing::UnitTest::GetInstance()->current_test_info();
+ LOG(INFO) << "[ SKIPPED ] " << testInfo->test_case_name() << "." << testInfo->name()
+ << " with message: " << msg;
+}
+
+bool isValidAmFmFreq(int64_t freq) {
+ ProgramIdentifier id = bcutils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq);
+ return bcutils::isValid(id);
+}
+
+void validateRange(const AmFmBandRange& range) {
+ EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
+ EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
+ EXPECT_LT(range.lowerBound, range.upperBound);
+ EXPECT_GT(range.spacing, 0u);
+ EXPECT_EQ((range.upperBound - range.lowerBound) % range.spacing, 0u);
+}
+
+bool supportsFM(const AmFmRegionConfig& config) {
+ for (const auto& range : config.ranges) {
+ if (bcutils::getBand(range.lowerBound) == bcutils::FrequencyBand::FM) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+class TunerCallbackMock : public BnTunerCallback {
+ public:
+ TunerCallbackMock();
+
+ MOCK_METHOD2(onTuneFailed, ScopedAStatus(Result, const ProgramSelector&));
+ MOCK_TIMEOUT_METHOD1(onCurrentProgramInfoChangedMock, ScopedAStatus(const ProgramInfo&));
+ ScopedAStatus onCurrentProgramInfoChanged(const ProgramInfo& info) override;
+ ScopedAStatus onProgramListUpdated(const ProgramListChunk& chunk) override;
+ MOCK_METHOD1(onAntennaStateChange, ScopedAStatus(bool connected));
+ MOCK_METHOD1(onParametersUpdated, ScopedAStatus(const vector<VendorKeyValue>& parameters));
+ MOCK_METHOD2(onConfigFlagUpdated, ScopedAStatus(ConfigFlag in_flag, bool in_value));
+ MOCK_TIMEOUT_METHOD0(onProgramListReady, void());
+
+ std::mutex mLock;
+ bcutils::ProgramInfoSet mProgramList GUARDED_BY(mLock);
+};
+
+struct AnnouncementListenerMock : public BnAnnouncementListener {
+ MOCK_METHOD1(onListUpdated, ScopedAStatus(const vector<Announcement>&));
+};
+
+class BroadcastRadioHalTest : public testing::TestWithParam<string> {
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
+ std::optional<bcutils::ProgramInfoSet> getProgramList();
+ std::optional<bcutils::ProgramInfoSet> getProgramList(const ProgramFilter& filter);
+
+ std::shared_ptr<IBroadcastRadio> mModule;
+ Properties mProperties;
+ std::shared_ptr<TunerCallbackMock> mCallback = SharedRefBase::make<TunerCallbackMock>();
+};
+
+MATCHER_P(InfoHasId, id, string(negation ? "does not contain" : "contains") + " " + id.toString()) {
+ vector<int> ids = bcutils::getAllIds(arg.selector, id.type);
+ return ids.end() != find(ids.begin(), ids.end(), id.value);
+}
+
+TunerCallbackMock::TunerCallbackMock() {
+ EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ // we expect the antenna is connected through the whole test
+ EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0);
+}
+
+ScopedAStatus TunerCallbackMock::onCurrentProgramInfoChanged(const ProgramInfo& info) {
+ for (const auto& id : info.selector) {
+ EXPECT_NE(id.type, IdentifierType::INVALID);
+ }
+
+ IdentifierType logically = info.logicallyTunedTo.type;
+ // This field is required for currently tuned program and should be INVALID
+ // for entries from the program list.
+ EXPECT_TRUE(logically == IdentifierType::AMFM_FREQUENCY_KHZ ||
+ logically == IdentifierType::RDS_PI ||
+ logically == IdentifierType::HD_STATION_ID_EXT ||
+ logically == IdentifierType::DAB_SID_EXT ||
+ logically == IdentifierType::DRMO_SERVICE_ID ||
+ logically == IdentifierType::SXM_SERVICE_ID ||
+ (logically >= IdentifierType::VENDOR_START &&
+ logically <= IdentifierType::VENDOR_END) ||
+ logically > IdentifierType::SXM_CHANNEL);
+
+ IdentifierType physically = info.physicallyTunedTo.type;
+ // ditto (see "logically" above)
+ EXPECT_TRUE(physically == IdentifierType::AMFM_FREQUENCY_KHZ ||
+ physically == IdentifierType::DAB_ENSEMBLE ||
+ physically == IdentifierType::DRMO_FREQUENCY_KHZ ||
+ physically == IdentifierType::SXM_CHANNEL ||
+ (physically >= IdentifierType::VENDOR_START &&
+ physically <= IdentifierType::VENDOR_END) ||
+ physically > IdentifierType::SXM_CHANNEL);
+
+ if (logically == IdentifierType::AMFM_FREQUENCY_KHZ) {
+ std::optional<string> ps = bcutils::getMetadataString(info, Metadata::rdsPs);
+ if (ps.has_value()) {
+ EXPECT_NE(::android::base::Trim(*ps), "")
+ << "Don't use empty RDS_PS as an indicator of missing RSD PS data.";
+ }
+ }
+
+ return onCurrentProgramInfoChangedMock(info);
+}
+
+ScopedAStatus TunerCallbackMock::onProgramListUpdated(const ProgramListChunk& chunk) {
+ std::lock_guard<std::mutex> lk(mLock);
+
+ updateProgramList(chunk, &mProgramList);
+
+ if (chunk.complete) {
+ onProgramListReady();
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+void BroadcastRadioHalTest::SetUp() {
+ EXPECT_EQ(mModule.get(), nullptr) << "Module is already open";
+
+ // lookup AIDL service (radio module)
+ AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
+ ASSERT_NE(binder, nullptr);
+ mModule = IBroadcastRadio::fromBinder(ndk::SpAIBinder(binder));
+ ASSERT_NE(mModule, nullptr) << "Couldn't find broadcast radio HAL implementation";
+
+ // get module properties
+ auto propResult = mModule->getProperties(&mProperties);
+
+ ASSERT_TRUE(propResult.isOk());
+ EXPECT_FALSE(mProperties.maker.empty());
+ EXPECT_FALSE(mProperties.product.empty());
+ EXPECT_GT(mProperties.supportedIdentifierTypes.size(), 0u);
+
+ // set callback
+ EXPECT_TRUE(mModule->setTunerCallback(mCallback).isOk());
+}
+
+void BroadcastRadioHalTest::TearDown() {
+ if (mModule) {
+ ASSERT_TRUE(mModule->unsetTunerCallback().isOk());
+ }
+}
+
+bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
+ auto halResult = mModule->getAmFmRegionConfig(full, config);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ return false;
+ }
+
+ EXPECT_TRUE(halResult.isOk());
+ return halResult.isOk();
+}
+
+std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
+ ProgramFilter emptyFilter = {};
+ return getProgramList(emptyFilter);
+}
+
+std::optional<bcutils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList(
+ const ProgramFilter& filter) {
+ EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
+
+ auto startResult = mModule->startProgramListUpdates(filter);
+
+ if (startResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("Program list not supported");
+ return std::nullopt;
+ }
+ EXPECT_TRUE(startResult.isOk());
+ if (!startResult.isOk()) {
+ return std::nullopt;
+ }
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, kProgramListScanTimeoutSec);
+
+ auto stopResult = mModule->stopProgramListUpdates();
+
+ EXPECT_TRUE(stopResult.isOk());
+
+ return mCallback->mProgramList;
+}
+
+/**
+ * Test setting tuner callback to null.
+ *
+ * Verifies that:
+ * - Setting to a null tuner callback results with INVALID_ARGUMENTS.
+ */
+TEST_P(BroadcastRadioHalTest, TunerCallbackFailsWithNull) {
+ LOG(DEBUG) << "TunerCallbackFailsWithNull Test";
+
+ auto halResult = mModule->setTunerCallback(nullptr);
+
+ EXPECT_EQ(halResult.getServiceSpecificError(), resultToInt(Result::INVALID_ARGUMENTS));
+}
+
+/**
+ * Test fetching AM/FM regional configuration.
+ *
+ * Verifies that:
+ * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
+ * - FM Deemphasis and RDS are correctly configured for FM-capable radio;
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfig) {
+ LOG(DEBUG) << "GetAmFmRegionConfig Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ false, &config);
+
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_LE(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
+ EXPECT_LE(popcountll(static_cast<unsigned long long>(config.fmRds)), 1);
+
+ if (supportsFM(config)) {
+ EXPECT_EQ(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
+ }
+}
+
+/**
+ * Test fetching ranges of AM/FM regional configuration.
+ *
+ * Verifies that:
+ * - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
+ * - there is at least one AM/FM band configured;
+ * - all channel grids (frequency ranges and spacings) are valid;
+ * - seek spacing is a multiple of the manual spacing value.
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigRanges) {
+ LOG(DEBUG) << "GetAmFmRegionConfigRanges Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ false, &config);
+
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_GT(config.ranges.size(), 0u);
+ for (const auto& range : config.ranges) {
+ validateRange(range);
+ EXPECT_EQ(range.seekSpacing % range.spacing, 0u);
+ EXPECT_GE(range.seekSpacing, range.spacing);
+ }
+}
+
+/**
+ * Test fetching FM regional capabilities.
+ *
+ * Verifies that:
+ * - AM/FM regional capabilities are either available or not supported at all by the hardware;
+ * - there is at least one de-emphasis filter mode supported for FM-capable radio;
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilitiesForFM) {
+ LOG(DEBUG) << "GetAmFmRegionConfigCapabilitiesForFM Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ true, &config);
+
+ if (supported && supportsFM(config)) {
+ EXPECT_GE(popcountll(static_cast<unsigned long long>(config.fmDeemphasis)), 1);
+ } else {
+ printSkipped("FM not supported");
+ }
+}
+
+/**
+ * Test fetching the ranges of AM/FM regional capabilities.
+ *
+ * Verifies that:
+ * - AM/FM regional capabilities are either available or not supported at all by the hardware;
+ * - there is at least one AM/FM range supported;
+ * - all channel grids (frequency ranges and spacings) are valid;
+ * - seek spacing is not set.
+ */
+TEST_P(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilitiesRanges) {
+ LOG(DEBUG) << "GetAmFmRegionConfigCapabilitiesRanges Test";
+
+ AmFmRegionConfig config;
+
+ bool supported = getAmFmRegionConfig(/* full= */ true, &config);
+
+ if (!supported) {
+ printSkipped("AM/FM not supported");
+ return;
+ }
+
+ EXPECT_GT(config.ranges.size(), 0u);
+
+ for (const auto& range : config.ranges) {
+ validateRange(range);
+ EXPECT_EQ(range.seekSpacing, 0u);
+ }
+}
+
+/**
+ * Test fetching DAB regional configuration.
+ *
+ * Verifies that:
+ * - DAB regional configuration is either set at startup or not supported at all by the hardware;
+ * - all channel labels match correct format;
+ * - all channel frequencies are in correct range.
+ */
+TEST_P(BroadcastRadioHalTest, GetDabRegionConfig) {
+ LOG(DEBUG) << "GetDabRegionConfig Test";
+ vector<DabTableEntry> config;
+
+ auto halResult = mModule->getDabRegionConfig(&config);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("DAB not supported");
+ return;
+ }
+ ASSERT_TRUE(halResult.isOk());
+
+ std::regex re("^[A-Z0-9][A-Z0-9 ]{0,5}[A-Z0-9]$");
+
+ for (const auto& entry : config) {
+ EXPECT_TRUE(std::regex_match(string(entry.label), re));
+
+ ProgramIdentifier id =
+ bcutils::makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, entry.frequencyKhz);
+ EXPECT_TRUE(bcutils::isValid(id));
+ }
+}
+
+/**
+ * Test tuning without tuner callback set.
+ *
+ * Verifies that:
+ * - No tuner callback set results in INVALID_STATE, regardless of whether the selector is
+ * supported.
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithoutTunerCallback) {
+ LOG(DEBUG) << "TuneFailsWithoutTunerCallback Test";
+
+ mModule->unsetTunerCallback();
+ int64_t freq = 90900; // 90.9 FM
+ ProgramSelector sel = makeSelectorAmfm(freq);
+
+ auto result = mModule->tune(sel);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+}
+
+/**
+ * Test tuning with selectors that can be not supported.
+ *
+ * Verifies that:
+ * - if the selector is not supported, an invalid value results with NOT_SUPPORTED, regardless of
+ * whether it is valid;
+ * - if it is supported, the test is ignored;
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithNotSupported) {
+ LOG(DEBUG) << "TuneFailsWithInvalid Test";
+
+ vector<ProgramIdentifier> supportTestId = {
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0), // invalid
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 94900), // valid
+ makeIdentifier(IdentifierType::RDS_PI, 0x10000), // invalid
+ makeIdentifier(IdentifierType::RDS_PI, 0x1001), // valid
+ makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000), // invalid
+ makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x10000001), // valid
+ makeIdentifier(IdentifierType::DAB_SID_EXT, 0), // invalid
+ makeIdentifier(IdentifierType::DAB_SID_EXT, 0xA00001), // valid
+ makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000), // invalid
+ makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x10000001), // valid
+ makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000), // invalid
+ makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x10000001), // valid
+ };
+
+ auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
+ for (const auto& id : supportTestId) {
+ ProgramSelector sel{id, {}};
+
+ auto result = mModule->tune(sel);
+
+ if (!bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), notSupportedError);
+ }
+ }
+}
+
+/**
+ * Test tuning with invalid selectors.
+ *
+ * Verifies that:
+ * - if the selector is not supported, it's ignored;
+ * - if it is supported, an invalid value results with INVALID_ARGUMENTS;
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithInvalid) {
+ LOG(DEBUG) << "TuneFailsWithInvalid Test";
+
+ vector<ProgramIdentifier> invalidId = {
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, 0),
+ makeIdentifier(IdentifierType::RDS_PI, 0x10000),
+ makeIdentifier(IdentifierType::HD_STATION_ID_EXT, 0x100000000),
+ makeIdentifier(IdentifierType::DAB_SID_EXT, 0),
+ makeIdentifier(IdentifierType::DRMO_SERVICE_ID, 0x100000000),
+ makeIdentifier(IdentifierType::SXM_SERVICE_ID, 0x100000000),
+ };
+
+ auto invalidArgumentsError = resultToInt(Result::INVALID_ARGUMENTS);
+ for (const auto& id : invalidId) {
+ ProgramSelector sel{id, {}};
+
+ auto result = mModule->tune(sel);
+
+ if (bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), invalidArgumentsError);
+ }
+ }
+}
+
+/**
+ * Test tuning with empty program selector.
+ *
+ * Verifies that:
+ * - tune fails with NOT_SUPPORTED when program selector is not initialized.
+ */
+TEST_P(BroadcastRadioHalTest, TuneFailsWithEmpty) {
+ LOG(DEBUG) << "TuneFailsWithEmpty Test";
+
+ // Program type is 1-based, so 0 will always be invalid.
+ ProgramSelector sel = {};
+
+ auto result = mModule->tune(sel);
+
+ ASSERT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
+}
+
+/**
+ * Test tuning with FM selector.
+ *
+ * Verifies that:
+ * - if AM/FM selector is not supported, the method returns NOT_SUPPORTED;
+ * - if it is supported, the method succeeds;
+ * - after a successful tune call, onCurrentProgramInfoChanged callback is
+ * invoked carrying a proper selector;
+ * - program changes exactly to what was requested.
+ */
+TEST_P(BroadcastRadioHalTest, FmTune) {
+ LOG(DEBUG) << "FmTune Test";
+
+ int64_t freq = 90900; // 90.9 FM
+ ProgramSelector sel = makeSelectorAmfm(freq);
+ // try tuning
+ ProgramInfo infoCb = {};
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
+ InfoHasId(makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, freq)))
+ .Times(AnyNumber())
+ .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))))
+ .WillRepeatedly(testing::InvokeWithoutArgs([] { return ndk::ScopedAStatus::ok(); }));
+
+ auto result = mModule->tune(sel);
+
+ // expect a failure if it's not supported
+ if (!bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
+ return;
+ }
+
+ // expect a callback if it succeeds
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+
+ LOG(DEBUG) << "Current program info: " << infoCb.toString();
+
+ // it should tune exactly to what was requested
+ vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
+ EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
+ << "FM freq " << freq << " kHz is not sent back by callback.";
+}
+
+/**
+ * Test tuning with DAB selector.
+ *
+ * Verifies that:
+ * - if DAB selector is not supported, the method returns NOT_SUPPORTED;
+ * - if it is supported, the method succeeds;
+ * - after a successful tune call, onCurrentProgramInfoChanged callback is
+ * invoked carrying a proper selector;
+ * - program changes exactly to what was requested.
+ */
+TEST_P(BroadcastRadioHalTest, DabTune) {
+ LOG(DEBUG) << "DabTune Test";
+ vector<DabTableEntry> config;
+
+ auto halResult = mModule->getDabRegionConfig(&config);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("DAB not supported");
+ return;
+ }
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_NE(config.size(), 0U);
+
+ // TODO(245787803): use a DAB frequency that can actually be tuned to.
+ ProgramSelector sel = {};
+ int64_t freq = config[config.size() / 2].frequencyKhz;
+ sel.primaryId = makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq);
+
+ // try tuning
+ ProgramInfo infoCb = {};
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock,
+ InfoHasId(makeIdentifier(IdentifierType::DAB_FREQUENCY_KHZ, freq)))
+ .Times(AnyNumber())
+ .WillOnce(
+ DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(ndk::ScopedAStatus::ok()))));
+
+ auto result = mModule->tune(sel);
+
+ // expect a failure if it's not supported
+ if (!bcutils::isSupported(mProperties, sel)) {
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::NOT_SUPPORTED));
+ return;
+ }
+
+ // expect a callback if it succeeds
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+ LOG(DEBUG) << "Current program info: " << infoCb.toString();
+
+ // it should tune exactly to what was requested
+ vector<int> freqs = bcutils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY_KHZ);
+ EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq))
+ << "DAB freq " << freq << " kHz is not sent back by callback.";
+ ;
+}
+
+/**
+ * Test seeking to next/prev station via IBroadcastRadio::seek().
+ *
+ * Verifies that:
+ * - the method succeeds;
+ * - the program info is changed within kTuneTimeoutSec;
+ * - works both directions and with or without skipping sub-channel.
+ */
+TEST_P(BroadcastRadioHalTest, Seek) {
+ LOG(DEBUG) << "Seek Test";
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
+
+ if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("Seek not supported");
+ return;
+ }
+
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
+
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+}
+
+/**
+ * Test seeking without tuner callback set.
+ *
+ * Verifies that:
+ * - No tuner callback set results in INVALID_STATE.
+ */
+TEST_P(BroadcastRadioHalTest, SeekFailsWithoutTunerCallback) {
+ LOG(DEBUG) << "SeekFailsWithoutTunerCallback Test";
+
+ mModule->unsetTunerCallback();
+
+ auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+
+ result = mModule->seek(/* in_directionUp= */ false, /* in_skipSubChannel= */ false);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+}
+
+/**
+ * Test step operation.
+ *
+ * Verifies that:
+ * - the method succeeds or returns NOT_SUPPORTED;
+ * - the program info is changed within kTuneTimeoutSec if the method succeeded;
+ * - works both directions.
+ */
+TEST_P(BroadcastRadioHalTest, Step) {
+ LOG(DEBUG) << "Step Test";
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ auto result = mModule->step(/* in_directionUp= */ true);
+
+ if (result.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ printSkipped("Step not supported");
+ return;
+ }
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+
+ EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChangedMock, _).Times(AnyNumber());
+
+ result = mModule->step(/* in_directionUp= */ false);
+
+ EXPECT_TRUE(result.isOk());
+ EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChangedMock, kTuneTimeoutSec);
+}
+
+/**
+ * Test step operation without tuner callback set.
+ *
+ * Verifies that:
+ * - No tuner callback set results in INVALID_STATE.
+ */
+TEST_P(BroadcastRadioHalTest, StepFailsWithoutTunerCallback) {
+ LOG(DEBUG) << "StepFailsWithoutTunerCallback Test";
+
+ mModule->unsetTunerCallback();
+
+ auto result = mModule->step(/* in_directionUp= */ true);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+
+ result = mModule->step(/* in_directionUp= */ false);
+
+ EXPECT_EQ(result.getServiceSpecificError(), resultToInt(Result::INVALID_STATE));
+}
+
+/**
+ * Test tune cancellation.
+ *
+ * Verifies that:
+ * - the method does not crash after being invoked multiple times.
+ *
+ * Since cancel() might be called after the HAL completes an operation (tune, seek, and step)
+ * and before the callback completions, the operation might not be actually canceled and the
+ * effect of cancel() is not deterministic to be tested here.
+ */
+TEST_P(BroadcastRadioHalTest, Cancel) {
+ LOG(DEBUG) << "Cancel Test";
+
+ auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
+ for (int i = 0; i < 10; i++) {
+ auto result = mModule->seek(/* in_directionUp= */ true, /* in_skipSubChannel= */ true);
+
+ if (result.getServiceSpecificError() == notSupportedError) {
+ printSkipped("Cancel is skipped because of seek not supported");
+ return;
+ }
+ EXPECT_TRUE(result.isOk());
+
+ auto cancelResult = mModule->cancel();
+
+ ASSERT_TRUE(cancelResult.isOk());
+ }
+}
+
+/**
+ * Test IBroadcastRadio::get|setParameters() methods called with no parameters.
+ *
+ * Verifies that:
+ * - callback is called for empty parameters set.
+ */
+TEST_P(BroadcastRadioHalTest, NoParameters) {
+ LOG(DEBUG) << "NoParameters Test";
+
+ vector<VendorKeyValue> parametersResults = {};
+
+ auto halResult = mModule->setParameters({}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+
+ parametersResults.clear();
+
+ halResult = mModule->getParameters({}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+}
+
+/**
+ * Test IBroadcastRadio::get|setParameters() methods called with unknown parameters.
+ *
+ * Verifies that:
+ * - unknown parameters are ignored;
+ * - callback is called also for empty results set.
+ */
+TEST_P(BroadcastRadioHalTest, UnknownParameters) {
+ LOG(DEBUG) << "UnknownParameters Test";
+
+ vector<VendorKeyValue> parametersResults = {};
+
+ auto halResult =
+ mModule->setParameters({{"com.android.unknown", "sample"}}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+
+ parametersResults.clear();
+
+ halResult = mModule->getParameters({"com.android.unknown*", "sample"}, ¶metersResults);
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_EQ(parametersResults.size(), 0u);
+}
+
+/**
+ * Test geting image of invalid ID.
+ *
+ * Verifies that:
+ * - getImage call handles argument 0 gracefully.
+ */
+TEST_P(BroadcastRadioHalTest, GetNoImage) {
+ LOG(DEBUG) << "GetNoImage Test";
+ vector<uint8_t> rawImage;
+
+ auto result = mModule->getImage(IBroadcastRadio::INVALID_IMAGE, &rawImage);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(rawImage.size(), 0u);
+}
+
+/**
+ * Test getting config flags.
+ *
+ * Verifies that:
+ * - isConfigFlagSet either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - call success or failure is consistent with setConfigFlag.
+ */
+TEST_P(BroadcastRadioHalTest, FetchConfigFlags) {
+ LOG(DEBUG) << "FetchConfigFlags Test";
+
+ for (const auto& flag : kConfigFlagValues) {
+ bool gotValue = false;
+
+ auto halResult = mModule->isConfigFlagSet(flag, &gotValue);
+
+ if (halResult.getServiceSpecificError() != resultToInt(Result::NOT_SUPPORTED) &&
+ halResult.getServiceSpecificError() != resultToInt(Result::INVALID_STATE)) {
+ ASSERT_TRUE(halResult.isOk());
+ }
+
+ // set must fail or succeed the same way as get
+ auto setResult = mModule->setConfigFlag(flag, /* value= */ false);
+
+ EXPECT_TRUE((halResult.isOk() && setResult.isOk()) ||
+ (halResult.getServiceSpecificError()) == setResult.getServiceSpecificError());
+
+ setResult = mModule->setConfigFlag(flag, /* value= */ true);
+
+ EXPECT_TRUE((halResult.isOk() && setResult.isOk()) ||
+ (halResult.getServiceSpecificError()) == setResult.getServiceSpecificError());
+ }
+}
+
+/**
+ * Test setting config flags.
+ *
+ * Verifies that:
+ * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - isConfigFlagSet reflects the state requested immediately after the set call.
+ */
+TEST_P(BroadcastRadioHalTest, SetConfigFlags) {
+ LOG(DEBUG) << "SetConfigFlags Test";
+
+ auto get = [&](ConfigFlag flag) -> bool {
+ bool* gotValue = nullptr;
+
+ auto halResult = mModule->isConfigFlagSet(flag, gotValue);
+
+ EXPECT_FALSE(gotValue == nullptr);
+ EXPECT_TRUE(halResult.isOk());
+ return *gotValue;
+ };
+
+ auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
+ auto invalidStateError = resultToInt(Result::INVALID_STATE);
+ for (const auto& flag : kConfigFlagValues) {
+ auto result = mModule->setConfigFlag(flag, /* value= */ false);
+
+ if (result.getServiceSpecificError() == notSupportedError ||
+ result.getServiceSpecificError() == invalidStateError) {
+ // setting to true must result in the same error as false
+ auto secondResult = mModule->setConfigFlag(flag, /* value= */ true);
+
+ EXPECT_TRUE((result.isOk() && secondResult.isOk()) ||
+ result.getServiceSpecificError() == secondResult.getServiceSpecificError());
+ continue;
+ } else {
+ ASSERT_TRUE(result.isOk());
+ }
+
+ // verify false is set
+ bool value = get(flag);
+ EXPECT_FALSE(value);
+
+ // try setting true this time
+ result = mModule->setConfigFlag(flag, /* value= */ true);
+
+ ASSERT_TRUE(result.isOk());
+ value = get(flag);
+ EXPECT_TRUE(value);
+
+ // false again
+ result = mModule->setConfigFlag(flag, /* value= */ false);
+
+ ASSERT_TRUE(result.isOk());
+ value = get(flag);
+ EXPECT_FALSE(value);
+ }
+}
+
+/**
+ * Test getting program list using empty program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within kProgramListScanTimeoutSec;
+ * - stopProgramListUpdates does not crash.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromEmptyFilter) {
+ LOG(DEBUG) << "GetProgramListFromEmptyFilter Test";
+
+ getProgramList();
+}
+
+/**
+ * Test getting program list using AMFM frequency program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within kProgramListScanTimeoutSec;
+ * - stopProgramListUpdates does not crash;
+ * - result for startProgramListUpdates using a filter with AMFM_FREQUENCY_KHZ value of the first
+ * AMFM program matches the expected result.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromAmFmFilter) {
+ LOG(DEBUG) << "GetProgramListFromAmFmFilter Test";
+
+ std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
+ if (!completeList) {
+ printSkipped("No program list available");
+ return;
+ }
+
+ ProgramFilter amfmFilter = {};
+ int expectedResultSize = 0;
+ uint64_t expectedFreq = 0;
+ for (const auto& program : *completeList) {
+ vector<int> amfmIds =
+ bcutils::getAllIds(program.selector, IdentifierType::AMFM_FREQUENCY_KHZ);
+ EXPECT_LE(amfmIds.size(), 1u);
+ if (amfmIds.size() == 0) {
+ continue;
+ }
+
+ if (expectedResultSize == 0) {
+ expectedFreq = amfmIds[0];
+ amfmFilter.identifiers = {
+ makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, expectedFreq)};
+ expectedResultSize = 1;
+ } else if (amfmIds[0] == expectedFreq) {
+ expectedResultSize++;
+ }
+ }
+
+ if (expectedResultSize == 0) {
+ printSkipped("No Am/FM programs available");
+ return;
+ }
+ std::optional<bcutils::ProgramInfoSet> amfmList = getProgramList(amfmFilter);
+ ASSERT_EQ(amfmList->size(), expectedResultSize) << "amfm filter result size is wrong";
+}
+
+/**
+ * Test getting program list using DAB ensemble program filter.
+ *
+ * Verifies that:
+ * - startProgramListUpdates either succeeds or returns NOT_SUPPORTED;
+ * - the complete list is fetched within kProgramListScanTimeoutSec;
+ * - stopProgramListUpdates does not crash;
+ * - result for startProgramListUpdates using a filter with DAB_ENSEMBLE value of the first DAB
+ * program matches the expected result.
+ */
+TEST_P(BroadcastRadioHalTest, GetProgramListFromDabFilter) {
+ LOG(DEBUG) << "GetProgramListFromDabFilter Test";
+
+ std::optional<bcutils::ProgramInfoSet> completeList = getProgramList();
+ if (!completeList) {
+ printSkipped("No program list available");
+ return;
+ }
+
+ ProgramFilter dabFilter = {};
+ int expectedResultSize = 0;
+ uint64_t expectedEnsemble = 0;
+ for (const auto& program : *completeList) {
+ auto dabEnsembles = bcutils::getAllIds(program.selector, IdentifierType::DAB_ENSEMBLE);
+ EXPECT_LE(dabEnsembles.size(), 1u);
+ if (dabEnsembles.size() == 0) {
+ continue;
+ }
+
+ if (expectedResultSize == 0) {
+ expectedEnsemble = dabEnsembles[0];
+ dabFilter.identifiers = {
+ makeIdentifier(IdentifierType::DAB_ENSEMBLE, expectedEnsemble)};
+ expectedResultSize = 1;
+ } else if (dabEnsembles[0] == expectedEnsemble) {
+ expectedResultSize++;
+ }
+ }
+
+ if (expectedResultSize == 0) {
+ printSkipped("No DAB programs available");
+ return;
+ }
+ std::optional<bcutils::ProgramInfoSet> dabList = getProgramList(dabFilter);
+ ASSERT_EQ(dabList->size(), expectedResultSize) << "dab filter result size is wrong";
+}
+
+/**
+ * Test HD_STATION_NAME correctness.
+ *
+ * Verifies that if a program on the list contains HD_STATION_NAME identifier:
+ * - the program provides station name in its metadata;
+ * - the identifier matches the name;
+ * - there is only one identifier of that type.
+ */
+TEST_P(BroadcastRadioHalTest, HdRadioStationNameId) {
+ LOG(DEBUG) << "HdRadioStationNameId Test";
+
+ std::optional<bcutils::ProgramInfoSet> list = getProgramList();
+ if (!list) {
+ printSkipped("No program list");
+ return;
+ }
+
+ for (const auto& program : *list) {
+ vector<int> nameIds = bcutils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
+ EXPECT_LE(nameIds.size(), 1u);
+ if (nameIds.size() == 0) {
+ continue;
+ }
+
+ std::optional<string> name = bcutils::getMetadataString(program, Metadata::programName);
+ if (!name) {
+ name = bcutils::getMetadataString(program, Metadata::rdsPs);
+ }
+ ASSERT_TRUE(name.has_value());
+
+ ProgramIdentifier expectedId = bcutils::makeHdRadioStationName(*name);
+ EXPECT_EQ(nameIds[0], expectedId.value);
+ }
+}
+
+/**
+ * Test announcement listener registration.
+ *
+ * Verifies that:
+ * - registerAnnouncementListener either succeeds or returns NOT_SUPPORTED;
+ * - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
+ * - closing handle does not crash.
+ */
+TEST_P(BroadcastRadioHalTest, AnnouncementListenerRegistration) {
+ LOG(DEBUG) << "AnnouncementListenerRegistration Test";
+ std::shared_ptr<AnnouncementListenerMock> listener =
+ SharedRefBase::make<AnnouncementListenerMock>();
+ std::shared_ptr<ICloseHandle> closeHandle = nullptr;
+
+ auto halResult = mModule->registerAnnouncementListener(listener, {AnnouncementType::EMERGENCY},
+ &closeHandle);
+
+ if (halResult.getServiceSpecificError() == resultToInt(Result::NOT_SUPPORTED)) {
+ ASSERT_EQ(closeHandle.get(), nullptr);
+ printSkipped("Announcements not supported");
+ return;
+ }
+
+ ASSERT_TRUE(halResult.isOk());
+ ASSERT_NE(closeHandle.get(), nullptr);
+
+ closeHandle->close();
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHalTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, BroadcastRadioHalTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IBroadcastRadio::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+} // namespace aidl::android::hardware::broadcastradio::vts
+
+int main(int argc, char** argv) {
+ android::base::SetDefaultTag("BcRadio.vts");
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(4);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/broadcastradio/common/utilsaidl/Android.bp b/broadcastradio/common/utilsaidl/Android.bp
new file mode 100644
index 0000000..fa6de19
--- /dev/null
+++ b/broadcastradio/common/utilsaidl/Android.bp
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+ name: "android.hardware.broadcastradio@common-utils-aidl-lib",
+ vendor_available: true,
+ relative_install_path: "hw",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-error=implicit-fallthrough",
+ ],
+ cppflags: [
+ "-std=c++1z",
+ ],
+ srcs: [
+ "Utils.cpp",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "android.hardware.broadcastradio-V1-ndk",
+ "libbase",
+ ],
+ static_libs: [
+ "libmath",
+ ],
+}
diff --git a/broadcastradio/common/utilsaidl/Utils.cpp b/broadcastradio/common/utilsaidl/Utils.cpp
new file mode 100644
index 0000000..52c7b40
--- /dev/null
+++ b/broadcastradio/common/utilsaidl/Utils.cpp
@@ -0,0 +1,545 @@
+/*
+ * 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 "BcRadioAidlDef.utils"
+
+#include "broadcastradio-utils-aidl/Utils.h"
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include <math/HashCombine.h>
+
+namespace aidl::android::hardware::broadcastradio {
+
+namespace utils {
+
+namespace {
+
+using ::android::base::EqualsIgnoreCase;
+using ::std::string;
+using ::std::vector;
+
+const int64_t kValueForNotFoundIdentifier = 0;
+
+bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType& type) {
+ return hasId(a, type) && hasId(b, type);
+}
+
+bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b, const IdentifierType& type) {
+ if (!bothHaveId(a, b, type)) {
+ return false;
+ }
+ /* We should check all Ids of a given type (ie. other AF),
+ * but it doesn't matter for default implementation.
+ */
+ return getId(a, type) == getId(b, type);
+}
+
+int getHdSubchannel(const ProgramSelector& sel) {
+ int64_t hdSidExt = getId(sel, IdentifierType::HD_STATION_ID_EXT, /* defaultValue */ 0);
+ hdSidExt >>= 32; // Station ID number
+ return hdSidExt & 0xF; // HD Radio subchannel
+}
+
+bool maybeGetId(const ProgramSelector& sel, const IdentifierType& type, int64_t* val) {
+ // iterate through primaryId and secondaryIds
+ for (auto it = begin(sel); it != end(sel); it++) {
+ if (it->type == type) {
+ if (val != nullptr) {
+ *val = it->value;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace
+
+IdentifierIterator::IdentifierIterator(const ProgramSelector& sel) : IdentifierIterator(sel, 0) {}
+
+IdentifierIterator::IdentifierIterator(const ProgramSelector& sel, size_t pos)
+ : mSel(sel), mPos(pos) {}
+
+const IdentifierIterator IdentifierIterator::operator++(int) {
+ IdentifierIterator i = *this;
+ mPos++;
+ return i;
+}
+
+IdentifierIterator& IdentifierIterator::operator++() {
+ ++mPos;
+ return *this;
+}
+
+IdentifierIterator::refType IdentifierIterator::operator*() const {
+ if (mPos == 0) {
+ return getSelector().primaryId;
+ }
+
+ // mPos is 1-based for secondary identifiers
+ DCHECK(mPos <= getSelector().secondaryIds.size());
+ return getSelector().secondaryIds[mPos - 1];
+}
+
+bool IdentifierIterator::operator==(const IdentifierIterator& rhs) const {
+ // Check, if both iterators points at the same selector.
+ if (reinterpret_cast<intptr_t>(&getSelector()) !=
+ reinterpret_cast<intptr_t>(&rhs.getSelector())) {
+ return false;
+ }
+
+ return mPos == rhs.mPos;
+}
+
+int32_t resultToInt(Result result) {
+ return static_cast<int32_t>(result);
+}
+
+FrequencyBand getBand(int64_t freq) {
+ // keep in sync with
+ // frameworks/base/services/core/java/com/android/server/broadcastradio/aidl/Utils.java
+ if (freq < 30) return FrequencyBand::UNKNOWN;
+ if (freq < 500) return FrequencyBand::AM_LW;
+ if (freq < 1705) return FrequencyBand::AM_MW;
+ if (freq < 30000) return FrequencyBand::AM_SW;
+ if (freq < 60000) return FrequencyBand::UNKNOWN;
+ if (freq < 110000) return FrequencyBand::FM;
+ return FrequencyBand::UNKNOWN;
+}
+
+bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
+ IdentifierType type = b.primaryId.type;
+
+ switch (type) {
+ case IdentifierType::HD_STATION_ID_EXT:
+ case IdentifierType::RDS_PI:
+ case IdentifierType::AMFM_FREQUENCY_KHZ:
+ if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
+ if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
+ return getHdSubchannel(b) == 0 &&
+ haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY_KHZ);
+ case IdentifierType::DAB_SID_EXT:
+ return haveEqualIds(a, b, IdentifierType::DAB_SID_EXT);
+ case IdentifierType::DRMO_SERVICE_ID:
+ return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
+ case IdentifierType::SXM_SERVICE_ID:
+ return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
+ default: // includes all vendor types
+ LOG(WARNING) << "unsupported program type: " << toString(type);
+ return false;
+ }
+}
+
+bool hasId(const ProgramSelector& sel, const IdentifierType& type) {
+ return maybeGetId(sel, type, /* val */ nullptr);
+}
+
+int64_t getId(const ProgramSelector& sel, const IdentifierType& type) {
+ int64_t val;
+
+ if (maybeGetId(sel, type, &val)) {
+ return val;
+ }
+
+ LOG(WARNING) << "identifier not found: " << toString(type);
+ return kValueForNotFoundIdentifier;
+}
+
+int64_t getId(const ProgramSelector& sel, const IdentifierType& type, int64_t defaultValue) {
+ if (!hasId(sel, type)) {
+ return defaultValue;
+ }
+ return getId(sel, type);
+}
+
+vector<int> getAllIds(const ProgramSelector& sel, const IdentifierType& type) {
+ vector<int> ret;
+
+ // iterate through primaryId and secondaryIds
+ for (auto it = begin(sel); it != end(sel); it++) {
+ if (it->type == type) {
+ ret.push_back(it->value);
+ }
+ }
+
+ return ret;
+}
+
+bool isSupported(const Properties& prop, const ProgramSelector& sel) {
+ for (auto it = prop.supportedIdentifierTypes.begin(); it != prop.supportedIdentifierTypes.end();
+ it++) {
+ if (hasId(sel, *it)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool isValid(const ProgramIdentifier& id) {
+ int64_t val = id.value;
+ bool valid = true;
+
+ auto expect = [&valid](bool condition, const string& message) {
+ if (!condition) {
+ valid = false;
+ LOG(ERROR) << "identifier not valid, expected " << message;
+ }
+ };
+
+ switch (id.type) {
+ case IdentifierType::INVALID:
+ expect(false, "IdentifierType::INVALID");
+ break;
+ case IdentifierType::DAB_FREQUENCY_KHZ:
+ expect(val > 100000u, "f > 100MHz");
+ [[fallthrough]];
+ case IdentifierType::AMFM_FREQUENCY_KHZ:
+ case IdentifierType::DRMO_FREQUENCY_KHZ:
+ expect(val > 100u, "f > 100kHz");
+ expect(val < 10000000u, "f < 10GHz");
+ break;
+ case IdentifierType::RDS_PI:
+ expect(val != 0u, "RDS PI != 0");
+ expect(val <= 0xFFFFu, "16bit id");
+ break;
+ case IdentifierType::HD_STATION_ID_EXT: {
+ int64_t stationId = val & 0xFFFFFFFF; // 32bit
+ val >>= 32;
+ int64_t subchannel = val & 0xF; // 4bit
+ val >>= 4;
+ int64_t freq = val & 0x3FFFF; // 18bit
+ expect(stationId != 0u, "HD station id != 0");
+ expect(subchannel < 8u, "HD subch < 8");
+ expect(freq > 100u, "f > 100kHz");
+ expect(freq < 10000000u, "f < 10GHz");
+ break;
+ }
+ case IdentifierType::HD_STATION_NAME: {
+ while (val > 0) {
+ char ch = static_cast<char>(val & 0xFF);
+ val >>= 8;
+ expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
+ "HD_STATION_NAME does not match [A-Z0-9]+");
+ }
+ break;
+ }
+ case IdentifierType::DAB_SID_EXT: {
+ int64_t sid = val & 0xFFFF; // 16bit
+ val >>= 16;
+ int64_t ecc = val & 0xFF; // 8bit
+ expect(sid != 0u, "DAB SId != 0");
+ expect(ecc >= 0xA0u && ecc <= 0xF6u, "Invalid ECC, see ETSI TS 101 756 V2.1.1");
+ break;
+ }
+ case IdentifierType::DAB_ENSEMBLE:
+ expect(val != 0u, "DAB ensemble != 0");
+ expect(val <= 0xFFFFu, "16bit id");
+ break;
+ case IdentifierType::DAB_SCID:
+ expect(val > 0xFu, "12bit SCId (not 4bit SCIdS)");
+ expect(val <= 0xFFFu, "12bit id");
+ break;
+ case IdentifierType::DRMO_SERVICE_ID:
+ expect(val != 0u, "DRM SId != 0");
+ expect(val <= 0xFFFFFFu, "24bit id");
+ break;
+ case IdentifierType::SXM_SERVICE_ID:
+ expect(val != 0u, "SXM SId != 0");
+ expect(val <= 0xFFFFFFFFu, "32bit id");
+ break;
+ case IdentifierType::SXM_CHANNEL:
+ expect(val < 1000u, "SXM channel < 1000");
+ break;
+ case IdentifierType::VENDOR_START:
+ case IdentifierType::VENDOR_END:
+ // skip
+ break;
+ }
+
+ return valid;
+}
+
+bool isValid(const ProgramSelector& sel) {
+ // iterate through primaryId and secondaryIds
+ for (auto it = begin(sel); it != end(sel); it++) {
+ if (!isValid(*it)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value) {
+ return {type, value};
+}
+
+ProgramSelector makeSelectorAmfm(int32_t frequency) {
+ ProgramSelector sel = {};
+ sel.primaryId = makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ, frequency);
+ return sel;
+}
+
+ProgramSelector makeSelectorDab(int32_t sidExt, int32_t ensemble) {
+ ProgramSelector sel = {};
+ // TODO(243686545): Have a helper function to create the sidExt instead of
+ // passing the whole identifier here. Something like makeDabSidExt.
+ sel.primaryId = makeIdentifier(IdentifierType::DAB_SID_EXT, sidExt);
+ vector<ProgramIdentifier> secondaryIds = {
+ makeIdentifier(IdentifierType::DAB_ENSEMBLE, ensemble),
+ // TODO(243686545): Include frequency here when the helper method to
+ // translate between ensemble and frequency is implemented.
+ };
+ sel.secondaryIds = std::move(secondaryIds);
+ return sel;
+}
+
+bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel) {
+ if (filter.identifierTypes.size() > 0) {
+ auto typeEquals = [](const ProgramIdentifier& id, IdentifierType type) {
+ return id.type == type;
+ };
+ auto it = std::find_first_of(begin(sel), end(sel), filter.identifierTypes.begin(),
+ filter.identifierTypes.end(), typeEquals);
+ if (it == end(sel)) {
+ return false;
+ }
+ }
+
+ if (filter.identifiers.size() > 0) {
+ auto it = std::find_first_of(begin(sel), end(sel), filter.identifiers.begin(),
+ filter.identifiers.end());
+ if (it == end(sel)) {
+ return false;
+ }
+ }
+
+ if (!filter.includeCategories && sel.primaryId.type == IdentifierType::DAB_ENSEMBLE) {
+ return false;
+ }
+
+ return true;
+}
+
+size_t ProgramInfoHasher::operator()(const ProgramInfo& info) const {
+ const ProgramIdentifier& id = info.selector.primaryId;
+
+ // This is not the best hash implementation, but good enough for default HAL
+ // implementation and tests.
+ size_t h = 0;
+ ::android::hashCombineSingle(h, id.type);
+ ::android::hashCombineSingle(h, id.value);
+ return h;
+}
+
+bool ProgramInfoKeyEqual::operator()(const ProgramInfo& info1, const ProgramInfo& info2) const {
+ const ProgramIdentifier& id1 = info1.selector.primaryId;
+ const ProgramIdentifier& id2 = info2.selector.primaryId;
+ return id1.type == id2.type && id1.value == id2.value;
+}
+
+void updateProgramList(const ProgramListChunk& chunk, ProgramInfoSet* list) {
+ if (chunk.purge) {
+ list->clear();
+ }
+
+ list->insert(chunk.modified.begin(), chunk.modified.end());
+
+ if (!chunk.removed.has_value()) {
+ return;
+ }
+
+ for (auto& id : chunk.removed.value()) {
+ if (id.has_value()) {
+ ProgramInfo info = {};
+ info.selector.primaryId = id.value();
+ list->erase(info);
+ }
+ }
+}
+
+std::optional<std::string> getMetadataString(const ProgramInfo& info, const Metadata::Tag& tag) {
+ auto isRdsPs = [tag](const Metadata& item) { return item.getTag() == tag; };
+
+ auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isRdsPs);
+ if (it == info.metadata.end()) {
+ return std::nullopt;
+ }
+
+ std::string metadataString;
+ switch (it->getTag()) {
+ case Metadata::rdsPs:
+ metadataString = it->get<Metadata::rdsPs>();
+ break;
+ case Metadata::rdsPty:
+ metadataString = std::to_string(it->get<Metadata::rdsPty>());
+ break;
+ case Metadata::rbdsPty:
+ metadataString = std::to_string(it->get<Metadata::rbdsPty>());
+ break;
+ case Metadata::rdsRt:
+ metadataString = it->get<Metadata::rdsRt>();
+ break;
+ case Metadata::songTitle:
+ metadataString = it->get<Metadata::songTitle>();
+ break;
+ case Metadata::songArtist:
+ metadataString = it->get<Metadata::songArtist>();
+ break;
+ case Metadata::songAlbum:
+ metadataString = it->get<Metadata::songAlbum>();
+ break;
+ case Metadata::stationIcon:
+ metadataString = std::to_string(it->get<Metadata::stationIcon>());
+ break;
+ case Metadata::albumArt:
+ metadataString = std::to_string(it->get<Metadata::albumArt>());
+ break;
+ case Metadata::programName:
+ metadataString = it->get<Metadata::programName>();
+ break;
+ case Metadata::dabEnsembleName:
+ metadataString = it->get<Metadata::dabEnsembleName>();
+ break;
+ case Metadata::dabEnsembleNameShort:
+ metadataString = it->get<Metadata::dabEnsembleNameShort>();
+ break;
+ case Metadata::dabServiceName:
+ metadataString = it->get<Metadata::dabServiceName>();
+ break;
+ case Metadata::dabServiceNameShort:
+ metadataString = it->get<Metadata::dabServiceNameShort>();
+ break;
+ case Metadata::dabComponentName:
+ metadataString = it->get<Metadata::dabComponentName>();
+ break;
+ case Metadata::dabComponentNameShort:
+ metadataString = it->get<Metadata::dabComponentNameShort>();
+ break;
+ default:
+ LOG(ERROR) << "Metadata " << it->toString() << " is not converted.";
+ return std::nullopt;
+ }
+ return metadataString;
+}
+
+ProgramIdentifier makeHdRadioStationName(const string& name) {
+ constexpr size_t maxlen = 8;
+
+ string shortName;
+ shortName.reserve(maxlen);
+
+ const auto& loc = std::locale::classic();
+ for (const char& ch : name) {
+ if (!std::isalnum(ch, loc)) {
+ continue;
+ }
+ shortName.push_back(std::toupper(ch, loc));
+ if (shortName.length() >= maxlen) {
+ break;
+ }
+ }
+
+ // Short name is converted to HD_STATION_NAME by encoding each char into its ASCII value in
+ // in little-endian order. For example, "Abc" is converted to 0x434241.
+ int64_t val = 0;
+ for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
+ val <<= 8;
+ val |= static_cast<char>(*rit);
+ }
+
+ return makeIdentifier(IdentifierType::HD_STATION_NAME, val);
+}
+
+IdentifierType getType(int typeAsInt) {
+ return static_cast<IdentifierType>(typeAsInt);
+}
+
+bool parseArgInt(const string& s, int* out) {
+ return ::android::base::ParseInt(s, out);
+}
+
+bool parseArgLong(const std::string& s, long* out) {
+ return ::android::base::ParseInt(s, out);
+}
+
+bool parseArgBool(const string& s, bool* out) {
+ if (EqualsIgnoreCase(s, "true")) {
+ *out = true;
+ } else if (EqualsIgnoreCase(s, "false")) {
+ *out = false;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool parseArgDirection(const string& s, bool* out) {
+ if (EqualsIgnoreCase(s, "up")) {
+ *out = true;
+ } else if (EqualsIgnoreCase(s, "down")) {
+ *out = false;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool parseArgIdentifierTypeArray(const string& s, vector<IdentifierType>* out) {
+ for (const string& val : ::android::base::Split(s, ",")) {
+ int outInt;
+ if (!parseArgInt(val, &outInt)) {
+ return false;
+ }
+ out->push_back(getType(outInt));
+ }
+ return true;
+}
+
+bool parseProgramIdentifierList(const std::string& s, vector<ProgramIdentifier>* out) {
+ for (const string& idStr : ::android::base::Split(s, ",")) {
+ const vector<string> idStrPair = ::android::base::Split(idStr, ":");
+ if (idStrPair.size() != 2) {
+ return false;
+ }
+ int idType;
+ if (!parseArgInt(idStrPair[0], &idType)) {
+ return false;
+ }
+ long idVal;
+ if (!parseArgLong(idStrPair[1], &idVal)) {
+ return false;
+ }
+ ProgramIdentifier id = {getType(idType), idVal};
+ out->push_back(id);
+ }
+ return true;
+}
+
+} // namespace utils
+
+utils::IdentifierIterator begin(const ProgramSelector& sel) {
+ return utils::IdentifierIterator(sel);
+}
+
+utils::IdentifierIterator end(const ProgramSelector& sel) {
+ return utils::IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
+}
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
new file mode 100644
index 0000000..8ea6319
--- /dev/null
+++ b/broadcastradio/common/utilsaidl/include/broadcastradio-utils-aidl/Utils.h
@@ -0,0 +1,188 @@
+/*
+ * 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/broadcastradio/IdentifierType.h>
+#include <aidl/android/hardware/broadcastradio/Metadata.h>
+#include <aidl/android/hardware/broadcastradio/ProgramFilter.h>
+#include <aidl/android/hardware/broadcastradio/ProgramIdentifier.h>
+#include <aidl/android/hardware/broadcastradio/ProgramInfo.h>
+#include <aidl/android/hardware/broadcastradio/ProgramListChunk.h>
+#include <aidl/android/hardware/broadcastradio/ProgramSelector.h>
+#include <aidl/android/hardware/broadcastradio/Properties.h>
+#include <aidl/android/hardware/broadcastradio/Result.h>
+
+#include <numeric>
+#include <optional>
+#include <thread>
+#include <unordered_set>
+
+namespace aidl::android::hardware::broadcastradio {
+
+namespace utils {
+
+enum class FrequencyBand {
+ UNKNOWN,
+ FM,
+ AM_LW,
+ AM_MW,
+ AM_SW,
+};
+
+class IdentifierIterator final
+ : public std::iterator<std::random_access_iterator_tag, ProgramIdentifier, ssize_t,
+ const ProgramIdentifier*, const ProgramIdentifier&> {
+ using ptrType = typename std::iterator_traits<IdentifierIterator>::pointer;
+ using refType = typename std::iterator_traits<IdentifierIterator>::reference;
+ using diffType = typename std::iterator_traits<IdentifierIterator>::difference_type;
+
+ public:
+ explicit IdentifierIterator(const ProgramSelector& sel);
+
+ const IdentifierIterator operator++(int);
+ IdentifierIterator& operator++();
+ refType operator*() const;
+ inline ptrType operator->() const { return &operator*(); }
+ IdentifierIterator operator+(diffType v) const { return IdentifierIterator(mSel, mPos + v); }
+ bool operator==(const IdentifierIterator& rhs) const;
+ inline bool operator!=(const IdentifierIterator& rhs) const { return !operator==(rhs); };
+
+ private:
+ explicit IdentifierIterator(const ProgramSelector& sel, size_t pos);
+
+ std::reference_wrapper<const ProgramSelector> mSel;
+
+ const ProgramSelector& getSelector() const { return mSel.get(); }
+
+ /** 0 is the primary identifier, 1-n are secondary identifiers. */
+ size_t mPos = 0;
+};
+
+/**
+ * Convert Result to int
+ */
+int32_t resultToInt(Result result);
+
+/**
+ * Guesses band from the frequency value.
+ *
+ * The band bounds are not exact to cover multiple regions.
+ * The function is biased towards success, i.e. it never returns
+ * FrequencyBand::UNKNOWN for correct frequency, but a result for
+ * incorrect one is undefined (it doesn't have to return UNKNOWN).
+ */
+FrequencyBand getBand(int64_t frequency);
+
+/**
+ * Checks, if {@code pointer} tunes to {@channel}.
+ *
+ * For example, having a channel {AMFM_FREQUENCY_KHZ = 103.3}:
+ * - selector {AMFM_FREQUENCY_KHZ = 103.3, HD_SUBCHANNEL = 0} can tune to this channel;
+ * - selector {AMFM_FREQUENCY_KHZ = 103.3, HD_SUBCHANNEL = 1} can't.
+ *
+ * @param pointer selector we're trying to match against channel.
+ * @param channel existing channel.
+ */
+bool tunesTo(const ProgramSelector& pointer, const ProgramSelector& channel);
+
+/**
+ * Checks whether a given program selector has the given ID (either primary or secondary).
+ */
+bool hasId(const ProgramSelector& sel, const IdentifierType& type);
+
+/**
+ * Returns ID (either primary or secondary) for a given program selector.
+ *
+ * If the selector does not contain given type, returns kValueForNotFoundIdentifier
+ * and emits a warning.
+ */
+int64_t getId(const ProgramSelector& sel, const IdentifierType& type);
+
+/**
+ * Returns ID (either primary or secondary) for a given program selector.
+ *
+ * If the selector does not contain given type, returns default value.
+ */
+int64_t getId(const ProgramSelector& sel, const IdentifierType& type, int64_t defaultValue);
+
+/**
+ * Returns all IDs of a given type.
+ */
+std::vector<int> getAllIds(const ProgramSelector& sel, const IdentifierType& type);
+
+/**
+ * Checks, if a given selector is supported by the radio module.
+ *
+ * @param prop Module description.
+ * @param sel The selector to check.
+ * @return True, if the selector is supported, false otherwise.
+ */
+bool isSupported(const Properties& prop, const ProgramSelector& sel);
+
+bool isValid(const ProgramIdentifier& id);
+bool isValid(const ProgramSelector& sel);
+
+ProgramIdentifier makeIdentifier(IdentifierType type, int64_t value);
+ProgramSelector makeSelectorAmfm(int32_t frequency);
+ProgramSelector makeSelectorDab(int32_t sidExt, int32_t ensemble);
+
+bool satisfies(const ProgramFilter& filter, const ProgramSelector& sel);
+
+struct ProgramInfoHasher {
+ size_t operator()(const ProgramInfo& info) const;
+};
+
+struct ProgramInfoKeyEqual {
+ bool operator()(const ProgramInfo& info1, const ProgramInfo& info2) const;
+};
+
+typedef std::unordered_set<ProgramInfo, ProgramInfoHasher, ProgramInfoKeyEqual> ProgramInfoSet;
+
+void updateProgramList(const ProgramListChunk& chunk, ProgramInfoSet* list);
+
+std::optional<std::string> getMetadataString(const ProgramInfo& info, const Metadata::Tag& tag);
+
+ProgramIdentifier makeHdRadioStationName(const std::string& name);
+
+template <typename aidl_type>
+inline std::string vectorToString(const std::vector<aidl_type>& in_values) {
+ return std::accumulate(std::begin(in_values), std::end(in_values), std::string{},
+ [](const std::string& ls, const aidl_type& rs) {
+ return ls + (ls.empty() ? "" : ",") + toString(rs);
+ });
+}
+
+IdentifierType getType(int typeAsInt);
+
+bool parseArgInt(const std::string& s, int* out);
+
+bool parseArgLong(const std::string& s, long* out);
+
+bool parseArgBool(const std::string& s, bool* out);
+
+bool parseArgDirection(const std::string& s, bool* out);
+
+bool parseArgIdentifierTypeArray(const std::string& s, std::vector<IdentifierType>* out);
+
+bool parseProgramIdentifierList(const std::string& s, std::vector<ProgramIdentifier>* out);
+
+} // namespace utils
+
+utils::IdentifierIterator begin(const ProgramSelector& sel);
+utils::IdentifierIterator end(const ProgramSelector& sel);
+
+} // namespace aidl::android::hardware::broadcastradio
diff --git a/camera/device/aidl/android/hardware/camera/device/StreamBuffer.aidl b/camera/device/aidl/android/hardware/camera/device/StreamBuffer.aidl
index e487494..29fec68 100644
--- a/camera/device/aidl/android/hardware/camera/device/StreamBuffer.aidl
+++ b/camera/device/aidl/android/hardware/camera/device/StreamBuffer.aidl
@@ -33,8 +33,8 @@
parcelable StreamBuffer {
/**
* The ID of the stream this buffer is associated with. -1 indicates an
- * invalid (empty) StreamBuffer, in which case buffer must also point to
- * null and bufferId must be 0.
+ * invalid (empty) StreamBuffer, in which case buffer must be empty
+ * and bufferId must be 0.
*/
int streamId;
@@ -48,7 +48,7 @@
* corresponding stream is removed from stream configuration or until camera
* device session is closed. After the first time a buffer is introduced to
* HAL, in the future camera service must refer to the same buffer using
- * only bufferId, and keep the buffer handle null.
+ * only bufferId, and keep the buffer handle empty.
*/
long bufferId;
@@ -59,10 +59,10 @@
* is not seen by the HAL before, this buffer handle is guaranteed to be a
* valid handle to a graphics buffer, with dimensions and format matching
* that of the stream. If the bufferId has been sent to the HAL before, this
- * buffer handle must be null and HAL must look up the actual buffer handle
+ * buffer handle must be empty and HAL must look up the actual buffer handle
* to use from its own bufferId to buffer handle map.
*
- * For StreamBuffers returned in a CaptureResult, this must be null, since
+ * For StreamBuffers returned in a CaptureResult, this must be empty, since
* the handle to the buffer is already known to the client (since the client
* sent it in the matching CaptureRequest), and the handle can be identified
* by the combination of frame number and stream ID.
@@ -81,11 +81,11 @@
* The acquire sync fence for this buffer. The HAL must wait on this fence
* fd before attempting to read from or write to this buffer.
*
- * In a buffer included in a CaptureRequest, the client may set this to null
+ * In a buffer included in a CaptureRequest, the client may leave this empty
* to indicate that no waiting is necessary for this buffer.
*
* When the HAL returns an input or output buffer to the framework with
- * processCaptureResult(), the acquireFence must be set to null. If the HAL
+ * processCaptureResult(), the acquireFence must be empty. If the HAL
* never waits on the acquireFence due to an error in filling or reading a
* buffer, when calling processCaptureResult() the HAL must set the
* releaseFence of the buffer to be the acquireFence passed to it by the
@@ -97,17 +97,17 @@
/**
* The release sync fence for this buffer. The HAL must set this to a valid
* fence fd when returning the input buffer or output buffers to the client
- * in a CaptureResult, or set it to null to indicate that no waiting is
+ * in a CaptureResult, or leave it empty to indicate that no waiting is
* required for this buffer.
*
- * The client must set this to be null for all buffers included in a
+ * The client must leave this empty for all buffers included in a
* processCaptureRequest call.
*
* After signaling the releaseFence for this buffer, the HAL
* must not make any further attempts to access this buffer as the
* ownership has been fully transferred back to the client.
*
- * If this is null, then the ownership of this buffer is transferred back
+ * If this is empty, then the ownership of the buffer is transferred back
* immediately upon the call of processCaptureResult.
*/
NativeHandle releaseFence;
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 6866776..b0ae20e 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -7883,6 +7883,7 @@
}
}
+ camera_metadata_t* staticMetadata;
camera_metadata_ro_entry physicalMultiResStreamConfigs;
camera_metadata_ro_entry physicalStreamConfigs;
camera_metadata_ro_entry physicalMaxResolutionStreamConfigs;
@@ -7901,8 +7902,9 @@
ret = subDevice->getCameraCharacteristics([&](auto status, const auto& chars) {
ASSERT_EQ(Status::OK, status);
- const camera_metadata_t* staticMetadata =
- reinterpret_cast<const camera_metadata_t*>(chars.data());
+ staticMetadata = clone_camera_metadata(
+ reinterpret_cast<const camera_metadata_t*>(chars.data()));
+ ASSERT_NE(nullptr, staticMetadata);
rc = getSystemCameraKind(staticMetadata, &physSystemCameraKind);
ASSERT_EQ(rc, Status::OK);
// Make sure that the system camera kind of a non-hidden
@@ -7936,7 +7938,9 @@
verifyCameraCharacteristics(status, chars);
verifyMonochromeCharacteristics(chars, deviceVersion);
- auto staticMetadata = (const camera_metadata_t*)chars.data();
+ staticMetadata = clone_camera_metadata(
+ reinterpret_cast<const camera_metadata_t*>(chars.data()));
+ ASSERT_NE(nullptr, staticMetadata);
retcode = find_camera_metadata_ro_entry(
staticMetadata, ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
@@ -8064,6 +8068,7 @@
}
}
}
+ free_camera_metadata(staticMetadata);
}
// If a multi-resolution stream is supported, there must be at least one
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index c4a2dc9..a530159 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -1183,6 +1183,7 @@
camera_metadata_ro_entry physicalMultiResStreamConfigs;
camera_metadata_ro_entry physicalStreamConfigs;
camera_metadata_ro_entry physicalMaxResolutionStreamConfigs;
+ CameraMetadata physChars;
bool isUltraHighRes = false;
std::unordered_set<int32_t> subCameraPrivacyTestPatterns;
if (isPublicId) {
@@ -1191,12 +1192,11 @@
ASSERT_TRUE(ret.isOk());
ASSERT_NE(subDevice, nullptr);
- CameraMetadata subDeviceChars;
- ret = subDevice->getCameraCharacteristics(&subDeviceChars);
+ ret = subDevice->getCameraCharacteristics(&physChars);
ASSERT_TRUE(ret.isOk());
const camera_metadata_t* staticMetadata =
- reinterpret_cast<const camera_metadata_t*>(subDeviceChars.metadata.data());
+ reinterpret_cast<const camera_metadata_t*>(physChars.metadata.data());
retStatus = getSystemCameraKind(staticMetadata, &physSystemCameraKind);
ASSERT_EQ(retStatus, Status::OK);
@@ -1217,7 +1217,6 @@
getPrivacyTestPatternModes(staticMetadata, &subCameraPrivacyTestPatterns);
} else {
// Check camera characteristics for hidden camera id
- CameraMetadata physChars;
ndk::ScopedAStatus ret =
device->getPhysicalCameraCharacteristics(physicalId, &physChars);
ASSERT_TRUE(ret.isOk());
diff --git a/common/support/NativeHandle.cpp b/common/support/NativeHandle.cpp
index 321d7a8..126ef2e 100644
--- a/common/support/NativeHandle.cpp
+++ b/common/support/NativeHandle.cpp
@@ -22,6 +22,13 @@
using aidl::android::hardware::common::NativeHandle;
+/**
+ * Checks if a NativeHandle is null
+ */
+bool isAidlNativeHandleEmpty(const NativeHandle& handle) {
+ return handle.fds.empty() && handle.ints.empty();
+}
+
static native_handle_t* fromAidl(const NativeHandle& handle, bool doDup) {
native_handle_t* to = native_handle_create(handle.fds.size(), handle.ints.size());
if (!to) return nullptr;
diff --git a/common/support/include/aidlcommonsupport/NativeHandle.h b/common/support/include/aidlcommonsupport/NativeHandle.h
index 10eecba..b5ea1dd 100644
--- a/common/support/include/aidlcommonsupport/NativeHandle.h
+++ b/common/support/include/aidlcommonsupport/NativeHandle.h
@@ -22,6 +22,11 @@
namespace android {
/**
+ * Checks if a NativeHandle is empty.
+ */
+bool isAidlNativeHandleEmpty(const aidl::android::hardware::common::NativeHandle& handle);
+
+/**
* Creates a libcutils native handle from an AIDL native handle, but it does not
* dup internally, so it will contain the same FDs as the handle itself. The
* result should be deleted with native_handle_delete.
diff --git a/common/support/test.cpp b/common/support/test.cpp
index 2359277..9b79581 100644
--- a/common/support/test.cpp
+++ b/common/support/test.cpp
@@ -62,6 +62,7 @@
TEST(ConvertNativeHandle, MakeFromAidlEmpty) {
NativeHandle handle;
+ EXPECT_TRUE(isAidlNativeHandleEmpty(handle));
native_handle_t* to = makeFromAidl(handle);
checkEq(handle, to, false /*exceptFds*/);
// no native_handle_close b/c fds are owned by NativeHandle
@@ -70,6 +71,7 @@
TEST(ConvertNativeHandle, MakeFromAidl) {
NativeHandle handle = makeTestAidlHandle();
+ EXPECT_FALSE(isAidlNativeHandleEmpty(handle));
native_handle_t* to = makeFromAidl(handle);
checkEq(handle, to, false /*exceptFds*/);
// no native_handle_close b/c fds are owned by NativeHandle
@@ -106,6 +108,7 @@
TEST(ConvertNativeHandle, MakeToAidlEmpty) {
native_handle_t* handle = native_handle_create(0, 0);
NativeHandle to = makeToAidl(handle);
+ EXPECT_TRUE(isAidlNativeHandleEmpty(to));
checkEq(to, handle, false /*exceptFds*/);
// no native_handle_close b/c fds are owned by NativeHandle now
EXPECT_EQ(0, native_handle_delete(handle));
@@ -114,6 +117,7 @@
TEST(ConvertNativeHandle, MakeToAidl) {
native_handle_t* handle = makeTestLibcutilsHandle();
NativeHandle to = makeToAidl(handle);
+ EXPECT_FALSE(isAidlNativeHandleEmpty(to));
checkEq(to, handle, false /*exceptFds*/);
// no native_handle_close b/c fds are owned by NativeHandle now
EXPECT_EQ(0, native_handle_delete(handle));
@@ -122,6 +126,7 @@
TEST(ConvertNativeHandle, DupToAidlEmpty) {
native_handle_t* handle = native_handle_create(0, 0);
NativeHandle to = dupToAidl(handle);
+ EXPECT_TRUE(isAidlNativeHandleEmpty(to));
checkEq(to, handle, true /*exceptFds*/);
EXPECT_EQ(0, native_handle_close(handle));
EXPECT_EQ(0, native_handle_delete(handle));
@@ -130,6 +135,7 @@
TEST(ConvertNativeHandle, DupToAidl) {
native_handle_t* handle = makeTestLibcutilsHandle();
NativeHandle to = dupToAidl(handle);
+ EXPECT_FALSE(isAidlNativeHandleEmpty(to));
checkEq(to, handle, true /*exceptFds*/);
EXPECT_EQ(0, native_handle_close(handle));
EXPECT_EQ(0, native_handle_delete(handle));
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 4cfe7e6..a395987 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -112,6 +112,13 @@
<regex-instance>.*</regex-instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.automotive.remoteaccess</name>
+ <interface>
+ <name>IRemoteAccess</name>
+ <regex-instance>.*</regex-instance>
+ </interface>
+ </hal>
<hal format="aidl" optional="true">
<name>android.hardware.biometrics.face</name>
<version>2</version>
@@ -152,30 +159,13 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
+ <hal format="aidl" optional="true">
<name>android.hardware.broadcastradio</name>
- <version>1.0-1</version>
- <interface>
- <name>IBroadcastRadioFactory</name>
- <instance>default</instance>
- </interface>
- </hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.broadcastradio</name>
- <version>2.0</version>
<interface>
<name>IBroadcastRadio</name>
<regex-instance>.*</regex-instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.camera.provider</name>
- <version>2.4-7</version>
- <interface>
- <name>ICameraProvider</name>
- <regex-instance>[^/]+/[0-9]+</regex-instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.camera.provider</name>
<version>1-2</version>
@@ -192,9 +182,9 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
+ <hal format="aidl" optional="true">
<name>android.hardware.confirmationui</name>
- <version>1.0</version>
+ <version>1</version>
<interface>
<name>IConfirmationUI</name>
<instance>default</instance>
@@ -240,7 +230,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.gnss</name>
- <version>2</version>
+ <version>3</version>
<interface>
<name>IGnss</name>
<instance>default</instance>
@@ -307,7 +297,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.identity</name>
- <version>1-4</version>
+ <version>1-5</version>
<interface>
<name>IIdentityCredentialStore</name>
<instance>default</instance>
@@ -354,7 +344,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.security.keymint</name>
- <version>1-2</version>
+ <version>1-3</version>
<interface>
<name>IKeyMintDevice</name>
<instance>default</instance>
@@ -363,7 +353,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.security.keymint</name>
- <version>1-2</version>
+ <version>1-3</version>
<interface>
<name>IRemotelyProvisionedComponent</name>
<instance>default</instance>
@@ -387,18 +377,6 @@
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.media.omx</name>
- <version>1.0</version>
- <interface>
- <name>IOmx</name>
- <instance>default</instance>
- </interface>
- <interface>
- <name>IOmxStore</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.memtrack</name>
<version>1</version>
@@ -440,7 +418,7 @@
</hal>
<hal format="aidl" optional="false">
<name>android.hardware.power</name>
- <version>2-3</version>
+ <version>2-4</version>
<interface>
<name>IPower</name>
<instance>default</instance>
@@ -602,27 +580,25 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="aidl" optional="false">
<name>android.hardware.thermal</name>
- <version>2.0</version>
+ <version>1</version>
<interface>
<name>IThermal</name>
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
+ <hal format="aidl" optional="true">
<name>android.hardware.tv.cec</name>
- <version>1.0-1</version>
<interface>
<name>IHdmiCec</name>
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.tv.input</name>
- <version>1.0</version>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.tv.hdmi</name>
<interface>
- <name>ITvInput</name>
+ <name>IHdmi</name>
<instance>default</instance>
</interface>
</hal>
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
index 6de9d03..cb77c7b 100644
--- a/compatibility_matrices/exclude/fcm_exclude.cpp
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -68,6 +68,8 @@
// does not depend on this HAL, hence it is not declared in any manifests or matrices.
"android.hardware.fastboot@1.0",
"android.hardware.fastboot@1.1",
+ // Fastboot AIDL
+ "android.hardware.fastboot",
// Deprecated HALs.
// HIDL
diff --git a/confirmationui/OWNERS b/confirmationui/OWNERS
new file mode 100644
index 0000000..2bcdb0e
--- /dev/null
+++ b/confirmationui/OWNERS
@@ -0,0 +1,2 @@
+swillden@google.com
+subrahmanyaman@google.com
diff --git a/confirmationui/aidl/Android.bp b/confirmationui/aidl/Android.bp
new file mode 100644
index 0000000..5395101
--- /dev/null
+++ b/confirmationui/aidl/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "android.hardware.confirmationui",
+ vendor_available: true,
+ imports: [
+ "android.hardware.security.keymint-V3",
+ ],
+ srcs: ["android/hardware/confirmationui/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ apps_enabled: false,
+ },
+ cpp: {
+ enabled: false,
+ },
+ },
+}
diff --git a/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/IConfirmationResultCallback.aidl b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/IConfirmationResultCallback.aidl
new file mode 100644
index 0000000..6d6ec97
--- /dev/null
+++ b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/IConfirmationResultCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.confirmationui;
+@VintfStability
+interface IConfirmationResultCallback {
+ void result(in int error, in byte[] formattedMessage, in byte[] confirmationToken);
+}
diff --git a/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/IConfirmationUI.aidl b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/IConfirmationUI.aidl
new file mode 100644
index 0000000..c5c7aa7
--- /dev/null
+++ b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/IConfirmationUI.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.confirmationui;
+@VintfStability
+interface IConfirmationUI {
+ void abort();
+ void deliverSecureInputEvent(in android.hardware.security.keymint.HardwareAuthToken secureInputToken);
+ void promptUserConfirmation(in android.hardware.confirmationui.IConfirmationResultCallback resultCB, in byte[] promptText, in byte[] extraData, in @utf8InCpp String locale, in android.hardware.confirmationui.UIOption[] uiOptions);
+ const int OK = 0;
+ const int CANCELED = 1;
+ const int ABORTED = 2;
+ const int OPERATION_PENDING = 3;
+ const int IGNORED = 4;
+ const int SYSTEM_ERROR = 5;
+ const int UNIMPLEMENTED = 6;
+ const int UNEXPECTED = 7;
+ const int UI_ERROR = 65536;
+ const int UI_ERROR_MISSING_GLYPH = 65537;
+ const int UI_ERROR_MESSAGE_TOO_LONG = 65538;
+ const int UI_ERROR_MALFORMED_UTF8ENCODING = 65539;
+ const int TEST_KEY_BYTE = 165;
+ const int MAX_MESSAGE_SIZE = 6144;
+}
diff --git a/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/TestModeCommands.aidl b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/TestModeCommands.aidl
new file mode 100644
index 0000000..b8629d1
--- /dev/null
+++ b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/TestModeCommands.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.confirmationui;
+@Backing(type="int") @VintfStability
+enum TestModeCommands {
+ OK_EVENT = 0,
+ CANCEL_EVENT = 1,
+}
diff --git a/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/UIOption.aidl b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/UIOption.aidl
new file mode 100644
index 0000000..ebecf9e
--- /dev/null
+++ b/confirmationui/aidl/aidl_api/android.hardware.confirmationui/current/android/hardware/confirmationui/UIOption.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.confirmationui;
+@Backing(type="int") @VintfStability
+enum UIOption {
+ ACCESSIBILITY_INVERTED = 0,
+ ACCESSIBILITY_MAGNIFIED = 1,
+}
diff --git a/confirmationui/aidl/android/hardware/confirmationui/IConfirmationResultCallback.aidl b/confirmationui/aidl/android/hardware/confirmationui/IConfirmationResultCallback.aidl
new file mode 100644
index 0000000..2165fdd
--- /dev/null
+++ b/confirmationui/aidl/android/hardware/confirmationui/IConfirmationResultCallback.aidl
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.confirmationui;
+
+import android.hardware.confirmationui.IConfirmationUI;
+
+/**
+ * Callback interface passed to IConfirmationUI::promptUserConfirmation().
+ * Informs the caller about the result of the prompt operation.
+ */
+@VintfStability
+interface IConfirmationResultCallback {
+ /**
+ * This callback is called by the confirmation provider when it stops prompting the user.
+ * Iff the user has confirmed the prompted text, error is ErrorCode::OK and the
+ * parameters formattedMessage and confirmationToken hold the values needed to request
+ * a signature from keymaster.
+ * In all other cases formattedMessage and confirmationToken must be of length 0.
+ *
+ * @param error - OK: IFF the user has confirmed the prompt.
+ * - CANCELED: If the user has pressed the cancel button.
+ * - ABORTED: If IConfirmationUI::abort() was called.
+ * - SYSTEM_ERROR: If an unexpected System error occurred that prevented the TUI
+ * from being shut down gracefully.
+ *
+ * @param formattedMessage holds the prompt text and extra data.
+ * The message is CBOR (RFC 7049) encoded and has the following format:
+ * CBOR_MAP{ "prompt", <promptText>, "extra", <extraData> }
+ * The message is a CBOR encoded map (type 5) with the keys
+ * "prompt" and "extra". The keys are encoded as CBOR text string
+ * (type 3). The value <promptText> is encoded as CBOR text string
+ * (type 3), and the value <extraData> is encoded as CBOR byte string
+ * (type 2). The map must have exactly one key value pair for each of
+ * the keys "prompt" and "extra". Other keys are not allowed.
+ * The value of "prompt" is given by the proptText argument to
+ * IConfirmationUI::promptUserConfirmation and must not be modified
+ * by the implementation.
+ * The value of "extra" is given by the extraData argument to
+ * IConfirmationUI::promptUserConfirmation and must not be modified
+ * or interpreted by the implementation.
+ *
+ * @param confirmationToken a 32-byte HMAC-SHA256 value, computed over
+ * "confirmation token" || <formattedMessage>
+ * i.e. the literal UTF-8 encoded string "confirmation token", without
+ * the "", concatenated with the formatted message as returned in the
+ * formattedMessage argument. The HMAC is keyed with a 256-bit secret
+ * which is shared with Keymaster. In test mode the test key MUST be
+ * used (see types.hal TestModeCommands and
+ * IConfirmationUI::TEST_KEY_BYTE).
+ */
+ void result(in int error, in byte[] formattedMessage, in byte[] confirmationToken);
+}
diff --git a/confirmationui/aidl/android/hardware/confirmationui/IConfirmationUI.aidl b/confirmationui/aidl/android/hardware/confirmationui/IConfirmationUI.aidl
new file mode 100644
index 0000000..f071126
--- /dev/null
+++ b/confirmationui/aidl/android/hardware/confirmationui/IConfirmationUI.aidl
@@ -0,0 +1,146 @@
+/*
+ * 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.confirmationui;
+
+import android.hardware.confirmationui.IConfirmationResultCallback;
+import android.hardware.confirmationui.UIOption;
+import android.hardware.security.keymint.HardwareAuthToken;
+
+@VintfStability
+interface IConfirmationUI {
+ // Possible Errors.
+ /**
+ * API call succeeded or the user gave approval (result callback).
+ */
+ const int OK = 0;
+ /**
+ * The user canceled the TUI (result callback).
+ */
+ const int CANCELED = 1;
+ /**
+ * IConfirmationUI::abort() was called. (result callback).
+ */
+ const int ABORTED = 2;
+ /**
+ * Cannot start another prompt.
+ */
+ const int OPERATION_PENDING = 3;
+ /**
+ * IConfirmationUI::deliverSecureInputEvent call was ignored.
+ */
+ const int IGNORED = 4;
+ /**
+ * An unexpected system error occurred.
+ */
+ const int SYSTEM_ERROR = 5;
+ /**
+ * Returned by an unimplemented API call.
+ */
+ const int UNIMPLEMENTED = 6;
+ /**
+ * This is returned when an error is diagnosed that should have been
+ * caught by earlier input sanitization. Should never be seen in production.
+ */
+ const int UNEXPECTED = 7;
+ /**
+ * General UI error.
+ */
+ const int UI_ERROR = 0x10000;
+ /**
+ * This is returned if there is no corresponding glyph for a character.
+ */
+ const int UI_ERROR_MISSING_GLYPH = 0x10001;
+ /**
+ * The implementation must return this error code on promptUserConfirmation if the
+ * resulting formatted message does not fit into MAX_MESSAGE_SIZE bytes. It is
+ * advised that the implementation formats the message upon receiving this API call to
+ * be able to diagnose this syndrome.
+ */
+ const int UI_ERROR_MESSAGE_TOO_LONG = 0x10002;
+ /**
+ * This is returned if the UTF8 encoding is malformed.
+ */
+ const int UI_ERROR_MALFORMED_UTF8ENCODING = 0x10003;
+
+ // Constants
+ /**
+ * The test key is 32byte word with all bytes set to TEST_KEY_BYTE.
+ */
+ const int TEST_KEY_BYTE = 0xA5;
+ /**
+ * This defines the maximum message size. This indirectly limits the size of the prompt text
+ * and the extra data that can be passed to the confirmation UI. The prompt text and extra data
+ * must fit in to this size including CBOR header information.
+ */
+ const int MAX_MESSAGE_SIZE = 0x1800;
+
+ /**
+ * Aborts a pending user prompt. This allows the framework to gracefully end a TUI dialog.
+ * If a TUI operation was pending the corresponding call back is informed with
+ * ErrorCode::Aborted.
+ */
+ void abort();
+
+ /**
+ * DeliverSecureInput is used by the framework to deliver a secure input event to the
+ * confirmation provider.
+ *
+ * VTS test mode:
+ * This function can be used to test certain code paths non-interactively.
+ * See TestModeCommands.aidl for details.
+ *
+ * @param secureInputToken An authentication token as generated by Android authentication
+ * providers.
+ */
+ void deliverSecureInputEvent(in HardwareAuthToken secureInputToken);
+
+ /**
+ * Asynchronously initiates a confirmation UI dialog prompting the user to confirm a given text.
+ * The TUI prompt must be implemented in such a way that a positive response indicates with
+ * high confidence that a user has seen the given prompt text even if the Android framework
+ * including the kernel was compromised.
+ *
+ * Service status return:
+ *
+ * OK IFF the dialog was successfully started. In this case, and only in this
+ * case, the implementation must, eventually, call the callback to
+ * indicate completion.
+ * OPERATION_PENDING is returned when the confirmation provider is currently
+ * in use.
+ * SYSTEM_ERROR an error occurred trying to communicate with the confirmation
+ * provider (e.g. trusted app).
+ * UI_ERROR the confirmation provider encountered an issue with displaying
+ * the prompt text to the user.
+ *
+ * @param resultCB Implementation of IResultCallback. Used by the implementation to report
+ * the result of the current pending user prompt.
+ *
+ * @param promptText UTF-8 encoded bytes which is to be presented to the user.
+ *
+ * @param extraData A binary blob that must be included in the formatted output message as is.
+ * It is opaque to the implementation. Implementations must neither interpret
+ * nor modify the content.
+ *
+ * @param locale String specifying the locale that must be used by the TUI dialog. The string
+ * is an IETF BCP 47 tag.
+ *
+ * @param uiOptions A set of uiOptions manipulating how the confirmation prompt is displayed.
+ * Refer to UIOption in types.hal for possible options.
+ */
+ void promptUserConfirmation(in IConfirmationResultCallback resultCB, in byte[] promptText,
+ in byte[] extraData, in @utf8InCpp String locale, in UIOption[] uiOptions);
+}
diff --git a/confirmationui/aidl/android/hardware/confirmationui/TestModeCommands.aidl b/confirmationui/aidl/android/hardware/confirmationui/TestModeCommands.aidl
new file mode 100644
index 0000000..5b1d8fb
--- /dev/null
+++ b/confirmationui/aidl/android/hardware/confirmationui/TestModeCommands.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.confirmationui;
+
+/**
+ * Test mode commands.
+ *
+ * IConfirmationUI::deliverSecureInputEvent can be used to test certain code paths.
+ * To that end, the caller passes an auth token that has an HMAC keyed with the test key
+ * (see IConfirmationUI::TEST_KEY_BYTE). Implementations first check the HMAC against test key.
+ * If the test key produces a matching HMAC, the implementation evaluates the challenge field
+ * of the auth token against the values defined in TestModeCommand.
+ * If the command indicates that a confirmation token is to be generated the test key MUST be used
+ * to generate this confirmation token.
+ *
+ * See command code for individual test command descriptions.
+ */
+@VintfStability
+@Backing(type="int")
+enum TestModeCommands {
+ /**
+ * Simulates the user pressing the OK button on the UI. If no operation is pending
+ * ResponseCode::Ignored must be returned. A pending operation is finalized successfully
+ * see IConfirmationResultCallback::result, however, the test key
+ * (see IConfirmationUI::TEST_KEY_BYTE) MUST be used to generate the confirmation token.
+ */
+ OK_EVENT = 0,
+ /**
+ * Simulates the user pressing the CANCEL button on the UI. If no operation is pending
+ * Result::Ignored must be returned. A pending operation is finalized as specified in
+ * IConfirmationResultCallback.hal.
+ */
+ CANCEL_EVENT = 1,
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/confirmationui/aidl/android/hardware/confirmationui/UIOption.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to confirmationui/aidl/android/hardware/confirmationui/UIOption.aidl
index 4b82dd0..b242c53 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/confirmationui/aidl/android/hardware/confirmationui/UIOption.aidl
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.confirmationui;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+/**
+ * UI modification options.
+ */
+@VintfStability
+@Backing(type="int")
+enum UIOption {
+ /**
+ * Accessibility: Requests color inverted style.
+ */
+ ACCESSIBILITY_INVERTED = 0,
+ /**
+ * Accessibility: Requests magnified style.
+ */
+ ACCESSIBILITY_MAGNIFIED = 1,
+}
diff --git a/confirmationui/aidl/vts/functional/Android.bp b/confirmationui/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..3649c87
--- /dev/null
+++ b/confirmationui/aidl/vts/functional/Android.bp
@@ -0,0 +1,45 @@
+//
+// 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
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalConfirmationUITargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalConfirmationUITargetTest.cpp",
+ ],
+ static_libs: [
+ "android.hardware.confirmationui-V1-ndk",
+ "libcn-cbor",
+ "android.hardware.confirmationui-support-lib",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "libcrypto",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/confirmationui/aidl/vts/functional/VtsHalConfirmationUITargetTest.cpp b/confirmationui/aidl/vts/functional/VtsHalConfirmationUITargetTest.cpp
new file mode 100644
index 0000000..61dae8b
--- /dev/null
+++ b/confirmationui/aidl/vts/functional/VtsHalConfirmationUITargetTest.cpp
@@ -0,0 +1,535 @@
+/*
+ * 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 "ConfirmationIOAidlHalTest"
+
+#include <algorithm>
+#include <condition_variable>
+#include <future>
+#include <iostream>
+#include <memory>
+#include <mutex>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/confirmationui/BnConfirmationResultCallback.h>
+#include <aidl/android/hardware/confirmationui/IConfirmationUI.h>
+#include <aidl/android/hardware/confirmationui/TestModeCommands.h>
+#include <aidl/android/hardware/confirmationui/UIOption.h>
+#include <android-base/thread_annotations.h>
+#include <android/hardware/confirmationui/support/confirmationui_utils.h>
+#include <cutils/log.h>
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include <cn-cbor/cn-cbor.h>
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/binder_status.h>
+
+static constexpr int TIMEOUT_PERIOD = 10;
+
+namespace aidl::android::hardware::confirmationui::test {
+using ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType;
+using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::android::hardware::confirmationui::support::auth_token_key_t;
+using ::android::hardware::confirmationui::support::ByteBufferProxy;
+using ::android::hardware::confirmationui::support::HMac;
+using ::android::hardware::confirmationui::support::hmac_t;
+using ::android::hardware::confirmationui::support::hton;
+using ::android::hardware::confirmationui::support::NullOr;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+namespace {
+const auth_token_key_t testKey(static_cast<uint8_t>(IConfirmationUI::TEST_KEY_BYTE));
+
+class HMacImplementation {
+ public:
+ static NullOr<hmac_t> hmac256(const auth_token_key_t& key,
+ std::initializer_list<ByteBufferProxy> buffers) {
+ HMAC_CTX hmacCtx;
+ HMAC_CTX_init(&hmacCtx);
+ if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) {
+ return {};
+ }
+ for (auto& buffer : buffers) {
+ if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
+ return {};
+ }
+ }
+ hmac_t result;
+ if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
+ return {};
+ }
+ return result;
+ }
+};
+
+using HMacer = HMac<HMacImplementation>;
+
+template <typename... Data>
+vector<uint8_t> testHMAC(const Data&... data) {
+ auto hmac = HMacer::hmac256(testKey, data...);
+ if (!hmac.isOk()) {
+ ADD_FAILURE() << "Failed to compute test hmac. This is a self-test error.";
+ return {};
+ }
+ vector<uint8_t> result(hmac.value().size());
+ std::copy(hmac.value().data(), hmac.value().data() + hmac.value().size(), result.data());
+ return result;
+}
+
+template <typename T>
+auto toBytes(const T& v) -> const uint8_t (&)[sizeof(T)] {
+ return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
+}
+
+HardwareAuthToken makeTestToken(const TestModeCommands command, uint64_t timestamp = 0) {
+ HardwareAuthToken auth_token;
+ auth_token.challenge = static_cast<uint64_t>(command);
+ auth_token.userId = 0;
+ auth_token.authenticatorId = 0;
+ auth_token.authenticatorType = HardwareAuthenticatorType::NONE;
+ auth_token.timestamp = {static_cast<int64_t>(timestamp)};
+
+ // Canonical form of auth-token v0
+ // version (1 byte)
+ // challenge (8 bytes)
+ // user_id (8 bytes)
+ // authenticator_id (8 bytes)
+ // authenticator_type (4 bytes)
+ // timestamp (8 bytes)
+ // total 37 bytes
+ auth_token.mac = testHMAC("\0",
+ toBytes(auth_token.challenge), //
+ toBytes(auth_token.userId), //
+ toBytes(auth_token.authenticatorId), //
+ toBytes(hton(auth_token.authenticatorType)), //
+ toBytes(hton(auth_token.timestamp))); //
+
+ return auth_token;
+}
+
+#define DEBUG_CONFRIMATIONUI_UTILS_TEST
+
+#ifdef DEBUG_CONFRIMATIONUI_UTILS_TEST
+std::ostream& hexdump(std::ostream& out, const uint8_t* data, size_t size) {
+ for (size_t i = 0; i < size; ++i) {
+ uint8_t byte = data[i];
+ out << std::hex << std::setw(2) << std::setfill('0') << (unsigned)byte;
+ switch (i & 0xf) {
+ case 0xf:
+ out << "\n";
+ break;
+ case 7:
+ out << " ";
+ break;
+ default:
+ out << " ";
+ break;
+ }
+ }
+ return out;
+}
+#endif
+
+constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9'
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+std::string hex2str(std::string a) {
+ std::string b;
+ size_t num = a.size() / 2;
+ b.resize(num);
+ for (size_t i = 0; i < num; i++) {
+ b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]);
+ }
+ return b;
+}
+
+int getReturnCode(const ::ndk::ScopedAStatus& result) {
+ if (result.isOk()) return IConfirmationUI::OK;
+
+ if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ return static_cast<int>(result.getServiceSpecificError());
+ }
+ return result.getStatus();
+}
+
+} // namespace
+
+class ConfirmationUIAidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ void TearDown() override { confirmator_->abort(); }
+ void SetUp() override {
+ std::string name = GetParam();
+ ASSERT_TRUE(AServiceManager_isDeclared(name.c_str())) << name;
+ ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
+ ASSERT_NE(binder, nullptr);
+ confirmator_ = IConfirmationUI::fromBinder(binder);
+ ASSERT_NE(confirmator_, nullptr);
+ }
+
+ // Used as a mechanism to inform the test about data/event callback
+ inline void notify() {
+ std::unique_lock<std::mutex> lock(mtx_);
+ cv_.notify_one();
+ }
+
+ // Test code calls this function to wait for data/event callback
+ inline std::cv_status wait() {
+ std::unique_lock<std::mutex> lock(mtx_);
+ auto now = std::chrono::system_clock::now();
+ std::cv_status status = cv_.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+ return status;
+ }
+
+ protected:
+ shared_ptr<IConfirmationUI> confirmator_;
+
+ private:
+ // synchronization objects
+ std::mutex mtx_;
+ std::condition_variable cv_;
+};
+
+class ConfirmationTestCallback
+ : public ::aidl::android::hardware::confirmationui::BnConfirmationResultCallback {
+ public:
+ ConfirmationTestCallback(ConfirmationUIAidlTest& parent) : parent_(parent){};
+ virtual ~ConfirmationTestCallback() = default;
+
+ ::ndk::ScopedAStatus result(int32_t err, const vector<uint8_t>& msg,
+ const vector<uint8_t>& confToken) override {
+ error_ = err;
+ formattedMessage_ = msg;
+ confirmationToken_ = confToken;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+
+ bool verifyConfirmationToken() {
+ static constexpr char confirmationPrefix[] = "confirmation token";
+ EXPECT_EQ(32U, confirmationToken_.size());
+ return 32U == confirmationToken_.size() &&
+ !memcmp(confirmationToken_.data(),
+ testHMAC(confirmationPrefix, formattedMessage_).data(), 32);
+ }
+
+ int error_;
+ vector<uint8_t> formattedMessage_;
+ vector<uint8_t> confirmationToken_;
+
+ private:
+ ConfirmationUIAidlTest& parent_;
+};
+
+struct CnCborDeleter {
+ void operator()(cn_cbor* ptr) { cn_cbor_free(ptr); }
+};
+
+typedef std::unique_ptr<cn_cbor, CnCborDeleter> CnCborPtr;
+
+// Simulates the User taping Ok
+TEST_P(ConfirmationUIAidlTest, UserOkTest) {
+ static constexpr char test_prompt[] = "Me first, gimme gimme!";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_TRUE(confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}).isOk());
+ // Simulate the user taping ok.
+ ASSERT_TRUE(confirmator_->deliverSecureInputEvent(makeTestToken(TestModeCommands::OK_EVENT))
+ .isOk());
+ // Wait for the callback.
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ ASSERT_EQ(IConfirmationUI::OK, conf_cb->error_);
+
+ ASSERT_TRUE(conf_cb->verifyConfirmationToken());
+
+ cn_cbor_errback cn_cbor_error;
+ auto parsed_message = CnCborPtr(cn_cbor_decode(
+ conf_cb->formattedMessage_.data(), conf_cb->formattedMessage_.size(), &cn_cbor_error));
+ // is parsable CBOR
+ ASSERT_TRUE(parsed_message.get());
+ // is a map
+ ASSERT_EQ(CN_CBOR_MAP, parsed_message->type);
+
+ // the message must have exactly 2 key value pairs.
+ // cn_cbor holds 2*<no_of_pairs> in the length field
+ ASSERT_EQ(4, parsed_message->length);
+ // map has key "prompt"
+ auto prompt = cn_cbor_mapget_string(parsed_message.get(), "prompt");
+ ASSERT_TRUE(prompt);
+ ASSERT_EQ(CN_CBOR_TEXT, prompt->type);
+ ASSERT_EQ(22, prompt->length);
+ ASSERT_EQ(0, memcmp(test_prompt, prompt->v.str, 22));
+ // map has key "extra"
+ auto extra_out = cn_cbor_mapget_string(parsed_message.get(), "extra");
+ ASSERT_TRUE(extra_out);
+ ASSERT_EQ(CN_CBOR_BYTES, extra_out->type);
+ ASSERT_EQ(3, extra_out->length);
+ ASSERT_EQ(0, memcmp(test_extra, extra_out->v.bytes, 3));
+}
+
+// Initiates a confirmation prompt with a message that is too long
+TEST_P(ConfirmationUIAidlTest, MessageTooLongTest) {
+ static constexpr uint8_t test_extra[IConfirmationUI::MAX_MESSAGE_SIZE] = {};
+ static constexpr char test_prompt[] = "D\'oh!";
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + sizeof(test_extra));
+ auto result = confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {});
+ ASSERT_EQ(IConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG, getReturnCode(result));
+}
+
+// If the message gets very long some HAL implementations might fail even before the message
+// reaches the trusted app implementation. But the HAL must still diagnose the correct error.
+TEST_P(ConfirmationUIAidlTest, MessageWayTooLongTest) {
+ static constexpr uint8_t test_extra[(IConfirmationUI::MAX_MESSAGE_SIZE)*10] = {};
+ static constexpr char test_prompt[] = "D\'oh!";
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + sizeof(test_extra));
+ auto result = confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {});
+ ASSERT_EQ(IConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG, getReturnCode(result));
+}
+
+// Simulates the User tapping the Cancel
+TEST_P(ConfirmationUIAidlTest, UserCancelTest) {
+ static constexpr char test_prompt[] = "Me first, gimme gimme!";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_TRUE(confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}).isOk());
+
+ // Simulate the user taping ok.
+ ASSERT_TRUE(confirmator_->deliverSecureInputEvent(makeTestToken(TestModeCommands::CANCEL_EVENT))
+ .isOk());
+ // Wait for the callback.
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ ASSERT_EQ(IConfirmationUI::CANCELED, conf_cb->error_);
+
+ ASSERT_EQ(0U, conf_cb->confirmationToken_.size());
+ ASSERT_EQ(0U, conf_cb->formattedMessage_.size());
+}
+
+// Simulates the framework cancelling an ongoing prompt
+TEST_P(ConfirmationUIAidlTest, AbortTest) {
+ static constexpr char test_prompt[] = "Me first, gimme gimme!";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_TRUE(confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}).isOk());
+
+ confirmator_->abort();
+
+ // Wait for the callback.
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ ASSERT_EQ(IConfirmationUI::ABORTED, conf_cb->error_);
+ ASSERT_EQ(0U, conf_cb->confirmationToken_.size());
+ ASSERT_EQ(0U, conf_cb->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 100 'W' characters as required by
+// the design guidelines.
+TEST_P(ConfirmationUIAidlTest, PortableMessageTest1) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
+ "WWWWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_TRUE(confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}).isOk());
+
+ confirmator_->abort();
+
+ // Wait for the callback.
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ ASSERT_EQ(IConfirmationUI::ABORTED, conf_cb->error_);
+ ASSERT_EQ(0U, conf_cb->confirmationToken_.size());
+ ASSERT_EQ(0U, conf_cb->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 100 'W' characters as required by
+// the design guidelines in magnified mode.
+TEST_P(ConfirmationUIAidlTest, PortableMessageTest1Magnified) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
+ "WWWWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_TRUE(confirmator_
+ ->promptUserConfirmation(conf_cb, prompt_text, extra, "en",
+ {UIOption::ACCESSIBILITY_MAGNIFIED})
+ .isOk());
+
+ confirmator_->abort();
+
+ // Wait for the callback.
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ ASSERT_EQ(IConfirmationUI::ABORTED, conf_cb->error_);
+ ASSERT_EQ(0U, conf_cb->confirmationToken_.size());
+ ASSERT_EQ(0U, conf_cb->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 8 groups of 12 'W' characters as
+// required by the design guidelines.
+TEST_P(ConfirmationUIAidlTest, PortableMessageTest2) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW "
+ "WWWWWWWWWWWW WWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_TRUE(confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}).isOk());
+
+ confirmator_->abort();
+
+ // Wait for the callback.
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ ASSERT_EQ(IConfirmationUI::ABORTED, conf_cb->error_);
+ ASSERT_EQ(0U, conf_cb->confirmationToken_.size());
+ ASSERT_EQ(0U, conf_cb->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 8 groups of 12 'W' characters as
+// required by the design guidelines in magnified mode.
+TEST_P(ConfirmationUIAidlTest, PortableMessageTest2Magnified) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW "
+ "WWWWWWWWWWWW WWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_TRUE(confirmator_
+ ->promptUserConfirmation(conf_cb, prompt_text, extra, "en",
+ {UIOption::ACCESSIBILITY_MAGNIFIED})
+ .isOk());
+
+ confirmator_->abort();
+
+ // Wait for the callback.
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ ASSERT_EQ(IConfirmationUI::ABORTED, conf_cb->error_);
+ ASSERT_EQ(0U, conf_cb->confirmationToken_.size());
+ ASSERT_EQ(0U, conf_cb->formattedMessage_.size());
+}
+
+// Passing malformed UTF-8 to the confirmation UI
+// This test passes a string that ends in the middle of a multibyte character
+TEST_P(ConfirmationUIAidlTest, MalformedUTF8Test1) {
+ static constexpr char test_prompt[] = {char(0xc0), 0};
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ auto result = confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {});
+ ASSERT_EQ(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING, getReturnCode(result));
+}
+
+// Passing malformed UTF-8 to the confirmation UI
+// This test passes a string with a 5-byte character.
+TEST_P(ConfirmationUIAidlTest, MalformedUTF8Test2) {
+ static constexpr char test_prompt[] = {char(0xf8), char(0x82), char(0x82),
+ char(0x82), char(0x82), 0};
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ auto result = confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {});
+ ASSERT_EQ(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING, getReturnCode(result));
+}
+
+// Passing malformed UTF-8 to the confirmation UI
+// This test passes a string with a 2-byte character followed by a stray non UTF-8 character.
+TEST_P(ConfirmationUIAidlTest, MalformedUTF8Test3) {
+ static constexpr char test_prompt[] = {char(0xc0), char(0x82), char(0x83), 0};
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ shared_ptr<ConfirmationTestCallback> conf_cb =
+ ::ndk::SharedRefBase::make<ConfirmationTestCallback>(*this);
+ vector<uint8_t> prompt_text(test_prompt, test_prompt + strlen(test_prompt));
+ vector<uint8_t> extra(test_extra, test_extra + 3);
+ auto result = confirmator_->promptUserConfirmation(conf_cb, prompt_text, extra, "en", {});
+ ASSERT_EQ(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING, getReturnCode(result));
+}
+
+// Test the implementation of HMAC SHA 256 against a golden blob.
+TEST(ConfirmationUITestSelfTest, HMAC256SelfTest) {
+ const char key_str[32] = "keykeykeykeykeykeykeykeykeykeyk";
+ const uint8_t(&key)[32] = *reinterpret_cast<const uint8_t(*)[32]>(key_str);
+ auto expected = hex2str("2377fbcaa7fb3f6c20cfa1d9ebc60e9922cf58c909e25e300f3cb57f7805c886");
+ auto result = HMacer::hmac256(key, "value1", "value2", "value3");
+
+#ifdef DEBUG_CONFRIMATIONUI_UTILS_TEST
+ hexdump(std::cout, reinterpret_cast<const uint8_t*>(expected.data()), 32) << std::endl;
+ hexdump(std::cout, result.value().data(), 32) << std::endl;
+#endif
+
+ ByteBufferProxy expected_bytes(expected);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(expected, result.value());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ConfirmationUIAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, ConfirmationUIAidlTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IConfirmationUI::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+} // namespace aidl::android::hardware::confirmationui::test
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
new file mode 100644
index 0000000..286b65f
--- /dev/null
+++ b/fastboot/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1228891
+dvander@google.com
+elsk@google.com
+dhavale@google.com
diff --git a/fastboot/aidl/Android.bp b/fastboot/aidl/Android.bp
new file mode 100644
index 0000000..a5735ab
--- /dev/null
+++ b/fastboot/aidl/Android.bp
@@ -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 {
+ // 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.fastboot",
+ vendor_available: true,
+ recovery_available: true,
+ srcs: ["android/hardware/fastboot/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ enabled: false,
+ },
+ },
+}
diff --git a/fastboot/aidl/aidl_api/android.hardware.fastboot/current/android/hardware/fastboot/FileSystemType.aidl b/fastboot/aidl/aidl_api/android.hardware.fastboot/current/android/hardware/fastboot/FileSystemType.aidl
new file mode 100644
index 0000000..b15d037
--- /dev/null
+++ b/fastboot/aidl/aidl_api/android.hardware.fastboot/current/android/hardware/fastboot/FileSystemType.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.fastboot;
+@Backing(type="byte") @VintfStability
+enum FileSystemType {
+ EXT4 = 0,
+ F2FS = 1,
+ RAW = 2,
+}
diff --git a/fastboot/aidl/aidl_api/android.hardware.fastboot/current/android/hardware/fastboot/IFastboot.aidl b/fastboot/aidl/aidl_api/android.hardware.fastboot/current/android/hardware/fastboot/IFastboot.aidl
new file mode 100644
index 0000000..445fcde
--- /dev/null
+++ b/fastboot/aidl/aidl_api/android.hardware.fastboot/current/android/hardware/fastboot/IFastboot.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.fastboot;
+@VintfStability
+interface IFastboot {
+ String doOemCommand(in String oemCmd);
+ void doOemSpecificErase();
+ int getBatteryVoltageFlashingThreshold();
+ boolean getOffModeChargeState();
+ android.hardware.fastboot.FileSystemType getPartitionType(in String partitionName);
+ String getVariant();
+ const int FAILURE_UNKNOWN = 1;
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/fastboot/aidl/android/hardware/fastboot/FileSystemType.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to fastboot/aidl/android/hardware/fastboot/FileSystemType.aidl
index 4b82dd0..b4027ec 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/fastboot/aidl/android/hardware/fastboot/FileSystemType.aidl
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.fastboot;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+@VintfStability
+@Backing(type="byte")
+enum FileSystemType {
+ /**
+ * Fourth extended file system.
+ */
+ EXT4,
+ /**
+ * Flash Friendly File System.
+ */
+ F2FS,
+ /**
+ * Raw file system.
+ */
+ RAW,
+}
diff --git a/fastboot/aidl/android/hardware/fastboot/IFastboot.aidl b/fastboot/aidl/android/hardware/fastboot/IFastboot.aidl
new file mode 100644
index 0000000..abdc215
--- /dev/null
+++ b/fastboot/aidl/android/hardware/fastboot/IFastboot.aidl
@@ -0,0 +1,94 @@
+/*
+ * 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.fastboot;
+
+import android.hardware.fastboot.FileSystemType;
+
+/**
+ * IFastboot interface implements vendor specific fastboot commands.
+ */
+@VintfStability
+interface IFastboot {
+ /**
+ * Status code for function.
+ * Operation failed due to unknown reason.
+ */
+ const int FAILURE_UNKNOWN = 1;
+ /**
+ * Executes a fastboot OEM command.
+ *
+ * @param oemCmd The oem command that is passed to the fastboot HAL.
+ * @return optional String if the operation is successful and output is expected
+ * for the command.
+ * @throws :
+ * - EX_ILLEGAL_ARGUMENT for bad arguments.
+ * - EX_UNSUPPORTED_OPERATION for unsupported commands.
+ * - EX_SERVICE_SPECIFIC with status FAILURE_UNKNOWN for other errors.
+ */
+ String doOemCommand(in String oemCmd);
+
+ /**
+ * Executes an OEM specific erase after fastboot erase userdata.
+ *
+ * @throws :
+ * - EX_UNSUPPORTED_OPERATION if it is not supported.
+ * - EX_SERVICE_SPECIFIC with status FAILURE_UNKNOWN for
+ * unknown error in oem specific command or other errors.
+ */
+ void doOemSpecificErase();
+
+ /**
+ * Returns the minimum battery voltage required for flashing in mV.
+ *
+ * @return Minimum batterery voltage (in mV) required for flashing to
+ * be successful.
+ * @throws :
+ * - EX_SERVICE_SPECIFIC with status FAILURE_UNKNOWN if error.
+ */
+ int getBatteryVoltageFlashingThreshold();
+
+ /**
+ * Returns whether off-mode-charging is enabled. If enabled, the device
+ * autoboots into a special mode when power is applied.
+ *
+ * @return Returns whether off-mode-charging is enabled.
+ * @throws :
+ * - EX_SERVICE_SPECIFIC with status FAILURE_UNKNOWN if error.
+ */
+ boolean getOffModeChargeState();
+
+ /**
+ * Returns the file system type of the partition. Implementation is only
+ * required for physical partitions that need to be wiped and reformatted.
+ * @param in partitionName Name of the partition.
+ * @return Returns the file system type of the partition. Type can be ext4,
+ * f2fs or raw.
+ * @throws :
+ * - EX_SERVICE_SPECIFIC with status FAILURE_UNKNOWN if the partition
+ * is invalid or does not require reformatting.
+ */
+ FileSystemType getPartitionType(in String partitionName);
+
+ /**
+ * Returns an OEM-defined string indicating the variant of the device, for
+ * example, US and ROW.
+ * @return Indicates the device variant.
+ * @throws :
+ * - EX_SERVICE_SPECIFIC with status FAILURE_UNKNOWN if error.
+ */
+ String getVariant();
+}
diff --git a/fastboot/aidl/default/Android.bp b/fastboot/aidl/default/Android.bp
new file mode 100644
index 0000000..5cd4542
--- /dev/null
+++ b/fastboot/aidl/default/Android.bp
@@ -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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library {
+ name: "android.hardware.fastboot-impl-mock",
+ recovery: true,
+ srcs: [
+ "Fastboot.cpp",
+ ],
+ relative_install_path: "hw",
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libutils",
+ "libcutils",
+ "android.hardware.fastboot-V1-ndk",
+ ],
+}
diff --git a/fastboot/aidl/default/Fastboot.cpp b/fastboot/aidl/default/Fastboot.cpp
new file mode 100644
index 0000000..1ba73d3
--- /dev/null
+++ b/fastboot/aidl/default/Fastboot.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Fastboot.h"
+
+using ndk::ScopedAStatus;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace fastboot {
+
+ScopedAStatus Fastboot::getPartitionType(const std::string& in_partitionName,
+ FileSystemType* _aidl_return) {
+ if (in_partitionName.empty()) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid partition name");
+ }
+ *_aidl_return = FileSystemType::RAW;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Fastboot::doOemCommand(const std::string& in_oemCmd, std::string* _aidl_return) {
+ *_aidl_return = "";
+ if (in_oemCmd.empty()) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "Invalid command");
+ }
+ return ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_UNSUPPORTED_OPERATION, "Command not supported in default implementation");
+}
+
+ScopedAStatus Fastboot::getVariant(std::string* _aidl_return) {
+ *_aidl_return = "NA";
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Fastboot::getOffModeChargeState(bool* _aidl_return) {
+ *_aidl_return = false;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Fastboot::getBatteryVoltageFlashingThreshold(int32_t* _aidl_return) {
+ *_aidl_return = 0;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Fastboot::doOemSpecificErase() {
+ return ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_UNSUPPORTED_OPERATION, "Command not supported in default implementation");
+}
+
+} // namespace fastboot
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/fastboot/aidl/default/Fastboot.h b/fastboot/aidl/default/Fastboot.h
new file mode 100644
index 0000000..48e2c38
--- /dev/null
+++ b/fastboot/aidl/default/Fastboot.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "aidl/android/hardware/fastboot/BnFastboot.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace fastboot {
+class Fastboot : public BnFastboot {
+ ::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;
+};
+
+} // namespace fastboot
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/gatekeeper/1.0/vts/OWNERS b/gatekeeper/1.0/vts/OWNERS
index 738c710..ee2af97 100644
--- a/gatekeeper/1.0/vts/OWNERS
+++ b/gatekeeper/1.0/vts/OWNERS
@@ -1,3 +1,3 @@
-jdanis@google.com
+# Bug component: 1124862
swillden@google.com
guangzhu@google.com
diff --git a/gatekeeper/OWNERS b/gatekeeper/OWNERS
index d95b856..fddc2ff 100644
--- a/gatekeeper/OWNERS
+++ b/gatekeeper/OWNERS
@@ -1,2 +1,4 @@
+# Bug component: 1124862
swillden@google.com
guangzhu@google.com
+subrahmanyaman@google.com
diff --git a/gatekeeper/aidl/Android.bp b/gatekeeper/aidl/Android.bp
index 6b1bc7e..770e8ab 100644
--- a/gatekeeper/aidl/Android.bp
+++ b/gatekeeper/aidl/Android.bp
@@ -11,7 +11,7 @@
name: "android.hardware.gatekeeper",
vendor_available: true,
imports: [
- "android.hardware.security.keymint-V2",
+ "android.hardware.security.keymint-V3",
],
srcs: ["android/hardware/gatekeeper/*.aidl"],
stability: "vintf",
diff --git a/gnss/1.1/default/Android.bp b/gnss/1.1/default/Android.bp
index a73182e..300e8de 100644
--- a/gnss/1.1/default/Android.bp
+++ b/gnss/1.1/default/Android.bp
@@ -27,7 +27,7 @@
"android.hardware.gnss@2.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@1.0",
- "android.hardware.gnss-V2-ndk",
+ "android.hardware.gnss-V3-ndk",
],
static_libs: [
"android.hardware.gnss@common-default-lib",
diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp
index f8fad94..2414cbc 100644
--- a/gnss/1.1/vts/functional/Android.bp
+++ b/gnss/1.1/vts/functional/Android.bp
@@ -36,7 +36,7 @@
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
"android.hardware.gnss@common-vts-lib",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
],
shared_libs: [
"android.hardware.gnss.measurement_corrections@1.0",
diff --git a/gnss/2.0/default/Android.bp b/gnss/2.0/default/Android.bp
index 769e8ae..83bc2cc 100644
--- a/gnss/2.0/default/Android.bp
+++ b/gnss/2.0/default/Android.bp
@@ -50,7 +50,7 @@
"android.hardware.gnss@2.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@1.0",
- "android.hardware.gnss-V2-ndk",
+ "android.hardware.gnss-V3-ndk",
],
static_libs: [
"android.hardware.gnss@common-default-lib",
diff --git a/gnss/2.0/vts/functional/Android.bp b/gnss/2.0/vts/functional/Android.bp
index 2042dd9..e8db886 100644
--- a/gnss/2.0/vts/functional/Android.bp
+++ b/gnss/2.0/vts/functional/Android.bp
@@ -39,7 +39,7 @@
"android.hardware.gnss@2.0",
"android.hardware.gnss@2.1",
"android.hardware.gnss@common-vts-lib",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
],
test_suites: [
"general-tests",
diff --git a/gnss/2.1/default/Android.bp b/gnss/2.1/default/Android.bp
index 2979f5c..4a4ce54 100644
--- a/gnss/2.1/default/Android.bp
+++ b/gnss/2.1/default/Android.bp
@@ -44,7 +44,7 @@
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
- "android.hardware.gnss-V2-ndk",
+ "android.hardware.gnss-V3-ndk",
],
static_libs: [
"android.hardware.gnss@common-default-lib",
diff --git a/gnss/2.1/vts/functional/Android.bp b/gnss/2.1/vts/functional/Android.bp
index d7b6eeb..76f9d07 100644
--- a/gnss/2.1/vts/functional/Android.bp
+++ b/gnss/2.1/vts/functional/Android.bp
@@ -40,7 +40,7 @@
"android.hardware.gnss@2.0",
"android.hardware.gnss@2.1",
"android.hardware.gnss@common-vts-lib",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
],
shared_libs: [
"libvintf",
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
index 48c88f5..fd07a6e 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
@@ -45,6 +45,7 @@
void gnssSetSystemInfoCb(in android.hardware.gnss.IGnssCallback.GnssSystemInfo info);
void gnssRequestTimeCb();
void gnssRequestLocationCb(in boolean independentFromGnss, in boolean isUserEmergency);
+ void gnssSetSignalTypeCapabilitiesCb(in android.hardware.gnss.GnssSignalType[] gnssSignalTypes);
const int CAPABILITY_SCHEDULING = 1;
const int CAPABILITY_MSB = 2;
const int CAPABILITY_MSA = 4;
diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl
index f0b583d..aaafe7f 100644
--- a/gnss/aidl/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl
@@ -104,7 +104,7 @@
*
* The framework calls this method to instruct the GPS engine to prepare for serving requests
* from the framework. The GNSS HAL implementation must respond to all GNSS requests from the
- * framework upon successful return from this method until cleanup() method is called to
+ * framework upon successful return from this method until close() method is called to
* close this interface.
*
* @param callback Callback interface for IGnss.
diff --git a/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
index 8633bea..2b2592b 100644
--- a/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
@@ -18,6 +18,7 @@
import android.hardware.gnss.GnssConstellationType;
import android.hardware.gnss.GnssLocation;
+import android.hardware.gnss.GnssSignalType;
import android.hardware.gnss.IGnssConfiguration;
import android.hardware.gnss.IGnssPsds;
@@ -308,4 +309,18 @@
* during-call to E911, or up to 5 minutes after end-of-call or text to E911).
*/
void gnssRequestLocationCb(in boolean independentFromGnss, in boolean isUserEmergency);
+
+ /**
+ * Callback to inform the framework of the list of GnssSignalTypes the GNSS HAL implementation
+ * supports.
+ *
+ * These capabilities are static at runtime, i.e., they represent the signal types the
+ * GNSS implementation supports without considering the temporary disabled signal types such as
+ * the blocklisted satellites/constellations or the constellations disabled by regional
+ * restrictions.
+ *
+ * @param gnssSignalTypes a list of GnssSignalTypes specifying the constellations, carrier
+ * frequencies, and the code types the GNSS HAL implementation supports.
+ */
+ void gnssSetSignalTypeCapabilitiesCb(in GnssSignalType[] gnssSignalTypes);
}
diff --git a/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl b/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl
index 8733754..4316407 100644
--- a/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl
@@ -78,6 +78,9 @@
* output rate of 1Hz (occasional intra-measurement time offsets in the range from 0-2000msec
* can be tolerated.)
*
+ * If setCallback() is invoked without a previous close(), the HAL must use the new callback
+ * and parameters to provide updates.
+ *
* @param callback Handle to GnssMeasurement callback interface.
* @param enableFullTracking If true, GNSS chipset must switch off duty cycling. In such mode
* no clock discontinuities are expected and, when supported, carrier phase should be
@@ -104,6 +107,9 @@
/**
* Initializes the interface and registers the callback routines with the HAL.
*
+ * If setCallbackWithOptions() is invoked without a previous close(), the HAL must use the new
+ * callback and options to provide updates.
+ *
* @param options See Options definition.
*/
void setCallbackWithOptions(in IGnssMeasurementCallback callback, in Options options);
diff --git a/gnss/aidl/default/Android.bp b/gnss/aidl/default/Android.bp
index c8ae6b2..ca5a41f 100644
--- a/gnss/aidl/default/Android.bp
+++ b/gnss/aidl/default/Android.bp
@@ -50,7 +50,7 @@
"android.hardware.gnss.measurement_corrections@1.1",
"android.hardware.gnss.measurement_corrections@1.0",
"android.hardware.gnss.visibility_control@1.0",
- "android.hardware.gnss-V2-ndk",
+ "android.hardware.gnss-V3-ndk",
],
srcs: [
"AGnssRil.cpp",
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index cf2c90d..8a4d186 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -74,7 +74,21 @@
if (!status.isOk()) {
ALOGE("%s: Unable to invoke callback.gnssSetSystemInfoCb", __func__);
}
-
+ GnssSignalType signalType1 = {
+ .constellation = GnssConstellationType::GPS,
+ .carrierFrequencyHz = 1.59975e+09,
+ .codeType = GnssSignalType::CODE_TYPE_C,
+ };
+ GnssSignalType signalType2 = {
+ .constellation = GnssConstellationType::GLONASS,
+ .carrierFrequencyHz = 1.59975e+09,
+ .codeType = GnssSignalType::CODE_TYPE_C,
+ };
+ status = sGnssCallback->gnssSetSignalTypeCapabilitiesCb(
+ std::vector<GnssSignalType>({signalType1, signalType2}));
+ if (!status.isOk()) {
+ ALOGE("%s: Unable to invoke callback.gnssSetSignalTypeCapabilitiesCb", __func__);
+ }
return ScopedAStatus::ok();
}
diff --git a/gnss/aidl/default/gnss-default.xml b/gnss/aidl/default/gnss-default.xml
index 7449310..73b841e 100644
--- a/gnss/aidl/default/gnss-default.xml
+++ b/gnss/aidl/default/gnss-default.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.gnss</name>
- <version>2</version>
+ <version>3</version>
<interface>
<name>IGnss</name>
<instance>default</instance>
diff --git a/gnss/aidl/vts/Android.bp b/gnss/aidl/vts/Android.bp
index f02a41e..2a09a56 100644
--- a/gnss/aidl/vts/Android.bp
+++ b/gnss/aidl/vts/Android.bp
@@ -51,7 +51,7 @@
"libbinder",
],
static_libs: [
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
"android.hardware.gnss@common-vts-lib",
],
test_suites: [
diff --git a/gnss/aidl/vts/GnssCallbackAidl.cpp b/gnss/aidl/vts/GnssCallbackAidl.cpp
index 2f6128b..d3be414 100644
--- a/gnss/aidl/vts/GnssCallbackAidl.cpp
+++ b/gnss/aidl/vts/GnssCallbackAidl.cpp
@@ -23,6 +23,7 @@
using android::hardware::gnss::GnssLocation;
using GnssSvInfo = android::hardware::gnss::IGnssCallback::GnssSvInfo;
using GnssSystemInfo = android::hardware::gnss::IGnssCallback::GnssSystemInfo;
+using GnssSignalType = android::hardware::gnss::GnssSignalType;
Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
ALOGI("Capabilities received %#08x", capabilities);
@@ -30,6 +31,20 @@
return Status::ok();
}
+Status GnssCallbackAidl::gnssSetSignalTypeCapabilitiesCb(
+ const std::vector<GnssSignalType>& signalTypes) {
+ ALOGI("SignalTypeCapabilities received");
+ std::ostringstream ss;
+ for (auto& signalType : signalTypes) {
+ ss << "[constellation=" << (int)signalType.constellation
+ << ", carrierFrequencyHz=" << signalType.carrierFrequencyHz
+ << ", codeType=" << signalType.codeType << "], ";
+ }
+ ALOGI("%s", ss.str().c_str());
+ signal_type_capabilities_cbq_.store(signalTypes);
+ return Status::ok();
+}
+
Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue /* status */) {
ALOGI("gnssStatusCb");
return Status::ok();
diff --git a/gnss/aidl/vts/GnssCallbackAidl.h b/gnss/aidl/vts/GnssCallbackAidl.h
index a9495ba..06526d3 100644
--- a/gnss/aidl/vts/GnssCallbackAidl.h
+++ b/gnss/aidl/vts/GnssCallbackAidl.h
@@ -25,6 +25,7 @@
public:
GnssCallbackAidl()
: capabilities_cbq_("capabilities"),
+ signal_type_capabilities_cbq_("signal_type_capabilities"),
info_cbq_("system_info"),
location_cbq_("location"),
sv_info_list_cbq_("sv_info"),
@@ -32,6 +33,8 @@
~GnssCallbackAidl(){};
android::binder::Status gnssSetCapabilitiesCb(const int capabilities) override;
+ android::binder::Status gnssSetSignalTypeCapabilitiesCb(
+ const std::vector<android::hardware::gnss::GnssSignalType>& signalTypes) override;
android::binder::Status gnssStatusCb(const GnssStatusValue status) override;
android::binder::Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
android::binder::Status gnssLocationCb(
@@ -45,11 +48,15 @@
const bool isUserEmergency) override;
int last_capabilities_;
+ std::vector<android::hardware::gnss::GnssSignalType> last_signal_type_capabilities;
android::hardware::gnss::IGnssCallback::GnssSystemInfo last_info_;
android::hardware::gnss::GnssLocation last_location_;
android::hardware::gnss::common::GnssCallbackEventQueue<int> capabilities_cbq_;
android::hardware::gnss::common::GnssCallbackEventQueue<
+ std::vector<android::hardware::gnss::GnssSignalType>>
+ signal_type_capabilities_cbq_;
+ android::hardware::gnss::common::GnssCallbackEventQueue<
android::hardware::gnss::IGnssCallback::GnssSystemInfo>
info_cbq_;
android::hardware::gnss::common::GnssCallbackEventQueue<android::hardware::gnss::GnssLocation>
diff --git a/gnss/aidl/vts/gnss_hal_test.cpp b/gnss/aidl/vts/gnss_hal_test.cpp
index 0e1218e..91cd917 100644
--- a/gnss/aidl/vts/gnss_hal_test.cpp
+++ b/gnss/aidl/vts/gnss_hal_test.cpp
@@ -105,6 +105,26 @@
EXPECT_TRUE(aidl_gnss_cb_->info_cbq_.retrieve(aidl_gnss_cb_->last_info_, TIMEOUT_SEC));
EXPECT_EQ(aidl_gnss_cb_->info_cbq_.calledCount(), 1);
}
+
+ /*
+ * SignalTypeCapabilities callback should trigger.
+ */
+ if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
+ EXPECT_TRUE(aidl_gnss_cb_->signal_type_capabilities_cbq_.retrieve(
+ aidl_gnss_cb_->last_signal_type_capabilities, TIMEOUT_SEC));
+ EXPECT_EQ(aidl_gnss_cb_->signal_type_capabilities_cbq_.calledCount(), 1);
+ }
+}
+
+void GnssHalTest::TearDown() {
+ GnssHalTestTemplate<IGnss_V2_1>::TearDown();
+ if (aidl_gnss_hal_ != nullptr) {
+ aidl_gnss_hal_->close();
+ aidl_gnss_hal_ = nullptr;
+ }
+
+ // Set to nullptr to destruct the callback event queues and warn of any unprocessed events.
+ aidl_gnss_cb_ = nullptr;
}
void GnssHalTest::CheckLocation(const GnssLocation& location, bool check_speed) {
diff --git a/gnss/aidl/vts/gnss_hal_test.h b/gnss/aidl/vts/gnss_hal_test.h
index 645fc82..c49c1b9 100644
--- a/gnss/aidl/vts/gnss_hal_test.h
+++ b/gnss/aidl/vts/gnss_hal_test.h
@@ -64,6 +64,7 @@
virtual void SetUp() override;
virtual void SetUpGnssCallback() override;
+ virtual void TearDown() override;
void CheckLocation(const android::hardware::gnss::GnssLocation& location,
const bool check_speed);
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 3696233..f1a9894 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -1494,3 +1494,32 @@
assertMeanAndStdev(locationIntervalMs, deltas);
}
}
+
+TEST_P(GnssHalTest, TestGnssMeasurementSetCallback) {
+ if (aidl_gnss_hal_->getInterfaceVersion() <= 2) {
+ return;
+ }
+
+ sp<IGnssMeasurementInterface> iGnssMeasurement;
+ auto status = aidl_gnss_hal_->getExtensionGnssMeasurement(&iGnssMeasurement);
+ ASSERT_TRUE(status.isOk());
+ ASSERT_TRUE(iGnssMeasurement != nullptr);
+
+ ALOGD("TestGnssMeasurementSetCallback");
+ auto callback = sp<GnssMeasurementCallbackAidl>::make();
+ std::vector<int> deltas;
+
+ // setCallback at 20s interval and wait for 1 measurement
+ startMeasurementWithInterval(20000, iGnssMeasurement, callback);
+ collectMeasurementIntervals(callback, /* numEvents= */ 1, /* timeoutSeconds= */ 10, deltas);
+
+ // setCallback at 1s interval and wait for 5 measurements
+ startMeasurementWithInterval(1000, iGnssMeasurement, callback);
+ collectMeasurementIntervals(callback, /* numEvents= */ 5, /* timeoutSeconds= */ 10, deltas);
+
+ // verify the measurements were received at 1Hz
+ assertMeanAndStdev(1000, deltas);
+
+ status = iGnssMeasurement->close();
+ ASSERT_TRUE(status.isOk());
+}
diff --git a/gnss/common/utils/default/Android.bp b/gnss/common/utils/default/Android.bp
index b896f04..4cf17a6 100644
--- a/gnss/common/utils/default/Android.bp
+++ b/gnss/common/utils/default/Android.bp
@@ -57,6 +57,6 @@
"android.hardware.gnss@2.1",
"android.hardware.gnss.measurement_corrections@1.1",
"android.hardware.gnss.measurement_corrections@1.0",
- "android.hardware.gnss-V2-ndk",
+ "android.hardware.gnss-V3-ndk",
],
}
diff --git a/gnss/common/utils/vts/Android.bp b/gnss/common/utils/vts/Android.bp
index f92e609..b5325b2 100644
--- a/gnss/common/utils/vts/Android.bp
+++ b/gnss/common/utils/vts/Android.bp
@@ -44,7 +44,7 @@
"android.hardware.gnss@2.1",
"android.hardware.gnss.measurement_corrections@1.0",
"android.hardware.gnss.measurement_corrections@1.1",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
],
static_libs: [
"libgtest",
diff --git a/graphics/Android.bp b/graphics/Android.bp
index b48844d..b17643e 100644
--- a/graphics/Android.bp
+++ b/graphics/Android.bp
@@ -47,13 +47,13 @@
cc_defaults {
name: "android.hardware.graphics.composer3-ndk_static",
static_libs: [
- "android.hardware.graphics.composer3-V1-ndk",
+ "android.hardware.graphics.composer3-V2-ndk",
],
}
cc_defaults {
name: "android.hardware.graphics.composer3-ndk_shared",
shared_libs: [
- "android.hardware.graphics.composer3-V1-ndk",
+ "android.hardware.graphics.composer3-V2-ndk",
],
}
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
index b49f239..a7e6535 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -75,6 +75,7 @@
void setReadbackBuffer(long display, in android.hardware.common.NativeHandle buffer, in @nullable ParcelFileDescriptor releaseFence);
void setVsyncEnabled(long display, boolean enabled);
void setIdleTimerEnabled(long display, int timeoutMs);
+ android.hardware.graphics.composer3.OverlayProperties getOverlaySupport();
const int EX_BAD_CONFIG = 1;
const int EX_BAD_DISPLAY = 2;
const int EX_BAD_LAYER = 3;
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/OverlayProperties.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/OverlayProperties.aidl
new file mode 100644
index 0000000..4d8fcac
--- /dev/null
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/OverlayProperties.aidl
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.graphics.composer3;
+@VintfStability
+parcelable OverlayProperties {
+ android.hardware.graphics.composer3.SupportedBufferCombinations[] combinations;
+}
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/SupportedBufferCombinations.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/SupportedBufferCombinations.aidl
new file mode 100644
index 0000000..1828be1
--- /dev/null
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/SupportedBufferCombinations.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.graphics.composer3;
+@VintfStability
+parcelable SupportedBufferCombinations {
+ android.hardware.graphics.common.PixelFormat[] pixelFormats;
+ android.hardware.graphics.common.Dataspace[] dataspaces;
+}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
index a4ea64f..88bb3a4 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -32,6 +32,7 @@
import android.hardware.graphics.composer3.FormatColorComponent;
import android.hardware.graphics.composer3.HdrCapabilities;
import android.hardware.graphics.composer3.IComposerCallback;
+import android.hardware.graphics.composer3.OverlayProperties;
import android.hardware.graphics.composer3.PerFrameMetadataKey;
import android.hardware.graphics.composer3.PowerMode;
import android.hardware.graphics.composer3.ReadbackBufferAttributes;
@@ -814,4 +815,14 @@
*
*/
void setIdleTimerEnabled(long display, int timeoutMs);
+
+ /**
+ * Hardware overlays is a technique to composite different buffers directly to the screen
+ * while bypassing GPU composition.
+ *
+ * This function returns what the device's overlays support.
+ *
+ * @return the overlay properties of the device.
+ */
+ OverlayProperties getOverlaySupport();
}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/OverlayProperties.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/OverlayProperties.aidl
new file mode 100644
index 0000000..d3bd7d3
--- /dev/null
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/OverlayProperties.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.graphics.composer3;
+
+import android.hardware.graphics.composer3.SupportedBufferCombinations;
+
+@VintfStability
+parcelable OverlayProperties {
+ // Array of all valid pixelformat and dataspace combinations.
+ // If all supported formats work with all supported dataspaces,
+ // then this list may only have 1 entry.
+ // If some dataspaces, e.g. scRGB, only work with specific formats,
+ // then this list may contain more than 1 entry.
+ SupportedBufferCombinations[] combinations;
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/graphics/composer/aidl/android/hardware/graphics/composer3/SupportedBufferCombinations.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to graphics/composer/aidl/android/hardware/graphics/composer3/SupportedBufferCombinations.aidl
index 4b82dd0..41f8817 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/SupportedBufferCombinations.aidl
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,18 +14,12 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.graphics.composer3;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+@VintfStability
+parcelable SupportedBufferCombinations {
+ // List of pixelformats and dataspaces that can be used together.
+ // All pixelformats and dataspaces stored inside are valid combinations.
+ android.hardware.graphics.common.PixelFormat[] pixelFormats;
+ android.hardware.graphics.common.Dataspace[] dataspaces;
+}
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.cpp b/graphics/composer/aidl/vts/VtsComposerClient.cpp
index 5bc7296..34cc802 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.cpp
+++ b/graphics/composer/aidl/vts/VtsComposerClient.cpp
@@ -338,6 +338,11 @@
outDisplayOrientation};
}
+std::pair<ScopedAStatus, composer3::OverlayProperties> VtsComposerClient::getOverlaySupport() {
+ OverlayProperties properties;
+ return {mComposerClient->getOverlaySupport(&properties), properties};
+}
+
ScopedAStatus VtsComposerClient::setIdleTimerEnabled(int64_t display, int32_t timeoutMs) {
return mComposerClient->setIdleTimerEnabled(display, timeoutMs);
}
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.h b/graphics/composer/aidl/vts/VtsComposerClient.h
index 3625c8c..6358b85 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.h
+++ b/graphics/composer/aidl/vts/VtsComposerClient.h
@@ -172,6 +172,8 @@
std::pair<ScopedAStatus, std::vector<VtsDisplay>> getDisplays();
+ std::pair<ScopedAStatus, OverlayProperties> getOverlaySupport();
+
private:
ScopedAStatus updateDisplayProperties(VtsDisplay* vtsDisplay, int32_t config);
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 93b646f..78dce63 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/graphics/common/BlendMode.h>
@@ -808,6 +809,18 @@
EXPECT_TRUE(status.isOk());
}
+// TODO(b/250036572): disable this due to no implementation and revup on cuttlefish
+TEST_P(GraphicsComposerAidlTest, DISABLED_GetOverlaySupport) {
+ const auto& [status, _] = mComposerClient->getOverlaySupport();
+ if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC &&
+ status.getServiceSpecificError() == IComposerClient::EX_UNSUPPORTED) {
+ GTEST_SUCCEED() << "getOverlaySupport is not supported";
+ return;
+ }
+
+ ASSERT_TRUE(status.isOk());
+}
+
TEST_P(GraphicsComposerAidlTest, GetDisplayPhysicalOrientation_BadDisplay) {
const auto& [status, _] = mComposerClient->getDisplayPhysicalOrientation(getInvalidDisplayId());
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
index 57451ed..9f2e498 100644
--- a/identity/aidl/Android.bp
+++ b/identity/aidl/Android.bp
@@ -14,8 +14,8 @@
"android/hardware/identity/*.aidl",
],
imports: [
- "android.hardware.keymaster",
- "android.hardware.security.keymint",
+ "android.hardware.keymaster-V3",
+ "android.hardware.security.keymint-V3",
],
stability: "vintf",
backend: {
@@ -66,20 +66,20 @@
cc_defaults {
name: "identity_use_latest_hal_aidl_ndk_static",
static_libs: [
- "android.hardware.identity-V4-ndk",
+ "android.hardware.identity-V5-ndk",
],
}
cc_defaults {
name: "identity_use_latest_hal_aidl_ndk_shared",
shared_libs: [
- "android.hardware.identity-V4-ndk",
+ "android.hardware.identity-V5-ndk",
],
}
cc_defaults {
name: "identity_use_latest_hal_aidl_cpp_static",
static_libs: [
- "android.hardware.identity-V4-cpp",
+ "android.hardware.identity-V5-cpp",
],
}
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index 31ab400..a57875a 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -10,6 +10,10 @@
cc_library_static {
name: "android.hardware.identity-libeic-hal-common",
vendor_available: true,
+ defaults: [
+ "identity_use_latest_hal_aidl_ndk_static",
+ "keymint_use_latest_hal_aidl_ndk_static",
+ ],
srcs: [
"common/IdentityCredential.cpp",
"common/IdentityCredentialStore.cpp",
@@ -40,9 +44,7 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-V4-ndk",
"android.hardware.keymaster-V3-ndk",
- "android.hardware.security.keymint-V2-ndk",
],
}
@@ -83,6 +85,7 @@
vintf_fragments: ["identity-default.xml"],
vendor: true,
defaults: [
+ "identity_use_latest_hal_aidl_ndk_static",
"keymint_use_latest_hal_aidl_ndk_static",
],
cflags: [
@@ -106,7 +109,6 @@
"libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
- "android.hardware.identity-V4-ndk",
"android.hardware.keymaster-V3-ndk",
"android.hardware.identity-libeic-hal-common",
"android.hardware.identity-libeic-library",
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 51ab110..dd29819 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -11,6 +11,7 @@
name: "VtsHalIdentityTargetTest",
defaults: [
"VtsHalTargetTestDefaults",
+ "identity_use_latest_hal_aidl_cpp_static",
"keymint_use_latest_hal_aidl_cpp_static",
"keymint_use_latest_hal_aidl_ndk_static",
"use_libaidlvintf_gtest_helper_static",
@@ -46,7 +47,6 @@
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
"android.hardware.identity-support-lib",
- "android.hardware.identity-V4-cpp",
"android.hardware.keymaster-V3-cpp",
"android.hardware.keymaster-V3-ndk",
"libkeymaster4support",
diff --git a/input/common/aidl/Android.bp b/input/common/aidl/Android.bp
index 390d8b5..95a14b2 100644
--- a/input/common/aidl/Android.bp
+++ b/input/common/aidl/Android.bp
@@ -9,6 +9,7 @@
aidl_interface {
name: "android.hardware.input.common",
+ host_supported: true,
vendor_available: true,
srcs: ["android/hardware/input/common/*.aidl"],
stability: "vintf",
diff --git a/input/processor/aidl/Android.bp b/input/processor/aidl/Android.bp
index 354816e..773bb49 100644
--- a/input/processor/aidl/Android.bp
+++ b/input/processor/aidl/Android.bp
@@ -9,6 +9,7 @@
aidl_interface {
name: "android.hardware.input.processor",
+ host_supported: true,
vendor_available: true,
srcs: ["android/hardware/input/processor/*.aidl"],
imports: [
diff --git a/keymaster/3.0/default/OWNERS b/keymaster/3.0/default/OWNERS
deleted file mode 100644
index 335660d..0000000
--- a/keymaster/3.0/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-jdanis@google.com
-swillden@google.com
diff --git a/keymaster/3.0/vts/OWNERS b/keymaster/3.0/vts/OWNERS
deleted file mode 100644
index 846bb84..0000000
--- a/keymaster/3.0/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-drysdale@google.com
-jdanis@google.com
-swillden@google.com
-yim@google.com
-yuexima@google.com
diff --git a/keymaster/3.0/vts/functional/OWNERS b/keymaster/3.0/vts/functional/OWNERS
deleted file mode 100644
index 2ef9086..0000000
--- a/keymaster/3.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 189335
-swillden@google.com
diff --git a/keymaster/4.0/default/OWNERS b/keymaster/4.0/default/OWNERS
deleted file mode 100644
index 335660d..0000000
--- a/keymaster/4.0/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-jdanis@google.com
-swillden@google.com
diff --git a/keymaster/4.0/support/OWNERS b/keymaster/4.0/support/OWNERS
deleted file mode 100644
index a9efe66..0000000
--- a/keymaster/4.0/support/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-jdanis@google.com
-swillden@google.com
-jbires@google.com
diff --git a/keymaster/4.0/vts/OWNERS b/keymaster/4.0/vts/OWNERS
deleted file mode 100644
index 0d6fa6c..0000000
--- a/keymaster/4.0/vts/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-drysdale@google.com
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-yim@google.com
-yuexima@google.com
diff --git a/keymaster/4.0/vts/functional/OWNERS b/keymaster/4.0/vts/functional/OWNERS
deleted file mode 100644
index 2ef9086..0000000
--- a/keymaster/4.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 189335
-swillden@google.com
diff --git a/keymaster/4.1/default/OWNERS b/keymaster/4.1/default/OWNERS
deleted file mode 100644
index 2b2ad2a..0000000
--- a/keymaster/4.1/default/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/support/OWNERS b/keymaster/4.1/support/OWNERS
deleted file mode 100644
index 2b2ad2a..0000000
--- a/keymaster/4.1/support/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/vts/OWNERS b/keymaster/4.1/vts/OWNERS
deleted file mode 100644
index 24ed042..0000000
--- a/keymaster/4.1/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-drysdale@google.com
-jbires@google.com
-jdanis@google.com
-swillden@google.com
-zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/vts/functional/OWNERS b/keymaster/4.1/vts/functional/OWNERS
deleted file mode 100644
index 2ef9086..0000000
--- a/keymaster/4.1/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 189335
-swillden@google.com
diff --git a/keymaster/OWNERS b/keymaster/OWNERS
new file mode 100644
index 0000000..ac8bd83
--- /dev/null
+++ b/keymaster/OWNERS
@@ -0,0 +1,14 @@
+# Bug component: 1084733
+
+# Please assign all bugs related to /hardware/interfaces/keymaster to the team alias:
+#
+# android-hardware-security@google.com
+#
+# This will get them auto-assigned to the on-call triage engineer, ensuring quickest response.
+
+drysdale@google.com
+eranm@google.com
+hasinitg@google.com
+jbires@google.com
+swillden@google.com
+zeuthen@google.com
diff --git a/power/TEST_MAPPING b/power/TEST_MAPPING
new file mode 100644
index 0000000..c0963f9
--- /dev/null
+++ b/power/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "VtsHalPowerTargetTest"
+ }
+ ]
+}
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPowerHintSession.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPowerHintSession.aidl
index 1d3ecb7..928668c 100644
--- a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPowerHintSession.aidl
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/IPowerHintSession.aidl
@@ -39,4 +39,5 @@
oneway void pause();
oneway void resume();
oneway void close();
+ oneway void sendHint(android.hardware.power.SessionHint hint);
}
diff --git a/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/SessionHint.aidl b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/SessionHint.aidl
new file mode 100644
index 0000000..9c1f381
--- /dev/null
+++ b/power/aidl/aidl_api/android.hardware.power/current/android/hardware/power/SessionHint.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.power;
+@Backing(type="int") @VintfStability
+enum SessionHint {
+ CPU_LOAD_UP = 0,
+ CPU_LOAD_DOWN = 1,
+ CPU_LOAD_RESET = 2,
+ CPU_LOAD_RESUME = 3,
+ POWER_EFFICIENCY = 4,
+}
diff --git a/power/aidl/android/hardware/power/IPowerHintSession.aidl b/power/aidl/android/hardware/power/IPowerHintSession.aidl
index c289448..4ca9c54 100644
--- a/power/aidl/android/hardware/power/IPowerHintSession.aidl
+++ b/power/aidl/android/hardware/power/IPowerHintSession.aidl
@@ -16,6 +16,7 @@
package android.hardware.power;
+import android.hardware.power.SessionHint;
import android.hardware.power.WorkDuration;
@VintfStability
@@ -56,4 +57,12 @@
* Close the session to release resources.
*/
void close();
+
+ /**
+ * Gives information to the PowerHintSession about upcoming or unexpected
+ * changes in load to supplement the normal updateTarget/reportActual cycle.
+ *
+ * @param hint The hint to provide to the PowerHintSession
+ */
+ void sendHint(SessionHint hint);
}
diff --git a/power/aidl/android/hardware/power/SessionHint.aidl b/power/aidl/android/hardware/power/SessionHint.aidl
new file mode 100644
index 0000000..a172e12
--- /dev/null
+++ b/power/aidl/android/hardware/power/SessionHint.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.power;
+
+@VintfStability
+@Backing(type="int")
+enum SessionHint {
+ /**
+ * This hint indicates an increase in CPU workload intensity. It means that
+ * this hint session needs extra CPU resources to meet the target duration.
+ * This hint must be sent before reporting the actual duration to the session.
+ */
+ CPU_LOAD_UP = 0,
+
+ /**
+ * This hint indicates a decrease in CPU workload intensity. It means that
+ * this hint session can reduce CPU resources and still meet the target duration.
+ */
+ CPU_LOAD_DOWN = 1,
+
+ /**
+ * This hint indicates an upcoming CPU workload that is completely changed and
+ * unknown. It means that the hint session should reset CPU resources to a known
+ * baseline to prepare for an arbitrary load, and must wake up if inactive.
+ */
+ CPU_LOAD_RESET = 2,
+
+ /**
+ * This hint indicates that the most recent CPU workload is resuming after a
+ * period of inactivity. It means that the hint session should allocate similar
+ * CPU resources to what was used previously, and must wake up if inactive.
+ */
+ CPU_LOAD_RESUME = 3,
+
+ /**
+ * This hint indicates that this power hint session should be applied with a
+ * power-efficient-first scheduling strategy. This means the work of this
+ * power hint session is noncritical despite its CPU intensity.
+ */
+ POWER_EFFICIENCY = 4,
+}
diff --git a/power/aidl/default/Android.bp b/power/aidl/default/Android.bp
index 223b9d5..da91ee6 100644
--- a/power/aidl/default/Android.bp
+++ b/power/aidl/default/Android.bp
@@ -30,11 +30,12 @@
shared_libs: [
"libbase",
"libbinder_ndk",
- "android.hardware.power-V3-ndk",
+ "android.hardware.power-V4-ndk",
],
srcs: [
"main.cpp",
"Power.cpp",
+ "PowerHintSession.cpp",
],
}
diff --git a/power/aidl/default/Power.cpp b/power/aidl/default/Power.cpp
index 7f08f44..8fe370c 100644
--- a/power/aidl/default/Power.cpp
+++ b/power/aidl/default/Power.cpp
@@ -15,6 +15,7 @@
*/
#include "Power.h"
+#include "PowerHintSession.h"
#include <android-base/logging.h>
@@ -25,42 +26,53 @@
namespace impl {
namespace example {
+using namespace std::chrono_literals;
+
+using ndk::ScopedAStatus;
+
const std::vector<Boost> BOOST_RANGE{ndk::enum_range<Boost>().begin(),
ndk::enum_range<Boost>().end()};
const std::vector<Mode> MODE_RANGE{ndk::enum_range<Mode>().begin(), ndk::enum_range<Mode>().end()};
-ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
+ScopedAStatus Power::setMode(Mode type, bool enabled) {
LOG(VERBOSE) << "Power setMode: " << static_cast<int32_t>(type) << " to: " << enabled;
- return ndk::ScopedAStatus::ok();
+ return ScopedAStatus::ok();
}
-ndk::ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
+ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
LOG(INFO) << "Power isModeSupported: " << static_cast<int32_t>(type);
*_aidl_return = type >= MODE_RANGE.front() && type <= MODE_RANGE.back();
- return ndk::ScopedAStatus::ok();
+ return ScopedAStatus::ok();
}
-ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
+ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
LOG(VERBOSE) << "Power setBoost: " << static_cast<int32_t>(type)
<< ", duration: " << durationMs;
- return ndk::ScopedAStatus::ok();
+ return ScopedAStatus::ok();
}
-ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool* _aidl_return) {
+ScopedAStatus Power::isBoostSupported(Boost type, bool* _aidl_return) {
LOG(INFO) << "Power isBoostSupported: " << static_cast<int32_t>(type);
*_aidl_return = type >= BOOST_RANGE.front() && type <= BOOST_RANGE.back();
- return ndk::ScopedAStatus::ok();
+ return ScopedAStatus::ok();
}
-ndk::ScopedAStatus Power::createHintSession(int32_t, int32_t, const std::vector<int32_t>&, int64_t,
- std::shared_ptr<IPowerHintSession>* _aidl_return) {
- *_aidl_return = nullptr;
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus Power::createHintSession(int32_t, int32_t, const std::vector<int32_t>& tids, int64_t,
+ std::shared_ptr<IPowerHintSession>* _aidl_return) {
+ if (tids.size() == 0) {
+ *_aidl_return = nullptr;
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ std::shared_ptr<IPowerHintSession> powerHintSession =
+ ndk::SharedRefBase::make<PowerHintSession>();
+ mPowerHintSessions.push_back(powerHintSession);
+ *_aidl_return = powerHintSession;
+ return ScopedAStatus::ok();
}
-ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t* outNanoseconds) {
- *outNanoseconds = -1;
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus Power::getHintSessionPreferredRate(int64_t* outNanoseconds) {
+ *outNanoseconds = std::chrono::nanoseconds(1ms).count();
+ return ScopedAStatus::ok();
}
} // namespace example
diff --git a/power/aidl/default/Power.h b/power/aidl/default/Power.h
index ef6439d..7f8405e 100644
--- a/power/aidl/default/Power.h
+++ b/power/aidl/default/Power.h
@@ -26,6 +26,7 @@
namespace example {
class Power : public BnPower {
+ public:
ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
ndk::ScopedAStatus isModeSupported(Mode type, bool* _aidl_return) override;
ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
@@ -35,6 +36,9 @@
int64_t durationNanos,
std::shared_ptr<IPowerHintSession>* _aidl_return) override;
ndk::ScopedAStatus getHintSessionPreferredRate(int64_t* outNanoseconds) override;
+
+ private:
+ std::vector<std::shared_ptr<IPowerHintSession>> mPowerHintSessions;
};
} // namespace example
diff --git a/power/aidl/default/PowerHintSession.cpp b/power/aidl/default/PowerHintSession.cpp
new file mode 100644
index 0000000..aa95be4
--- /dev/null
+++ b/power/aidl/default/PowerHintSession.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "PowerHintSession.h"
+
+#include <android-base/logging.h>
+
+namespace aidl::android::hardware::power::impl::example {
+
+using ndk::ScopedAStatus;
+
+PowerHintSession::PowerHintSession() {}
+
+ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
+ LOG(VERBOSE) << __func__ << "target duration in nanoseconds: " << targetDurationNanos;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::reportActualWorkDuration(
+ const std::vector<WorkDuration>& /* durations */) {
+ LOG(VERBOSE) << __func__;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::pause() {
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::resume() {
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::close() {
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus PowerHintSession::sendHint(SessionHint /* hint */) {
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::power::impl::example
diff --git a/power/aidl/default/PowerHintSession.h b/power/aidl/default/PowerHintSession.h
new file mode 100644
index 0000000..6a7627a
--- /dev/null
+++ b/power/aidl/default/PowerHintSession.h
@@ -0,0 +1,37 @@
+/*
+ * 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/power/BnPowerHintSession.h>
+#include <aidl/android/hardware/power/SessionHint.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
+
+namespace aidl::android::hardware::power::impl::example {
+
+class PowerHintSession : public BnPowerHintSession {
+ public:
+ explicit PowerHintSession();
+ ndk::ScopedAStatus updateTargetWorkDuration(int64_t targetDurationNanos) override;
+ ndk::ScopedAStatus reportActualWorkDuration(
+ const std::vector<WorkDuration>& durations) override;
+ ndk::ScopedAStatus pause() override;
+ ndk::ScopedAStatus resume() override;
+ ndk::ScopedAStatus close() override;
+ ndk::ScopedAStatus sendHint(SessionHint hint) override;
+};
+
+} // namespace aidl::android::hardware::power::impl::example
diff --git a/power/aidl/default/power-default.xml b/power/aidl/default/power-default.xml
index 927ba22..f5dd6b9 100644
--- a/power/aidl/default/power-default.xml
+++ b/power/aidl/default/power-default.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.power</name>
- <version>3</version>
+ <version>4</version>
<fqname>IPower/default</fqname>
</hal>
</manifest>
diff --git a/power/aidl/vts/Android.bp b/power/aidl/vts/Android.bp
index ea398ac..56c98bd 100644
--- a/power/aidl/vts/Android.bp
+++ b/power/aidl/vts/Android.bp
@@ -32,9 +32,10 @@
"libbinder_ndk",
],
static_libs: [
- "android.hardware.power-V3-ndk",
+ "android.hardware.power-V4-ndk",
],
test_suites: [
+ "general-tests",
"vts",
],
}
diff --git a/power/aidl/vts/VtsHalPowerTargetTest.cpp b/power/aidl/vts/VtsHalPowerTargetTest.cpp
index 2cfa04a..e51f756 100644
--- a/power/aidl/vts/VtsHalPowerTargetTest.cpp
+++ b/power/aidl/vts/VtsHalPowerTargetTest.cpp
@@ -34,12 +34,16 @@
using android::hardware::power::IPower;
using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
+using android::hardware::power::SessionHint;
using android::hardware::power::WorkDuration;
const std::vector<Boost> kBoosts{ndk::enum_range<Boost>().begin(), ndk::enum_range<Boost>().end()};
const std::vector<Mode> kModes{ndk::enum_range<Mode>().begin(), ndk::enum_range<Mode>().end()};
+const std::vector<SessionHint> kSessionHints{ndk::enum_range<SessionHint>().begin(),
+ ndk::enum_range<SessionHint>().end()};
+
const std::vector<Boost> kInvalidBoosts = {
static_cast<Boost>(static_cast<int32_t>(kBoosts.front()) - 1),
static_cast<Boost>(static_cast<int32_t>(kBoosts.back()) + 1),
@@ -50,6 +54,11 @@
static_cast<Mode>(static_cast<int32_t>(kModes.back()) + 1),
};
+const std::vector<SessionHint> kInvalidSessionHints = {
+ static_cast<SessionHint>(static_cast<int32_t>(kSessionHints.front()) - 1),
+ static_cast<SessionHint>(static_cast<int32_t>(kSessionHints.back()) + 1),
+};
+
class DurationWrapper : public WorkDuration {
public:
DurationWrapper(int64_t dur, int64_t time) {
@@ -83,6 +92,14 @@
DurationWrapper(1000000000L, 4L),
};
+// DEVICEs launching with Android 11 MUST meet the requirements for the
+// target-level=5 compatibility_matrix file.
+const uint64_t kCompatibilityMatrix5ApiLevel = 30;
+
+// DEVICEs launching with Android 13 MUST meet the requirements for the
+// target-level=7 compatibility_matrix file.
+const uint64_t kCompatibilityMatrix7ApiLevel = 33;
+
inline bool isUnknownOrUnsupported(const ndk::ScopedAStatus& status) {
return status.getStatus() == STATUS_UNKNOWN_TRANSACTION ||
status.getExceptionCode() == EX_UNSUPPORTED_OPERATION;
@@ -94,9 +111,13 @@
AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
ASSERT_NE(binder, nullptr);
power = IPower::fromBinder(ndk::SpAIBinder(binder));
+
+ mApiLevel = GetUintProperty<uint64_t>("ro.vendor.api_level", 0);
+ ASSERT_NE(mApiLevel, 0);
}
std::shared_ptr<IPower> power;
+ uint64_t mApiLevel;
};
TEST_P(PowerAidl, setMode) {
@@ -152,11 +173,11 @@
TEST_P(PowerAidl, getHintSessionPreferredRate) {
int64_t rate = -1;
auto status = power->getHintSessionPreferredRate(&rate);
- if (!status.isOk()) {
+ if (mApiLevel < kCompatibilityMatrix7ApiLevel && !status.isOk()) {
EXPECT_TRUE(isUnknownOrUnsupported(status));
- return;
+ GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
}
-
+ ASSERT_TRUE(status.isOk());
// At least 1ms rate limit from HAL
ASSERT_GE(rate, 1000000);
}
@@ -164,10 +185,11 @@
TEST_P(PowerAidl, createAndCloseHintSession) {
std::shared_ptr<IPowerHintSession> session;
auto status = power->createHintSession(getpid(), getuid(), kSelfTids, 16666666L, &session);
- if (!status.isOk()) {
+ if (mApiLevel < kCompatibilityMatrix7ApiLevel && !status.isOk()) {
EXPECT_TRUE(isUnknownOrUnsupported(status));
- return;
+ GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
}
+ ASSERT_TRUE(status.isOk());
ASSERT_NE(nullptr, session);
ASSERT_TRUE(session->pause().isOk());
ASSERT_TRUE(session->resume().isOk());
@@ -175,12 +197,18 @@
ASSERT_TRUE(session->close().isOk());
session.reset();
}
+
TEST_P(PowerAidl, createHintSessionFailed) {
std::shared_ptr<IPowerHintSession> session;
auto status = power->createHintSession(getpid(), getuid(), kEmptyTids, 16666666L, &session);
+
+ // Regardless of whether V2 and beyond is supported, the status is always not STATUS_OK.
ASSERT_FALSE(status.isOk());
- if (isUnknownOrUnsupported(status)) {
- return;
+
+ // If device not launching with Android 13 and beyond, check whether it's supported,
+ // if not, skip the test.
+ if (mApiLevel < kCompatibilityMatrix7ApiLevel && isUnknownOrUnsupported(status)) {
+ GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
}
ASSERT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
}
@@ -188,27 +216,42 @@
TEST_P(PowerAidl, updateAndReportDurations) {
std::shared_ptr<IPowerHintSession> session;
auto status = power->createHintSession(getpid(), getuid(), kSelfTids, 16666666L, &session);
- if (!status.isOk()) {
+ if (mApiLevel < kCompatibilityMatrix7ApiLevel && !status.isOk()) {
EXPECT_TRUE(isUnknownOrUnsupported(status));
- return;
+ GTEST_SKIP() << "DEVICE not launching with Android 13 and beyond.";
}
+ ASSERT_TRUE(status.isOk());
ASSERT_NE(nullptr, session);
ASSERT_TRUE(session->updateTargetWorkDuration(16666667LL).isOk());
ASSERT_TRUE(session->reportActualWorkDuration(kDurations).isOk());
}
+TEST_P(PowerAidl, sendSessionHint) {
+ std::shared_ptr<IPowerHintSession> session;
+ auto status = power->createHintSession(getpid(), getuid(), kSelfTids, 16666666L, &session);
+ if (!status.isOk()) {
+ EXPECT_TRUE(isUnknownOrUnsupported(status));
+ return;
+ }
+ for (const auto& sessionHint : kSessionHints) {
+ ASSERT_TRUE(session->sendHint(sessionHint).isOk());
+ }
+ for (const auto& sessionHint : kInvalidSessionHints) {
+ ASSERT_TRUE(session->sendHint(sessionHint).isOk());
+ }
+}
+
// FIXED_PERFORMANCE mode is required for all devices which ship on Android 11
// or later
TEST_P(PowerAidl, hasFixedPerformance) {
- auto apiLevel = GetUintProperty<uint64_t>("ro.vendor.api_level", 0);
- ASSERT_NE(apiLevel, 0);
-
- if (apiLevel >= 30) {
- bool supported;
- ASSERT_TRUE(power->isModeSupported(Mode::FIXED_PERFORMANCE, &supported).isOk());
- ASSERT_TRUE(supported);
+ if (mApiLevel < kCompatibilityMatrix5ApiLevel) {
+ GTEST_SKIP() << "FIXED_PERFORMANCE mode is only required for all devices launching Android "
+ "11 or later.";
}
+ bool supported;
+ ASSERT_TRUE(power->isModeSupported(Mode::FIXED_PERFORMANCE, &supported).isOk());
+ ASSERT_TRUE(supported);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerAidl);
diff --git a/radio/aidl/Android.bp b/radio/aidl/Android.bp
index fde98fc..613f31b 100644
--- a/radio/aidl/Android.bp
+++ b/radio/aidl/Android.bp
@@ -36,7 +36,7 @@
host_supported: true,
srcs: ["android/hardware/radio/config/*.aidl"],
stability: "vintf",
- imports: ["android.hardware.radio"],
+ imports: ["android.hardware.radio-V1"],
backend: {
cpp: {
enabled: true,
@@ -60,7 +60,7 @@
host_supported: true,
srcs: ["android/hardware/radio/data/*.aidl"],
stability: "vintf",
- imports: ["android.hardware.radio"],
+ imports: ["android.hardware.radio-V1"],
backend: {
cpp: {
enabled: true,
@@ -84,7 +84,7 @@
host_supported: true,
srcs: ["android/hardware/radio/messaging/*.aidl"],
stability: "vintf",
- imports: ["android.hardware.radio"],
+ imports: ["android.hardware.radio-V1"],
backend: {
cpp: {
enabled: true,
@@ -108,7 +108,7 @@
host_supported: true,
srcs: ["android/hardware/radio/modem/*.aidl"],
stability: "vintf",
- imports: ["android.hardware.radio"],
+ imports: ["android.hardware.radio-V1"],
backend: {
cpp: {
enabled: true,
@@ -157,7 +157,7 @@
srcs: ["android/hardware/radio/sim/*.aidl"],
stability: "vintf",
imports: [
- "android.hardware.radio",
+ "android.hardware.radio-V1",
"android.hardware.radio.config",
],
backend: {
@@ -186,7 +186,7 @@
host_supported: true,
srcs: ["android/hardware/radio/voice/*.aidl"],
stability: "vintf",
- imports: ["android.hardware.radio"],
+ imports: ["android.hardware.radio-V1"],
backend: {
cpp: {
enabled: true,
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
index 523d1aa..7d99a53 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/EmergencyRegResult.aidl
@@ -37,9 +37,10 @@
android.hardware.radio.AccessNetwork accessNetwork;
android.hardware.radio.network.RegState regState;
android.hardware.radio.network.Domain emcDomain;
+ boolean isVopsSupported;
boolean isEmcBearerSupported;
byte nwProvidedEmc;
byte nwProvidedEmf;
- String mcc;
- String mnc;
+ String mcc = "";
+ String mnc = "";
}
diff --git a/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl b/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
index c22317a..2215149 100644
--- a/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
+++ b/radio/aidl/android/hardware/radio/network/EmergencyRegResult.aidl
@@ -16,8 +16,8 @@
package android.hardware.radio.network;
import android.hardware.radio.AccessNetwork;
-import android.hardware.radio.network.RegState;
import android.hardware.radio.network.Domain;
+import android.hardware.radio.network.RegState;
@VintfStability
@JavaDerive(toString=true)
@@ -38,6 +38,11 @@
Domain emcDomain;
/**
+ * This indicates whether the network supports voice over PS network.
+ */
+ boolean isVopsSupported;
+
+ /**
* This indicates if camped network support VoLTE emergency bearers.
* This should only be set if the UE is in LTE mode.
*/
@@ -56,8 +61,8 @@
byte nwProvidedEmf;
/** 3-digit Mobile Country Code, 000..999, empty string if unknown. */
- String mcc;
+ String mcc = "";
/** 2 or 3-digit Mobile Network Code, 00..999, empty string if unknown. */
- String mnc;
+ String mnc = "";
}
diff --git a/security/OWNERS b/security/OWNERS
index f061cd6..fbaf854 100644
--- a/security/OWNERS
+++ b/security/OWNERS
@@ -1,7 +1,14 @@
-# Please assign all bugs related to /hardware/interfaces/security to the team alias,
-# android-hardware-security@google.com. This will get them auto-assigned to the on-call triage
-# engineer, ensuring quickest response.
+# Bug component: 1084733
+
+# Please assign all bugs related to /hardware/interfaces/security/ to the team alias:
+#
+# android-hardware-security@google.com
+#
+# This will get them auto-assigned to the on-call triage engineer, ensuring quickest response.
+
drysdale@google.com
+eranm@google.com
+hasinitg@google.com
jbires@google.com
-seleneh@google.com
swillden@google.com
+zeuthen@google.com
diff --git a/security/keymint/RKP_CHANGELOG.md b/security/keymint/RKP_CHANGELOG.md
index dfcc938..243fc26 100644
--- a/security/keymint/RKP_CHANGELOG.md
+++ b/security/keymint/RKP_CHANGELOG.md
@@ -21,13 +21,13 @@
## IRemotelyProvisionedComponent 2 -> 3
* ProtectedData has been removed.
* DeviceInfo
- * `cert_type` has been added, with values corresponding to `widevine` or `keymint`
* `version` has moved to a top-level field within the CSR generated by the HAL
* IRemotelyProvisionedComponent
* The need for an EEK has been removed. There is no longer an encrypted portion of the CSR.
* Test mode has been removed.
* The schema for the CSR itself has been significantly simplified, please see
- IRemotelyProvisionedComponent.aidl for more details.
- * Notably, the chain of signing, MACing, and encryption operations has been replaced with a single
+ IRemotelyProvisionedComponent.aidl for more details. Notably,
+ * the chain of signing, MACing, and encryption operations has been replaced with a single
COSE_Sign1 object.
+ * CertificateType has been added to identify the type of certificate being requested.
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 96d44a7..6efff2f 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -52,21 +52,21 @@
cc_defaults {
name: "keymint_use_latest_hal_aidl_ndk_static",
static_libs: [
- "android.hardware.security.keymint-V2-ndk",
+ "android.hardware.security.keymint-V3-ndk",
],
}
cc_defaults {
name: "keymint_use_latest_hal_aidl_ndk_shared",
shared_libs: [
- "android.hardware.security.keymint-V2-ndk",
+ "android.hardware.security.keymint-V3-ndk",
],
}
cc_defaults {
name: "keymint_use_latest_hal_aidl_cpp_static",
static_libs: [
- "android.hardware.security.keymint-V2-cpp",
+ "android.hardware.security.keymint-V3-cpp",
],
}
@@ -76,6 +76,6 @@
rust_defaults {
name: "keymint_use_latest_hal_aidl_rust",
rustlibs: [
- "android.hardware.security.keymint-V2-rust",
+ "android.hardware.security.keymint-V3-rust",
],
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
index 6954d65..f0af619 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
@@ -49,7 +49,6 @@
* "security_level" : "tee" / "strongbox",
* "fused": 1 / 0, ; 1 if secure boot is enforced for the processor that the IRPC
* ; implementation is contained in. 0 otherwise.
- * "cert_type": "widevine" / "keymint"
* }
*/
byte[] deviceInfo;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 926d105..2e4fc15 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -624,9 +624,15 @@
*
* o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
*
- * o The timestamp in the auth token plus the value of the Tag::AUTH_TIMEOUT must be less than
- * the current secure timestamp (which is a monotonic timer counting milliseconds since
- * boot.)
+ * o If the device has a source of secure time, then the timestamp in the auth token plus the
+ * value of the Tag::AUTH_TIMEOUT must be greater than the current secure timestamp (which
+ * is a monotonic timer counting milliseconds since boot).
+ *
+ * o If the device does not have a source of secure time, then the timestamp check should be
+ * performed on the first update(), updateAad() or finish() invocation for the operation,
+ * using the timeStampToken parameter provided on the invocation to indicate the current
+ * timestamp. It may optionally also be performed on subsequent update() / updateAad() /
+ * finish() invocations.
*
* If any of these conditions are not met, begin() must return
* ErrorCode::KEY_USER_NOT_AUTHENTICATED.
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index c30c183..82c8a0d 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -40,31 +40,7 @@
*
* == Authorization Enforcement ==
*
- * Key authorization enforcement is performed primarily in begin(). The one exception is the
- * case where the key has:
- *
- * o One or more Tag::USER_SECURE_IDs, and
- *
- * o Does not have a Tag::AUTH_TIMEOUT
- *
- * In this case, the key requires an authorization per operation, and the update method must
- * receive a non-null and valid HardwareAuthToken. For the auth token to be valid, all of the
- * following has to be true:
- *
- * o The HMAC field must validate correctly.
- *
- * o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
- * the secure ID values in the token.
- *
- * o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
- *
- * o The challenge field in the auth token must contain the value returned from
- * IKeyMintDevice::begin(), given by the challenge field of the BeginResult structure.
- *
- * If any of these conditions are not met, updateAad() must return
- * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
- *
- * The caller must provide the auth token on every call to updateAad(), update() and finish().
+ * See the Authorization Enforcement section for the update() method.
*
*
* For GCM encryption, the AEAD tag must be appended to the ciphertext by finish(). During
@@ -104,16 +80,22 @@
*
* == Authorization Enforcement ==
*
- * Key authorization enforcement is performed primarily in IKeyMintDevice::begin(). The one
- * exception is the case where the key has:
+ * Key authorization enforcement is performed primarily in IKeyMintDevice::begin(). There are
+ * two exceptions to this:
*
- * o One or more Tag::USER_SECURE_IDs, and
+ * 1) Key with USER_SECURE_IDs but no AUTH_TIMEOUT
*
- * o Does not have a Tag::AUTH_TIMEOUT
+ * 2) Key with USER_SECURE_IDs and AUTH_TIMEOUT, but the device does not support secure time.
*
- * In this case, the key requires an authorization per operation, and the update method must
- * receive a non-empty and valid HardwareAuthToken. For the auth token to be valid, all of the
- * following has to be true:
+ * The first exception is the case where the key:
+ *
+ * o Has one or more Tag::USER_SECURE_IDs, and
+ *
+ * o Does not have a Tag::AUTH_TIMEOUT
+ *
+ * In this case, the key requires an authorization per operation, and update() / updateAad() /
+ * finish() methods must receive a non-null and valid HardwareAuthToken. For the auth token to
+ * be valid, all of the following has to be true:
*
* o The HMAC field must validate correctly.
*
@@ -125,10 +107,47 @@
* o The challenge field in the auth token must contain the challenge value contained in the
* BeginResult returned from IKeyMintDevice::begin().
*
- * If any of these conditions are not met, update() must return
+ * If any of these conditions are not met, the method must return
* ErrorCode::KEY_USER_NOT_AUTHENTICATED.
*
- * The caller must provide the auth token on every call to update() and finish().
+ * The caller must provide the auth token on every call to update(), updateAad() and finish().
+ *
+ *
+ * The second exception is the case where the key:
+ *
+ * o Has one or more Tag::USER_SECURE_IDs, and
+ *
+ * o Has a Tag::AUTH_TIMEOUT value, but the device does not have a source of secure time (as
+ * indicated by the KeyMintHardwareInfo.timestampTokenRequired field).
+ *
+ * In this case, the key requires an per-operation authorization on the first call to update(),
+ * updateAad() or finish() for the operation, using the provided timeStampToken as a source of
+ * secure time. For this timeStampToken to be valid, all of the following has to be true:
+ *
+ * o The HMAC field must validate correctly.
+ *
+ * o The challenge field in the auth token must contain the challenge value contained in the
+ * BeginResult returned from IKeyMintDevice::begin().
+ *
+ * The resulting secure time value is then used to authenticate the HardwareAuthToken. For the
+ * auth token to be valid, all of the following has to be true:
+ *
+ * o The HMAC field must validate correctly.
+ *
+ * o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+ * the secure ID values in the token.
+ *
+ * o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+ *
+ * o The challenge field in the auth token must contain the challenge value contained in the
+ * BeginResult returned from IKeyMintDevice::begin().
+ *
+ * o The timestamp in the auth token plus the value of the Tag::AUTH_TIMEOUT must be greater
+ * than the provided secure timestamp.
+
+ * If any of these conditions are not met, the method must return
+ * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+ *
*
* -- RSA keys --
*
@@ -187,24 +206,7 @@
* Key authorization enforcement is performed primarily in begin(). The exceptions are
* authorization per operation keys and confirmation-required keys.
*
- * Authorization per operation keys are the case where the key has one or more
- * Tag::USER_SECURE_IDs, and does not have a Tag::AUTH_TIMEOUT. In this case, the key requires
- * an authorization per operation, and the finish method must receive a non-empty and valid
- * authToken. For the auth token to be valid, all of the following has to be true:
- *
- * o The HMAC field must validate correctly.
- *
- * o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
- * the secure ID values in the token.
- *
- * o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
- *
- * o The challenge field in the auth token must contain the operation challenge.
- *
- * If any of these conditions are not met, update() must return
- * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
- *
- * The caller must provide the auth token on every call to update() and finish().
+ * Authorization per operation keys must be authorized as described for the update() method.
*
* Confirmation-required keys are keys that were generated with
* Tag::TRUSTED_CONFIRMATION_REQUIRED. For these keys, when doing a signing operation the
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index c2acbed..86c1717 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -29,7 +29,7 @@
* validate the request and create certificates.
*
* This interface does not provide any way to use the generated and certified key pairs. It's
- * intended to be implemented by a HAL service that does other things with keys (e.g. Keymint).
+ * intended to be implemented by a HAL service that does other things with keys (e.g. KeyMint).
*
* The root of trust for secure provisioning is something called the Device Identifier Composition
* Engine (DICE) Chain. The DICE Chain is a chain of certificates, represented as COSE_Sign1 objects
@@ -79,9 +79,9 @@
* While a proper DICE Chain, as described above, reflects the complete boot sequence from boot ROM
* to the secure area image of the IRemotelyProvisionedComponent, it's also possible to use a
* "degenerate" DICE Chain which consists only of a single, self-signed certificate containing the
- * public key of a hardware-bound key pair. This is an appopriate solution for devices which haven't
- * implemented everything necessary to produce a proper DICE Chain, but can derive a unique key pair
- * in the secure area. In this degenerate case, UDS_Pub is the same as CDI_Leaf_Pub.
+ * public key of a hardware-bound key pair. This is an appropriate solution for devices which
+ * haven't implemented everything necessary to produce a proper DICE Chain, but can derive a unique
+ * key pair in the secure area. In this degenerate case, UDS_Pub is the same as CDI_Leaf_Pub.
*
* DICE Chain Privacy
* ==================
@@ -151,7 +151,8 @@
/**
* This method has been removed in version 3 of the HAL. The header is kept around for
- * backwards compatibility purposes. Calling this method should return STATUS_REMOVED on v3.
+ * backwards compatibility purposes. From v3, this method should raise a
+ * ServiceSpecificException with an error code of STATUS_REMOVED.
*
* For v1 and v2 implementations:
* generateCertificateRequest creates a certificate request to be sent to the provisioning
@@ -170,7 +171,7 @@
* If testMode is false, the keysToCertify array must not contain any keys flagged as
* test keys. Otherwise, the method must return STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
*
- * @param in endpointEncryptionKey contains an X22519 public key which will be used to encrypt
+ * @param in endpointEncryptionKey contains an X25519 public key which will be used to encrypt
* the BCC. For flexibility, this is represented as a certificate chain, represented as a
* CBOR array of COSE_Sign1 objects, ordered from root to leaf. The leaf contains the
* X25519 encryption key, each other element is an Ed25519 key signing the next in the
@@ -197,7 +198,7 @@
* -2 : bstr ; Ed25519 public key
* }
*
- * SignatureKeyP256 = {
+ * SignatureKeyP256 = { ; COSE_Key
* 1 : 2, ; Key type : EC2
* 3 : AlgorithmES256, ; Algorithm
* -1 : 1, ; Curve: P256
@@ -227,7 +228,7 @@
* 2 : bstr ; KID : EEK ID
* 3 : -25, ; Algorithm : ECDH-ES + HKDF-256
* -1 : 4, ; Curve : X25519
- * -2 : bstr ; Ed25519 public key
+ * -2 : bstr ; X25519 public key
* }
*
* EekP256 = { ; COSE_Key
@@ -246,8 +247,8 @@
* payload: bstr .cbor EekX25519 / .cbor EekP256
* ]
*
- * AlgorithmES256 = -7
- * AlgorithmEdDSA = -8
+ * AlgorithmES256 = -7 ; RFC 8152 section 8.1
+ * AlgorithmEdDSA = -8 ; RFC 8152 section 8.2
*
* If the contents of endpointEncryptionKey do not match the SignedEek structure above,
* the method must return STATUS_INVALID_EEK.
@@ -256,7 +257,7 @@
* in the chain, which implies that it must not attempt to validate the signature.
*
* If testMode is false, the method must validate the chain signatures, and must verify
- * that the public key in the root certifictate is in its pre-configured set of
+ * that the public key in the root certificate is in its pre-configured set of
* authorized EEK root keys. If the public key is not in the database, or if signature
* verification fails, the method must return STATUS_INVALID_EEK.
*
@@ -270,7 +271,7 @@
* @param out ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
* authenticate the keysToSign (see keysToSignMac output argument).
*
- * @return The of KeysToSign in the CertificateRequest structure. Specifically, it contains:
+ * @return The MAC of KeysToSign in the CertificateRequest structure. Specifically, it contains:
*
* HMAC-256(EK_mac, .cbor KeysToMacStructure)
*
@@ -314,38 +315,50 @@
*
* @return the following CBOR Certificate Signing Request (Csr) serialized into a byte array:
*
- * Csr = [
- * version: 3, ; The CDDL Schema version.
- * UdsCerts,
- * DiceCertChain,
- * SignedData
- * ]
+ * Csr = AuthenticatedMessage<CsrPayload>
*
- * ; COSE_Sign1 (untagged)
- * SignedData = [
- * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
- * unprotected: {},
- * payload: bstr .cbor SignedDataPayload,
- * signature: bstr ; PureEd25519(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct) /
- * ; ECDSA(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct)
- * ]
- *
- * ; Sig_structure for SignedData
- * SignedDataSigStruct = [
- * context: "Signature1",
- * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
- * external_aad: bstr .size 0,
- * payload: bstr .cbor SignedDataPayload
- * ]
- *
- * SignedDataPayload = [ ; CBOR Array defining the payload for SignedData
+ * CsrPayload = [ ; CBOR Array defining the payload for Csr
+ * version: 1, ; 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
* ]
*
+ * ; A tstr identifying the type of certificate. The set of supported certificate types may
+ * ; be extended without requiring a version bump of the HAL. Custom certificate types may
+ * ; be used, but the provisioning server may reject the request for an unknown certificate
+ * ; type. The currently defined certificate types are:
+ * ; - "widevine"
+ * ; - "keymint"
+ * CertificateType = tstr
+ *
* KeysToSign = [ * PublicKey ] ; Please see MacedPublicKey.aidl for the PublicKey definition.
*
+ * AuthenticatedMessage<T> = [
+ * version: 3, ; The AuthenticatedMessage CDDL Schema version.
+ * UdsCerts,
+ * DiceCertChain,
+ * SignedData<T>,
+ * ]
+ *
+ * ; COSE_Sign1 (untagged)
+ * SignedData<T> = [
+ * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
+ * unprotected: {},
+ * payload: bstr .cbor T / nil,
+ * signature: bstr ; PureEd25519(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct<T>) /
+ * ; ECDSA(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct<T>)
+ * ]
+ *
+ * ; Sig_structure for SignedData
+ * SignedDataSigStruct<T> = [
+ * context: "Signature1",
+ * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
+ * external_aad: bstr .size 0,
+ * payload: bstr .cbor T
+ * ]
+ *
* ; UdsCerts allows the platform to provide additional certifications for the UDS_Pub. For
* ; example, this could be provided by the hardware vendor, who certifies all of their chips.
* ; The SignerName is a free-form string describing who generated the signature. The root
@@ -365,7 +378,7 @@
* ; intermediate certificates between Root and Leaf.
* ]
*
- * ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA)
+ * ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or EdDSA)
* X509Certificate = bstr
*
* ; The DICE Chain contains measurements about the device firmware.
@@ -378,8 +391,8 @@
* ; Last certificate corresponds to KeyMint's DICE key.
* ]
*
- * ; This is the signed payload for each entry in the DCC. Note that the "Configuration
- * ; Input Values" described by the Open Profile are not used here. Instead, the Dcc
+ * ; This is the signed payload for each entry in the DICE chain. Note that the "Configuration
+ * ; Input Values" described by the Open Profile are not used here. Instead, the DICE chain
* ; defines its own configuration values for the Configuration Descriptor field. See
* ; the Open Profile for DICE for more details on the fields. SHA256 and SHA512 are acceptable
* ; hash algorithms. The digest bstr values in the payload are the digest values without any
@@ -408,8 +421,8 @@
* -4670551 : bstr, ; Mode
* }
*
- * ; Each entry in the Dcc is a DiceChainEntryPayload signed by the key from the previous entry
- * ; in the Dcc array.
+ * ; Each entry in the DICE chain is a DiceChainEntryPayload signed by the key from the previous
+ * ; entry in the DICE chain array.
* DiceChainEntry = [ ; COSE_Sign1 (untagged)
* protected : bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
* unprotected: {},
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index e1f65fe..4f361bb 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -46,6 +46,7 @@
namespace {
constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2;
+constexpr int32_t VERSION_WITHOUT_TEST_MODE = 3;
#define INSTANTIATE_REM_PROV_AIDL_TEST(name) \
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name); \
@@ -180,6 +181,15 @@
return params;
}
+ void checkMacedPubkeyVersioned(const MacedPublicKey& macedPubKey, bool testMode,
+ vector<uint8_t>* payload_value) {
+ if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
+ check_maced_pubkey(macedPubKey, false, payload_value);
+ } else {
+ check_maced_pubkey(macedPubKey, testMode, payload_value);
+ }
+ }
+
protected:
std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
RpcHardwareInfo rpcHardwareInfo;
@@ -200,9 +210,7 @@
RpcHardwareInfo hwInfo;
ASSERT_TRUE(rpc->getHardwareInfo(&hwInfo).isOk());
- int32_t version;
- ASSERT_TRUE(rpc->getInterfaceVersion(&version).isOk());
- if (version >= VERSION_WITH_UNIQUE_ID_SUPPORT) {
+ if (hwInfo.versionNumber >= VERSION_WITH_UNIQUE_ID_SUPPORT) {
ASSERT_TRUE(hwInfo.uniqueId);
auto [_, wasInserted] = uniqueIds.insert(*hwInfo.uniqueId);
EXPECT_TRUE(wasInserted);
@@ -232,10 +240,7 @@
* Verify that the unique id is within the length limits as described in RpcHardwareInfo.aidl.
*/
TEST_P(GetHardwareInfoTests, uniqueId) {
- int32_t version;
- ASSERT_TRUE(provisionable_->getInterfaceVersion(&version).isOk());
-
- if (version < VERSION_WITH_UNIQUE_ID_SUPPORT) {
+ if (rpcHardwareInfo.versionNumber < VERSION_WITH_UNIQUE_ID_SUPPORT) {
return;
}
@@ -261,7 +266,7 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
vector<uint8_t> coseKeyData;
- check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+ checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
}
/**
@@ -284,7 +289,7 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
vector<uint8_t> coseKeyData;
- check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+ checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
AttestationKey attestKey;
attestKey.keyBlob = std::move(privateKeyBlob);
@@ -339,13 +344,13 @@
bool testMode = true;
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
-
- check_maced_pubkey(macedPubKey, testMode, nullptr);
+ checkMacedPubkeyVersioned(macedPubKey, testMode, nullptr);
}
-class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
+class CertificateRequestTestBase : public VtsRemotelyProvisionedComponentTests {
protected:
- CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {}
+ CertificateRequestTestBase()
+ : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {}
void generateTestEekChain(size_t eekLength) {
auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_);
@@ -364,7 +369,7 @@
ASSERT_TRUE(status.isOk()) << status.getMessage();
vector<uint8_t> payload_value;
- check_maced_pubkey(key, testMode, &payload_value);
+ checkMacedPubkeyVersioned(key, testMode, &payload_value);
cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
}
}
@@ -377,6 +382,18 @@
cppbor::Array cborKeysToSign_;
};
+class CertificateRequestTest : public CertificateRequestTestBase {
+ protected:
+ void SetUp() override {
+ CertificateRequestTestBase::SetUp();
+
+ if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
+ GTEST_SKIP() << "This test case only applies to RKP v1 and v2. "
+ << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
+ }
+ }
+};
+
/**
* Generate an empty certificate request in test mode, and decrypt and verify the structure and
* content.
@@ -643,4 +660,131 @@
INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
+class CertificateRequestV2Test : public CertificateRequestTestBase {
+ void SetUp() override {
+ CertificateRequestTestBase::SetUp();
+
+ if (rpcHardwareInfo.versionNumber < VERSION_WITHOUT_TEST_MODE) {
+ GTEST_SKIP() << "This test case only applies to RKP v3 and above. "
+ << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
+ }
+ }
+};
+
+/**
+ * Generate an empty certificate request, and decrypt and verify the structure and content.
+ */
+TEST_P(CertificateRequestV2Test, EmptyRequest) {
+ bytevec csr;
+
+ auto status =
+ provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge_, &csr);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge_);
+ ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request. Decrypt, parse and validate the contents.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequest) {
+ generateKeys(false /* testMode */, 1 /* numKeys */);
+
+ bytevec csr;
+
+ auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+ ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request. Make sure contents are reproducible.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
+ generateKeys(false /* testMode */, 1 /* numKeys */);
+
+ bytevec csr;
+
+ 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();
+
+ 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();
+
+ ASSERT_EQ(firstBcc->size(), secondBcc->size());
+ for (auto i = 0; i < firstBcc->size(); i++) {
+ ASSERT_EQ(firstBcc->at(i).pubKey, secondBcc->at(i).pubKey);
+ }
+}
+
+/**
+ * Generate a non-empty certificate request with multiple keys.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestMultipleKeys) {
+ // TODO(b/254137722): define a minimum number of keys that must be supported.
+ generateKeys(false /* testMode */, 5 /* numKeys */);
+
+ bytevec csr;
+
+ auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+ ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestCorruptMac) {
+ generateKeys(false /* testMode */, 1 /* numKeys */);
+ auto result = corrupt_maced_key(keysToSign_[0]);
+ ASSERT_TRUE(result) << result.moveMessage();
+ MacedPublicKey keyWithCorruptMac = result.moveValue();
+
+ bytevec csr;
+ auto status =
+ provisionable_->generateCertificateRequestV2({keyWithCorruptMac}, challenge_, &csr);
+ ASSERT_FALSE(status.isOk()) << status.getMessage();
+ EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, with test keys. Test mode must be
+ * ignored, i.e. test must pass.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequest_testKeyInProdCert) {
+ generateKeys(true /* testMode */, 1 /* numKeys */);
+
+ bytevec csr;
+ auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+}
+
+/**
+ * Call generateCertificateRequest(). Make sure it's removed.
+ */
+TEST_P(CertificateRequestV2Test, CertificateRequestV1Removed) {
+ generateTestEekChain(2);
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ true /* testMode */, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
+ ASSERT_FALSE(status.isOk()) << status.getMessage();
+ EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_REMOVED);
+}
+
+INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestV2Test);
+
} // namespace aidl::android::hardware::security::keymint::test
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 9ea20ac..6871e1b 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -177,4 +177,19 @@
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
+/**
+ * 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);
+/**
+ * Verify the CSR as if the device is a final production sample.
+ */
+ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
+ const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
+ IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
+
} // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 0651496..f7ab3ac 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -32,6 +32,7 @@
#include <openssl/base64.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
+#include <openssl/x509.h>
#include <remote_prov/remote_prov_utils.h>
namespace aidl::android::hardware::security::keymint::remote_prov {
@@ -45,6 +46,8 @@
using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>;
+using X509_Ptr = bssl::UniquePtr<X509>;
+using CRYPTO_BUFFER_Ptr = bssl::UniquePtr<CRYPTO_BUFFER>;
ErrMsgOr<bytevec> ecKeyGetPrivateKey(const EC_KEY* ecKey) {
// Extract private key.
@@ -527,22 +530,27 @@
if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
return "DeviceInfo ordering is non-canonical.";
}
- const std::unique_ptr<cppbor::Item>& version = parsed->get("version");
- if (!version) {
- return "Device info is missing version";
- }
- if (!version->asUint()) {
- return "version must be an unsigned integer";
- }
+
RpcHardwareInfo info;
provisionable->getHardwareInfo(&info);
- if (version->asUint()->value() != info.versionNumber) {
- return "DeviceInfo version (" + std::to_string(version->asUint()->value()) +
- ") does not match the remotely provisioned component version (" +
- std::to_string(info.versionNumber) + ").";
+ if (info.versionNumber < 3) {
+ const std::unique_ptr<cppbor::Item>& version = parsed->get("version");
+ if (!version) {
+ return "Device info is missing version";
+ }
+ if (!version->asUint()) {
+ return "version must be an unsigned integer";
+ }
+ if (version->asUint()->value() != info.versionNumber) {
+ return "DeviceInfo version (" + std::to_string(version->asUint()->value()) +
+ ") does not match the remotely provisioned component version (" +
+ std::to_string(info.versionNumber) + ").";
+ }
}
+
std::string error;
- switch (version->asUint()->value()) {
+ switch (info.versionNumber) {
+ case 3:
case 2:
for (const auto& entry : kAttestationIdEntrySet) {
error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
@@ -579,7 +587,7 @@
kValidAttIdStates);
break;
default:
- return "Unrecognized version: " + std::to_string(version->asUint()->value());
+ return "Unrecognized version: " + std::to_string(info.versionNumber);
}
if (!error.empty()) {
@@ -734,4 +742,264 @@
/*isFactory=*/false);
}
-} // namespace aidl::android::hardware::security::keymint::remote_prov
\ No newline at end of file
+ErrMsgOr<X509_Ptr> parseX509Cert(const std::vector<uint8_t>& cert) {
+ CRYPTO_BUFFER_Ptr certBuf(CRYPTO_BUFFER_new(cert.data(), cert.size(), nullptr));
+ if (!certBuf.get()) {
+ return "Failed to create crypto buffer.";
+ }
+ X509_Ptr result(X509_parse_from_buffer(certBuf.get()));
+ if (!result.get()) {
+ return "Failed to parse certificate.";
+ }
+ return result;
+}
+
+std::string getX509IssuerName(const X509_Ptr& cert) {
+ char* name = X509_NAME_oneline(X509_get_issuer_name(cert.get()), nullptr, 0);
+ std::string result(name);
+ OPENSSL_free(name);
+ return result;
+}
+
+std::string getX509SubjectName(const X509_Ptr& cert) {
+ char* name = X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0);
+ std::string result(name);
+ OPENSSL_free(name);
+ return result;
+}
+
+// Validates the certificate chain and returns the leaf public key.
+ErrMsgOr<bytevec> validateCertChain(const cppbor::Array& chain) {
+ uint8_t rawPubKey[64];
+ size_t rawPubKeySize = sizeof(rawPubKey);
+ for (size_t i = 0; i < chain.size(); ++i) {
+ // Root must be self-signed.
+ size_t signingCertIndex = (i > 1) ? i - 1 : i;
+ auto& keyCertItem = chain[i];
+ auto& signingCertItem = chain[signingCertIndex];
+ if (!keyCertItem || !keyCertItem->asBstr()) {
+ return "Key certificate must be a Bstr.";
+ }
+ if (!signingCertItem || !signingCertItem->asBstr()) {
+ return "Signing certificate must be a Bstr.";
+ }
+
+ auto keyCert = parseX509Cert(keyCertItem->asBstr()->value());
+ if (!keyCert) {
+ return keyCert.message();
+ }
+ auto signingCert = parseX509Cert(keyCertItem->asBstr()->value());
+ if (!signingCert) {
+ return signingCert.message();
+ }
+
+ EVP_PKEY_Ptr pubKey(X509_get_pubkey(keyCert->get()));
+ if (!pubKey.get()) {
+ return "Failed to get public key.";
+ }
+ EVP_PKEY_Ptr signingPubKey(X509_get_pubkey(signingCert->get()));
+ if (!signingPubKey.get()) {
+ return "Failed to get signing public key.";
+ }
+
+ if (!X509_verify(keyCert->get(), signingPubKey.get())) {
+ return "Verification of certificate " + std::to_string(i) +
+ " faile. OpenSSL error string: " + ERR_error_string(ERR_get_error(), NULL);
+ }
+
+ auto certIssuer = getX509IssuerName(*keyCert);
+ auto signerSubj = getX509SubjectName(*signingCert);
+ if (certIssuer != signerSubj) {
+ return "Certificate " + std::to_string(i) + " has wrong issuer. Signer subject is " +
+ signerSubj + " Issuer subject is " + certIssuer;
+ }
+
+ rawPubKeySize = sizeof(rawPubKey);
+ if (!EVP_PKEY_get_raw_public_key(pubKey.get(), rawPubKey, &rawPubKeySize)) {
+ return "Failed to get raw public key.";
+ }
+ }
+
+ return bytevec(rawPubKey, rawPubKey + rawPubKeySize);
+}
+
+std::string validateUdsCerts(const cppbor::Map& udsCerts, const bytevec& udsPub) {
+ for (const auto& [signerName, udsCertChain] : udsCerts) {
+ if (!signerName || !signerName->asTstr()) {
+ return "Signer Name must be a Tstr.";
+ }
+ if (!udsCertChain || !udsCertChain->asArray()) {
+ return "UDS certificate chain must be an Array.";
+ }
+ if (udsCertChain->asArray()->size() < 2) {
+ return "UDS certificate chain must have at least two entries: root and leaf.";
+ }
+
+ auto leafPubKey = validateCertChain(*udsCertChain->asArray());
+ if (!leafPubKey) {
+ return leafPubKey.message();
+ }
+ if (*leafPubKey != udsPub) {
+ return "Leaf public key in UDS certificat chain doesn't match UDS public key.";
+ }
+ }
+ 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) {
+ auto [parsedCsrPayload, _, errMsg] = cppbor::parse(csrPayload);
+ if (!parsedCsrPayload) {
+ return errMsg;
+ }
+ if (!parsedCsrPayload->asArray()) {
+ 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. "
+ "However, the parsed CSR payload has " +
+ std::to_string(parsedCsrPayload->asArray()->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);
+
+ if (!signedVersion || !signedVersion->asUint() || signedVersion->asUint()->value() != 1U) {
+ return "CSR payload version must be an unsigned integer and must be equal to 1.";
+ }
+ if (!signedCertificateType || !signedCertificateType->asTstr()) {
+ // 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()) {
+ return "Device info must be an Map.";
+ }
+ if (!signedChallenge || !signedChallenge->asBstr()) {
+ return "Challenge must be a Bstr.";
+ }
+ if (!signedKeys || !signedKeys->asArray()) {
+ return "Keys must be an Array.";
+ }
+
+ auto result = parseAndValidateDeviceInfo(signedDeviceInfo->asMap()->encode(), provisionable,
+ isFactory);
+ if (!result) {
+ return result.message();
+ }
+
+ if (challenge.size() < 32 || challenge.size() > 64) {
+ return "Challenge size must be between 32 and 64 bytes inclusive. "
+ "However, challenge is " +
+ std::to_string(challenge.size()) + " bytes long.";
+ }
+
+ auto challengeBstr = cppbor::Bstr(challenge);
+ if (*signedChallenge->asBstr() != 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.";
+ }
+
+ return std::move(*parsedCsrPayload->asArray());
+}
+
+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) {
+ return csrErrMsg;
+ }
+ if (!parsedCsr->asArray()) {
+ return "CSR 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.";
+ }
+
+ auto& version = parsedCsr->asArray()->get(0);
+ auto& udsCerts = parsedCsr->asArray()->get(1);
+ auto& diceCertChain = parsedCsr->asArray()->get(2);
+ auto& signedData = parsedCsr->asArray()->get(3);
+
+ if (!version || !version->asUint() || version->asUint()->value() != 3U) {
+ return "Version must be an unsigned integer and must be equal to 3.";
+ }
+ if (!udsCerts || !udsCerts->asMap()) {
+ return "UdsCerts must be an Map.";
+ }
+ if (!diceCertChain || !diceCertChain->asArray()) {
+ return "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) + ").";
+ }
+
+ // DICE chain is [ pubkey, + DiceChainEntry ]. Its format is the same as BCC from RKP v1-2.
+ auto diceContents = validateBcc(diceCertChain->asArray());
+ if (!diceContents) {
+ return diceContents.message() + "\n" + prettyPrint(diceCertChain.get());
+ }
+ if (diceContents->size() == 0U) {
+ return "The DICE chain is empty. It must contain at least one entry.";
+ }
+
+ auto& udsPub = diceContents->back().pubKey;
+
+ auto error = validateUdsCerts(*udsCerts->asMap(), udsPub);
+ if (!error.empty()) {
+ return error;
+ }
+
+ auto csrPayload = verifyAndParseCoseSign1(signedData->asArray(), udsPub, {} /* aad */);
+ if (!csrPayload) {
+ return csrPayload.message();
+ }
+
+ auto parsedCsrPayload = parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable,
+ challenge, isFactory);
+ if (!parsedCsrPayload) {
+ return parsedCsrPayload.message();
+ }
+
+ return *diceContents;
+}
+
+ErrMsgOr<std::vector<BccEntryData>> 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(
+ 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);
+}
+
+} // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/thermal/OWNERS b/thermal/OWNERS
new file mode 100644
index 0000000..7229b22
--- /dev/null
+++ b/thermal/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 826709
+
+# ADPF virtual team
+lpy@google.com
+wvw@google.com
diff --git a/thermal/aidl/Android.bp b/thermal/aidl/Android.bp
new file mode 100644
index 0000000..b132746
--- /dev/null
+++ b/thermal/aidl/Android.bp
@@ -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.
+
+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.thermal",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/thermal/*.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ platform_apis: true,
+ },
+ },
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl
new file mode 100644
index 0000000..50be508
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingDevice.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+1 * 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.thermal;
+@VintfStability
+parcelable CoolingDevice {
+ android.hardware.thermal.CoolingType type;
+ String name;
+ long value;
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingType.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingType.aidl
new file mode 100644
index 0000000..57c8939
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/CoolingType.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.thermal;
+@Backing(type="int") @VintfStability
+enum CoolingType {
+ FAN = 0,
+ BATTERY = 1,
+ CPU = 2,
+ GPU = 3,
+ MODEM = 4,
+ NPU = 5,
+ COMPONENT = 6,
+ TPU = 7,
+ POWER_AMPLIFIER = 8,
+ DISPLAY = 9,
+ SPEAKER = 10,
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl
new file mode 100644
index 0000000..0aed5ec
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermal.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.thermal;
+@VintfStability
+interface IThermal {
+ android.hardware.thermal.CoolingDevice[] getCoolingDevices();
+ android.hardware.thermal.CoolingDevice[] getCoolingDevicesWithType(in android.hardware.thermal.CoolingType type);
+ android.hardware.thermal.Temperature[] getTemperatures();
+ android.hardware.thermal.Temperature[] getTemperaturesWithType(in android.hardware.thermal.TemperatureType type);
+ android.hardware.thermal.TemperatureThreshold[] getTemperatureThresholds();
+ android.hardware.thermal.TemperatureThreshold[] getTemperatureThresholdsWithType(in android.hardware.thermal.TemperatureType type);
+ void registerThermalChangedCallback(in android.hardware.thermal.IThermalChangedCallback callback);
+ void registerThermalChangedCallbackWithType(in android.hardware.thermal.IThermalChangedCallback callback, in android.hardware.thermal.TemperatureType type);
+ void unregisterThermalChangedCallback(in android.hardware.thermal.IThermalChangedCallback callback);
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermalChangedCallback.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermalChangedCallback.aidl
new file mode 100644
index 0000000..6b3f922
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/IThermalChangedCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.thermal;
+@VintfStability
+interface IThermalChangedCallback {
+ oneway void notifyThrottling(in android.hardware.thermal.Temperature temperature);
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/Temperature.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/Temperature.aidl
new file mode 100644
index 0000000..7156415
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/Temperature.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.thermal;
+@VintfStability
+parcelable Temperature {
+ android.hardware.thermal.TemperatureType type;
+ String name;
+ float value;
+ android.hardware.thermal.ThrottlingSeverity throttlingStatus;
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureThreshold.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureThreshold.aidl
new file mode 100644
index 0000000..6da561f
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureThreshold.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.thermal;
+@VintfStability
+parcelable TemperatureThreshold {
+ android.hardware.thermal.TemperatureType type;
+ String name;
+ float[] hotThrottlingThresholds;
+ float[] coldThrottlingThresholds;
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureType.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureType.aidl
new file mode 100644
index 0000000..c6a08c1
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/TemperatureType.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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.thermal;
+@Backing(type="int") @VintfStability
+enum TemperatureType {
+ UNKNOWN = -1,
+ CPU = 0,
+ GPU = 1,
+ BATTERY = 2,
+ SKIN = 3,
+ USB_PORT = 4,
+ POWER_AMPLIFIER = 5,
+ BCL_VOLTAGE = 6,
+ BCL_CURRENT = 7,
+ BCL_PERCENTAGE = 8,
+ NPU = 9,
+ TPU = 10,
+ DISPLAY = 11,
+ MODEM = 12,
+ SOC = 13,
+}
diff --git a/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ThrottlingSeverity.aidl b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ThrottlingSeverity.aidl
new file mode 100644
index 0000000..e86b581
--- /dev/null
+++ b/thermal/aidl/aidl_api/android.hardware.thermal/current/android/hardware/thermal/ThrottlingSeverity.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.thermal;
+@Backing(type="int") @VintfStability
+enum ThrottlingSeverity {
+ NONE = 0,
+ LIGHT = 1,
+ MODERATE = 2,
+ SEVERE = 3,
+ CRITICAL = 4,
+ EMERGENCY = 5,
+ SHUTDOWN = 6,
+}
diff --git a/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl b/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl
new file mode 100644
index 0000000..6d974a5
--- /dev/null
+++ b/thermal/aidl/android/hardware/thermal/CoolingDevice.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+1 * 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.thermal;
+
+import android.hardware.thermal.CoolingType;
+
+@VintfStability
+parcelable CoolingDevice {
+ /**
+ * This cooling device type, CPU, GPU, BATTERY, and etc.
+ */
+ CoolingType type;
+ /**
+ * Name of this cooling device.
+ * All cooling devices of the same "type" must have a different "name".
+ * The name is usually defined in kernel device tree, and this is for client
+ * logging purpose.
+ */
+ String name;
+ /**
+ * Current throttle state of the cooling device. The value can any unsigned integer
+ * numbers between 0 and max_state defined in its driver, usually representing the
+ * associated device's power state. 0 means device is not in throttling, higher value
+ * means deeper throttling.
+ */
+ long value;
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/thermal/aidl/android/hardware/thermal/CoolingType.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to thermal/aidl/android/hardware/thermal/CoolingType.aidl
index 4b82dd0..1b430d2 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/thermal/aidl/android/hardware/thermal/CoolingType.aidl
@@ -14,18 +14,23 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.thermal;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+/**
+ * Device cooling device types
+ */
+@VintfStability
+@Backing(type="int")
+enum CoolingType {
+ FAN,
+ BATTERY,
+ CPU,
+ GPU,
+ MODEM,
+ NPU,
+ COMPONENT,
+ TPU,
+ POWER_AMPLIFIER,
+ DISPLAY,
+ SPEAKER,
+}
diff --git a/thermal/aidl/android/hardware/thermal/IThermal.aidl b/thermal/aidl/android/hardware/thermal/IThermal.aidl
new file mode 100644
index 0000000..8b79cb4
--- /dev/null
+++ b/thermal/aidl/android/hardware/thermal/IThermal.aidl
@@ -0,0 +1,202 @@
+/*
+ * 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.thermal;
+
+import android.hardware.thermal.CoolingDevice;
+import android.hardware.thermal.CoolingType;
+import android.hardware.thermal.IThermalChangedCallback;
+import android.hardware.thermal.Temperature;
+import android.hardware.thermal.TemperatureThreshold;
+import android.hardware.thermal.TemperatureType;
+
+@VintfStability
+interface IThermal {
+ /**
+ * Retrieves the cooling devices information.
+ *
+ * @return devices If succeed, it's filled with the
+ * current cooling device information. The order of built-in cooling
+ * devices in the list must be kept the same regardless the number
+ * of calls to this method even if they go offline, if these devices
+ * exist on boot. The method always returns and never removes from
+ * the list such cooling devices.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, getMessage() must be populated with the human-readable
+ * error message.
+ */
+ CoolingDevice[] getCoolingDevices();
+
+ /**
+ * Retrieves the cooling devices information of a given CoolingType.
+ *
+ * @param type the CoolingDevice such as CPU/GPU.
+ *
+ * @return devices If succeed, it's filled with the current
+ * cooling device information. The order of built-in cooling
+ * devices in the list must be kept the same regardless of the number
+ * of calls to this method even if they go offline, if these devices
+ * exist on boot. The method always returns and never removes from
+ * the list such cooling devices.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message.
+ */
+ CoolingDevice[] getCoolingDevicesWithType(in CoolingType type);
+
+ /**
+ * Retrieves temperatures in Celsius.
+ *
+ * @return temperatures If succeed, it's filled with the
+ * current temperatures. The order of temperatures of built-in
+ * devices (such as CPUs, GPUs and etc.) in the list must be kept
+ * the same regardless the number of calls to this method even if
+ * they go offline, if these devices exist on boot. The method
+ * always returns and never removes such temperatures.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message.
+ */
+ Temperature[] getTemperatures();
+
+ /**
+ * Retrieves temperatures in Celsius with a given TemperatureType.
+ *
+ * @param type the TemperatureType such as battery or skin.
+ *
+ * @return temperatures If succeed, it's filled with the
+ * current temperatures. The order of temperatures of built-in
+ * devices (such as CPUs, GPUs and etc.) in the list must be kept
+ * the same regardless of the number of calls to this method even if
+ * they go offline, if these devices exist on boot. The method
+ * always returns and never removes such temperatures.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message.
+ */
+ Temperature[] getTemperaturesWithType(in TemperatureType type);
+
+ /**
+ * Retrieves static temperature thresholds in Celsius.
+ *
+ * @return temperatureThresholds If succeed, it's filled with the
+ * temperatures thresholds. The order of temperatures of built-in
+ * devices (such as CPUs, GPUs and etc.) in the list must be kept
+ * the same regardless of the number of calls to this method even if
+ * they go offline, if these devices exist on boot. The method
+ * always returns and never removes such temperatures. The thresholds
+ * are returned as static values and must not change across calls. The actual
+ * throttling state is determined in device thermal mitigation policy/agorithm
+ * which might not be simple thresholds so these values Thermal HAL provided
+ * may not be accurate to detemin the throttling status. To get accurate
+ * throttling status, use getTemperatures or registerThermalChangedCallback
+ * and listen to the callback.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message.
+ */
+ TemperatureThreshold[] getTemperatureThresholds();
+
+ /**
+ * Retrieves static temperature thresholds in Celsius of a given temperature
+ * type.
+ *
+ * @param type the TemperatureType such as battery or skin.
+ *
+ * @return temperatureThresholds If succeed, it's filled with the
+ * temperatures thresholds. The order of temperatures of built-in
+ * devices (such as CPUs, GPUs and etc.) in the list must be kept
+ * the same regardless of the number of calls to this method even if
+ * they go offline, if these devices exist on boot. The method
+ * always returns and never removes such temperatures. The thresholds
+ * are returned as static values and must not change across calls. The actual
+ * throttling state is determined in device thermal mitigation policy/agorithm
+ * which might not be simple thresholds so these values Thermal HAL provided
+ * may not be accurate to detemin the throttling status. To get accurate
+ * throttling status, use getTemperatures or registerThermalChangedCallback
+ * and listen to the callback.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message.
+ */
+ TemperatureThreshold[] getTemperatureThresholdsWithType(in TemperatureType type);
+
+ /**
+ * Register an IThermalChangedCallback, used by the Thermal HAL to receive
+ * thermal events when thermal mitigation status changed.
+ * Multiple registrations with different IThermalChangedCallback must be allowed.
+ * Multiple registrations with same IThermalChangedCallback is not allowed, client
+ * should unregister the given IThermalChangedCallback first.
+ *
+ * @param callback the IThermalChangedCallback to use for receiving
+ * thermal events. if nullptr callback is given, the status code will be
+ * STATUS_BAD_VALUE and the operation will fail.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message. If callback is given nullptr, the returned status code
+ * will be STATUS_BAD_VALUE and the exception will be EX_ILLEGAL_ARGUMENT.
+ * if callback is already registered, the returned status code will be
+ * STATUS_INVALID_OPERATION, the exception will be EX_ILLEGAL_ARGUMENT.
+ */
+ void registerThermalChangedCallback(in IThermalChangedCallback callback);
+
+ /**
+ * Register an IThermalChangedCallback for a given TemperatureType, used by
+ * the Thermal HAL to receive thermal events when thermal mitigation status
+ * changed.
+ * Multiple registrations with different IThermalChangedCallback must be allowed.
+ * Multiple registrations with same IThermalChangedCallback is not allowed, client
+ * should unregister the given IThermalChangedCallback first.
+ *
+ * @param callback the IThermalChangedCallback to use for receiving
+ * thermal events. if nullptr callback is given, the status code will be
+ * STATUS_BAD_VALUE and the operation will fail.
+ * @param type the type to be filtered.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message. If callback is given nullptr, the returned status code
+ * will be STATUS_BAD_VALUE and the exception will be EX_ILLEGAL_ARGUMENT.
+ * if callback is already registered, the returned status code will be
+ * STATUS_INVALID_OPERATION, the exception will be EX_ILLEGAL_ARGUMENT.
+ */
+ void registerThermalChangedCallbackWithType(
+ in IThermalChangedCallback callback, in TemperatureType type);
+
+ /**
+ * Unregister an IThermalChangedCallback, used by the Thermal HAL
+ * to receive thermal events when thermal mitigation status changed.
+ *
+ * @param callback the IThermalChangedCallback to use for receiving
+ * thermal events. if nullptr callback is given, the status code will be
+ * STATUS_BAD_VALUE and the operation will fail.
+ *
+ * @throws ScopedAStatus Status of the operation. If status code is not
+ * STATUS_OK, the getMessage() must be populated with the human-readable
+ * error message. If callback is given nullptr, the returned status code
+ * will be STATUS_BAD_VALUE and the exception will be EX_ILLEGAL_ARGUMENT.
+ * if callback is not registered, the returned status code will be
+ * STATUS_INVALID_OPERATION, the exception will be EX_ILLEGAL_ARGUMENT.
+ */
+ void unregisterThermalChangedCallback(in IThermalChangedCallback callback);
+}
diff --git a/thermal/aidl/android/hardware/thermal/IThermalChangedCallback.aidl b/thermal/aidl/android/hardware/thermal/IThermalChangedCallback.aidl
new file mode 100644
index 0000000..6fe2dac
--- /dev/null
+++ b/thermal/aidl/android/hardware/thermal/IThermalChangedCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.thermal;
+
+import android.hardware.thermal.Temperature;
+
+/**
+ * IThermalChangedCallback send throttling notification to clients.
+ */
+@VintfStability
+interface IThermalChangedCallback {
+ /**
+ * Send a thermal throttling event to all ThermalHAL
+ * thermal event listeners.
+ *
+ * @param temperature The temperature associated with the
+ * throttling event.
+ */
+ oneway void notifyThrottling(in Temperature temperature);
+}
diff --git a/thermal/aidl/android/hardware/thermal/Temperature.aidl b/thermal/aidl/android/hardware/thermal/Temperature.aidl
new file mode 100644
index 0000000..f0041ed
--- /dev/null
+++ b/thermal/aidl/android/hardware/thermal/Temperature.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.thermal;
+
+import android.hardware.thermal.TemperatureType;
+import android.hardware.thermal.ThrottlingSeverity;
+
+@VintfStability
+parcelable Temperature {
+ /**
+ * This temperature's type.
+ */
+ TemperatureType type;
+ /**
+ * Name of this temperature matching the TemperatureThreshold.
+ * All temperatures of the same "type" must have a different "name",
+ * e.g., cpu0, battery. Clients use it to match with TemperatureThreshold
+ * struct.
+ */
+ String name;
+ /**
+ * For BCL, this is the current reading of the virtual sensor and the unit is
+ * millivolt, milliamp, percentage for BCL_VOLTAGE, BCL_CURRENT and BCL_PERCENTAGE
+ * respectively. For everything else, this is the current temperature in Celsius.
+ * If not available set by HAL to NAN.
+ */
+ float value;
+ /**
+ * The current throttling level of the sensor.
+ */
+ ThrottlingSeverity throttlingStatus;
+}
diff --git a/thermal/aidl/android/hardware/thermal/TemperatureThreshold.aidl b/thermal/aidl/android/hardware/thermal/TemperatureThreshold.aidl
new file mode 100644
index 0000000..9ecdab3
--- /dev/null
+++ b/thermal/aidl/android/hardware/thermal/TemperatureThreshold.aidl
@@ -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.
+ */
+
+package android.hardware.thermal;
+
+import android.hardware.thermal.TemperatureType;
+
+@VintfStability
+parcelable TemperatureThreshold {
+ /**
+ * This temperature's type.
+ */
+ TemperatureType type;
+ /**
+ * Name of this temperature matching the Temperature struct.
+ * All temperatures of the same "type" must have a different "name",
+ * e.g., cpu0, battery. Clients use it to match Temperature struct.
+ */
+ String name;
+ /**
+ * Hot throttling temperature constant for this temperature sensor in
+ * level defined in ThrottlingSeverity including shutdown. Throttling
+ * happens when temperature >= threshold. If not available, set to NAN.
+ * Unit is same as Temperature's value.
+ * The number of thresholds must be the same as ThrottlingSeverity#len.
+ */
+ float[] hotThrottlingThresholds;
+ /**
+ * Cold throttling temperature constant for this temperature sensor in
+ * level defined in ThrottlingSeverity including shutdown. Throttling
+ * happens when temperature <= threshold. If not available, set to NAN.
+ * Unit is same as Temperature's value.
+ * The number of theresholds must be the same as ThrottlingSeverity#len.
+ */
+ float[] coldThrottlingThresholds;
+}
diff --git a/thermal/aidl/android/hardware/thermal/TemperatureType.aidl b/thermal/aidl/android/hardware/thermal/TemperatureType.aidl
new file mode 100644
index 0000000..aebe7ce
--- /dev/null
+++ b/thermal/aidl/android/hardware/thermal/TemperatureType.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.thermal;
+
+/**
+ * Device temperature types
+ */
+@VintfStability
+@Backing(type="int")
+enum TemperatureType {
+ UNKNOWN = -1,
+ CPU = 0,
+ GPU = 1,
+ BATTERY = 2,
+ SKIN = 3,
+ USB_PORT = 4,
+ POWER_AMPLIFIER = 5,
+ /**
+ * Battery Current Limit - virtual sensors
+ */
+ BCL_VOLTAGE = 6,
+ BCL_CURRENT = 7,
+ BCL_PERCENTAGE = 8,
+ /**
+ * Neural Processing Unit
+ */
+ NPU = 9,
+ TPU = 10,
+ DISPLAY = 11,
+ MODEM = 12,
+ SOC = 13,
+}
diff --git a/thermal/aidl/android/hardware/thermal/ThrottlingSeverity.aidl b/thermal/aidl/android/hardware/thermal/ThrottlingSeverity.aidl
new file mode 100644
index 0000000..29f0724
--- /dev/null
+++ b/thermal/aidl/android/hardware/thermal/ThrottlingSeverity.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.thermal;
+
+/**
+ * Device throttling severity
+ */
+@VintfStability
+@Backing(type="int")
+enum ThrottlingSeverity {
+ /**
+ * Not under throttling.
+ */
+ NONE = 0,
+ /**
+ * Light throttling where UX is not impacted.
+ */
+ LIGHT,
+ /**
+ * Moderate throttling where UX is not largely impacted.
+ */
+ MODERATE,
+ /**
+ * Severe throttling where UX is largely impacted.
+ * Similar to 1.0 throttlingThreshold.
+ */
+ SEVERE,
+ /**
+ * Platform has done everything to reduce power.
+ */
+ CRITICAL,
+ /**
+ * Key components in platform are shutting down due to thermal condition.
+ * Device functionalities will be limited.
+ */
+ EMERGENCY,
+ /**
+ * Need shutdown immediately.
+ */
+ SHUTDOWN,
+}
diff --git a/thermal/aidl/default/Android.bp b/thermal/aidl/default/Android.bp
new file mode 100644
index 0000000..49a578b
--- /dev/null
+++ b/thermal/aidl/default/Android.bp
@@ -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.
+
+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_binary {
+ name: "android.hardware.thermal-service.example",
+ relative_install_path: "hw",
+ init_rc: [":android.hardware.thermal.example.rc"],
+ vintf_fragments: [":android.hardware.thermal.example.xml"],
+ vendor: true,
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.thermal-V1-ndk",
+ ],
+ srcs: [
+ "main.cpp",
+ "Thermal.cpp",
+ ],
+}
+
+filegroup {
+ name: "android.hardware.thermal.example.xml",
+ srcs: ["thermal-example.xml"],
+}
+
+filegroup {
+ name: "android.hardware.thermal.example.rc",
+ srcs: ["thermal-example.rc"],
+}
diff --git a/thermal/aidl/default/Thermal.cpp b/thermal/aidl/default/Thermal.cpp
new file mode 100644
index 0000000..5771e0e
--- /dev/null
+++ b/thermal/aidl/default/Thermal.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "Thermal.h"
+
+#include <android-base/logging.h>
+
+namespace aidl::android::hardware::thermal::impl::example {
+
+using ndk::ScopedAStatus;
+
+ScopedAStatus Thermal::getCoolingDevices(std::vector<CoolingDevice>* /* out_devices */) {
+ LOG(VERBOSE) << __func__;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::getCoolingDevicesWithType(CoolingType in_type,
+ std::vector<CoolingDevice>* /* out_devices */) {
+ LOG(VERBOSE) << __func__ << " CoolingType: " << static_cast<int32_t>(in_type);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::getTemperatures(std::vector<Temperature>* /* out_temperatures */) {
+ LOG(VERBOSE) << __func__;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::getTemperaturesWithType(TemperatureType in_type,
+ std::vector<Temperature>* /* out_temperatures */) {
+ LOG(VERBOSE) << __func__ << " TemperatureType: " << static_cast<int32_t>(in_type);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::getTemperatureThresholds(
+ std::vector<TemperatureThreshold>* /* out_temperatureThresholds */) {
+ LOG(VERBOSE) << __func__;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::getTemperatureThresholdsWithType(
+ TemperatureType in_type,
+ std::vector<TemperatureThreshold>* /* out_temperatureThresholds */) {
+ LOG(VERBOSE) << __func__ << " TemperatureType: " << static_cast<int32_t>(in_type);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::registerThermalChangedCallback(
+ const std::shared_ptr<IThermalChangedCallback>& in_callback) {
+ LOG(VERBOSE) << __func__ << " IThermalChangedCallback: " << in_callback;
+ if (in_callback == nullptr) {
+ return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
+ }
+ if (mCallbacks.find(in_callback) != mCallbacks.end()) {
+ return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
+ }
+ mCallbacks.insert(in_callback);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::registerThermalChangedCallbackWithType(
+ const std::shared_ptr<IThermalChangedCallback>& in_callback, TemperatureType in_type) {
+ LOG(VERBOSE) << __func__ << " IThermalChangedCallback: " << in_callback
+ << ", TemperatureType: " << static_cast<int32_t>(in_type);
+ if (in_callback == nullptr) {
+ return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
+ }
+ if (mCallbacks.find(in_callback) != mCallbacks.end()) {
+ return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
+ }
+ mCallbacks.insert(in_callback);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Thermal::unregisterThermalChangedCallback(
+ const std::shared_ptr<IThermalChangedCallback>& in_callback) {
+ LOG(VERBOSE) << __func__ << " IThermalChangedCallback: " << in_callback;
+ bool found = false;
+ if (in_callback == nullptr) {
+ return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
+ }
+ if (mCallbacks.find(in_callback) == mCallbacks.end()) {
+ return ScopedAStatus::fromStatus(STATUS_INVALID_OPERATION);
+ }
+ mCallbacks.erase(in_callback);
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::thermal::impl::example
diff --git a/thermal/aidl/default/Thermal.h b/thermal/aidl/default/Thermal.h
new file mode 100644
index 0000000..788af4a
--- /dev/null
+++ b/thermal/aidl/default/Thermal.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <set>
+
+#include <aidl/android/hardware/thermal/BnThermal.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace impl {
+namespace example {
+
+class Thermal : public BnThermal {
+ public:
+ ndk::ScopedAStatus getCoolingDevices(std::vector<CoolingDevice>* out_devices) override;
+ ndk::ScopedAStatus getCoolingDevicesWithType(CoolingType in_type,
+ std::vector<CoolingDevice>* out_devices) override;
+
+ ndk::ScopedAStatus getTemperatures(std::vector<Temperature>* out_temperatures) override;
+ ndk::ScopedAStatus getTemperaturesWithType(TemperatureType in_type,
+ std::vector<Temperature>* out_temperatures) override;
+
+ ndk::ScopedAStatus getTemperatureThresholds(
+ std::vector<TemperatureThreshold>* out_temperatureThresholds) override;
+
+ ndk::ScopedAStatus getTemperatureThresholdsWithType(
+ TemperatureType in_type,
+ std::vector<TemperatureThreshold>* out_temperatureThresholds) override;
+
+ ndk::ScopedAStatus registerThermalChangedCallback(
+ const std::shared_ptr<IThermalChangedCallback>& in_callback) override;
+ ndk::ScopedAStatus registerThermalChangedCallbackWithType(
+ const std::shared_ptr<IThermalChangedCallback>& in_callback,
+ TemperatureType in_type) override;
+
+ ndk::ScopedAStatus unregisterThermalChangedCallback(
+ const std::shared_ptr<IThermalChangedCallback>& in_callback) override;
+
+ private:
+ std::set<std::shared_ptr<IThermalChangedCallback>> mCallbacks;
+};
+
+} // namespace example
+} // namespace impl
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/thermal/aidl/default/main.cpp b/thermal/aidl/default/main.cpp
new file mode 100644
index 0000000..61d8ad0
--- /dev/null
+++ b/thermal/aidl/default/main.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "Thermal.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::thermal::impl::example::Thermal;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<Thermal> thermal = ndk::SharedRefBase::make<Thermal>();
+
+ const std::string instance = std::string() + Thermal::descriptor + "/default";
+ binder_status_t status =
+ AServiceManager_addService(thermal->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/thermal/aidl/default/thermal-example.rc b/thermal/aidl/default/thermal-example.rc
new file mode 100644
index 0000000..591ca03
--- /dev/null
+++ b/thermal/aidl/default/thermal-example.rc
@@ -0,0 +1,4 @@
+service vendor.thermal-example /vendor/bin/hw/android.hardware.thermal-service.example
+ class hal
+ user nobody
+ group system
diff --git a/thermal/aidl/default/thermal-example.xml b/thermal/aidl/default/thermal-example.xml
new file mode 100644
index 0000000..bdee744
--- /dev/null
+++ b/thermal/aidl/default/thermal-example.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.thermal</name>
+ <version>1</version>
+ <fqname>IThermal/default</fqname>
+ </hal>
+</manifest>
diff --git a/thermal/aidl/vts/Android.bp b/thermal/aidl/vts/Android.bp
new file mode 100644
index 0000000..b00eb33
--- /dev/null
+++ b/thermal/aidl/vts/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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_test {
+ name: "VtsHalThermalTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalThermalTargetTest.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.thermal-V1-ndk",
+ ],
+ test_suites: [
+ "vts",
+ ],
+}
diff --git a/thermal/aidl/vts/VtsHalThermalTargetTest.cpp b/thermal/aidl/vts/VtsHalThermalTargetTest.cpp
new file mode 100644
index 0000000..b93250e
--- /dev/null
+++ b/thermal/aidl/vts/VtsHalThermalTargetTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 <algorithm>
+#include <chrono>
+#include <cmath>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#define LOG_TAG "thermal_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/thermal/BnThermal.h>
+#include <aidl/android/hardware/thermal/BnThermalChangedCallback.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/binder_status.h>
+#include <gtest/gtest.h>
+
+#include <unistd.h>
+
+namespace aidl::android::hardware::thermal {
+
+namespace {
+
+using ::android::sp;
+using android::hardware::thermal::CoolingDevice;
+using android::hardware::thermal::IThermal;
+using android::hardware::thermal::Temperature;
+using android::hardware::thermal::TemperatureType;
+
+using namespace std::string_literals;
+using namespace std::chrono_literals;
+
+static const Temperature kThrottleTemp = {
+ .type = TemperatureType::SKIN,
+ .name = "test temperature sensor",
+ .value = 98.6,
+ .throttlingStatus = ThrottlingSeverity::CRITICAL,
+};
+
+// Callback class for receiving thermal event notifications from main class
+class ThermalCallback : public BnThermalChangedCallback {
+ public:
+ ndk::ScopedAStatus notifyThrottling(const Temperature&) override {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mInvoke = true;
+ }
+ mNotifyThrottling.notify_all();
+ return ndk::ScopedAStatus::ok();
+ }
+
+ template <typename R, typename P>
+ [[nodiscard]] bool waitForCallback(std::chrono::duration<R, P> duration) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ bool r = mNotifyThrottling.wait_for(lock, duration, [this] { return this->mInvoke; });
+ mInvoke = false;
+ return r;
+ }
+
+ private:
+ std::mutex mMutex;
+ std::condition_variable mNotifyThrottling;
+ bool mInvoke = false;
+};
+
+// The main test class for THERMAL HIDL HAL.
+class ThermalAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
+ ASSERT_NE(binder, nullptr);
+ mThermal = IThermal::fromBinder(ndk::SpAIBinder(binder));
+
+ mThermalCallback = ndk::SharedRefBase::make<ThermalCallback>();
+ ASSERT_NE(mThermalCallback, nullptr);
+ auto ret = mThermal->registerThermalChangedCallback(mThermalCallback);
+ ASSERT_TRUE(ret.isOk());
+ // Expect to fail if register again
+ ret = mThermal->registerThermalChangedCallback(mThermalCallback);
+ ASSERT_FALSE(ret.isOk());
+ ASSERT_TRUE(ret.getStatus() == STATUS_INVALID_OPERATION);
+ }
+
+ void TearDown() override {
+ auto ret = mThermal->unregisterThermalChangedCallback(mThermalCallback);
+ ASSERT_TRUE(ret.isOk());
+ // Expect to fail if unregister again
+ ret = mThermal->unregisterThermalChangedCallback(mThermalCallback);
+ ASSERT_FALSE(ret.isOk());
+ ASSERT_TRUE(ret.getStatus() == STATUS_INVALID_OPERATION);
+ }
+
+ protected:
+ std::shared_ptr<IThermal> mThermal;
+ std::shared_ptr<ThermalCallback> mThermalCallback;
+};
+
+// Test ThermalChangedCallback::notifyThrottling().
+// This just calls into and back from our local ThermalChangedCallback impl.
+TEST_P(ThermalAidlTest, NotifyThrottlingTest) {
+ std::shared_ptr<ThermalCallback> thermalCallback = ndk::SharedRefBase::make<ThermalCallback>();
+ auto ret = thermalCallback->notifyThrottling(kThrottleTemp);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_TRUE(thermalCallback->waitForCallback(200ms));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThermalAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ Thermal, ThermalAidlTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IThermal::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+} // namespace
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
+
+} // namespace aidl::android::hardware::thermal
diff --git a/tv/cec/aidl/Android.bp b/tv/cec/aidl/Android.bp
new file mode 100644
index 0000000..0b0e7cf
--- /dev/null
+++ b/tv/cec/aidl/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.tv.cec",
+ vendor_available: true,
+ srcs: ["android/hardware/tv/cec/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ },
+}
diff --git a/tv/cec/aidl/OWNERS b/tv/cec/aidl/OWNERS
new file mode 100644
index 0000000..d9c6783
--- /dev/null
+++ b/tv/cec/aidl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 826094
+include platform/frameworks/base:/core/java/android/hardware/hdmi/OWNERS
\ No newline at end of file
diff --git a/tv/cec/aidl/TEST_MAPPING b/tv/cec/aidl/TEST_MAPPING
new file mode 100644
index 0000000..ef4bee1
--- /dev/null
+++ b/tv/cec/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "VtsHalTvCecAidlTargetTest"
+ }
+ ]
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/AbortReason.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/AbortReason.aidl
new file mode 100644
index 0000000..7377d81
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/AbortReason.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="int") @VintfStability
+enum AbortReason {
+ UNRECOGNIZED_MODE = 0,
+ NOT_IN_CORRECT_MODE = 1,
+ CANNOT_PROVIDE_SOURCE = 2,
+ INVALID_OPERAND = 3,
+ REFUSED = 4,
+ UNABLE_TO_DETERMINE = 5,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecDeviceType.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecDeviceType.aidl
new file mode 100644
index 0000000..4d991cd
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecDeviceType.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="byte") @VintfStability
+enum CecDeviceType {
+ INACTIVE = -1,
+ TV = 0,
+ RECORDER = 1,
+ TUNER = 3,
+ PLAYBACK = 4,
+ AUDIO_SYSTEM = 5,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecLogicalAddress.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecLogicalAddress.aidl
new file mode 100644
index 0000000..a36935b
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecLogicalAddress.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.tv.cec;
+@Backing(type="byte") @VintfStability
+enum CecLogicalAddress {
+ TV = 0,
+ RECORDER_1 = 1,
+ RECORDER_2 = 2,
+ TUNER_1 = 3,
+ PLAYBACK_1 = 4,
+ AUDIO_SYSTEM = 5,
+ TUNER_2 = 6,
+ TUNER_3 = 7,
+ PLAYBACK_2 = 8,
+ RECORDER_3 = 9,
+ TUNER_4 = 10,
+ PLAYBACK_3 = 11,
+ BACKUP_1 = 12,
+ BACKUP_2 = 13,
+ FREE_USE = 14,
+ BROADCAST = 15,
+ UNREGISTERED = 15,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessage.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessage.aidl
new file mode 100644
index 0000000..5ce5ce8
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessage.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@VintfStability
+parcelable CecMessage {
+ android.hardware.tv.cec.CecLogicalAddress initiator;
+ android.hardware.tv.cec.CecLogicalAddress destination;
+ byte[] body;
+ const int MAX_MESSAGE_BODY_LENGTH = 15;
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessageType.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessageType.aidl
new file mode 100644
index 0000000..61ebb94
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/CecMessageType.aidl
@@ -0,0 +1,111 @@
+/*
+ * 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.tv.cec;
+@Backing(type="int") @VintfStability
+enum CecMessageType {
+ FEATURE_ABORT = 0,
+ IMAGE_VIEW_ON = 4,
+ TUNER_STEP_INCREMENT = 5,
+ TUNER_STEP_DECREMENT = 6,
+ TUNER_DEVICE_STATUS = 7,
+ GIVE_TUNER_DEVICE_STATUS = 8,
+ RECORD_ON = 9,
+ RECORD_STATUS = 10,
+ RECORD_OFF = 11,
+ TEXT_VIEW_ON = 13,
+ RECORD_TV_SCREEN = 15,
+ GIVE_DECK_STATUS = 26,
+ DECK_STATUS = 27,
+ SET_MENU_LANGUAGE = 50,
+ CLEAR_ANALOG_TIMER = 51,
+ SET_ANALOG_TIMER = 52,
+ TIMER_STATUS = 53,
+ STANDBY = 54,
+ PLAY = 65,
+ DECK_CONTROL = 66,
+ TIMER_CLEARED_STATUS = 67,
+ USER_CONTROL_PRESSED = 68,
+ USER_CONTROL_RELEASED = 69,
+ GIVE_OSD_NAME = 70,
+ SET_OSD_NAME = 71,
+ SET_OSD_STRING = 100,
+ SET_TIMER_PROGRAM_TITLE = 103,
+ SYSTEM_AUDIO_MODE_REQUEST = 112,
+ GIVE_AUDIO_STATUS = 113,
+ SET_SYSTEM_AUDIO_MODE = 114,
+ REPORT_AUDIO_STATUS = 122,
+ GIVE_SYSTEM_AUDIO_MODE_STATUS = 125,
+ SYSTEM_AUDIO_MODE_STATUS = 126,
+ ROUTING_CHANGE = 128,
+ ROUTING_INFORMATION = 129,
+ ACTIVE_SOURCE = 130,
+ GIVE_PHYSICAL_ADDRESS = 131,
+ REPORT_PHYSICAL_ADDRESS = 132,
+ REQUEST_ACTIVE_SOURCE = 133,
+ SET_STREAM_PATH = 134,
+ DEVICE_VENDOR_ID = 135,
+ VENDOR_COMMAND = 137,
+ VENDOR_REMOTE_BUTTON_DOWN = 138,
+ VENDOR_REMOTE_BUTTON_UP = 139,
+ GIVE_DEVICE_VENDOR_ID = 140,
+ MENU_REQUEST = 141,
+ MENU_STATUS = 142,
+ GIVE_DEVICE_POWER_STATUS = 143,
+ REPORT_POWER_STATUS = 144,
+ GET_MENU_LANGUAGE = 145,
+ SELECT_ANALOG_SERVICE = 146,
+ SELECT_DIGITAL_SERVICE = 147,
+ SET_DIGITAL_TIMER = 151,
+ CLEAR_DIGITAL_TIMER = 153,
+ SET_AUDIO_RATE = 154,
+ INACTIVE_SOURCE = 157,
+ CEC_VERSION = 158,
+ GET_CEC_VERSION = 159,
+ VENDOR_COMMAND_WITH_ID = 160,
+ CLEAR_EXTERNAL_TIMER = 161,
+ SET_EXTERNAL_TIMER = 162,
+ REPORT_SHORT_AUDIO_DESCRIPTOR = 163,
+ REQUEST_SHORT_AUDIO_DESCRIPTOR = 164,
+ INITIATE_ARC = 192,
+ REPORT_ARC_INITIATED = 193,
+ REPORT_ARC_TERMINATED = 194,
+ REQUEST_ARC_INITIATION = 195,
+ REQUEST_ARC_TERMINATION = 196,
+ TERMINATE_ARC = 197,
+ ABORT = 255,
+ GIVE_FEATURES = 165,
+ REPORT_FEATURES = 166,
+ REQUEST_CURRENT_LATENCY = 167,
+ REPORT_CURRENT_LATENCY = 168,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCec.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCec.aidl
new file mode 100644
index 0000000..cf8425e
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCec.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.tv.cec;
+@VintfStability
+interface IHdmiCec {
+ android.hardware.tv.cec.Result addLogicalAddress(in android.hardware.tv.cec.CecLogicalAddress addr);
+ void clearLogicalAddress();
+ void enableAudioReturnChannel(in int portId, in boolean enable);
+ int getCecVersion();
+ int getPhysicalAddress();
+ int getVendorId();
+ android.hardware.tv.cec.SendMessageResult sendMessage(in android.hardware.tv.cec.CecMessage message);
+ void setCallback(in android.hardware.tv.cec.IHdmiCecCallback callback);
+ void setLanguage(in String language);
+ void enableWakeupByOtp(in boolean value);
+ void enableCec(in boolean value);
+ void enableSystemCecControl(in boolean value);
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCecCallback.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCecCallback.aidl
new file mode 100644
index 0000000..1918765
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/IHdmiCecCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@VintfStability
+interface IHdmiCecCallback {
+ oneway void onCecMessage(in android.hardware.tv.cec.CecMessage message);
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/Result.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/Result.aidl
new file mode 100644
index 0000000..a5ba276
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/Result.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="byte") @VintfStability
+enum Result {
+ SUCCESS = 0,
+ FAILURE_UNKNOWN = 1,
+ FAILURE_INVALID_ARGS = 2,
+ FAILURE_INVALID_STATE = 3,
+ FAILURE_NOT_SUPPORTED = 4,
+ FAILURE_BUSY = 5,
+}
diff --git a/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/SendMessageResult.aidl b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/SendMessageResult.aidl
new file mode 100644
index 0000000..58206c8
--- /dev/null
+++ b/tv/cec/aidl/aidl_api/android.hardware.tv.cec/current/android/hardware/tv/cec/SendMessageResult.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.cec;
+@Backing(type="byte") @VintfStability
+enum SendMessageResult {
+ SUCCESS = 0,
+ NACK = 1,
+ BUSY = 2,
+ FAIL = 3,
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/tv/cec/aidl/android/hardware/tv/cec/AbortReason.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to tv/cec/aidl/android/hardware/tv/cec/AbortReason.aidl
index 4b82dd0..3ae23ec 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/tv/cec/aidl/android/hardware/tv/cec/AbortReason.aidl
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.tv.cec;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+/**
+ * Operand description [Abort Reason]
+ */
+@VintfStability
+@Backing(type="int")
+enum AbortReason {
+ UNRECOGNIZED_MODE = 0,
+ NOT_IN_CORRECT_MODE = 1,
+ CANNOT_PROVIDE_SOURCE = 2,
+ INVALID_OPERAND = 3,
+ REFUSED = 4,
+ UNABLE_TO_DETERMINE = 5,
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/tv/cec/aidl/android/hardware/tv/cec/CecDeviceType.aidl
similarity index 60%
rename from audio/aidl/default/include/visualizer-impl/Visualizer.h
rename to tv/cec/aidl/android/hardware/tv/cec/CecDeviceType.aidl
index 4b82dd0..16dfbec 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecDeviceType.aidl
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.tv.cec;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+@VintfStability
+@Backing(type="byte")
+enum CecDeviceType {
+ INACTIVE = -1,
+ TV = 0,
+ RECORDER = 1,
+ TUNER = 3,
+ PLAYBACK = 4,
+ AUDIO_SYSTEM = 5,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/CecLogicalAddress.aidl b/tv/cec/aidl/android/hardware/tv/cec/CecLogicalAddress.aidl
new file mode 100644
index 0000000..fbf5328
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecLogicalAddress.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.
+ */
+
+package android.hardware.tv.cec;
+
+@VintfStability
+@Backing(type="byte")
+enum CecLogicalAddress {
+ TV = 0,
+ RECORDER_1 = 1,
+ RECORDER_2 = 2,
+ TUNER_1 = 3,
+ PLAYBACK_1 = 4,
+ AUDIO_SYSTEM = 5,
+ TUNER_2 = 6,
+ TUNER_3 = 7,
+ PLAYBACK_2 = 8,
+ RECORDER_3 = 9,
+ TUNER_4 = 10,
+ PLAYBACK_3 = 11,
+ BACKUP_1 = 12,
+ BACKUP_2 = 13,
+ FREE_USE = 14,
+ BROADCAST = 15,
+ UNREGISTERED = 15,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/CecMessage.aidl b/tv/cec/aidl/android/hardware/tv/cec/CecMessage.aidl
new file mode 100644
index 0000000..b126045
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecMessage.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.cec;
+
+import android.hardware.tv.cec.CecLogicalAddress;
+
+@VintfStability
+parcelable CecMessage {
+ /**
+ * Maximum length of the message body
+ */
+ const int MAX_MESSAGE_BODY_LENGTH = 15;
+ /**
+ * logical address of the initiator
+ */
+ CecLogicalAddress initiator;
+ /**
+ * logical address of destination
+ */
+ CecLogicalAddress destination;
+ /**
+ * The maximum size of body is 15 (MAX_MESSAGE_BODY_LENGTH) as specified in
+ * the section 6 of the CEC Spec 1.4b. Overflowed data must be ignored.
+ */
+ byte[] body;
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/CecMessageType.aidl b/tv/cec/aidl/android/hardware/tv/cec/CecMessageType.aidl
new file mode 100644
index 0000000..b544a91
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/CecMessageType.aidl
@@ -0,0 +1,96 @@
+/*
+ * 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.tv.cec;
+
+@VintfStability
+@Backing(type="int")
+enum CecMessageType {
+ FEATURE_ABORT = 0x00,
+ IMAGE_VIEW_ON = 0x04,
+ TUNER_STEP_INCREMENT = 0x05,
+ TUNER_STEP_DECREMENT = 0x06,
+ TUNER_DEVICE_STATUS = 0x07,
+ GIVE_TUNER_DEVICE_STATUS = 0x08,
+ RECORD_ON = 0x09,
+ RECORD_STATUS = 0x0A,
+ RECORD_OFF = 0x0B,
+ TEXT_VIEW_ON = 0x0D,
+ RECORD_TV_SCREEN = 0x0F,
+ GIVE_DECK_STATUS = 0x1A,
+ DECK_STATUS = 0x1B,
+ SET_MENU_LANGUAGE = 0x32,
+ CLEAR_ANALOG_TIMER = 0x33,
+ SET_ANALOG_TIMER = 0x34,
+ TIMER_STATUS = 0x35,
+ STANDBY = 0x36,
+ PLAY = 0x41,
+ DECK_CONTROL = 0x42,
+ TIMER_CLEARED_STATUS = 0x43,
+ USER_CONTROL_PRESSED = 0x44,
+ USER_CONTROL_RELEASED = 0x45,
+ GIVE_OSD_NAME = 0x46,
+ SET_OSD_NAME = 0x47,
+ SET_OSD_STRING = 0x64,
+ SET_TIMER_PROGRAM_TITLE = 0x67,
+ SYSTEM_AUDIO_MODE_REQUEST = 0x70,
+ GIVE_AUDIO_STATUS = 0x71,
+ SET_SYSTEM_AUDIO_MODE = 0x72,
+ REPORT_AUDIO_STATUS = 0x7A,
+ GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D,
+ SYSTEM_AUDIO_MODE_STATUS = 0x7E,
+ ROUTING_CHANGE = 0x80,
+ ROUTING_INFORMATION = 0x81,
+ ACTIVE_SOURCE = 0x82,
+ GIVE_PHYSICAL_ADDRESS = 0x83,
+ REPORT_PHYSICAL_ADDRESS = 0x84,
+ REQUEST_ACTIVE_SOURCE = 0x85,
+ SET_STREAM_PATH = 0x86,
+ DEVICE_VENDOR_ID = 0x87,
+ VENDOR_COMMAND = 0x89,
+ VENDOR_REMOTE_BUTTON_DOWN = 0x8A,
+ VENDOR_REMOTE_BUTTON_UP = 0x8B,
+ GIVE_DEVICE_VENDOR_ID = 0x8C,
+ MENU_REQUEST = 0x8D,
+ MENU_STATUS = 0x8E,
+ GIVE_DEVICE_POWER_STATUS = 0x8F,
+ REPORT_POWER_STATUS = 0x90,
+ GET_MENU_LANGUAGE = 0x91,
+ SELECT_ANALOG_SERVICE = 0x92,
+ SELECT_DIGITAL_SERVICE = 0x93,
+ SET_DIGITAL_TIMER = 0x97,
+ CLEAR_DIGITAL_TIMER = 0x99,
+ SET_AUDIO_RATE = 0x9A,
+ INACTIVE_SOURCE = 0x9D,
+ CEC_VERSION = 0x9E,
+ GET_CEC_VERSION = 0x9F,
+ VENDOR_COMMAND_WITH_ID = 0xA0,
+ CLEAR_EXTERNAL_TIMER = 0xA1,
+ SET_EXTERNAL_TIMER = 0xA2,
+ REPORT_SHORT_AUDIO_DESCRIPTOR = 0xA3,
+ REQUEST_SHORT_AUDIO_DESCRIPTOR = 0xA4,
+ INITIATE_ARC = 0xC0,
+ REPORT_ARC_INITIATED = 0xC1,
+ REPORT_ARC_TERMINATED = 0xC2,
+ REQUEST_ARC_INITIATION = 0xC3,
+ REQUEST_ARC_TERMINATION = 0xC4,
+ TERMINATE_ARC = 0xC5,
+ ABORT = 0xFF,
+ GIVE_FEATURES = 0xA5,
+ REPORT_FEATURES = 0xA6,
+ REQUEST_CURRENT_LATENCY = 0xA7,
+ REPORT_CURRENT_LATENCY = 0xA8,
+}
diff --git a/tv/cec/aidl/android/hardware/tv/cec/IHdmiCec.aidl b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCec.aidl
new file mode 100644
index 0000000..dbf7139
--- /dev/null
+++ b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCec.aidl
@@ -0,0 +1,156 @@
+/*
+ * 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.tv.cec;
+
+import android.hardware.tv.cec.CecLogicalAddress;
+import android.hardware.tv.cec.CecMessage;
+import android.hardware.tv.cec.IHdmiCecCallback;
+import android.hardware.tv.cec.Result;
+import android.hardware.tv.cec.SendMessageResult;
+
+/**
+ * HDMI-CEC HAL interface definition.
+ */
+@VintfStability
+interface IHdmiCec {
+ /**
+ * Passes the logical address that must be used in this system.
+ *
+ * HAL must use it to configure the hardware so that the CEC commands
+ * addressed the given logical address can be filtered in. This method must
+ * be able to be called as many times as necessary in order to support
+ * multiple logical devices.
+ *
+ * @param addr Logical address that must be used in this system. It must be
+ * in the range of valid logical addresses for the call to succeed.
+ * @return Result status of the operation. SUCCESS if successful,
+ * FAILURE_INVALID_ARGS if the given logical address is invalid,
+ * FAILURE_BUSY if device or resource is busy
+ */
+ Result addLogicalAddress(in CecLogicalAddress addr);
+
+ /**
+ * Clears all the logical addresses.
+ *
+ * It is used when the system doesn't need to process CEC command any more,
+ * hence to tell HAL to stop receiving commands from the CEC bus, and change
+ * the state back to the beginning.
+ */
+ void clearLogicalAddress();
+
+ /**
+ * Configures ARC circuit in the hardware logic to start or stop the
+ * feature.
+ *
+ * @param portId Port id to be configured.
+ * @param enable Flag must be either true to start the feature or false to
+ * stop it.
+ */
+ void enableAudioReturnChannel(in int portId, in boolean enable);
+
+ /**
+ * Returns the CEC version supported by underlying hardware.
+ *
+ * @return the CEC version supported by underlying hardware.
+ */
+ int getCecVersion();
+
+ /**
+ * Gets the CEC physical address.
+ *
+ * The physical address depends on the topology of the network formed by
+ * connected HDMI devices. It is therefore likely to change if the cable is
+ * plugged off and on again. It is advised to call getPhysicalAddress to get
+ * the updated address when hot plug event takes place.
+ *
+ * @param out addr Physical address of this device.
+ */
+ int getPhysicalAddress();
+
+ /**
+ * Gets the identifier of the vendor.
+ *
+ * @return Identifier of the vendor that is the 24-bit unique
+ * company ID obtained from the IEEE Registration Authority
+ * Committee (RAC). The upper 8 bits must be 0.
+ */
+ int getVendorId();
+
+ /**
+ * Transmits HDMI-CEC message to other HDMI device.
+ *
+ * The method must be designed to return in a certain amount of time and not
+ * hanging forever which may happen if CEC signal line is pulled low for
+ * some reason.
+ *
+ * It must try retransmission at least once as specified in the section '7.1
+ * Frame Re-transmissions' of the CEC Spec 1.4b.
+ *
+ * @param message CEC message to be sent to other HDMI device.
+ * @return Result status of the operation. SUCCESS if successful,
+ * NACK if the sent message is not acknowledged,
+ * BUSY if the CEC bus is busy,
+ * FAIL if the message could not be sent.
+ */
+ SendMessageResult sendMessage(in CecMessage message);
+
+ /**
+ * Sets a callback that HDMI-CEC HAL must later use for incoming CEC
+ * messages.
+ *
+ * @param callback Callback object to pass hdmi events to the system. The
+ * previously registered callback must be replaced with this one.
+ * setCallback(null) should deregister the callback.
+ */
+ void setCallback(in IHdmiCecCallback callback);
+
+ /**
+ * Passes the updated language information of Android system. Contains
+ * three-letter code as defined in ISO/FDIS 639-2. Must be used for HAL to
+ * respond to <Get Menu Language> while in standby mode.
+ *
+ * @param language Three-letter code defined in ISO/FDIS 639-2. Must be
+ * lowercase letters. (e.g., eng for English)
+ */
+ void setLanguage(in String language);
+
+ /**
+ * Determines whether a TV panel device in standby mode should wake up when
+ * it receives an OTP (One Touch Play) from a source device.
+ *
+ * @param value If true, the TV device will wake up when OTP is received
+ * and if false, the TV device will not wake up for an OTP.
+ */
+ void enableWakeupByOtp(in boolean value);
+
+ /**
+ * Switch to enable or disable CEC on the device.
+ *
+ * @param value If true, the device will have all CEC functionalities
+ * and if false, the device will not perform any CEC functions.
+ */
+ void enableCec(in boolean value);
+
+ /**
+ * Determines which module processes CEC messages - the Android framework or
+ * the HAL.
+ *
+ * @param value If true, the Android framework will actively process CEC
+ * messages and if false, only the HAL will process the CEC messages.
+ */
+ void enableSystemCecControl(in boolean value);
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCecCallback.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to tv/cec/aidl/android/hardware/tv/cec/IHdmiCecCallback.aidl
index 4b82dd0..4934a64 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/tv/cec/aidl/android/hardware/tv/cec/IHdmiCecCallback.aidl
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.tv.cec;
-#include <cstdlib>
+import android.hardware.tv.cec.CecMessage;
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+/**
+ * Callbacks from the HAL implementation to notify the system of new events.
+ */
+@VintfStability
+oneway interface IHdmiCecCallback {
+ /**
+ * The callback function that must be called by HAL implementation to notify
+ * the system of new CEC message arrival.
+ */
+ void onCecMessage(in CecMessage message);
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/tv/cec/aidl/android/hardware/tv/cec/Result.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to tv/cec/aidl/android/hardware/tv/cec/Result.aidl
index 4b82dd0..3184c46 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/tv/cec/aidl/android/hardware/tv/cec/Result.aidl
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.tv.cec;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+@VintfStability
+@Backing(type="byte")
+enum Result {
+ SUCCESS = 0,
+ FAILURE_UNKNOWN = 1,
+ FAILURE_INVALID_ARGS = 2,
+ FAILURE_INVALID_STATE = 3,
+ FAILURE_NOT_SUPPORTED = 4,
+ FAILURE_BUSY = 5,
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/tv/cec/aidl/android/hardware/tv/cec/SendMessageResult.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to tv/cec/aidl/android/hardware/tv/cec/SendMessageResult.aidl
index 4b82dd0..8cb98bc 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/tv/cec/aidl/android/hardware/tv/cec/SendMessageResult.aidl
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.tv.cec;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+/**
+ * error code used for send_message.
+ */
+@VintfStability
+@Backing(type="byte")
+enum SendMessageResult {
+ SUCCESS = 0,
+ NACK = 1,
+ BUSY = 2,
+ FAIL = 3,
+}
diff --git a/tv/cec/aidl/default/Android.bp b/tv/cec/aidl/default/Android.bp
new file mode 100644
index 0000000..5479601
--- /dev/null
+++ b/tv/cec/aidl/default/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+ name: "android.hardware.tv.cec-service",
+ vintf_fragments: ["android.hardware.tv.cec-service.xml"],
+ relative_install_path: "hw",
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ init_rc: ["android.hardware.tv.cec-service.rc"],
+ srcs: [
+ "serviceMock.cpp",
+ "HdmiCecMock.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "liblog",
+ "libbase",
+ "libutils",
+ "libhardware",
+ "libhidlbase",
+ "android.hardware.tv.cec-V1-ndk",
+ ],
+}
+
+cc_fuzz {
+ name: "android.hardware.tv.cec-service_fuzzer",
+ defaults: ["service_fuzzer_defaults"],
+ static_libs: [
+ "android.hardware.tv.cec-V1-ndk",
+ "liblog",
+ ],
+ srcs: [
+ "fuzzer.cpp",
+ "HdmiCecMock.cpp",
+ ],
+ fuzz_config: {
+ componentid: 826094,
+ },
+}
diff --git a/tv/cec/aidl/default/HdmiCecMock.cpp b/tv/cec/aidl/default/HdmiCecMock.cpp
new file mode 100644
index 0000000..d8d655b
--- /dev/null
+++ b/tv/cec/aidl/default/HdmiCecMock.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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 "android.hardware.tv.cec"
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <utils/Log.h>
+
+#include <hardware/hardware.h>
+#include <hardware/hdmi_cec.h>
+#include "HdmiCecMock.h"
+
+using ndk::ScopedAStatus;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace implementation {
+
+void HdmiCecMock::serviceDied(void* cookie) {
+ ALOGE("HdmiCecMock died");
+ auto hdmiCecMock = static_cast<HdmiCecMock*>(cookie);
+ hdmiCecMock->mCecThreadRun = false;
+}
+
+ScopedAStatus HdmiCecMock::addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) {
+ // Have a list to maintain logical addresses
+ mLogicalAddresses.push_back(addr);
+ *_aidl_return = Result::SUCCESS;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::clearLogicalAddress() {
+ // Remove logical address from the list
+ mLogicalAddresses = {};
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) {
+ // Maintain ARC status
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::getCecVersion(int32_t* _aidl_return) {
+ // Maintain a cec version and return it
+ *_aidl_return = mCecVersion;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::getPhysicalAddress(int32_t* _aidl_return) {
+ // Maintain a physical address and return it
+ // Default 0xFFFF, update on hotplug event
+ *_aidl_return = mPhysicalAddress;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::getVendorId(int32_t* _aidl_return) {
+ *_aidl_return = mCecVendorId;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::sendMessage(const CecMessage& message, SendMessageResult* _aidl_return) {
+ if (message.body.size() == 0) {
+ *_aidl_return = SendMessageResult::NACK;
+ } else {
+ sendMessageToFifo(message);
+ *_aidl_return = SendMessageResult::SUCCESS;
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::setCallback(const std::shared_ptr<IHdmiCecCallback>& callback) {
+ // If callback is null, mCallback is also set to null so we do not call the old callback.
+ mCallback = callback;
+
+ if (callback != nullptr) {
+ AIBinder_linkToDeath(this->asBinder().get(), mDeathRecipient.get(), 0 /* cookie */);
+
+ mInputFile = open(CEC_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
+ mOutputFile = open(CEC_MSG_OUT_FIFO, O_RDWR | O_CLOEXEC);
+ pthread_create(&mThreadId, NULL, __threadLoop, this);
+ pthread_setname_np(mThreadId, "hdmi_cec_loop");
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::setLanguage(const std::string& language) {
+ if (language.size() != 3) {
+ LOG(ERROR) << "Wrong language code: expected 3 letters, but it was " << language.size()
+ << ".";
+ return ScopedAStatus::ok();
+ }
+ // TODO Validate if language is a valid language code
+ const char* languageStr = language.c_str();
+ int convertedLanguage = ((languageStr[0] & 0xFF) << 16) | ((languageStr[1] & 0xFF) << 8) |
+ (languageStr[2] & 0xFF);
+ mOptionLanguage = convertedLanguage;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableWakeupByOtp(bool value) {
+ mOptionWakeUp = value;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableCec(bool value) {
+ mOptionEnableCec = value;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiCecMock::enableSystemCecControl(bool value) {
+ mOptionSystemCecControl = value;
+ return ScopedAStatus::ok();
+}
+
+void* HdmiCecMock::__threadLoop(void* user) {
+ HdmiCecMock* const self = static_cast<HdmiCecMock*>(user);
+ self->threadLoop();
+ return 0;
+}
+
+int HdmiCecMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
+ if (msgCount <= 0 || !buf) {
+ return 0;
+ }
+
+ int ret = -1;
+ // Maybe blocked at driver
+ ret = read(mInputFile, buf, msgCount);
+ if (ret < 0) {
+ ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", CEC_MSG_IN_FIFO, ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+int HdmiCecMock::sendMessageToFifo(const CecMessage& message) {
+ unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH + 1] = {0};
+ int ret = -1;
+
+ msgBuf[0] = ((static_cast<uint8_t>(message.initiator) & 0xf) << 4) |
+ (static_cast<uint8_t>(message.destination) & 0xf);
+
+ size_t length = std::min(static_cast<size_t>(message.body.size()),
+ static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
+ for (size_t i = 0; i < length; ++i) {
+ msgBuf[i + 1] = static_cast<unsigned char>(message.body[i]);
+ }
+
+ // Open the output pipe for writing outgoing cec message
+ mOutputFile = open(CEC_MSG_OUT_FIFO, O_WRONLY | O_CLOEXEC);
+ if (mOutputFile < 0) {
+ ALOGD("[halimp_aidl] file open failed for writing");
+ return -1;
+ }
+
+ // Write message into the output pipe
+ ret = write(mOutputFile, msgBuf, length + 1);
+ close(mOutputFile);
+ if (ret < 0) {
+ ALOGE("[halimp_aidl] write :%s failed, ret:%d\n", CEC_MSG_OUT_FIFO, ret);
+ return -1;
+ }
+ return ret;
+}
+
+void HdmiCecMock::printCecMsgBuf(const char* msg_buf, int len) {
+ int i, size = 0;
+ const int bufSize = CEC_MESSAGE_BODY_MAX_LENGTH * 3;
+ // Use 2 characters for each byte in the message plus 1 space
+ char buf[bufSize] = {0};
+
+ // Messages longer than max length will be truncated.
+ for (i = 0; i < len && size < bufSize; i++) {
+ size += sprintf(buf + size, " %02x", msg_buf[i]);
+ }
+ ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
+}
+
+void HdmiCecMock::handleCecMessage(unsigned char* msgBuf, int msgSize) {
+ CecMessage message;
+ size_t length = std::min(static_cast<size_t>(msgSize - 1),
+ static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
+ message.body.resize(length);
+
+ for (size_t i = 0; i < length; ++i) {
+ message.body[i] = static_cast<uint8_t>(msgBuf[i + 1]);
+ ALOGD("[halimp_aidl] msg body %x", message.body[i]);
+ }
+
+ message.initiator = static_cast<CecLogicalAddress>((msgBuf[0] >> 4) & 0xf);
+ ALOGD("[halimp_aidl] msg init %hhd", message.initiator);
+ message.destination = static_cast<CecLogicalAddress>((msgBuf[0] >> 0) & 0xf);
+ ALOGD("[halimp_aidl] msg dest %hhd", message.destination);
+
+ if (mCallback != nullptr) {
+ mCallback->onCecMessage(message);
+ }
+}
+
+void HdmiCecMock::threadLoop() {
+ ALOGD("[halimp_aidl] threadLoop start.");
+ unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH];
+ int r = -1;
+
+ // Open the input pipe
+ while (mInputFile < 0) {
+ usleep(1000 * 1000);
+ mInputFile = open(CEC_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
+ }
+ ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
+
+ while (mCecThreadRun) {
+ if (!mOptionSystemCecControl) {
+ usleep(1000 * 1000);
+ continue;
+ }
+
+ memset(msgBuf, 0, sizeof(msgBuf));
+ // Try to get a message from dev.
+ // echo -n -e '\x04\x83' >> /dev/cec
+ r = readMessageFromFifo(msgBuf, CEC_MESSAGE_BODY_MAX_LENGTH);
+ if (r <= 1) {
+ // Ignore received ping messages
+ continue;
+ }
+
+ printCecMsgBuf((const char*)msgBuf, r);
+
+ if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
+ // The message is a hotplug event, handled by HDMI HAL.
+ continue;
+ }
+
+ handleCecMessage(msgBuf, r);
+ }
+
+ ALOGD("[halimp_aidl] thread end.");
+}
+
+HdmiCecMock::HdmiCecMock() {
+ ALOGE("[halimp_aidl] Opening a virtual CEC HAL for testing and virtual machine.");
+ mCallback = nullptr;
+ mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
+}
+
+} // namespace implementation
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/cec/aidl/default/HdmiCecMock.h b/tv/cec/aidl/default/HdmiCecMock.h
new file mode 100644
index 0000000..08f4d6f
--- /dev/null
+++ b/tv/cec/aidl/default/HdmiCecMock.h
@@ -0,0 +1,94 @@
+/*
+ * 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 <aidl/android/hardware/tv/cec/BnHdmiCec.h>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace implementation {
+
+using ::aidl::android::hardware::tv::cec::BnHdmiCec;
+using ::aidl::android::hardware::tv::cec::CecLogicalAddress;
+using ::aidl::android::hardware::tv::cec::CecMessage;
+using ::aidl::android::hardware::tv::cec::IHdmiCec;
+using ::aidl::android::hardware::tv::cec::IHdmiCecCallback;
+using ::aidl::android::hardware::tv::cec::Result;
+using ::aidl::android::hardware::tv::cec::SendMessageResult;
+
+#define CEC_MSG_IN_FIFO "/dev/cec_aidl_in_pipe"
+#define CEC_MSG_OUT_FIFO "/dev/cec_aidl_out_pipe"
+
+struct HdmiCecMock : public BnHdmiCec {
+ HdmiCecMock();
+ ::ndk::ScopedAStatus addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) override;
+ ::ndk::ScopedAStatus clearLogicalAddress() override;
+ ::ndk::ScopedAStatus enableAudioReturnChannel(int32_t portId, bool enable) override;
+ ::ndk::ScopedAStatus getCecVersion(int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus getPhysicalAddress(int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus getVendorId(int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus sendMessage(const CecMessage& message,
+ SendMessageResult* _aidl_return) override;
+ ::ndk::ScopedAStatus setCallback(const std::shared_ptr<IHdmiCecCallback>& callback) override;
+ ::ndk::ScopedAStatus setLanguage(const std::string& language) override;
+ ::ndk::ScopedAStatus enableWakeupByOtp(bool value) override;
+ ::ndk::ScopedAStatus enableCec(bool value) override;
+ ::ndk::ScopedAStatus enableSystemCecControl(bool value) override;
+ void printCecMsgBuf(const char* msg_buf, int len);
+
+ private:
+ static void* __threadLoop(void* data);
+ void threadLoop();
+ int readMessageFromFifo(unsigned char* buf, int msgCount);
+ int sendMessageToFifo(const CecMessage& message);
+ void handleCecMessage(unsigned char* msgBuf, int length);
+
+ private:
+ static void serviceDied(void* cookie);
+ std::shared_ptr<IHdmiCecCallback> mCallback;
+
+ // Variables for the virtual cec hal impl
+ uint16_t mPhysicalAddress = 0xFFFF;
+ vector<CecLogicalAddress> mLogicalAddresses;
+ int32_t mCecVersion = 0x06;
+ uint32_t mCecVendorId = 0x01;
+
+ // CEC Option value
+ bool mOptionWakeUp = 0;
+ bool mOptionEnableCec = 0;
+ bool mOptionSystemCecControl = 0;
+ int mOptionLanguage;
+
+ // Testing variables
+ // Input file descriptor
+ int mInputFile;
+ // Output file descriptor
+ int mOutputFile;
+ bool mCecThreadRun = true;
+ pthread_t mThreadId = 0;
+
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+} // namespace implementation
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/cec/aidl/default/android.hardware.tv.cec-service.rc b/tv/cec/aidl/default/android.hardware.tv.cec-service.rc
new file mode 100644
index 0000000..c79520c
--- /dev/null
+++ b/tv/cec/aidl/default/android.hardware.tv.cec-service.rc
@@ -0,0 +1,5 @@
+service vendor.cec-default /vendor/bin/hw/android.hardware.tv.cec-service
+ interface aidl android.hardware.tv.cec.IHdmiCec/default
+ class hal
+ user system
+ group system
diff --git a/tv/cec/aidl/default/android.hardware.tv.cec-service.xml b/tv/cec/aidl/default/android.hardware.tv.cec-service.xml
new file mode 100644
index 0000000..e68450d
--- /dev/null
+++ b/tv/cec/aidl/default/android.hardware.tv.cec-service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.tv.cec</name>
+ <version>1</version>
+ <interface>
+ <name>IHdmiCec</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/tv/cec/aidl/default/fuzzer.cpp b/tv/cec/aidl/default/fuzzer.cpp
new file mode 100644
index 0000000..9f6a9ac
--- /dev/null
+++ b/tv/cec/aidl/default/fuzzer.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <HdmiCecMock.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::fuzzService;
+using android::hardware::tv::cec::implementation::HdmiCecMock;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto hdmiCecAidl = SharedRefBase::make<HdmiCecMock>();
+
+ fuzzService(hdmiCecAidl->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/tv/cec/aidl/default/serviceMock.cpp b/tv/cec/aidl/default/serviceMock.cpp
new file mode 100644
index 0000000..ab86c3f
--- /dev/null
+++ b/tv/cec/aidl/default/serviceMock.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.cec-service-shim"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Log.h>
+#include "HdmiCecMock.h"
+
+using android::hardware::tv::cec::implementation::HdmiCecMock;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ std::shared_ptr<HdmiCecMock> hdmiCecAidl = ndk::SharedRefBase::make<HdmiCecMock>();
+ const std::string instance = std::string() + HdmiCecMock::descriptor + "/default";
+ binder_status_t status =
+ AServiceManager_addService(hdmiCecAidl->asBinder().get(), instance.c_str());
+ CHECK_EQ(status, STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return 0;
+}
diff --git a/tv/cec/aidl/vts/functional/Android.bp b/tv/cec/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..37fbaf0
--- /dev/null
+++ b/tv/cec/aidl/vts/functional/Android.bp
@@ -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 {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalTvCecAidlTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalTvCecAidlTargetTest.cpp"],
+ static_libs: [
+ "android.hardware.tv.cec-V1-ndk",
+ "android.hardware.tv.hdmi-V1-ndk",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ disable_framework: true,
+}
diff --git a/tv/cec/aidl/vts/functional/AndroidTest.xml b/tv/cec/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..8604147
--- /dev/null
+++ b/tv/cec/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalTvCecAidlTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalTvCecAidlTargetTest->/data/local/tmp/VtsHalTvCecAidlTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalTvCecAidlTargetTest" />
+ <option name="native-test-timeout" value="30m" />
+ </test>
+</configuration>
diff --git a/tv/cec/aidl/vts/functional/VtsHalTvCecAidlTargetTest.cpp b/tv/cec/aidl/vts/functional/VtsHalTvCecAidlTargetTest.cpp
new file mode 100644
index 0000000..69c209f
--- /dev/null
+++ b/tv/cec/aidl/vts/functional/VtsHalTvCecAidlTargetTest.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 "HdmiCec_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/tv/cec/BnHdmiCec.h>
+#include <aidl/android/hardware/tv/cec/BnHdmiCecCallback.h>
+#include <aidl/android/hardware/tv/cec/CecDeviceType.h>
+#include <aidl/android/hardware/tv/hdmi/BnHdmi.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <sstream>
+#include <vector>
+
+using ::aidl::android::hardware::tv::cec::BnHdmiCecCallback;
+using ::aidl::android::hardware::tv::cec::CecDeviceType;
+using ::aidl::android::hardware::tv::cec::CecLogicalAddress;
+using ::aidl::android::hardware::tv::cec::CecMessage;
+using ::aidl::android::hardware::tv::cec::IHdmiCec;
+using ::aidl::android::hardware::tv::cec::IHdmiCecCallback;
+using ::aidl::android::hardware::tv::cec::Result;
+using ::aidl::android::hardware::tv::cec::SendMessageResult;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortInfo;
+using ::ndk::SpAIBinder;
+
+#define CEC_VERSION 0x05
+#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
+
+// The main test class for TV CEC HAL.
+class HdmiCecTest : public ::testing::TestWithParam<std::string> {
+ static void serviceDied(void* /* cookie */) { ALOGE("VtsHalTvCecAidlTargetTest died"); }
+
+ public:
+ void SetUp() override {
+ hdmiCec = IHdmiCec::fromBinder(
+ SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+ ASSERT_NE(hdmiCec, nullptr);
+ ALOGI("%s: getService() for hdmiCec is %s", __func__,
+ hdmiCec->isRemote() ? "remote" : "local");
+
+ hdmiCecCallback = ::ndk::SharedRefBase::make<CecCallback>();
+ ASSERT_NE(hdmiCecCallback, nullptr);
+ hdmiCecDeathRecipient =
+ ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(&serviceDied));
+ ASSERT_EQ(AIBinder_linkToDeath(hdmiCec->asBinder().get(), hdmiCecDeathRecipient.get(), 0),
+ STATUS_OK);
+ }
+
+ std::vector<int> getDeviceTypes() {
+ std::vector<int> deviceTypes;
+ FILE* p = popen("getprop ro.hdmi.device_type", "re");
+ if (p) {
+ char* line = NULL;
+ size_t len = 0;
+ if (getline(&line, &len, p) > 0) {
+ std::istringstream stream(line);
+ std::string number{};
+ while (std::getline(stream, number, ',')) {
+ deviceTypes.push_back(stoi(number));
+ }
+ }
+ pclose(p);
+ }
+ return deviceTypes;
+ }
+
+ bool hasDeviceType(CecDeviceType type) {
+ std::vector<int> deviceTypes = getDeviceTypes();
+ return std::find(deviceTypes.begin(), deviceTypes.end(), (int)type) != deviceTypes.end();
+ }
+
+ class CecCallback : public BnHdmiCecCallback {
+ public:
+ ::ndk::ScopedAStatus onCecMessage(const CecMessage& message __unused) {
+ return ::ndk::ScopedAStatus::ok();
+ };
+ };
+
+ std::shared_ptr<IHdmiCec> hdmiCec;
+ std::shared_ptr<IHdmiCecCallback> hdmiCecCallback;
+ ::ndk::ScopedAIBinder_DeathRecipient hdmiCecDeathRecipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiCecTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, HdmiCecTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IHdmiCec::descriptor)),
+ android::PrintInstanceNameToString);
+
+TEST_P(HdmiCecTest, ClearAddLogicalAddress) {
+ Result addLaResult;
+ ASSERT_TRUE(hdmiCec->clearLogicalAddress().isOk());
+ ASSERT_TRUE(hdmiCec->addLogicalAddress(CecLogicalAddress::PLAYBACK_3, &addLaResult).isOk());
+ EXPECT_EQ(addLaResult, Result::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, PhysicalAddress) {
+ int32_t addr;
+ ASSERT_TRUE(hdmiCec->getPhysicalAddress(&addr).isOk());
+ if (!hasDeviceType(CecDeviceType::TV)) {
+ EXPECT_NE(addr, TV_PHYSICAL_ADDRESS);
+ }
+}
+
+TEST_P(HdmiCecTest, SendMessage) {
+ CecMessage message;
+ message.initiator = CecLogicalAddress::PLAYBACK_1;
+ message.destination = CecLogicalAddress::BROADCAST;
+ message.body.resize(1);
+ message.body[0] = 131;
+ SendMessageResult result;
+ ASSERT_TRUE(hdmiCec->sendMessage(message, &result).isOk());
+ EXPECT_EQ(result, SendMessageResult::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, CecVersion) {
+ int32_t version;
+ ASSERT_TRUE(hdmiCec->getCecVersion(&version).isOk());
+ EXPECT_GE(version, CEC_VERSION);
+}
+
+TEST_P(HdmiCecTest, SetCallback) {
+ ASSERT_TRUE(hdmiCec->setCallback(::ndk::SharedRefBase::make<CecCallback>()).isOk());
+}
+
+TEST_P(HdmiCecTest, VendorId) {
+ int32_t vendorId;
+ ASSERT_TRUE(hdmiCec->getVendorId(&vendorId).isOk());
+ EXPECT_NE(vendorId, INCORRECT_VENDOR_ID);
+}
+
+TEST_P(HdmiCecTest, EnableWakeupByOtp) {
+ ASSERT_TRUE(hdmiCec->enableWakeupByOtp(false).isOk());
+ // Restore option to its default value
+ ASSERT_TRUE(hdmiCec->enableWakeupByOtp(true).isOk());
+}
+
+TEST_P(HdmiCecTest, EnableCec) {
+ ASSERT_TRUE(hdmiCec->enableCec(false).isOk());
+ // Restore option to its default value
+ ASSERT_TRUE(hdmiCec->enableCec(true).isOk());
+}
+
+TEST_P(HdmiCecTest, EnableSystemCecControl) {
+ ASSERT_TRUE(hdmiCec->enableSystemCecControl(true).isOk());
+ // Restore option to its default value
+ ASSERT_TRUE(hdmiCec->enableSystemCecControl(false).isOk());
+}
+
+TEST_P(HdmiCecTest, SetLanguage) {
+ ASSERT_TRUE(hdmiCec->setLanguage("eng").isOk());
+}
diff --git a/tv/hdmi/aidl/Android.bp b/tv/hdmi/aidl/Android.bp
new file mode 100644
index 0000000..d8c6e5f
--- /dev/null
+++ b/tv/hdmi/aidl/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.tv.hdmi",
+ vendor_available: true,
+ srcs: ["android/hardware/tv/hdmi/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ },
+}
diff --git a/tv/hdmi/aidl/OWNERS b/tv/hdmi/aidl/OWNERS
new file mode 100644
index 0000000..d9c6783
--- /dev/null
+++ b/tv/hdmi/aidl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 826094
+include platform/frameworks/base:/core/java/android/hardware/hdmi/OWNERS
\ No newline at end of file
diff --git a/tv/hdmi/aidl/TEST_MAPPING b/tv/hdmi/aidl/TEST_MAPPING
new file mode 100644
index 0000000..6bd3b57
--- /dev/null
+++ b/tv/hdmi/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "VtsHalTvHdmiAidlTargetTest"
+ }
+ ]
+}
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl
new file mode 100644
index 0000000..a5e3a2a
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.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.tv.hdmi;
+@VintfStability
+parcelable HdmiPortInfo {
+ android.hardware.tv.hdmi.HdmiPortType type;
+ int portId;
+ boolean cecSupported;
+ boolean arcSupported;
+ int physicalAddress;
+}
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortType.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortType.aidl
new file mode 100644
index 0000000..af5f0f7
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortType.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.tv.hdmi;
+@Backing(type="byte") @VintfStability
+enum HdmiPortType {
+ INPUT = 0,
+ OUTPUT = 1,
+}
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmi.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmi.aidl
new file mode 100644
index 0000000..3fc7f41
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmi.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.hdmi;
+@VintfStability
+interface IHdmi {
+ android.hardware.tv.hdmi.HdmiPortInfo[] getPortInfo();
+ boolean isConnected(in int portId);
+ void setCallback(in android.hardware.tv.hdmi.IHdmiCallback callback);
+}
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmiCallback.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmiCallback.aidl
new file mode 100644
index 0000000..05fe623
--- /dev/null
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/IHdmiCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.hdmi;
+@VintfStability
+interface IHdmiCallback {
+ oneway void onHotplugEvent(in boolean connected, in int portId);
+}
diff --git a/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl
new file mode 100644
index 0000000..1d6f27d
--- /dev/null
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.tv.hdmi;
+
+import android.hardware.tv.hdmi.HdmiPortType;
+
+/**
+ * HDMI port descriptor
+ */
+@VintfStability
+parcelable HdmiPortInfo {
+ HdmiPortType type;
+ int portId; // Should start from 1 which corresponds to HDMI "port 1".
+ boolean cecSupported;
+ boolean arcSupported;
+ // The physical address of the device connected to this port, valid range is 0x0000 to 0xFFFF
+ // (ref Sec 8.7.2 of HDMI 1.4b).
+ int physicalAddress;
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortType.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortType.aidl
index 4b82dd0..59c0d42 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortType.aidl
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.tv.hdmi;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+/**
+ * HDMI port type.
+ */
+@VintfStability
+@Backing(type="byte")
+enum HdmiPortType {
+ INPUT = 0,
+ OUTPUT = 1,
+}
diff --git a/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmi.aidl b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmi.aidl
new file mode 100644
index 0000000..5536846
--- /dev/null
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmi.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.hdmi;
+
+import android.hardware.tv.hdmi.HdmiPortInfo;
+import android.hardware.tv.hdmi.IHdmiCallback;
+
+/**
+ * HDMI HAL interface definition.
+ */
+@VintfStability
+interface IHdmi {
+ /**
+ * Gets the hdmi port information of underlying hardware.
+ *
+ * @return The list of HDMI port information
+ */
+ HdmiPortInfo[] getPortInfo();
+
+ /**
+ * Gets the connection status of the specified port.
+ *
+ * @param portId Port id to be inspected for the connection status.
+ * @return True if a device is connected, otherwise false. The HAL
+ * must watch for +5V power signal to determine the status.
+ */
+ boolean isConnected(in int portId);
+
+ /**
+ * Sets a callback that HDMI HAL must later use for internal HDMI events
+ *
+ * @param callback Callback object to pass hdmi events to the system. The
+ * previously registered callback must be replaced with this one.
+ * setCallback(null) should deregister the callback.
+ */
+ void setCallback(in IHdmiCallback callback);
+}
diff --git a/audio/aidl/default/include/visualizer-impl/Visualizer.h b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmiCallback.aidl
similarity index 60%
copy from audio/aidl/default/include/visualizer-impl/Visualizer.h
copy to tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmiCallback.aidl
index 4b82dd0..51275b0 100644
--- a/audio/aidl/default/include/visualizer-impl/Visualizer.h
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/IHdmiCallback.aidl
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#pragma once
+package android.hardware.tv.hdmi;
-#include <cstdlib>
-
-namespace aidl::android::hardware::audio::effect {
-
-// Visualizer implementation UUID.
-static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
- static_cast<int32_t>(0x1d4033c0),
- 0x8557,
- 0x11df,
- 0x9f2d,
- {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
-
-} // namespace aidl::android::hardware::audio::effect
\ No newline at end of file
+/**
+ * Callbacks from the HDMI HAL implementation to notify the system of new events.
+ */
+@VintfStability
+oneway interface IHdmiCallback {
+ /**
+ * The callback function that must be called by HAL implementation to notify
+ * the system of new hotplug event.
+ */
+ void onHotplugEvent(in boolean connected, in int portId);
+}
diff --git a/tv/hdmi/aidl/default/Android.bp b/tv/hdmi/aidl/default/Android.bp
new file mode 100644
index 0000000..3e466a0
--- /dev/null
+++ b/tv/hdmi/aidl/default/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+ name: "android.hardware.tv.hdmi-service",
+ vintf_fragments: ["android.hardware.tv.hdmi-service.xml"],
+ relative_install_path: "hw",
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ init_rc: ["android.hardware.tv.hdmi-service.rc"],
+ srcs: [
+ "serviceMock.cpp",
+ "HdmiMock.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ "liblog",
+ "libbase",
+ "libutils",
+ "libhardware",
+ "libhidlbase",
+ "android.hardware.tv.hdmi-V1-ndk",
+ ],
+}
+
+cc_fuzz {
+ name: "android.hardware.tv.hdmi-service_fuzzer",
+ defaults: ["service_fuzzer_defaults"],
+ static_libs: [
+ "android.hardware.tv.hdmi-V1-ndk",
+ "liblog",
+ ],
+ srcs: [
+ "fuzzer.cpp",
+ "HdmiMock.cpp",
+ ],
+ fuzz_config: {
+ componentid: 826094,
+ },
+}
diff --git a/tv/hdmi/aidl/default/HdmiMock.cpp b/tv/hdmi/aidl/default/HdmiMock.cpp
new file mode 100644
index 0000000..0cf5118
--- /dev/null
+++ b/tv/hdmi/aidl/default/HdmiMock.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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 "android.hardware.tv.hdmi"
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <utils/Log.h>
+
+#include "HdmiMock.h"
+
+using ndk::ScopedAStatus;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace hdmi {
+namespace implementation {
+
+void HdmiMock::serviceDied(void* cookie) {
+ ALOGE("HdmiMock died");
+ auto hdmi = static_cast<HdmiMock*>(cookie);
+ hdmi->mHdmiThreadRun = false;
+}
+
+ScopedAStatus HdmiMock::getPortInfo(std::vector<HdmiPortInfo>* _aidl_return) {
+ *_aidl_return = mPortInfos;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiMock::isConnected(int32_t portId, bool* _aidl_return) {
+ // Maintain port connection status and update on hotplug event
+ if (portId <= mTotalPorts && portId >= 1) {
+ *_aidl_return = mPortConnectionStatus[portId];
+ } else {
+ *_aidl_return = false;
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HdmiMock::setCallback(const std::shared_ptr<IHdmiCallback>& callback) {
+ if (mCallback != nullptr) {
+ mCallback = nullptr;
+ }
+
+ if (callback != nullptr) {
+ mCallback = callback;
+ AIBinder_linkToDeath(this->asBinder().get(), mDeathRecipient.get(), 0 /* cookie */);
+
+ mInputFile = open(HDMI_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
+ pthread_create(&mThreadId, NULL, __threadLoop, this);
+ pthread_setname_np(mThreadId, "hdmi_loop");
+ }
+ return ScopedAStatus::ok();
+}
+
+void* HdmiMock::__threadLoop(void* user) {
+ HdmiMock* const self = static_cast<HdmiMock*>(user);
+ self->threadLoop();
+ return 0;
+}
+
+int HdmiMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
+ if (msgCount <= 0 || !buf) {
+ return 0;
+ }
+
+ int ret = -1;
+ // Maybe blocked at driver
+ ret = read(mInputFile, buf, msgCount);
+ if (ret < 0) {
+ ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", HDMI_MSG_IN_FIFO, ret);
+ return -1;
+ }
+
+ return ret;
+}
+
+void HdmiMock::printEventBuf(const char* msg_buf, int len) {
+ int i, size = 0;
+ const int bufSize = MESSAGE_BODY_MAX_LENGTH * 3;
+ // Use 2 characters for each byte in the message plus 1 space
+ char buf[bufSize] = {0};
+
+ // Messages longer than max length will be truncated.
+ for (i = 0; i < len && size < bufSize; i++) {
+ size += sprintf(buf + size, " %02x", msg_buf[i]);
+ }
+ ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
+}
+
+void HdmiMock::handleHotplugMessage(unsigned char* msgBuf) {
+ bool connected = ((msgBuf[3]) & 0xf) > 0;
+ int32_t portId = static_cast<uint32_t>(msgBuf[0] & 0xf);
+
+ if (portId > static_cast<int32_t>(mPortInfos.size())) {
+ ALOGD("[halimp_aidl] ignore hot plug message, id %x does not exist", portId);
+ return;
+ }
+
+ ALOGD("[halimp_aidl] hot plug port id %x, is connected %x", (msgBuf[0] & 0xf),
+ (msgBuf[3] & 0xf));
+ mPortConnectionStatus[portId] = connected;
+ if (mPortInfos[portId].type == HdmiPortType::OUTPUT) {
+ mPhysicalAddress = (connected ? 0xffff : ((msgBuf[1] << 8) | (msgBuf[2])));
+ mPortInfos[portId].physicalAddress = mPhysicalAddress;
+ ALOGD("[halimp_aidl] hot plug physical address %x", mPhysicalAddress);
+ }
+
+ if (mCallback != nullptr) {
+ mCallback->onHotplugEvent(connected, portId);
+ }
+}
+
+void HdmiMock::threadLoop() {
+ ALOGD("[halimp_aidl] threadLoop start.");
+ unsigned char msgBuf[MESSAGE_BODY_MAX_LENGTH];
+ int r = -1;
+
+ // Open the input pipe
+ while (mInputFile < 0) {
+ usleep(1000 * 1000);
+ mInputFile = open(HDMI_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
+ }
+ ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
+
+ while (mHdmiThreadRun) {
+ memset(msgBuf, 0, sizeof(msgBuf));
+ // Try to get a message from dev.
+ // echo -n -e '\x04\x83' >> /dev/cec
+ r = readMessageFromFifo(msgBuf, MESSAGE_BODY_MAX_LENGTH);
+ if (r <= 1) {
+ // Ignore received ping messages
+ continue;
+ }
+
+ printEventBuf((const char*)msgBuf, r);
+
+ if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
+ handleHotplugMessage(msgBuf);
+ }
+ }
+
+ ALOGD("[halimp_aidl] thread end.");
+}
+
+HdmiMock::HdmiMock() {
+ ALOGE("[halimp_aidl] Opening a virtual HDMI HAL for testing and virtual machine.");
+ mCallback = nullptr;
+ mPortInfos.resize(mTotalPorts);
+ mPortConnectionStatus.resize(mTotalPorts);
+ mPortInfos[0] = {.type = HdmiPortType::OUTPUT,
+ .portId = static_cast<uint32_t>(1),
+ .cecSupported = true,
+ .arcSupported = false,
+ .physicalAddress = mPhysicalAddress};
+ mPortConnectionStatus[0] = false;
+ mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
+}
+
+} // namespace implementation
+} // namespace hdmi
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/hdmi/aidl/default/HdmiMock.h b/tv/hdmi/aidl/default/HdmiMock.h
new file mode 100644
index 0000000..05795dd
--- /dev/null
+++ b/tv/hdmi/aidl/default/HdmiMock.h
@@ -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 <aidl/android/hardware/tv/hdmi/BnHdmi.h>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace hdmi {
+namespace implementation {
+
+using ::aidl::android::hardware::tv::hdmi::BnHdmi;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortInfo;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortType;
+using ::aidl::android::hardware::tv::hdmi::IHdmi;
+using ::aidl::android::hardware::tv::hdmi::IHdmiCallback;
+
+#define HDMI_MSG_IN_FIFO "/dev/hdmi_in_pipe"
+#define MESSAGE_BODY_MAX_LENGTH 4
+
+struct HdmiMock : public BnHdmi {
+ HdmiMock();
+
+ ::ndk::ScopedAStatus getPortInfo(std::vector<HdmiPortInfo>* _aidl_return) override;
+ ::ndk::ScopedAStatus isConnected(int32_t portId, bool* _aidl_return) override;
+ ::ndk::ScopedAStatus setCallback(const std::shared_ptr<IHdmiCallback>& callback) override;
+
+ void printEventBuf(const char* msg_buf, int len);
+
+ private:
+ static void* __threadLoop(void* data);
+ void threadLoop();
+ int readMessageFromFifo(unsigned char* buf, int msgCount);
+ void handleHotplugMessage(unsigned char* msgBuf);
+
+ private:
+ static void serviceDied(void* cookie);
+ std::shared_ptr<IHdmiCallback> mCallback;
+
+ // Variables for the virtual HDMI hal impl
+ std::vector<HdmiPortInfo> mPortInfos;
+ std::vector<bool> mPortConnectionStatus;
+
+ // Port configuration
+ uint16_t mPhysicalAddress = 0xFFFF;
+ int mTotalPorts = 1;
+
+ // Testing variables
+ // Input file descriptor
+ int mInputFile;
+ bool mHdmiThreadRun = true;
+ pthread_t mThreadId = 0;
+
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+};
+} // namespace implementation
+} // namespace hdmi
+} // Namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.rc b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.rc
new file mode 100644
index 0000000..c926221
--- /dev/null
+++ b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.rc
@@ -0,0 +1,5 @@
+service vendor.hdmi-default /vendor/bin/hw/android.hardware.tv.hdmi-service
+ interface aidl android.hardware.tv.hdmi.IHdmi/default
+ class hal
+ user system
+ group system
diff --git a/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.xml b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.xml
new file mode 100644
index 0000000..a03c199
--- /dev/null
+++ b/tv/hdmi/aidl/default/android.hardware.tv.hdmi-service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.tv.hdmi</name>
+ <version>1</version>
+ <interface>
+ <name>IHdmi</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/tv/hdmi/aidl/default/fuzzer.cpp b/tv/hdmi/aidl/default/fuzzer.cpp
new file mode 100644
index 0000000..06a2bc0
--- /dev/null
+++ b/tv/hdmi/aidl/default/fuzzer.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <HdmiMock.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using android::fuzzService;
+using android::hardware::tv::hdmi::implementation::HdmiMock;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto hdmiAidl = SharedRefBase::make<HdmiMock>();
+
+ fuzzService(hdmiAidl->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/tv/hdmi/aidl/default/serviceMock.cpp b/tv/hdmi/aidl/default/serviceMock.cpp
new file mode 100644
index 0000000..1d8bf51
--- /dev/null
+++ b/tv/hdmi/aidl/default/serviceMock.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.tv.hdmi-service-shim"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Log.h>
+#include "HdmiMock.h"
+
+using android::hardware::tv::hdmi::implementation::HdmiMock;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ std::shared_ptr<HdmiMock> hdmiAidl = ndk::SharedRefBase::make<HdmiMock>();
+ const std::string instance = std::string() + HdmiMock::descriptor + "/default";
+ binder_status_t status =
+ AServiceManager_addService(hdmiAidl->asBinder().get(), instance.c_str());
+ CHECK_EQ(status, STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return 0;
+}
diff --git a/tv/hdmi/aidl/vts/functional/Android.bp b/tv/hdmi/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..f9af58d
--- /dev/null
+++ b/tv/hdmi/aidl/vts/functional/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalTvHdmiAidlTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalTvHdmiAidlTargetTest.cpp"],
+ static_libs: [
+ "android.hardware.tv.hdmi-V1-ndk",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ disable_framework: true,
+}
diff --git a/tv/hdmi/aidl/vts/functional/AndroidTest.xml b/tv/hdmi/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..0640b2d
--- /dev/null
+++ b/tv/hdmi/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalTvHdmiAidlTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalTvHdmiAidlTargetTest->/data/local/tmp/VtsHalTvHdmiAidlTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalTvHdmiAidlTargetTest" />
+ <option name="native-test-timeout" value="30m" />
+ </test>
+</configuration>
diff --git a/tv/hdmi/aidl/vts/functional/VtsHalTvHdmiAidlTargetTest.cpp b/tv/hdmi/aidl/vts/functional/VtsHalTvHdmiAidlTargetTest.cpp
new file mode 100644
index 0000000..78c2590
--- /dev/null
+++ b/tv/hdmi/aidl/vts/functional/VtsHalTvHdmiAidlTargetTest.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 "Hdmi_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/tv/hdmi/BnHdmi.h>
+#include <aidl/android/hardware/tv/hdmi/BnHdmiCallback.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <sstream>
+#include <vector>
+
+using ::aidl::android::hardware::tv::hdmi::BnHdmiCallback;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortInfo;
+using ::aidl::android::hardware::tv::hdmi::HdmiPortType;
+using ::aidl::android::hardware::tv::hdmi::IHdmi;
+using ::aidl::android::hardware::tv::hdmi::IHdmiCallback;
+using ::ndk::SpAIBinder;
+
+#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
+
+// The main test class for TV HDMI HAL.
+class HdmiTest : public ::testing::TestWithParam<std::string> {
+ static void serviceDied(void* /* cookie */) { ALOGE("VtsHalTvCecAidlTargetTest died"); }
+
+ public:
+ void SetUp() override {
+ hdmi = IHdmi::fromBinder(SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+ ASSERT_NE(hdmi, nullptr);
+ ALOGI("%s: getService() for hdmi is %s", __func__, hdmi->isRemote() ? "remote" : "local");
+
+ hdmiCallback = ::ndk::SharedRefBase::make<HdmiCallback>();
+ ASSERT_NE(hdmiCallback, nullptr);
+ hdmiDeathRecipient =
+ ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(&serviceDied));
+ ASSERT_EQ(AIBinder_linkToDeath(hdmi->asBinder().get(), hdmiDeathRecipient.get(), 0),
+ STATUS_OK);
+ }
+
+ class HdmiCallback : public BnHdmiCallback {
+ public:
+ ::ndk::ScopedAStatus onHotplugEvent(bool connected __unused, int32_t portId __unused) {
+ return ::ndk::ScopedAStatus::ok();
+ };
+ };
+
+ std::shared_ptr<IHdmi> hdmi;
+ std::shared_ptr<IHdmiCallback> hdmiCallback;
+ ::ndk::ScopedAIBinder_DeathRecipient hdmiDeathRecipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, HdmiTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IHdmi::descriptor)),
+ android::PrintInstanceNameToString);
+
+TEST_P(HdmiTest, SetCallback) {
+ ASSERT_TRUE(hdmi->setCallback(::ndk::SharedRefBase::make<HdmiCallback>()).isOk());
+}
+
+TEST_P(HdmiTest, GetPortInfo) {
+ std::vector<HdmiPortInfo> ports;
+ ASSERT_TRUE(hdmi->getPortInfo(&ports).isOk());
+
+ bool cecSupportedOnDevice = false;
+ for (size_t i = 0; i < ports.size(); ++i) {
+ EXPECT_TRUE((ports[i].type == HdmiPortType::OUTPUT) ||
+ (ports[i].type == HdmiPortType::INPUT));
+ if (ports[i].portId == 0) {
+ ALOGW("%s: Port id should start from 1", __func__);
+ }
+ cecSupportedOnDevice = cecSupportedOnDevice | ports[i].cecSupported;
+ }
+ EXPECT_NE(cecSupportedOnDevice, false) << "At least one port should support CEC";
+}
+
+TEST_P(HdmiTest, IsConnected) {
+ std::vector<HdmiPortInfo> ports;
+ ASSERT_TRUE(hdmi->getPortInfo(&ports).isOk());
+ for (size_t i = 0; i < ports.size(); ++i) {
+ bool connected;
+ ASSERT_TRUE(hdmi->isConnected(ports[i].portId, &connected).isOk());
+ }
+}
diff --git a/tv/input/aidl/default/TvInput.cpp b/tv/input/aidl/default/TvInput.cpp
index 6c3a138..ed12cbc 100644
--- a/tv/input/aidl/default/TvInput.cpp
+++ b/tv/input/aidl/default/TvInput.cpp
@@ -96,8 +96,7 @@
}
mStreamConfigs[in_deviceId][in_streamId]->handle = createNativeHandle(in_streamId);
mStreamConfigs[in_deviceId][in_streamId]->isOpen = true;
- NativeHandle aidlHandle = makeToAidl(mStreamConfigs[in_deviceId][in_streamId]->handle);
- _aidl_return = &aidlHandle;
+ *_aidl_return = makeToAidl(mStreamConfigs[in_deviceId][in_streamId]->handle);
return ::ndk::ScopedAStatus::ok();
}
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IDvr.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IDvr.aidl
index 450cd79..4648712 100644
--- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IDvr.aidl
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/IDvr.aidl
@@ -43,4 +43,5 @@
void stop();
void flush();
void close();
+ void setStatusCheckIntervalHint(in long milliseconds);
}
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/IDvr.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/IDvr.aidl
index 0534f9d..4cb5b15 100644
--- a/tv/tuner/aidl/android/hardware/tv/tuner/IDvr.aidl
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/IDvr.aidl
@@ -18,7 +18,6 @@
import android.hardware.common.fmq.MQDescriptor;
import android.hardware.common.fmq.SynchronizedReadWrite;
-
import android.hardware.tv.tuner.DvrSettings;
import android.hardware.tv.tuner.IFilter;
@@ -101,4 +100,12 @@
* instance any more and all methods should return a failure.
*/
void close();
+
+ /**
+ * Set status check time interval.
+ *
+ * This time interval hint will be used by the Dvr to decide how often
+ * to evaluate data.
+ */
+ void setStatusCheckIntervalHint(in long milliseconds);
}
diff --git a/tv/tuner/aidl/default/Android.bp b/tv/tuner/aidl/default/Android.bp
index c10ad22..65fa821 100644
--- a/tv/tuner/aidl/default/Android.bp
+++ b/tv/tuner/aidl/default/Android.bp
@@ -29,7 +29,7 @@
],
shared_libs: [
"android.hardware.common.fmq-V1-ndk",
- "android.hardware.tv.tuner-V1-ndk",
+ "android.hardware.tv.tuner-V2-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
diff --git a/tv/tuner/aidl/default/Dvr.cpp b/tv/tuner/aidl/default/Dvr.cpp
index 9928a59..c9dd8ee 100644
--- a/tv/tuner/aidl/default/Dvr.cpp
+++ b/tv/tuner/aidl/default/Dvr.cpp
@@ -154,6 +154,14 @@
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus Dvr::setStatusCheckIntervalHint(int64_t /* in_milliseconds */) {
+ ALOGV("%s", __FUNCTION__);
+
+ // There is no active polling in this default implementation,
+ // so directly return ok here.
+ return ::ndk::ScopedAStatus::ok();
+}
+
bool Dvr::createDvrMQ() {
ALOGV("%s", __FUNCTION__);
diff --git a/tv/tuner/aidl/default/Dvr.h b/tv/tuner/aidl/default/Dvr.h
index 6ff71cd..293c533 100644
--- a/tv/tuner/aidl/default/Dvr.h
+++ b/tv/tuner/aidl/default/Dvr.h
@@ -70,6 +70,7 @@
::ndk::ScopedAStatus stop() override;
::ndk::ScopedAStatus flush() override;
::ndk::ScopedAStatus close() override;
+ ::ndk::ScopedAStatus setStatusCheckIntervalHint(int64_t in_milliseconds) override;
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
diff --git a/tv/tuner/aidl/vts/functional/Android.bp b/tv/tuner/aidl/vts/functional/Android.bp
index e5fb1e6..6a71544 100644
--- a/tv/tuner/aidl/vts/functional/Android.bp
+++ b/tv/tuner/aidl/vts/functional/Android.bp
@@ -53,7 +53,7 @@
"android.hardware.cas@1.2",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
- "android.hardware.tv.tuner-V1-ndk",
+ "android.hardware.tv.tuner-V2-ndk",
"libaidlcommonsupport",
"libfmq",
"libcutils",
diff --git a/tv/tuner/aidl/vts/functional/DvrTests.cpp b/tv/tuner/aidl/vts/functional/DvrTests.cpp
index a9c3b51..50f4de2 100644
--- a/tv/tuner/aidl/vts/functional/DvrTests.cpp
+++ b/tv/tuner/aidl/vts/functional/DvrTests.cpp
@@ -323,3 +323,41 @@
ASSERT_TRUE(mDvrRecord);
ASSERT_TRUE(mDvrRecord->close().isOk());
}
+
+AssertionResult DvrTests::setPlaybackStatusCheckIntervalHint(int64_t milliseconds) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
+
+ status = mDvrPlayback->setStatusCheckIntervalHint(milliseconds);
+
+ if (getDvrPlaybackInterfaceVersion() < 2) {
+ return AssertionResult(status.getStatus() == STATUS_UNKNOWN_TRANSACTION);
+ }
+ return AssertionResult(status.isOk());
+}
+
+AssertionResult DvrTests::setRecordStatusCheckIntervalHint(int64_t milliseconds) {
+ ndk::ScopedAStatus status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->setStatusCheckIntervalHint(milliseconds);
+
+ if (getDvrRecordInterfaceVersion() < 2) {
+ return AssertionResult(status.getStatus() == STATUS_UNKNOWN_TRANSACTION);
+ }
+ return AssertionResult(status.isOk());
+}
+
+int32_t DvrTests::getDvrPlaybackInterfaceVersion() {
+ int32_t version;
+ mDvrPlayback->getInterfaceVersion(&version);
+ return version;
+}
+
+int32_t DvrTests::getDvrRecordInterfaceVersion() {
+ int32_t version;
+ mDvrRecord->getInterfaceVersion(&version);
+ return version;
+}
diff --git a/tv/tuner/aidl/vts/functional/DvrTests.h b/tv/tuner/aidl/vts/functional/DvrTests.h
index 6662637..a88ba24 100644
--- a/tv/tuner/aidl/vts/functional/DvrTests.h
+++ b/tv/tuner/aidl/vts/functional/DvrTests.h
@@ -162,8 +162,12 @@
AssertionResult startDvrPlayback();
AssertionResult stopDvrRecord();
AssertionResult startDvrRecord();
+ AssertionResult setPlaybackStatusCheckIntervalHint(int64_t milliseconds);
+ AssertionResult setRecordStatusCheckIntervalHint(int64_t milliseconds);
void closeDvrPlayback();
void closeDvrRecord();
+ int32_t getDvrPlaybackInterfaceVersion();
+ int32_t getDvrRecordInterfaceVersion();
protected:
static AssertionResult failure() { return ::testing::AssertionFailure(); }
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index 9a17d05..6aa1e16 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -241,6 +241,28 @@
ASSERT_TRUE(mDemuxTests.closeDemux());
}
+void TunerPlaybackAidlTest::setStatusCheckIntervalHintTest(int64_t statusCheckIntervalHint,
+ DvrConfig dvrConf) {
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mDvrTests.setDemux(demux);
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+
+ ASSERT_TRUE(mDvrTests.setPlaybackStatusCheckIntervalHint(statusCheckIntervalHint));
+
+ mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile,
+ dvrConf.settings.get<DvrSettings::Tag::playback>());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+ mDvrTests.closeDvrPlayback();
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
void TunerRecordAidlTest::recordSingleFilterTestWithLnb(FilterConfig filterConf,
FrontendConfig frontendConf,
DvrConfig dvrConf, LnbConfig lnbConf) {
@@ -426,6 +448,45 @@
ASSERT_TRUE(mDemuxTests.closeDemux());
}
+void TunerRecordAidlTest::setStatusCheckIntervalHintTest(int64_t statusCheckIntervalHint,
+ FrontendConfig frontendConf,
+ DvrConfig dvrConf) {
+ int32_t demuxId;
+ std::shared_ptr<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mDvrTests.setDemux(demux);
+
+ DvrConfig dvrSourceConfig;
+ if (record.hasFrontendConnection) {
+ int32_t feId;
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ } else {
+ dvrSourceConfig = dvrMap[record.dvrSourceId];
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+ }
+
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
+
+ ASSERT_TRUE(mDvrTests.setRecordStatusCheckIntervalHint(statusCheckIntervalHint));
+
+ ASSERT_TRUE(mDvrTests.startDvrRecord());
+ ASSERT_TRUE(mDvrTests.stopDvrRecord());
+ mDvrTests.closeDvrRecord();
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+
+ if (record.hasFrontendConnection) {
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+ }
+}
+
void TunerDescramblerAidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
FrontendConfig frontendConf,
DescramblerConfig descConfig,
@@ -1000,6 +1061,18 @@
}
}
+TEST_P(TunerPlaybackAidlTest, SetStatusCheckIntervalHintToPlaybackTest) {
+ description("Set status check interval hint to playback test.");
+ if (!playback.support) {
+ return;
+ }
+ vector<DvrPlaybackHardwareConnections> playback_configs = generatePlaybackConfigs();
+ for (auto& configuration : playback_configs) {
+ playback = configuration;
+ setStatusCheckIntervalHintTest(STATUS_CHECK_INTERVAL_MS, dvrMap[playback.dvrId]);
+ }
+}
+
TEST_P(TunerRecordAidlTest, RecordDataFlowWithTsRecordFilterTest) {
description("Feed ts data from frontend to recording and test with ts record filter");
if (!record.support) {
@@ -1046,6 +1119,19 @@
}
}
+TEST_P(TunerRecordAidlTest, SetStatusCheckIntervalHintToRecordTest) {
+ description("Set status check interval hint to record test.");
+ if (!record.support) {
+ return;
+ }
+ auto record_configs = generateRecordConfigurations();
+ for (auto& configuration : record_configs) {
+ record = configuration;
+ setStatusCheckIntervalHintTest(STATUS_CHECK_INTERVAL_MS, frontendMap[record.frontendId],
+ dvrMap[record.dvrRecordId]);
+ }
+}
+
TEST_P(TunerFrontendAidlTest, TuneFrontend) {
description("Tune one Frontend with specific setting and check Lock event");
if (!live.hasFrontendConnection) {
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
index f68e1ec..3bfa78f 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.h
@@ -255,6 +255,8 @@
AssertionResult filterDataOutputTest();
void playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf);
+
+ void setStatusCheckIntervalHintTest(int64_t milliseconds, DvrConfig dvrConf);
};
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerPlaybackAidlTest);
@@ -294,6 +296,8 @@
DvrConfig dvrConf, LnbConfig lnbConf);
void recordSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf,
DvrConfig dvrConf, Dataflow_Context context);
+ void setStatusCheckIntervalHintTest(int64_t milliseconds, FrontendConfig frontendConf,
+ DvrConfig dvrConf);
std::shared_ptr<ITuner> mService;
FrontendTests mFrontendTests;
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
index fea5f83..516cb62 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTestConfigurations.h
@@ -55,6 +55,7 @@
const string configFilePath = "/vendor/etc/tuner_vts_config_aidl_V1.xml";
#define FILTER_MAIN_TYPE_BIT_COUNT 5
+#define STATUS_CHECK_INTERVAL_MS 100L
// Hardware configs
static map<string, FrontendConfig> frontendMap;
diff --git a/wifi/1.0/vts/OWNERS b/wifi/1.0/vts/OWNERS
deleted file mode 100644
index 287152d..0000000
--- a/wifi/1.0/vts/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
index e6e61cf..02f8209 100644
--- a/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
+++ b/wifi/1.0/vts/functional/wifi_hidl_test_utils.cpp
@@ -66,7 +66,7 @@
bool configureChipToSupportIfaceTypeInternal(const sp<IWifiChip>& wifi_chip,
IfaceType type,
ChipModeId* configured_mode_id) {
- if (!configured_mode_id) {
+ if (!configured_mode_id || !wifi_chip.get()) {
return false;
}
const auto& status_and_modes = HIDL_INVOKE(wifi_chip, getAvailableModes);
diff --git a/wifi/1.1/vts/OWNERS b/wifi/1.1/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.1/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.2/vts/OWNERS b/wifi/1.2/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.2/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.3/vts/OWNERS b/wifi/1.3/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.3/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.4/vts/OWNERS b/wifi/1.4/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.4/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.5/vts/OWNERS b/wifi/1.5/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.5/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/1.6/default/Android.bp b/wifi/1.6/default/Android.bp
index a132dee..0f98e71 100644
--- a/wifi/1.6/default/Android.bp
+++ b/wifi/1.6/default/Android.bp
@@ -226,3 +226,13 @@
"libwifi-system-iface",
],
}
+
+filegroup {
+ name: "default-android.hardware.wifi@1.0-service.rc",
+ srcs: ["android.hardware.wifi@1.0-service.rc"],
+}
+
+filegroup {
+ name: "default-android.hardware.wifi@1.0-service.xml",
+ srcs: ["android.hardware.wifi@1.0-service.xml"],
+}
diff --git a/wifi/1.6/default/OWNERS b/wifi/1.6/default/OWNERS
deleted file mode 100644
index cf81c79..0000000
--- a/wifi/1.6/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/1.6/vts/OWNERS b/wifi/1.6/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/1.6/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/OWNERS b/wifi/OWNERS
new file mode 100644
index 0000000..c10bbab
--- /dev/null
+++ b/wifi/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 33618
+
+# Please assign all bugs related to /hardware/interfaces/wifi to the team alias:
+#
+# android-wifi-team@google.com
+#
+# This will get them auto-assigned to the on-call triage engineer, ensuring quickest response.
+#
+arabawy@google.com
+etancohen@google.com
diff --git a/wifi/apex/Android.bp b/wifi/apex/Android.bp
new file mode 100644
index 0000000..0afb96b
--- /dev/null
+++ b/wifi/apex/Android.bp
@@ -0,0 +1,55 @@
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+apex_key {
+ name: "com.android.hardware.wifi.key",
+ public_key: "com.android.hardware.wifi.avbpubkey",
+ private_key: "com.android.hardware.wifi.pem",
+}
+
+android_app_certificate {
+ name: "com.android.hardware.wifi.certificate",
+ certificate: "com.android.hardware.wifi",
+}
+
+genrule {
+ name: "gen-android.hardware.wifi.rc",
+ srcs: [":default-android.hardware.wifi@1.0-service.rc"],
+ out: ["com.android.hardware.wifi-service.rc"],
+ cmd: "sed -e 's@/vendor/bin/@/apex/com.android.hardware.wifi/bin/@' $(in) > $(out)",
+}
+
+prebuilt_etc {
+ name: "com.android.hardware.wifi.rc",
+ src: ":gen-android.hardware.wifi.rc",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "com.android.hardware.wifi.xml",
+ src: ":default-android.hardware.wifi@1.0-service.xml",
+ installable: false,
+}
+
+apex {
+ name: "com.android.hardware.wifi",
+ manifest: "apex_manifest.json",
+ key: "com.android.hardware.wifi.key",
+ certificate: ":com.android.hardware.wifi.certificate",
+ file_contexts: "file_contexts",
+ vintf_fragments: [":com.android.hardware.wifi.xml"],
+ use_vndk_as_stable: true,
+ updatable: false,
+ soc_specific: true,
+ binaries: [
+ "android.hardware.wifi@1.0-service",
+ ],
+ prebuilts: [
+ "com.android.hardware.wifi.rc",
+ "com.android.hardware.wifi.xml",
+ ],
+ overrides: [
+ "android.hardware.wifi@1.0-service",
+ ],
+}
diff --git a/wifi/apex/apex_manifest.json b/wifi/apex/apex_manifest.json
new file mode 100644
index 0000000..cab0814
--- /dev/null
+++ b/wifi/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.hardware.wifi",
+ "version": 1
+}
diff --git a/wifi/apex/com.android.hardware.wifi.avbpubkey b/wifi/apex/com.android.hardware.wifi.avbpubkey
new file mode 100644
index 0000000..63fba77
--- /dev/null
+++ b/wifi/apex/com.android.hardware.wifi.avbpubkey
Binary files differ
diff --git a/wifi/apex/com.android.hardware.wifi.pem b/wifi/apex/com.android.hardware.wifi.pem
new file mode 100644
index 0000000..9e589ac
--- /dev/null
+++ b/wifi/apex/com.android.hardware.wifi.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCtT/vNnVVg2QVD
+eMdG+YZ8qYz/fbAQF9hnH8dE5RWHXzoYItBG7DSOuVT4T6POBVmFaVKlWDd7tDyw
+nFO3MmE2/FzhVSiBPwhMITa7UIERr3od3rWJ5g6oaTCOu4wa98L466Jp60f2fYSZ
+M9lGiKaDpLDSpxTU9hexjp7C4PfinnkYnlHBnrFXTmiO6f8AvOEwFFx73/rUNoe7
+F3TkGvjZDqHvE+pjz/nilkhXYuOl3zgSaeznJ9+TO5C/Z+Xr+zRhaJGI4v5Dkgmc
+jNy74+0hjwobXO3iWE44InQMvMh8zDKBx9l1oSsFoG3waj9ayqSYD7M74RX3PkUL
+QrhgAHZWi5iEnpu50xBzAqZB1ZDVkdZiKiGzweJ8OqltnVpvzlnW3rA3y3HtFyLm
+73C4ll9MdLaw266vBxgZfFOcjpphbbh9J9uGjOCJY1AxUzsqKygFD2CyOdb1jab3
+AC6VvRa+bLtv8fd2etp3atXv+Y9ACUX6zNK6Oa8Zktoo2Z//OLtcrk7xhgKKDkUF
+OPQrIjW9x0fdClDioIS+y7EHNUrfyRL7XPtUqGCszgz5jK2SMVGMpFaEtfbyNP1H
+BTGXdzcDP0RZdOOKTdBFgoRW5+6TH5CU9Nl4uevpxzwsTkXg0a+W1BGP+s9J7ssH
+rNPmHz+pbKZPDs/nxlpsKq+N+K8cCwIDAQABAoICAAJL943Lgnikl53DyXxGzUH0
+q0Itg7pK3prLQIRItubS2727JGB0O+QST650u7p8tql+clJvn1ib1FwQzkk0uTYV
+1RNFYiKIV89Od1+3GubFmQwxSd2Yd2RC9JpHoP0wgFx1HvNhY1RAaJPxLHVzVSWU
+dqVsAmoqErlPJwp1GcPejsNFQdcbh8Uc7GTMdA0p86AD/Q/FMZlDWbwgfPOS6e5S
+c9HrxSTqeijHDhFeZZ7qnN8dmT6c+CkG1o26zkC41QJfdOJIA8+YbVkuQrSYuilC
+MIOZUSu5ONwklL4geFWzDQ5MPDUDXEMYU6ymc817tv+u4ZSvEG/02sxh53iaOPc6
+C6im4nm6ZlRP9HzIvLAiRSbvwEb9cnLKgYpioSGXehDYg3zMPURwoqIxw+7IlIUh
+s+rhmCJV62PK1Z0nmo7m7S8r+z6j4G7aztGcbvdecocOJYOQB1VB8Zh4bNEIWp0a
+GDteG6aWXOCHoYRWAOluppDWa7Z+EgesU3Oj9Prn/ekUzzXx3V30zSTZYxRnQCO0
+vZIZ6hrlsNJcNrDpxmiWBaEOd4k5QI39pu6fDHc+UEEJMN+7eVk8d9QVA/LhrCjc
+JH1EVjtWosMUeMaMTpcmHTQKnEvmT2LO2fxGlF4JvjzDdVMloJrIP2LSQjovo2PX
+x9UXVu8Dt3kQRefZ42XxAoIBAQDkBoSYVajaFlyv9lW8oPmrQyzp93ite+TKVR8z
+PmcFQukjp5Gm8PGlGtGoH8aeE9Ev+R86aNPFy4n4ZJ7fmZYH1hxZ6dSc/56k6lLn
+uVKvTudYqwcRgBKuSZ8IDPFz3sHd+MnOBonDIri28fcBTDNv4LJP/6cAUoIOCCPm
+lQtJBfMNMDOXG4jv1Rv/1P5opGFATerRCubOxmeaFhZIDEjvN5WvLK5jmL7I8lX3
+X+gPiMHFWBQFmVTOHeVYEORDO5xFCOvHqCVB78b2xe1NkkrQ0CexpdflJVLE9IWt
+wWH43nhjxaK+eiBPrj37BliJvNaYfuxqcAj3p8c5KweFEtP7AoIBAQDCkx72K/Ja
+rFrrI0Avfo+3n6yoTMtMkiVhMuIKKPBJ2o4Opc/uixk+uLH5UH0X5GZsE51FAhUD
+L3bWMxV+vzVefWS8Iryo94ucvnfqhsN3KZXHDGoAuYv72jND8qPLKksM0dceMBfl
+aoyXSPAe8zAZeEx4iR2axgtnA0EbiXIaNVE/LF+WYdM74jvk/QtRzJ8IAAkSc5Rr
+GiTHeeP3nRnkjWjkmwKXYSJHcrEl26c/2ckeZORtblqxR9wMU5OgvJvMSzmOIpVH
+Y5lylZUOTuJCkApHFNKdRsawsSNKsy4yfY1byN/WkODb7le6Crt1gcwldMxDZAo9
+iB25FHq4zksxAoIBAQC+SBYkDO9PtnN4PycCto5B9VeokmN42bdthKT5nSxY/qIQ
+p8fquIvdzEiCdKnIxh69WrVNh6aZGyWySz0suDyzo1+bRH6w2LrpQcUXK9YtBroV
+ivrmBqsQF82G6U4f9BZxhifZLimN1g6wU7Bcu9r8lFQYX+1bXn66+N4EkAGP2VAe
+hEe45Dhccsjfrzzx06J4B81YzjEXAgf4VFAZpW7DeO4G9VE9OXyTsW49dSHwvJ1+
+ceabWX2kVtxIpiflVvwru6sNvGoC4PV2fmptXhPitqE5JHzJ8mBkjOx0t7hq9jMe
+hxEsxDrsYynDrWL65cNqFBhzJbTF/ZNJSHgI+1I7AoIBAQC7m0shJOJ61vCbA9Qh
+dzBvZn/9jn3/CHMOMxeLoEl/jEGokevZHzlqJn9D2n2jCdBPqOHc5dMIzT0R7xNs
+sERvJQx58ixh5r0wlt3cva++N9R4pdmXdVApuAvyGgQgIllWtQVr0AdaZs/EFsmf
+re/Uvw9MsThgQVBBNPwT5wSjjIEYHlrUDuKzPMFvWyUM6/Tyq8YTimmykvSfeUF7
+QHj0y/w1X9ixyTBaH5X64L10bTLkIXe2o87CXH0pTXRsaS73XhjSmTnCKaCMwPmF
+YD383BFs1AD3MITnXQSgQ//pIvGnbBmXMv38UOU5NpvlAw+pleJVoCHXjmTKTZq+
+kfohAoIBAQCrEecN8XEPjGdtY71rDYEwHGM6G4czHX0PNhlMjQos3aBVZ/YvVxPG
+pkXbms3GRXv4W92u7b2MwEKBPxcBepEdDZN9wSpe63bDFE6gpkipDhgj97JlLEUd
+s7h6oOoazdxmsRZyFho99SRQWrvyOiiKdLJCRZiqjUB4roOQmy7f9VAz6+OxyGV9
+XZigwW6bfUzMCmhx5Ss6zW8wZI+gINQh+2sDmiBiMQv14Ya5zlNYN+4/257Sk/jS
+o3QUDJITbReq/DNZ6UUzQS+AZ7ztc81rk5kRg0I33FZarRJ7TLAz+XmZZFoIOORz
+etEvMk8bJ4u7X89NWW/i2J+kQiDQg819
+-----END PRIVATE KEY-----
diff --git a/wifi/apex/com.android.hardware.wifi.pk8 b/wifi/apex/com.android.hardware.wifi.pk8
new file mode 100644
index 0000000..f4481bf
--- /dev/null
+++ b/wifi/apex/com.android.hardware.wifi.pk8
Binary files differ
diff --git a/wifi/apex/com.android.hardware.wifi.x509.pem b/wifi/apex/com.android.hardware.wifi.x509.pem
new file mode 100644
index 0000000..c942e71
--- /dev/null
+++ b/wifi/apex/com.android.hardware.wifi.x509.pem
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIGOzCCBCOgAwIBAgIUIEueuBFEoCFmLyEvXDsKVuZeH0EwDQYJKoZIhvcNAQEL
+BQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMScwJQYDVQQDDB5jb20uYW5kcm9pZC5oYXJkd2FyZS53aWZpLmFwZXgxIjAg
+BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMjIxMDA2MTY1MDQy
+WhgPNDc2MDA5MDExNjUwNDJaMIGrMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs
+aWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9p
+ZDEQMA4GA1UECwwHQW5kcm9pZDEnMCUGA1UEAwweY29tLmFuZHJvaWQuaGFyZHdh
+cmUud2lmaS5hcGV4MSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAo4N23iL0J42qG2lt4ysD
+t6XxAKdi4sTWB6ZjFWerBm11s3yLr1KBZV82z3j2o7+EOaWa/91Mgc+aoiGYUa1f
+IweugKTrnRmrzOvtq/Wp5PGqSxsmLPPvH3wyEB/CMgAn0pqDf0WyCWJ2kAlcCkmy
+qVNFupkGFyIE6gkoc+AmJGbSTquDlL07R/8XYDicqrcgeqy9ZaCJ5FLfmbnnRb2A
+vm4Un7e5dFz5+dPaOJXf4AOMUSPUd7fuBliGYFLzcZnYQbzMktXa4XnPuSlmerwy
+EwY767L2bjRjaSgPb0Js13gZ4S4ZHZe07AV7HPlt/EzbxV/jtMgHl4xn5p0WhVnK
+MPtLsO/e7FkDPAKpT02sgUK6pVKqgBGOWm27tmTB09lscMLQeVFqwpoFq2zMUrDn
+ipDFMWRBeLrEDKx41zF3gqdPmP+SMkQYJu4OATIXOndIeo7AN9iE+N6snHkckNyE
+saCwmnzyhVAbMn/epfIQZz3zcyljA9yfOpv5jctN4es+3i0htDnoNO9ij4M5fxuP
+jtNAP3EA61nKZ5+Js0/MMQKrfwCLogPl/4vCNuuGT2rhCkhq1CLYXmb9ghvVzcPe
+BOGXNDKdB+aUTxrQUOYlHf0cRDHdU6cchrz9+QhR7t9dlibtiyCZE34xgR3klmyz
+XJ3M1r/QRihjARH7exrrwiUCAwEAAaNTMFEwHQYDVR0OBBYEFGN9tMk+4SDk7twk
+MrLjM+nRxGDJMB8GA1UdIwQYMBaAFGN9tMk+4SDk7twkMrLjM+nRxGDJMA8GA1Ud
+EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAHoTfVBRG0rVE7gkV526gwR5
+/3mXyYA57lKJhZ21fu2qfTE6WYj4DsOTZKrPIAUFv/SnzZ6Rvv7W81XV5M/1m+5R
+/1wYvWwm3FBOvvt4VDUiel0Zfc9+nwynjz1seYdXU8fNIOzBcr9hutkYdRZDkNpc
+Zcl4NG04TzyedkQ/0SyHnygmq4ZY9OUEvrNaWBFHzw2SQhYvHh8jUrqpPvoJz0Px
+avKI8bOgXTJRJ+Pk7hjXDFQY/fbE0RGivorPMLs+XHaEIb+YPyXsX4OZwowG5KL8
+txyvUsH+qZToytdPk4LCuwYBobBlr+AAg7pxOtbDW1ITDhQ9n05UFK2iUwsJecgg
+VDA0K3GuCjmGVmkw7SFRYfToCyGWah8sQCUMCCcsof7gS+dMxuWeee+sRxRJcJY+
+xR2uySe8g4ohwNjQ0zLQv7PZOKQh91yEWxRXmhPYVpiVAjpSD2zH7Vd6CJ9xji//
+5S1UrxWwQ5igvu8v5veqNAW7uXGXADnxL69HVGTLm0XDIUUOAUIG8waiVkYUo3UU
+AzAFbF7ewYMKdg7ylUYcTaMRIsKYW/3/1t3NJv2z99W4V/p8e1kRCeWMPB5C+/Lo
+b/hSWj1NF9AJ30ukBndMh6bRprq+G5NLV6OaoCLp606CMdXdT8uw9lYCt7DbQHG9
+Hw3iw61svpUwcaWfN1hI
+-----END CERTIFICATE-----
diff --git a/wifi/apex/file_contexts b/wifi/apex/file_contexts
new file mode 100644
index 0000000..812d51d
--- /dev/null
+++ b/wifi/apex/file_contexts
@@ -0,0 +1,3 @@
+(/.*)? u:object_r:vendor_file:s0
+/bin/hw/android\.hardware\.wifi@1.0-service u:object_r:hal_wifi_default_exec:s0
+
diff --git a/wifi/hostapd/1.0/vts/OWNERS b/wifi/hostapd/1.0/vts/OWNERS
deleted file mode 100644
index 287152d..0000000
--- a/wifi/hostapd/1.0/vts/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/hostapd/1.1/vts/OWNERS b/wifi/hostapd/1.1/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/hostapd/1.1/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/hostapd/1.2/vts/OWNERS b/wifi/hostapd/1.2/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/hostapd/1.2/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/hostapd/1.3/vts/OWNERS b/wifi/hostapd/1.3/vts/OWNERS
deleted file mode 100644
index 294fc82..0000000
--- a/wifi/hostapd/1.3/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.0/vts/OWNERS
diff --git a/wifi/hostapd/aidl/vts/OWNERS b/wifi/hostapd/aidl/vts/OWNERS
deleted file mode 100644
index 2a7a7b0..0000000
--- a/wifi/hostapd/aidl/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-etancohen@google.com
-lzye@google.com
diff --git a/wifi/netlinkinterceptor/aidl/default/OWNERS b/wifi/netlinkinterceptor/aidl/default/OWNERS
deleted file mode 100644
index b738dac..0000000
--- a/wifi/netlinkinterceptor/aidl/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chrisweir@google.com
-twasilczyk@google.com
diff --git a/wifi/netlinkinterceptor/libnlinterceptor/Android.bp b/wifi/netlinkinterceptor/libnlinterceptor/Android.bp
index 00cae32..671cd85 100644
--- a/wifi/netlinkinterceptor/libnlinterceptor/Android.bp
+++ b/wifi/netlinkinterceptor/libnlinterceptor/Android.bp
@@ -37,10 +37,8 @@
"libutils",
],
sanitize: {
- address: true,
undefined: true,
all_undefined: true,
- fuzzer: true,
cfi: true,
integer_overflow: true,
scs: true,
diff --git a/wifi/netlinkinterceptor/vts/OWNERS b/wifi/netlinkinterceptor/vts/OWNERS
deleted file mode 100644
index b738dac..0000000
--- a/wifi/netlinkinterceptor/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chrisweir@google.com
-twasilczyk@google.com
diff --git a/wifi/offload/1.0/vts/OWNERS b/wifi/offload/1.0/vts/OWNERS
deleted file mode 100644
index 287152d..0000000
--- a/wifi/offload/1.0/vts/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
diff --git a/wifi/supplicant/1.0/vts/OWNERS b/wifi/supplicant/1.0/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.0/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.1/vts/OWNERS b/wifi/supplicant/1.1/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.1/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.2/vts/OWNERS b/wifi/supplicant/1.2/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.2/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/1.4/vts/OWNERS b/wifi/supplicant/1.4/vts/OWNERS
deleted file mode 100644
index b16dc11..0000000
--- a/wifi/supplicant/1.4/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 33618
-include ../../1.3/vts/OWNERS
diff --git a/wifi/supplicant/OWNERS b/wifi/supplicant/OWNERS
deleted file mode 100644
index 7febd60..0000000
--- a/wifi/supplicant/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 33618
-arabawy@google.com
-etancohen@google.com
-gbiren@google.com
-lzye@google.com