Merge "Fix SetCountryCode test when US is not supported"
diff --git a/audio/7.0/config/api/current.txt b/audio/7.0/config/api/current.txt
index eb8c2dd..b5b3925 100644
--- a/audio/7.0/config/api/current.txt
+++ b/audio/7.0/config/api/current.txt
@@ -45,6 +45,8 @@
enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_NONE;
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_13POINT_360RA;
+ enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_22POINT2;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT0POINT2;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1;
enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1POINT2;
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index 007e250..4f5614b 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -504,6 +504,8 @@
<xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1"/>
<xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT2"/>
<xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_13POINT_360RA"/>
+ <xs:enumeration value="AUDIO_CHANNEL_OUT_22POINT2"/>
<xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_A"/>
<xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A"/>
<xs:enumeration value="AUDIO_CHANNEL_OUT_HAPTIC_AB"/>
diff --git a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
index 7d83556..fe3e4ea 100644
--- a/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
+++ b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
@@ -94,6 +94,7 @@
case AudioChannelMask::AUDIO_CHANNEL_OUT_7POINT1POINT4:
case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_12:
return 12;
+ case AudioChannelMask::AUDIO_CHANNEL_OUT_13POINT_360RA:
case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_13:
return 13;
case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_14:
@@ -116,6 +117,7 @@
return 22;
case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_23:
return 23;
+ case AudioChannelMask::AUDIO_CHANNEL_OUT_22POINT2:
case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_24:
return 24;
case AudioChannelMask::UNKNOWN:
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index f24b1f5..c13efde 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -91,13 +91,13 @@
"impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
"impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
"impl/vhal_v2_0/GeneratorHub.cpp",
+ "impl/vhal_v2_0/qemu_pipe.cpp",
],
local_include_dirs: ["common/include/vhal_v2_0"],
export_include_dirs: ["impl"],
whole_static_libs: [
"android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
"android.hardware.automotive.vehicle@2.0-manager-lib",
- "libqemu_pipe",
],
shared_libs: [
"libbase",
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
index f024287..81e7c78 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
@@ -18,9 +18,9 @@
#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
#include <log/log.h>
-#include <qemu_pipe.h>
#include "PipeComm.h"
+#include "qemu_pipe.h"
#define CAR_SERVICE_NAME "pipe:qemud:car"
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.cpp
new file mode 100644
index 0000000..cf1a002
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "qemu_pipe.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+using android::base::ReadFully;
+using android::base::WriteFully;
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+#define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+int qemu_pipe_open(const char* pipeName) {
+ if (!pipeName) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+ if (fd < 0) {
+ QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__, strerror(errno));
+ return -1;
+ }
+
+ // Write the pipe name, *including* the trailing zero which is necessary.
+ size_t pipeNameLen = strlen(pipeName);
+ if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+
+ // now, add 'pipe:' prefix and try again
+ // Note: host side will wait for the trailing '\0' to start
+ // service lookup.
+ const char pipe_prefix[] = "pipe:";
+ if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
+ WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+ QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s", __FUNCTION__, pipeName,
+ strerror(errno));
+ close(fd);
+ return -1;
+}
+
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
+ char header[5];
+ snprintf(header, sizeof(header), "%04zx", len);
+ if (!WriteFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ if (!WriteFully(fd, buff, len)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+ char header[5];
+ if (!ReadFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ header[4] = '\0';
+ size_t size;
+ if (sscanf(header, "%04zx", &size) != 1) {
+ QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+ return -1;
+ }
+ if (size > len) {
+ QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size, len);
+ return -1;
+ }
+ if (!ReadFully(fd, buff, size)) {
+ QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return size;
+}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.h
new file mode 100644
index 0000000..0987498
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+// For backward compatibility, the 'pipe:' prefix can be omitted, and in
+// that case, qemu_pipe_open will add it for you.
+
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL -> unknown/unsupported pipeName
+// ENOSYS -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+int qemu_pipe_open(const char* pipeName);
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
index 0328af1..fd82f49 100644
--- a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
+++ b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
@@ -179,7 +179,7 @@
bluetooth_cb->SetWaitTimeout(kCallbackNameScoEventReceived,
WAIT_FOR_SCO_DATA_TIMEOUT);
- EXPECT_TRUE(
+ ASSERT_TRUE(
bluetooth_cb->WaitForCallback(kCallbackNameInitializationComplete)
.no_timeout);
@@ -289,7 +289,7 @@
void BluetoothHidlTest::handle_no_ops() {
while (event_queue.size() > 0) {
hidl_vec<uint8_t> event = event_queue.front();
- EXPECT_GE(event.size(),
+ ASSERT_GE(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
bool event_is_no_op =
(event[EVENT_CODE_BYTE] == EVENT_COMMAND_COMPLETE) &&
@@ -327,7 +327,7 @@
bluetooth_cb->WaitForCallback(kCallbackNameHciEventReceived).no_timeout;
EXPECT_TRUE(no_timeout || !timeout_is_error);
if (no_timeout && timeout_is_error) {
- EXPECT_LT(static_cast<size_t>(0), event_queue.size());
+ ASSERT_LT(static_cast<size_t>(0), event_queue.size());
}
if (event_queue.size() == 0) {
// WaitForCallback timed out.
@@ -343,12 +343,12 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
}
// Send the command to read the controller's buffer sizes.
@@ -362,10 +362,10 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
max_acl_data_packet_length =
event[EVENT_COMMAND_COMPLETE_STATUS_BYTE + 1] +
@@ -415,10 +415,10 @@
size_t compare_length =
(cmd.size() > static_cast<size_t>(0xff) ? static_cast<size_t>(0xff)
: cmd.size());
- EXPECT_GT(event.size(), compare_length + EVENT_FIRST_PAYLOAD_BYTE - 1);
+ ASSERT_GT(event.size(), compare_length + EVENT_FIRST_PAYLOAD_BYTE - 1);
- EXPECT_EQ(EVENT_LOOPBACK_COMMAND, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(compare_length, event[EVENT_LENGTH_BYTE]);
+ ASSERT_EQ(EVENT_LOOPBACK_COMMAND, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(compare_length, event[EVENT_LENGTH_BYTE]);
// Don't compare past the end of the event.
if (compare_length + EVENT_FIRST_PAYLOAD_BYTE > event.size()) {
@@ -455,12 +455,12 @@
bluetooth->sendScoData(sco_vector);
// Check the loopback of the SCO packet
- EXPECT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameScoEventReceived)
+ ASSERT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameScoEventReceived)
.no_timeout);
hidl_vec<uint8_t> sco_loopback = sco_queue.front();
sco_queue.pop();
- EXPECT_EQ(sco_packet.size(), sco_loopback.size());
+ ASSERT_EQ(sco_packet.size(), sco_loopback.size());
size_t successful_bytes = 0;
for (size_t i = 0; i < sco_packet.size(); i++) {
@@ -474,7 +474,7 @@
break;
}
}
- EXPECT_EQ(sco_packet.size(), successful_bytes + 1);
+ ASSERT_EQ(sco_packet.size(), successful_bytes + 1);
}
logger.setTotalBytes(num_packets * size * 2);
}
@@ -500,26 +500,15 @@
bluetooth->sendAclData(acl_vector);
// Check the loopback of the ACL packet
- EXPECT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameAclEventReceived)
+ ASSERT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameAclEventReceived)
.no_timeout);
hidl_vec<uint8_t> acl_loopback = acl_queue.front();
acl_queue.pop();
EXPECT_EQ(acl_packet.size(), acl_loopback.size());
- size_t successful_bytes = 0;
-
- for (size_t i = 0; i < acl_packet.size(); i++) {
- if (acl_packet[i] == acl_loopback[i]) {
- successful_bytes = i;
- } else {
- ALOGE("Miscompare at %d (expected %x, got %x)", static_cast<int>(i),
- acl_packet[i], acl_loopback[i]);
- ALOGE("At %d (expected %x, got %x)", static_cast<int>(i + 1),
- acl_packet[i + 1], acl_loopback[i + 1]);
- break;
- }
+ for (size_t i = 0; i < acl_packet.size() && i < acl_loopback.size(); i++) {
+ EXPECT_EQ(acl_packet[i], acl_loopback[i]) << " at byte number " << i;
}
- EXPECT_EQ(acl_packet.size(), successful_bytes + 1);
}
logger.setTotalBytes(num_packets * size * 2);
}
@@ -560,22 +549,22 @@
wait_for_event(false);
if (event_queue.size() == 0) {
// Fail if there was no event received or no connections completed.
- EXPECT_TRUE(command_complete_received);
- EXPECT_LT(0, connection_event_count);
+ ASSERT_TRUE(command_complete_received);
+ ASSERT_LT(0, connection_event_count);
return;
}
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
if (event[EVENT_CODE_BYTE] == EVENT_CONNECTION_COMPLETE) {
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_CONNECTION_COMPLETE_TYPE));
- EXPECT_EQ(event[EVENT_LENGTH_BYTE],
+ ASSERT_EQ(event[EVENT_LENGTH_BYTE],
EVENT_CONNECTION_COMPLETE_PARAM_LENGTH);
uint8_t connection_type = event[EVENT_CONNECTION_COMPLETE_TYPE];
- EXPECT_TRUE(connection_type == EVENT_CONNECTION_COMPLETE_TYPE_SCO ||
+ ASSERT_TRUE(connection_type == EVENT_CONNECTION_COMPLETE_TYPE_SCO ||
connection_type == EVENT_CONNECTION_COMPLETE_TYPE_ACL);
// Save handles
@@ -590,10 +579,10 @@
event[EVENT_CONNECTION_COMPLETE_TYPE], handle);
connection_event_count++;
} else {
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
command_complete_received = true;
}
}
@@ -620,15 +609,15 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(), static_cast<size_t>(EVENT_LOCAL_LMP_VERSION_BYTE));
+ ASSERT_GT(event.size(), static_cast<size_t>(EVENT_LOCAL_LMP_VERSION_BYTE));
- EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+ ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
- EXPECT_LE(HCI_MINIMUM_HCI_VERSION, event[EVENT_LOCAL_HCI_VERSION_BYTE]);
- EXPECT_LE(HCI_MINIMUM_LMP_VERSION, event[EVENT_LOCAL_LMP_VERSION_BYTE]);
+ ASSERT_LE(HCI_MINIMUM_HCI_VERSION, event[EVENT_LOCAL_HCI_VERSION_BYTE]);
+ ASSERT_LE(HCI_MINIMUM_LMP_VERSION, event[EVENT_LOCAL_LMP_VERSION_BYTE]);
}
// Send an unknown HCI command and wait for the error message.
@@ -642,18 +631,18 @@
hidl_vec<uint8_t> event = event_queue.front();
event_queue.pop();
- EXPECT_GT(event.size(),
+ ASSERT_GT(event.size(),
static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
if (event[EVENT_CODE_BYTE] == EVENT_COMMAND_COMPLETE) {
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
} else {
- EXPECT_EQ(EVENT_COMMAND_STATUS, event[EVENT_CODE_BYTE]);
- EXPECT_EQ(cmd[0], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE]);
- EXPECT_EQ(cmd[1], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE + 1]);
- EXPECT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
+ ASSERT_EQ(EVENT_COMMAND_STATUS, event[EVENT_CODE_BYTE]);
+ ASSERT_EQ(cmd[0], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE]);
+ ASSERT_EQ(cmd[1], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE + 1]);
+ ASSERT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
event[EVENT_COMMAND_STATUS_STATUS_BYTE]);
}
}
@@ -678,7 +667,7 @@
// This should work, but breaks on some current platforms. Figure out how to
// grandfather older devices but test new ones.
if (0 && sco_connection_handles.size() > 0) {
- EXPECT_LT(0, max_sco_data_packet_length);
+ ASSERT_LT(0, max_sco_data_packet_length);
sendAndCheckSCO(1, max_sco_data_packet_length, sco_connection_handles[0]);
int sco_packets_sent = 1;
int completed_packets =
@@ -690,7 +679,7 @@
}
if (acl_connection_handles.size() > 0) {
- EXPECT_LT(0, max_acl_data_packet_length);
+ ASSERT_LT(0, max_acl_data_packet_length);
sendAndCheckACL(1, max_acl_data_packet_length, acl_connection_handles[0]);
int acl_packets_sent = 1;
int completed_packets =
@@ -715,7 +704,7 @@
// This should work, but breaks on some current platforms. Figure out how to
// grandfather older devices but test new ones.
if (0 && sco_connection_handles.size() > 0) {
- EXPECT_LT(0, max_sco_data_packet_length);
+ ASSERT_LT(0, max_sco_data_packet_length);
sendAndCheckSCO(NUM_SCO_PACKETS_BANDWIDTH, max_sco_data_packet_length,
sco_connection_handles[0]);
int sco_packets_sent = NUM_SCO_PACKETS_BANDWIDTH;
@@ -728,7 +717,7 @@
}
if (acl_connection_handles.size() > 0) {
- EXPECT_LT(0, max_acl_data_packet_length);
+ ASSERT_LT(0, max_acl_data_packet_length);
sendAndCheckACL(NUM_ACL_PACKETS_BANDWIDTH, max_acl_data_packet_length,
acl_connection_handles[0]);
int acl_packets_sent = NUM_ACL_PACKETS_BANDWIDTH;
diff --git a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl
new file mode 100644
index 0000000..a438031
--- /dev/null
+++ b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common;
+@VintfStability
+parcelable Ashmem {
+ ParcelFileDescriptor fd;
+ long size;
+}
diff --git a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/MappableFile.aidl b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/MappableFile.aidl
new file mode 100644
index 0000000..394ea8f
--- /dev/null
+++ b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/MappableFile.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common;
+@VintfStability
+parcelable MappableFile {
+ long length;
+ int prot;
+ ParcelFileDescriptor fd;
+ long offset;
+}
diff --git a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl
index f37b7d5..2ed5c0b 100644
--- a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl
+++ b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// 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 changes to the AIDL files built
+// 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
diff --git a/common/aidl/android/hardware/common/Ashmem.aidl b/common/aidl/android/hardware/common/Ashmem.aidl
new file mode 100644
index 0000000..8e40266
--- /dev/null
+++ b/common/aidl/android/hardware/common/Ashmem.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.common;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Type that holds same memory as the "ashmem" hidl_memory type from HIDL.
+ */
+@VintfStability
+parcelable Ashmem {
+ /**
+ * A handle to a memory region.
+ */
+ ParcelFileDescriptor fd;
+ /**
+ * Size of the memory region in bytes.
+ */
+ long size;
+}
diff --git a/common/aidl/android/hardware/common/MappableFile.aidl b/common/aidl/android/hardware/common/MappableFile.aidl
new file mode 100644
index 0000000..a7763ea
--- /dev/null
+++ b/common/aidl/android/hardware/common/MappableFile.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.common;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * A region of a file that can be mapped into memory.
+ *
+ * In Linux, MappableFile may be used with mmap as `MAP_SHARED`.
+ *
+ * MappableFile is compatible with ::android::base::MappedFile.
+ */
+@VintfStability
+parcelable MappableFile {
+ /**
+ * Length of the mapping region in bytes.
+ */
+ long length;
+ /**
+ * The desired memory protection for the mapping.
+ *
+ * In Linux, prot is either `PROT_NONE` (indicating that mapped pages may not be accessed) or
+ * the bitwise OR of one or more of the following flags:
+ * - `PROT_READ` (indicating that the mapped pages may be read)
+ * - `PROT_WRITE` (indicating that the mapped pages may be written)
+ */
+ int prot;
+ /**
+ * A handle to a mappable file.
+ */
+ ParcelFileDescriptor fd;
+ /**
+ * The offset in the file to the beginning of the mapping region in number of bytes.
+ *
+ * Note: Some mapping functions require that the offset is aligned to the page size.
+ */
+ long offset;
+}
diff --git a/common/support/Android.bp b/common/support/Android.bp
index 8aea306..730798d 100644
--- a/common/support/Android.bp
+++ b/common/support/Android.bp
@@ -18,6 +18,11 @@
"android.hardware.common-V2-ndk_platform",
"libcutils",
],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.neuralnetworks",
+ ],
+ min_sdk_version: "29",
}
cc_test {
diff --git a/current.txt b/current.txt
index eb5cf00..454d43e 100644
--- a/current.txt
+++ b/current.txt
@@ -778,6 +778,8 @@
f729ee6a5f136b25d79ea6895d24700fce413df555baaecf2c39e4440d15d043 android.hardware.neuralnetworks@1.0::types
a84f8dac7a9b75de1cc2936a9b429b9b62b32a31ea88ca52c29f98f5ddc0fa95 android.hardware.neuralnetworks@1.2::types
cd331b92312d16ab89f475c39296abbf539efc4114a8c5c2b136ad99b904ef33 android.hardware.neuralnetworks@1.3::types
+c3fec5bd470984402997f78a74b6511efc4063b270f2bd9ee7b78f48b683a1bb android.hardware.neuralnetworks@1.3::IDevice
+0fdfad62c2ec33b52e6687004e5a1971c02d10b93ee4d26df5ccff7ce032494a android.hardware.neuralnetworks@1.3::IPreparedModel
e8c86c69c438da8d1549856c1bb3e2d1b8da52722f8235ff49a30f2cce91742c android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
b9fbb6e2e061ed0960939d48b785e9700210add1f13ed32ecd688d0f1ca20ef7 android.hardware.renderscript@1.0::types
0f53d70e1eadf8d987766db4bf6ae2048004682168f4cab118da576787def3fa android.hardware.radio@1.0::types
diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp
index 2db3607..e6d4e84 100644
--- a/drm/1.0/default/CryptoPlugin.cpp
+++ b/drm/1.0/default/CryptoPlugin.cpp
@@ -124,7 +124,11 @@
return Void();
}
- if (source.offset + offset + source.size > sourceBase->getSize()) {
+ size_t totalSize = 0;
+ if (__builtin_add_overflow(source.offset, offset, &totalSize) ||
+ __builtin_add_overflow(totalSize, source.size, &totalSize) ||
+ totalSize > sourceBase->getSize()) {
+ android_errorWriteLog(0x534e4554, "176496160");
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size");
return Void();
}
diff --git a/graphics/common/aidl/Android.bp b/graphics/common/aidl/Android.bp
index 2a46f9d..cadd13c 100644
--- a/graphics/common/aidl/Android.bp
+++ b/graphics/common/aidl/Android.bp
@@ -34,6 +34,7 @@
apex_available: [
"//apex_available:platform",
"com.android.media.swcodec",
+ "com.android.neuralnetworks",
],
min_sdk_version: "29",
},
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index 05b3662..7c68aee 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -31,7 +31,8 @@
],
static_libs: [
"libbase",
- "libcppbor",
+ "libcppbor_external",
+ "libcppcose_rkp",
"libutils",
"libsoft_attestation_cert",
"libkeymaster_portable",
@@ -91,7 +92,8 @@
],
static_libs: [
"libbase",
- "libcppbor",
+ "libcppbor_external",
+ "libcppcose_rkp",
"libutils",
"libsoft_attestation_cert",
"libkeymaster_portable",
diff --git a/identity/aidl/default/common/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp
index c8ee0dd..95557b5 100644
--- a/identity/aidl/default/common/IdentityCredential.cpp
+++ b/identity/aidl/default/common/IdentityCredential.cpp
@@ -488,7 +488,7 @@
}
for (size_t n = 0; n < nsMap->size(); n++) {
- auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
+ auto& [nsKeyItem, nsValueItem] = (*nsMap)[n];
const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
const cppbor::Map* nsInnerMap = nsValueItem->asMap();
if (nsKey == nullptr || nsInnerMap == nullptr) {
diff --git a/identity/aidl/default/common/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h
index 9913b86..ef9d133 100644
--- a/identity/aidl/default/common/IdentityCredential.h
+++ b/identity/aidl/default/common/IdentityCredential.h
@@ -27,7 +27,7 @@
#include <string>
#include <vector>
-#include <cppbor/cppbor.h>
+#include <cppbor.h>
#include "IdentityCredentialStore.h"
#include "SecureHardwareProxy.h"
diff --git a/identity/aidl/default/common/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp
index 2d897c7..25f129b 100644
--- a/identity/aidl/default/common/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/common/WritableIdentityCredential.cpp
@@ -23,8 +23,8 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include <cppbor/cppbor.h>
-#include <cppbor/cppbor_parse.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
#include <utility>
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 82c4011..61d15d2 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -34,7 +34,8 @@
"libcrypto",
],
static_libs: [
- "libcppbor",
+ "libcppbor_external",
+ "libcppcose_rkp",
"libkeymaster_portable",
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
diff --git a/identity/aidl/vts/AuthenticationKeyTests.cpp b/identity/aidl/vts/AuthenticationKeyTests.cpp
index bda3e70..25d74d4 100644
--- a/identity/aidl/vts/AuthenticationKeyTests.cpp
+++ b/identity/aidl/vts/AuthenticationKeyTests.cpp
@@ -118,7 +118,7 @@
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
- string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
diff --git a/identity/aidl/vts/DeleteCredentialTests.cpp b/identity/aidl/vts/DeleteCredentialTests.cpp
index 1d30067..d3addf4 100644
--- a/identity/aidl/vts/DeleteCredentialTests.cpp
+++ b/identity/aidl/vts/DeleteCredentialTests.cpp
@@ -126,7 +126,7 @@
optional<vector<uint8_t>> proofOfDeletion =
support::coseSignGetPayload(proofOfDeletionSignature);
ASSERT_TRUE(proofOfDeletion);
- string cborPretty = support::cborPrettyPrint(proofOfDeletion.value(), 32, {});
+ string cborPretty = cppbor::prettyPrint(proofOfDeletion.value(), 32, {});
EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', true, ]", cborPretty);
EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {}, // Additional data
credentialPubKey_));
@@ -153,7 +153,7 @@
optional<vector<uint8_t>> proofOfDeletion =
support::coseSignGetPayload(proofOfDeletionSignature);
ASSERT_TRUE(proofOfDeletion);
- string cborPretty = support::cborPrettyPrint(proofOfDeletion.value(), 32, {});
+ string cborPretty = cppbor::prettyPrint(proofOfDeletion.value(), 32, {});
EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', {0x41, 0x42, 0x43}, true, ]",
cborPretty);
EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {}, // Additional data
diff --git a/identity/aidl/vts/EndToEndTests.cpp b/identity/aidl/vts/EndToEndTests.cpp
index 5798b4c..67db915 100644
--- a/identity/aidl/vts/EndToEndTests.cpp
+++ b/identity/aidl/vts/EndToEndTests.cpp
@@ -231,7 +231,7 @@
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
- cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+ cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
@@ -339,8 +339,8 @@
vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
cppbor::Array sessionTranscript = cppbor::Array()
- .add(cppbor::Semantic(24, deviceEngagementBytes))
- .add(cppbor::Semantic(24, eReaderPubBytes));
+ .add(cppbor::SemanticTag(24, deviceEngagementBytes))
+ .add(cppbor::SemanticTag(24, eReaderPubBytes));
vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
vector<uint8_t> itemsRequestBytes =
@@ -353,7 +353,7 @@
.add("Home address", true))
.add("Image", cppbor::Map().add("Portrait image", false)))
.encode();
- cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
+ cborPretty = cppbor::prettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
EXPECT_EQ(
"{\n"
" 'nameSpaces' : {\n"
@@ -373,10 +373,10 @@
cppbor::Array()
.add("ReaderAuthentication")
.add(sessionTranscript.clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
+ .add(cppbor::SemanticTag(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
- cppbor::Semantic(24, encodedReaderAuthentication).encode();
+ cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
optional<vector<uint8_t>> readerSignature =
support::coseSignEcDsa(readerKey, {}, // content
encodedReaderAuthenticationBytes, // detached content
@@ -443,7 +443,7 @@
vector<uint8_t> mac;
vector<uint8_t> deviceNameSpacesEncoded;
ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
+ cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ(
"{\n"
" 'PersonalData' : {\n"
@@ -462,10 +462,11 @@
string docType = "org.iso.18013-5.2019.mdl";
optional<vector<uint8_t>> readerEphemeralPrivateKey =
support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
- optional<vector<uint8_t>> eMacKey = support::calcEMacKey(
- readerEphemeralPrivateKey.value(), // Private Key
- signingPubKey.value(), // Public Key
- cppbor::Semantic(24, sessionTranscript.encode()).encode()); // SessionTranscriptBytes
+ optional<vector<uint8_t>> eMacKey =
+ support::calcEMacKey(readerEphemeralPrivateKey.value(), // Private Key
+ signingPubKey.value(), // Public Key
+ cppbor::SemanticTag(24, sessionTranscript.encode())
+ .encode()); // SessionTranscriptBytes
optional<vector<uint8_t>> calculatedMac =
support::calcMac(sessionTranscript.encode(), // SessionTranscript
docType, // DocType
@@ -486,7 +487,7 @@
testEntriesEntryCounts)
.isOk());
ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
+ cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
@@ -508,7 +509,7 @@
testEntriesEntryCounts)
.isOk());
ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
+ cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
diff --git a/identity/aidl/vts/ProveOwnershipTests.cpp b/identity/aidl/vts/ProveOwnershipTests.cpp
index d1a3d39..fa0e293 100644
--- a/identity/aidl/vts/ProveOwnershipTests.cpp
+++ b/identity/aidl/vts/ProveOwnershipTests.cpp
@@ -131,7 +131,7 @@
optional<vector<uint8_t>> proofOfOwnership =
support::coseSignGetPayload(proofOfOwnershipSignature);
ASSERT_TRUE(proofOfOwnership);
- string cborPretty = support::cborPrettyPrint(proofOfOwnership.value(), 32, {});
+ string cborPretty = cppbor::prettyPrint(proofOfOwnership.value(), 32, {});
EXPECT_EQ("['ProofOfOwnership', 'org.iso.18013-5.2019.mdl', {0x11, 0x12}, true, ]", cborPretty);
EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfOwnershipSignature, {}, // Additional data
credentialPubKey_));
diff --git a/identity/aidl/vts/ReaderAuthTests.cpp b/identity/aidl/vts/ReaderAuthTests.cpp
index 7656c8e..c4a58c3 100644
--- a/identity/aidl/vts/ReaderAuthTests.cpp
+++ b/identity/aidl/vts/ReaderAuthTests.cpp
@@ -262,8 +262,8 @@
vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
cppbor::Array sessionTranscript = cppbor::Array()
- .add(cppbor::Semantic(24, deviceEngagementBytes))
- .add(cppbor::Semantic(24, eReaderPubBytes));
+ .add(cppbor::SemanticTag(24, deviceEngagementBytes))
+ .add(cppbor::SemanticTag(24, eReaderPubBytes));
vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
vector<uint8_t> itemsRequestBytes;
@@ -293,10 +293,10 @@
cppbor::Array()
.add("ReaderAuthentication")
.add(sessionTranscript.clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
+ .add(cppbor::SemanticTag(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
- cppbor::Semantic(24, encodedReaderAuthentication).encode();
+ cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
optional<vector<uint8_t>> readerSignature =
support::coseSignEcDsa(readerPrivateKey, // private key for reader
@@ -517,8 +517,8 @@
vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
cppbor::Array sessionTranscript = cppbor::Array()
- .add(cppbor::Semantic(24, deviceEngagementBytes))
- .add(cppbor::Semantic(24, eReaderPubBytes));
+ .add(cppbor::SemanticTag(24, deviceEngagementBytes))
+ .add(cppbor::SemanticTag(24, eReaderPubBytes));
vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
vector<uint8_t> itemsRequestBytes;
@@ -535,10 +535,10 @@
cppbor::Array()
.add("ReaderAuthentication")
.add(sessionTranscript.clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
+ .add(cppbor::SemanticTag(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
- cppbor::Semantic(24, encodedReaderAuthentication).encode();
+ cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_};
optional<vector<uint8_t>> readerSignature =
diff --git a/identity/aidl/vts/TestCredentialTests.cpp b/identity/aidl/vts/TestCredentialTests.cpp
index d53de3b..46c2229 100644
--- a/identity/aidl/vts/TestCredentialTests.cpp
+++ b/identity/aidl/vts/TestCredentialTests.cpp
@@ -114,7 +114,7 @@
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
- string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
diff --git a/identity/aidl/vts/UpdateCredentialTests.cpp b/identity/aidl/vts/UpdateCredentialTests.cpp
index 9c5ca55..e05bb3b 100644
--- a/identity/aidl/vts/UpdateCredentialTests.cpp
+++ b/identity/aidl/vts/UpdateCredentialTests.cpp
@@ -114,7 +114,7 @@
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
- string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
@@ -195,7 +195,7 @@
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
- string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
diff --git a/identity/aidl/vts/UserAuthTests.cpp b/identity/aidl/vts/UserAuthTests.cpp
index ef89d1c..edd1725 100644
--- a/identity/aidl/vts/UserAuthTests.cpp
+++ b/identity/aidl/vts/UserAuthTests.cpp
@@ -160,8 +160,8 @@
// Let SessionTranscript be a map here (it's an array in EndToEndTest) just
// to check that the implementation can deal with either.
cppbor::Map sessionTranscript;
- sessionTranscript.add(42, cppbor::Semantic(24, deviceEngagementBytes));
- sessionTranscript.add(43, cppbor::Semantic(24, eReaderPubBytes));
+ sessionTranscript.add(42, cppbor::SemanticTag(24, deviceEngagementBytes));
+ sessionTranscript.add(43, cppbor::SemanticTag(24, eReaderPubBytes));
return sessionTranscript;
}
@@ -209,7 +209,7 @@
vector<uint8_t> dataToSign = cppbor::Array()
.add("ReaderAuthentication")
.add(sessionTranscript_.clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
+ .add(cppbor::SemanticTag(24, itemsRequestBytes))
.encode();
}
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index cc63c48..bc37020 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -338,8 +338,7 @@
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
- string cborPretty =
- support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+ string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
@@ -449,9 +448,9 @@
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
- string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(),
- 32, //
- {"readerCertificate"});
+ string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(),
+ 32, //
+ {"readerCertificate"});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
index d00f59a..db1a945 100644
--- a/identity/support/Android.bp
+++ b/identity/support/Android.bp
@@ -35,6 +35,7 @@
"android.hardware.keymaster@4.0",
"libcrypto",
"libbase",
+ "libcppcose_rkp",
"libhidlbase",
"libhardware",
"libkeymaster_portable",
@@ -42,7 +43,7 @@
"libpuresoftkeymasterdevice",
],
static_libs: [
- "libcppbor",
+ "libcppbor_external",
],
}
@@ -59,7 +60,7 @@
"libhardware",
],
static_libs: [
- "libcppbor",
+ "libcppbor_external",
"libgmock",
],
test_suites: ["general-tests"],
@@ -89,7 +90,7 @@
"tests/cppbor_test.cpp",
],
shared_libs: [
- "libcppbor",
+ "libcppbor_external",
"libbase",
],
static_libs: [
@@ -104,7 +105,7 @@
"tests/cppbor_test.cpp",
],
shared_libs: [
- "libcppbor",
+ "libcppbor_external",
"libbase",
],
static_libs: [
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index aba89c1..4547624 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -148,199 +148,6 @@
}
// ---------------------------------------------------------------------------
-// CBOR utilities.
-// ---------------------------------------------------------------------------
-
-static bool cborAreAllElementsNonCompound(const cppbor::CompoundItem* compoundItem) {
- if (compoundItem->type() == cppbor::ARRAY) {
- const cppbor::Array* array = compoundItem->asArray();
- for (size_t n = 0; n < array->size(); n++) {
- const cppbor::Item* entry = (*array)[n].get();
- switch (entry->type()) {
- case cppbor::ARRAY:
- case cppbor::MAP:
- return false;
- default:
- break;
- }
- }
- } else {
- const cppbor::Map* map = compoundItem->asMap();
- for (size_t n = 0; n < map->size(); n++) {
- auto [keyEntry, valueEntry] = (*map)[n];
- switch (keyEntry->type()) {
- case cppbor::ARRAY:
- case cppbor::MAP:
- return false;
- default:
- break;
- }
- switch (valueEntry->type()) {
- case cppbor::ARRAY:
- case cppbor::MAP:
- return false;
- default:
- break;
- }
- }
- }
- return true;
-}
-
-static bool cborPrettyPrintInternal(const cppbor::Item* item, string& out, size_t indent,
- size_t maxBStrSize, const vector<string>& mapKeysToNotPrint) {
- char buf[80];
-
- string indentString(indent, ' ');
-
- switch (item->type()) {
- case cppbor::UINT:
- snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue());
- out.append(buf);
- break;
-
- case cppbor::NINT:
- snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value());
- out.append(buf);
- break;
-
- case cppbor::BSTR: {
- const cppbor::Bstr* bstr = item->asBstr();
- const vector<uint8_t>& value = bstr->value();
- if (value.size() > maxBStrSize) {
- unsigned char digest[SHA_DIGEST_LENGTH];
- SHA_CTX ctx;
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, value.data(), value.size());
- SHA1_Final(digest, &ctx);
- char buf2[SHA_DIGEST_LENGTH * 2 + 1];
- for (size_t n = 0; n < SHA_DIGEST_LENGTH; n++) {
- snprintf(buf2 + n * 2, 3, "%02x", digest[n]);
- }
- snprintf(buf, sizeof(buf), "<bstr size=%zd sha1=%s>", value.size(), buf2);
- out.append(buf);
- } else {
- out.append("{");
- for (size_t n = 0; n < value.size(); n++) {
- if (n > 0) {
- out.append(", ");
- }
- snprintf(buf, sizeof(buf), "0x%02x", value[n]);
- out.append(buf);
- }
- out.append("}");
- }
- } break;
-
- case cppbor::TSTR:
- out.append("'");
- {
- // TODO: escape "'" characters
- out.append(item->asTstr()->value().c_str());
- }
- out.append("'");
- break;
-
- case cppbor::ARRAY: {
- const cppbor::Array* array = item->asArray();
- if (array->size() == 0) {
- out.append("[]");
- } else if (cborAreAllElementsNonCompound(array)) {
- out.append("[");
- for (size_t n = 0; n < array->size(); n++) {
- if (!cborPrettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
- mapKeysToNotPrint)) {
- return false;
- }
- out.append(", ");
- }
- out.append("]");
- } else {
- out.append("[\n" + indentString);
- for (size_t n = 0; n < array->size(); n++) {
- out.append(" ");
- if (!cborPrettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
- mapKeysToNotPrint)) {
- return false;
- }
- out.append(",\n" + indentString);
- }
- out.append("]");
- }
- } break;
-
- case cppbor::MAP: {
- const cppbor::Map* map = item->asMap();
-
- if (map->size() == 0) {
- out.append("{}");
- } else {
- out.append("{\n" + indentString);
- for (size_t n = 0; n < map->size(); n++) {
- out.append(" ");
-
- auto [map_key, map_value] = (*map)[n];
-
- if (!cborPrettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize,
- mapKeysToNotPrint)) {
- return false;
- }
- out.append(" : ");
- if (map_key->type() == cppbor::TSTR &&
- std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(),
- map_key->asTstr()->value()) != mapKeysToNotPrint.end()) {
- out.append("<not printed>");
- } else {
- if (!cborPrettyPrintInternal(map_value.get(), out, indent + 2, maxBStrSize,
- mapKeysToNotPrint)) {
- return false;
- }
- }
- out.append(",\n" + indentString);
- }
- out.append("}");
- }
- } break;
-
- case cppbor::SEMANTIC: {
- const cppbor::Semantic* semantic = item->asSemantic();
- snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", semantic->value());
- out.append(buf);
- cborPrettyPrintInternal(semantic->child().get(), out, indent, maxBStrSize,
- mapKeysToNotPrint);
- } break;
-
- case cppbor::SIMPLE:
- const cppbor::Bool* asBool = item->asSimple()->asBool();
- const cppbor::Null* asNull = item->asSimple()->asNull();
- if (asBool != nullptr) {
- out.append(asBool->value() ? "true" : "false");
- } else if (asNull != nullptr) {
- out.append("null");
- } else {
- LOG(ERROR) << "Only boolean/null is implemented for SIMPLE";
- return false;
- }
- break;
- }
-
- return true;
-}
-
-string cborPrettyPrint(const vector<uint8_t>& encodedCbor, size_t maxBStrSize,
- const vector<string>& mapKeysToNotPrint) {
- auto [item, _, message] = cppbor::parse(encodedCbor);
- if (item == nullptr) {
- LOG(ERROR) << "Data to pretty print is not valid CBOR: " << message;
- return "";
- }
-
- string out;
- cborPrettyPrintInternal(item.get(), out, 0, maxBStrSize, mapKeysToNotPrint);
- return out;
-}
-
-// ---------------------------------------------------------------------------
// Crypto functionality / abstraction.
// ---------------------------------------------------------------------------
@@ -2140,7 +1947,7 @@
}
for (size_t n = 0; n < protectedHeaders->size(); n++) {
- auto [keyItem, valueItem] = (*protectedHeaders)[n];
+ auto& [keyItem, valueItem] = (*protectedHeaders)[n];
const cppbor::Int* number = keyItem->asInt();
if (number == nullptr) {
LOG(ERROR) << "Key item in top-level map is not a number";
@@ -2183,7 +1990,7 @@
}
for (size_t n = 0; n < unprotectedHeaders->size(); n++) {
- auto [keyItem, valueItem] = (*unprotectedHeaders)[n];
+ auto& [keyItem, valueItem] = (*unprotectedHeaders)[n];
const cppbor::Int* number = keyItem->asInt();
if (number == nullptr) {
LOG(ERROR) << "Key item in top-level map is not a number";
@@ -2335,9 +2142,9 @@
.add("DeviceAuthentication")
.add(std::move(sessionTranscriptItem))
.add(docType)
- .add(cppbor::Semantic(kSemanticTagEncodedCbor, deviceNameSpacesEncoded));
+ .add(cppbor::SemanticTag(kSemanticTagEncodedCbor, deviceNameSpacesEncoded));
vector<uint8_t> deviceAuthenticationBytes =
- cppbor::Semantic(kSemanticTagEncodedCbor, deviceAuthentication.encode()).encode();
+ cppbor::SemanticTag(kSemanticTagEncodedCbor, deviceAuthentication.encode()).encode();
optional<vector<uint8_t>> calculatedMac =
support::coseMac0(eMacKey, {}, // payload
deviceAuthenticationBytes); // detached content
diff --git a/identity/support/tests/IdentityCredentialSupportTest.cpp b/identity/support/tests/IdentityCredentialSupportTest.cpp
index 509133c..4c9b87a 100644
--- a/identity/support/tests/IdentityCredentialSupportTest.cpp
+++ b/identity/support/tests/IdentityCredentialSupportTest.cpp
@@ -55,99 +55,6 @@
EXPECT_FALSE(support::decodeHex("012"));
}
-TEST(IdentityCredentialSupport, CborPrettyPrint) {
- EXPECT_EQ("'Some text'", support::cborPrettyPrint(cppbor::Tstr("Some text").encode()));
-
- EXPECT_EQ("''", support::cborPrettyPrint(cppbor::Tstr("").encode()));
-
- EXPECT_EQ("{0x01, 0x00, 0x02, 0xf0, 0xff, 0x40}",
- support::cborPrettyPrint(
- cppbor::Bstr(vector<uint8_t>({1, 0, 2, 240, 255, 64})).encode()));
-
- EXPECT_EQ("{}", support::cborPrettyPrint(cppbor::Bstr(vector<uint8_t>()).encode()));
-
- EXPECT_EQ("true", support::cborPrettyPrint(cppbor::Bool(true).encode()));
-
- EXPECT_EQ("false", support::cborPrettyPrint(cppbor::Bool(false).encode()));
-
- EXPECT_EQ("42", support::cborPrettyPrint(cppbor::Uint(42).encode()));
-
- EXPECT_EQ("9223372036854775807", // 0x7fff ffff ffff ffff
- support::cborPrettyPrint(cppbor::Uint(std::numeric_limits<int64_t>::max()).encode()));
-
- EXPECT_EQ("-42", support::cborPrettyPrint(cppbor::Nint(-42).encode()));
-
- EXPECT_EQ("-9223372036854775808", // -0x8000 0000 0000 0000
- support::cborPrettyPrint(cppbor::Nint(std::numeric_limits<int64_t>::min()).encode()));
-}
-
-TEST(IdentityCredentialSupport, CborPrettyPrintCompound) {
- cppbor::Array array = cppbor::Array("foo", "bar", "baz");
- EXPECT_EQ("['foo', 'bar', 'baz', ]", support::cborPrettyPrint(array.encode()));
-
- cppbor::Map map = cppbor::Map().add("foo", 42).add("bar", 43).add("baz", 44);
- EXPECT_EQ(
- "{\n"
- " 'foo' : 42,\n"
- " 'bar' : 43,\n"
- " 'baz' : 44,\n"
- "}",
- support::cborPrettyPrint(map.encode()));
-
- cppbor::Array array2 = cppbor::Array(cppbor::Tstr("Some text"), cppbor::Nint(-42));
- EXPECT_EQ("['Some text', -42, ]", support::cborPrettyPrint(array2.encode()));
-
- cppbor::Map map2 = cppbor::Map().add(42, "foo").add(43, "bar").add(44, "baz");
- EXPECT_EQ(
- "{\n"
- " 42 : 'foo',\n"
- " 43 : 'bar',\n"
- " 44 : 'baz',\n"
- "}",
- support::cborPrettyPrint(map2.encode()));
-
- cppbor::Array deeplyNestedArrays =
- cppbor::Array(cppbor::Array(cppbor::Array("a", "b", "c")),
- cppbor::Array(cppbor::Array("d", "e", cppbor::Array("f", "g"))));
- EXPECT_EQ(
- "[\n"
- " ['a', 'b', 'c', ],\n"
- " [\n 'd',\n"
- " 'e',\n"
- " ['f', 'g', ],\n"
- " ],\n"
- "]",
- support::cborPrettyPrint(deeplyNestedArrays.encode()));
-
- EXPECT_EQ(
- "[\n"
- " {0x0a, 0x0b},\n"
- " 'foo',\n"
- " 42,\n"
- " ['foo', 'bar', 'baz', ],\n"
- " {\n"
- " 'foo' : 42,\n"
- " 'bar' : 43,\n"
- " 'baz' : 44,\n"
- " },\n"
- " {\n"
- " 'deep1' : ['Some text', -42, ],\n"
- " 'deep2' : {\n"
- " 42 : 'foo',\n"
- " 43 : 'bar',\n"
- " 44 : 'baz',\n"
- " },\n"
- " },\n"
- "]",
- support::cborPrettyPrint(cppbor::Array(cppbor::Bstr(vector<uint8_t>{10, 11}),
- cppbor::Tstr("foo"), cppbor::Uint(42),
- std::move(array), std::move(map),
- (cppbor::Map()
- .add("deep1", std::move(array2))
- .add("deep2", std::move(map2))))
- .encode()));
-}
-
TEST(IdentityCredentialSupport, Signatures) {
vector<uint8_t> data = {1, 2, 3};
@@ -219,7 +126,7 @@
ASSERT_EQ(data, payload.value());
// Finally, check that |coseSign1| are the bytes of a valid COSE_Sign1 message
- string out = support::cborPrettyPrint(coseSign1.value());
+ string out = cppbor::prettyPrint(coseSign1.value());
out = replaceLine(out, -2, " [] // Signature Removed");
EXPECT_EQ(
"[\n"
@@ -250,7 +157,7 @@
ASSERT_EQ(0, payload.value().size());
// Finally, check that |coseSign1| are the bytes of a valid COSE_Sign1 message
- string out = support::cborPrettyPrint(coseSign1.value());
+ string out = cppbor::prettyPrint(coseSign1.value());
out = replaceLine(out, -2, " [] // Signature Removed");
EXPECT_EQ(
"[\n"
@@ -411,7 +318,7 @@
"0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, 0x5e, 0xbb, "
"0xe2, 0x2d, 0x42, 0xbe, 0x53},\n"
"]",
- support::cborPrettyPrint(mac.value()));
+ cppbor::prettyPrint(mac.value()));
}
TEST(IdentityCredentialSupport, CoseMac0DetachedContent) {
@@ -433,7 +340,7 @@
"0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, 0x5e, 0xbb, "
"0xe2, 0x2d, 0x42, 0xbe, 0x53},\n"
"]",
- support::cborPrettyPrint(mac.value()));
+ cppbor::prettyPrint(mac.value()));
}
// Generates a private key in DER format for a small value of 'd'.
@@ -460,8 +367,8 @@
const cppbor::Item* findValueForTstr(const cppbor::Map* map, const string& keyValue) {
// TODO: Need cast until libcppbor's Map::get() is marked as const
- auto [item, found] = ((cppbor::Map*)map)->get(keyValue);
- if (!found) {
+ const auto& item = map->get(keyValue);
+ if (!item) {
return nullptr;
}
return item.get();
@@ -483,12 +390,13 @@
return item->asMap();
}
-const cppbor::Semantic* findSemanticValueForTstr(const cppbor::Map* map, const string& keyValue) {
+const cppbor::SemanticTag* findSemanticValueForTstr(const cppbor::Map* map,
+ const string& keyValue) {
const cppbor::Item* item = findValueForTstr(map, keyValue);
if (item == nullptr) {
return nullptr;
}
- return item->asSemantic();
+ return item->asSemanticTag();
}
const std::string findStringValueForTstr(const cppbor::Map* map, const string& keyValue) {
@@ -576,11 +484,11 @@
auto [sessionEstablishmentItem, _se, _se2] = cppbor::parse(sessionEstablishmentEncoded.value());
const cppbor::Map* sessionEstablishment = sessionEstablishmentItem->asMap();
ASSERT_NE(sessionEstablishment, nullptr);
- const cppbor::Semantic* eReaderKeyBytes =
+ const cppbor::SemanticTag* eReaderKeyBytes =
findSemanticValueForTstr(sessionEstablishment, "eReaderKeyBytes");
ASSERT_NE(eReaderKeyBytes, nullptr);
- ASSERT_EQ(eReaderKeyBytes->value(), 24);
- const cppbor::Bstr* eReaderKeyBstr = eReaderKeyBytes->child()->asBstr();
+ ASSERT_EQ(eReaderKeyBytes->semanticTag(), 24);
+ const cppbor::Bstr* eReaderKeyBstr = eReaderKeyBytes->asBstr();
ASSERT_NE(eReaderKeyBstr, nullptr);
vector<uint8_t> eReaderKeyEncoded = eReaderKeyBstr->value();
// TODO: verify this agrees with ephemeralReaderKeyX and ephemeralReaderKeyY
@@ -605,12 +513,12 @@
// SessionTranscriptBytes = #6.24(bstr .cbor SessionTranscript)
//
cppbor::Array sessionTranscript;
- sessionTranscript.add(cppbor::Semantic(24, deviceEngagementEncoded));
- sessionTranscript.add(cppbor::Semantic(24, eReaderKeyEncoded));
+ sessionTranscript.add(cppbor::SemanticTag(24, deviceEngagementEncoded));
+ sessionTranscript.add(cppbor::SemanticTag(24, eReaderKeyEncoded));
sessionTranscript.add(cppbor::Null());
vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
vector<uint8_t> sessionTranscriptBytes =
- cppbor::Semantic(24, sessionTranscriptEncoded).encode();
+ cppbor::SemanticTag(24, sessionTranscriptEncoded).encode();
// The expected EMacKey is 4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2
//
@@ -696,11 +604,11 @@
// Dig out the encoded form of DeviceNameSpaces
//
- const cppbor::Semantic* deviceNameSpacesBytes =
+ const cppbor::SemanticTag* deviceNameSpacesBytes =
findSemanticValueForTstr(deviceSigned, "nameSpaces");
ASSERT_NE(deviceNameSpacesBytes, nullptr);
- ASSERT_EQ(deviceNameSpacesBytes->value(), 24);
- const cppbor::Bstr* deviceNameSpacesBstr = deviceNameSpacesBytes->child()->asBstr();
+ ASSERT_EQ(deviceNameSpacesBytes->semanticTag(), 24);
+ const cppbor::Bstr* deviceNameSpacesBstr = deviceNameSpacesBytes->asBstr();
ASSERT_NE(deviceNameSpacesBstr, nullptr);
vector<uint8_t> deviceNameSpacesEncoded = deviceNameSpacesBstr->value();
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 8d6e74a..ea40971 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -117,6 +117,9 @@
DECLARE_TYPED_TAG(ATTESTATION_ID_PRODUCT);
DECLARE_TYPED_TAG(ATTESTATION_ID_MANUFACTURER);
DECLARE_TYPED_TAG(ATTESTATION_ID_MODEL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_SERIAL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_IMEI);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MEID);
DECLARE_TYPED_TAG(AUTH_TIMEOUT);
DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
DECLARE_TYPED_TAG(BLOCK_MODE);
diff --git a/light/2.0/default/Light.cpp b/light/2.0/default/Light.cpp
index 5484d2d..3febf6b 100644
--- a/light/2.0/default/Light.cpp
+++ b/light/2.0/default/Light.cpp
@@ -140,7 +140,7 @@
ret = hwModule->methods->open(hwModule, name,
reinterpret_cast<hw_device_t**>(&lightDevice));
if (ret != 0) {
- ALOGE("light_open %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
+ ALOGI("light_open %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
}
} else {
ALOGE("hw_get_module %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
index 8329303..7849ca7 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
@@ -44,7 +44,9 @@
OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
private:
const nn::SharedPreparedModel kPreparedModel;
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
index b695f48..1baabdf 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -22,10 +22,15 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_0::utils {
+constexpr auto kVersion = nn::Version::ANDROID_OC_MR1;
+
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
const auto maybeCanonical = nn::convert(halObject);
@@ -45,6 +50,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
index 971ad08..e3a9757 100644
--- a/neuralnetworks/1.0/utils/src/Burst.cpp
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -20,6 +20,7 @@
#include <nnapi/IBurst.h>
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <memory>
@@ -48,8 +49,10 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
- const nn::Request& request, nn::MeasureTiming measure) const {
- return kPreparedModel->execute(request, measure, {}, {});
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
}
} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index 700b050..c0498eb 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -35,6 +35,8 @@
#include <utility>
#include <variant>
+#include "Utils.h"
+
namespace {
template <typename Type>
@@ -42,8 +44,6 @@
return static_cast<std::underlying_type_t<Type>>(value);
}
-constexpr auto kVersion = android::nn::Version::ANDROID_OC_MR1;
-
} // namespace
namespace android::nn {
@@ -53,13 +53,13 @@
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -68,16 +68,9 @@
}
template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_0::utils::compliantVersion(canonical));
return canonical;
}
@@ -248,13 +241,13 @@
namespace {
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(utils::unvalidatedConvert(arguments[i]));
}
@@ -262,15 +255,8 @@
}
template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
return utils::unvalidatedConvert(canonical);
}
diff --git a/neuralnetworks/1.0/utils/test/MockDevice.h b/neuralnetworks/1.0/utils/test/MockDevice.h
index 0fb59e3..7c399ec 100644
--- a/neuralnetworks/1.0/utils/test/MockDevice.h
+++ b/neuralnetworks/1.0/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.0/IDevice.h>
#include <gmock/gmock.h>
@@ -83,4 +83,4 @@
} // namespace android::hardware::neuralnetworks::V1_0::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.0/utils/test/MockPreparedModel.h b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
index 7a48a83..03f1a4b 100644
--- a/neuralnetworks/1.0/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -82,4 +82,4 @@
} // namespace android::hardware::neuralnetworks::V1_0::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
index a5cbc72..f19ed77 100644
--- a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
@@ -224,7 +224,19 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
index 09597a3..a8cf8cf 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -22,12 +22,16 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.1/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_1::utils {
constexpr auto kDefaultExecutionPreference = ExecutionPreference::FAST_SINGLE_ANSWER;
+constexpr auto kVersion = nn::Version::ANDROID_P;
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
@@ -48,6 +52,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index d07f7d0..467ceb3 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -35,11 +35,7 @@
#include <type_traits>
#include <utility>
-namespace {
-
-constexpr auto kVersion = android::nn::Version::ANDROID_P;
-
-} // namespace
+#include "Utils.h"
namespace android::nn {
namespace {
@@ -47,13 +43,13 @@
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -62,16 +58,9 @@
}
template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_1::utils::compliantVersion(canonical));
return canonical;
}
@@ -180,13 +169,13 @@
}
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
}
@@ -194,16 +183,9 @@
}
template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
- return utils::unvalidatedConvert(canonical);
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
+ return unvalidatedConvert(canonical);
}
} // anonymous namespace
diff --git a/neuralnetworks/1.1/utils/test/MockDevice.h b/neuralnetworks/1.1/utils/test/MockDevice.h
index 3b92e58..db7392d 100644
--- a/neuralnetworks/1.1/utils/test/MockDevice.h
+++ b/neuralnetworks/1.1/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.1/IDevice.h>
#include <gmock/gmock.h>
@@ -92,4 +92,4 @@
} // namespace android::hardware::neuralnetworks::V1_1::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.1/utils/test/MockPreparedModel.h b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
index aba731e..257397d 100644
--- a/neuralnetworks/1.1/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -41,4 +41,4 @@
} // namespace android::hardware::neuralnetworks::V1_0::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
index 6b6fc71..9669d8c0 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
@@ -57,7 +57,8 @@
public:
using FallbackFunction =
std::function<nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>(
- const nn::Request&, nn::MeasureTiming)>;
+ const nn::Request&, nn::MeasureTiming, const nn::OptionalTimePoint&,
+ const nn::OptionalDuration&)>;
/**
* NN runtime memory cache.
@@ -168,7 +169,9 @@
// See IBurst::execute for information on this method.
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
private:
mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
index 3233114..09691b6 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -22,19 +22,25 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.1/Utils.h>
+#include <nnapi/hal/HandleError.h>
#include <limits>
namespace android::hardware::neuralnetworks::V1_2::utils {
using CacheToken = hidl_array<uint8_t, static_cast<size_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+using V1_1::utils::kDefaultExecutionPreference;
constexpr auto kDefaultMesaureTiming = MeasureTiming::NO;
constexpr auto kNoTiming = Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
.timeInDriver = std::numeric_limits<uint64_t>::max()};
+constexpr auto kVersion = nn::Version::ANDROID_Q;
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
@@ -55,6 +61,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 2c45583..29945b7 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -37,6 +37,8 @@
#include <type_traits>
#include <utility>
+#include "Utils.h"
+
namespace {
template <typename Type>
@@ -45,50 +47,23 @@
}
using HalDuration = std::chrono::duration<uint64_t, std::micro>;
-constexpr auto kVersion = android::nn::Version::ANDROID_Q;
-constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
} // namespace
namespace android::nn {
namespace {
-constexpr bool validOperandType(OperandType operandType) {
- switch (operandType) {
- case OperandType::FLOAT32:
- case OperandType::INT32:
- case OperandType::UINT32:
- case OperandType::TENSOR_FLOAT32:
- case OperandType::TENSOR_INT32:
- case OperandType::TENSOR_QUANT8_ASYMM:
- case OperandType::BOOL:
- case OperandType::TENSOR_QUANT16_SYMM:
- case OperandType::TENSOR_FLOAT16:
- case OperandType::TENSOR_BOOL8:
- case OperandType::FLOAT16:
- case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
- case OperandType::TENSOR_QUANT16_ASYMM:
- case OperandType::TENSOR_QUANT8_SYMM:
- case OperandType::OEM:
- case OperandType::TENSOR_OEM_BYTE:
- return true;
- default:
- break;
- }
- return isExtension(operandType);
-}
-
using hardware::hidl_handle;
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -97,29 +72,16 @@
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const hidl_vec<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
-template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_2::utils::compliantVersion(canonical));
return canonical;
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(validatedConvert(argument)));
@@ -145,8 +107,7 @@
const bool validOperandTypes = std::all_of(
capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
[](const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
- const auto maybeType = unvalidatedConvert(operandPerformance.type);
- return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+ return validatedConvert(operandPerformance.type).has_value();
});
if (!validOperandTypes) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -275,6 +236,7 @@
GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing) {
constexpr uint64_t kMaxTiming = std::chrono::floor<HalDuration>(Duration::max()).count();
constexpr auto convertTiming = [](uint64_t halTiming) -> OptionalDuration {
+ constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
if (halTiming == kNoTiming) {
return {};
}
@@ -378,25 +340,19 @@
}
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
}
return halObject;
}
-template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const std::vector<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
nn::GeneralResult<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
return Operand::ExtraParams{};
}
@@ -416,22 +372,15 @@
}
template <typename Type>
-decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
- return utils::unvalidatedConvert(canonical);
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
+ return unvalidatedConvert(canonical);
}
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(validatedConvert(arguments[i]));
}
@@ -469,7 +418,7 @@
capabilities.operandPerformance.asVector().end(),
std::back_inserter(operandPerformance),
[](const nn::Capabilities::OperandPerformance& operandPerformance) {
- return nn::validOperandType(operandPerformance.type);
+ return compliantVersion(operandPerformance.type).has_value();
});
return Capabilities{
@@ -570,6 +519,7 @@
nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
constexpr auto convertTiming = [](nn::OptionalDuration canonicalTiming) -> uint64_t {
+ constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
if (!canonicalTiming.has_value()) {
return kNoTiming;
}
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
index eedf591..7a17f25 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -276,7 +276,9 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-ExecutionBurstController::execute(const nn::Request& request, nn::MeasureTiming measure) const {
+ExecutionBurstController::execute(const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
// This is the first point when we know an execution is occurring, so begin to collect
// systraces. Note that the first point we can begin collecting systraces in
// ExecutionBurstServer is when the RequestChannelReceiver realizes there is data in the FMQ, so
@@ -289,7 +291,7 @@
version > nn::Version::ANDROID_Q) {
// fallback to another execution path if the packet could not be sent
if (kFallback) {
- return kFallback(request, measure);
+ return kFallback(request, measure, deadline, loopTimeoutDuration);
}
return NN_ERROR() << "Request object has features not supported by IBurst::execute";
}
@@ -323,7 +325,7 @@
if (!sendStatus.ok()) {
// fallback to another execution path if the packet could not be sent
if (kFallback) {
- return kFallback(request, measure);
+ return kFallback(request, measure, deadline, loopTimeoutDuration);
}
return NN_ERROR() << "Error sending FMQ packet: " << sendStatus.error();
}
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
index 50af881..c67159e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
@@ -259,7 +259,7 @@
nn::MeasureTiming canonicalMeasure = NN_TRY(makeExecutionFailure(nn::convert(measure)));
const auto [outputShapes, timing] =
- NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure));
+ NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {}));
return std::make_pair(NN_TRY(makeExecutionFailure(convert(outputShapes))),
NN_TRY(makeExecutionFailure(convert(timing))));
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index 71a4ea8..b209a44 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -122,10 +122,12 @@
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
auto self = shared_from_this();
- auto fallback = [preparedModel = std::move(self)](const nn::Request& request,
- nn::MeasureTiming measure)
+ auto fallback = [preparedModel = std::move(self)](
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration)
-> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
- return preparedModel->execute(request, measure, {}, {});
+ return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
};
const auto pollingTimeWindow = getBurstControllerPollingTimeWindow();
return ExecutionBurstController::create(kPreparedModel, std::move(fallback), pollingTimeWindow);
diff --git a/neuralnetworks/1.2/utils/test/MockBurstContext.h b/neuralnetworks/1.2/utils/test/MockBurstContext.h
new file mode 100644
index 0000000..e364178
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/MockBurstContext.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
+
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class MockBurstContext final : public IBurstContext {
+ public:
+ // V1_2 methods below.
+ MOCK_METHOD(Return<void>, freeMemory, (int32_t slot), (override));
+};
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
diff --git a/neuralnetworks/1.2/utils/test/MockDevice.h b/neuralnetworks/1.2/utils/test/MockDevice.h
index b459943..0d34c70 100644
--- a/neuralnetworks/1.2/utils/test/MockDevice.h
+++ b/neuralnetworks/1.2/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.2/IDevice.h>
#include <gmock/gmock.h>
@@ -114,4 +114,4 @@
} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.2/utils/test/MockPreparedModel.h b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
index f5fd1f3..bd81712 100644
--- a/neuralnetworks/1.2/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -98,4 +98,4 @@
} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
index 5062ac9..d297b1a 100644
--- a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
@@ -16,6 +16,8 @@
#include "MockPreparedModel.h"
+#include "MockBurstContext.h"
+
#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -67,6 +69,17 @@
return launchStatus;
};
}
+auto makeConfigureExecutionBurstReturn(V1_0::ErrorStatus status,
+ const sp<MockBurstContext>& burstContext) {
+ return [status, burstContext](
+ const sp<V1_2::IBurstCallback>& /*callback*/,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& /*requestChannel*/,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& /*resultChannel*/,
+ V1_2::IPreparedModel::configureExecutionBurst_cb cb) -> hardware::Return<void> {
+ cb(status, burstContext);
+ return hardware::Void();
+ };
+}
std::function<hardware::Status()> makeTransportFailure(status_t status) {
return [status] { return hardware::Status::fromStatusT(status); };
@@ -321,7 +334,76 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto mockBurstContext = sp<MockBurstContext>::make();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::NONE, mockBurstContext));
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(
+ makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::GENERAL_FAILURE, nullptr));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index e0b04a8..de889e4 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -131,6 +131,14 @@
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
+ * The deadline is represented as nanoseconds since the epoch of the steady
+ * clock (as if from std::chrono::steady_clock::time_point), but the service
+ * may convert it to the nanoseconds since boot time (as if from
+ * clock_gettime(CLOCK_BOOTTIME, &ts) or
+ * android::base::boot_clock::time_point) to account for time when the
+ * system is suspended. This conversion can by done by finding the timeout
+ * duration remaining compared to the steady_clock and adding it to the
+ * current boot_clock time.
*
* Optionally, the driver may save the prepared model to cache during the
* asynchronous preparation. Any error that occurs when saving to cache must
@@ -249,7 +257,15 @@
* ErrorStatus::MISSED_DEADLINE_TRANSIENT}
* or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The
* error due to an abort must be sent the same way as other errors,
- * described above.
+ * described above. The deadline is represented as nanoseconds since the
+ * epoch of the steady clock (as if from
+ * std::chrono::steady_clock::time_point), but the service may convert it to
+ * the nanoseconds since boot time (as if from
+ * clock_gettime(CLOCK_BOOTTIME, &ts) or
+ * android::base::boot_clock::time_point) to account for time when the
+ * system is suspended. This conversion can by done by finding the timeout
+ * duration remaining compared to the steady_clock and adding it to the
+ * current boot_clock time.
*
* The only information that may be unknown to the model at this stage is
* the shape of the tensors, which may only be known at execution time. As
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index e7d63f4..8b86a1a 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -74,6 +74,14 @@
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
+ * The deadline is represented as nanoseconds since the epoch of the steady
+ * clock (as if from std::chrono::steady_clock::time_point), but the service
+ * may convert it to the nanoseconds since boot time (as if from
+ * clock_gettime(CLOCK_BOOTTIME, &ts) or
+ * android::base::boot_clock::time_point) to account for time when the
+ * system is suspended. This conversion can by done by finding the timeout
+ * duration remaining compared to the steady_clock and adding it to the
+ * current boot_clock time.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -150,6 +158,14 @@
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
+ * The deadline is represented as nanoseconds since the epoch of the steady
+ * clock (as if from std::chrono::steady_clock::time_point), but the service
+ * may convert it to the nanoseconds since boot time (as if from
+ * clock_gettime(CLOCK_BOOTTIME, &ts) or
+ * android::base::boot_clock::time_point) to account for time when the
+ * system is suspended. This conversion can by done by finding the timeout
+ * duration remaining compared to the steady_clock and adding it to the
+ * current boot_clock time.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -231,6 +247,14 @@
* {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
* ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
+ * The deadline is represented as nanoseconds since the epoch of the steady
+ * clock (as if from std::chrono::steady_clock::time_point), but the service
+ * may convert it to the nanoseconds since boot time (as if from
+ * clock_gettime(CLOCK_BOOTTIME, &ts) or
+ * android::base::boot_clock::time_point) to account for time when the
+ * system is suspended. This conversion can by done by finding the timeout
+ * duration remaining compared to the steady_clock and adding it to the
+ * current boot_clock time.
*
* If any of the sync fences in waitFor changes to error status after the executeFenced
* call succeeds, or the execution is aborted because it cannot finish before the deadline
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
index 3ce412c..1d76caa 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -22,14 +22,25 @@
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.3/types.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.1/Utils.h>
#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/Utils.h>
+#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_3::utils {
+using V1_1::utils::kDefaultExecutionPreference;
+using V1_2::utils::CacheToken;
+using V1_2::utils::kDefaultMesaureTiming;
+using V1_2::utils::kNoTiming;
+
constexpr auto kDefaultPriority = Priority::MEDIUM;
+constexpr auto kVersion = nn::Version::ANDROID_R;
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
@@ -50,6 +61,15 @@
}
template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
auto convertFromNonCanonical(const Type& nonCanonicalObject)
-> decltype(convert(nn::convert(nonCanonicalObject).value())) {
return convert(NN_TRY(nn::convert(nonCanonicalObject)));
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 9788fe1..e8a4f55 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -38,55 +38,47 @@
#include <type_traits>
#include <utility>
+#include "Utils.h"
+
namespace {
+std::chrono::nanoseconds makeNanosFromUint64(uint64_t nanoseconds) {
+ constexpr auto kMaxCount = std::chrono::nanoseconds::max().count();
+ using CommonType = std::common_type_t<std::chrono::nanoseconds::rep, uint64_t>;
+ const auto count = std::min<CommonType>(kMaxCount, nanoseconds);
+ return std::chrono::nanoseconds{static_cast<std::chrono::nanoseconds::rep>(count)};
+}
+
+uint64_t makeUint64FromNanos(std::chrono::nanoseconds nanoseconds) {
+ if (nanoseconds < std::chrono::nanoseconds::zero()) {
+ return 0;
+ }
+ constexpr auto kMaxCount = std::numeric_limits<uint64_t>::max();
+ using CommonType = std::common_type_t<std::chrono::nanoseconds::rep, uint64_t>;
+ const auto count = std::min<CommonType>(kMaxCount, nanoseconds.count());
+ return static_cast<uint64_t>(count);
+}
+
template <typename Type>
constexpr std::underlying_type_t<Type> underlyingType(Type value) {
return static_cast<std::underlying_type_t<Type>>(value);
}
-constexpr auto kVersion = android::nn::Version::ANDROID_R;
-
} // namespace
namespace android::nn {
namespace {
-constexpr auto validOperandType(nn::OperandType operandType) {
- switch (operandType) {
- case nn::OperandType::FLOAT32:
- case nn::OperandType::INT32:
- case nn::OperandType::UINT32:
- case nn::OperandType::TENSOR_FLOAT32:
- case nn::OperandType::TENSOR_INT32:
- case nn::OperandType::TENSOR_QUANT8_ASYMM:
- case nn::OperandType::BOOL:
- case nn::OperandType::TENSOR_QUANT16_SYMM:
- case nn::OperandType::TENSOR_FLOAT16:
- case nn::OperandType::TENSOR_BOOL8:
- case nn::OperandType::FLOAT16:
- case nn::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
- case nn::OperandType::TENSOR_QUANT16_ASYMM:
- case nn::OperandType::TENSOR_QUANT8_SYMM:
- case nn::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
- case nn::OperandType::SUBGRAPH:
- case nn::OperandType::OEM:
- case nn::OperandType::TENSOR_OEM_BYTE:
- return true;
- }
- return nn::isExtension(operandType);
-}
-
using hardware::hidl_vec;
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
@@ -95,29 +87,16 @@
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const hidl_vec<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
-template <typename Type>
-decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(hal::V1_3::utils::compliantVersion(canonical));
return canonical;
}
template <typename Type>
-GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
const hidl_vec<Type>& arguments) {
- std::vector<unvalidatedConvertOutput<Type>> canonical;
+ std::vector<UnvalidatedConvertOutput<Type>> canonical;
canonical.reserve(arguments.size());
for (const auto& argument : arguments) {
canonical.push_back(NN_TRY(validatedConvert(argument)));
@@ -143,8 +122,7 @@
const bool validOperandTypes = std::all_of(
capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
[](const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
- const auto maybeType = unvalidatedConvert(operandPerformance.type);
- return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+ return validatedConvert(operandPerformance.type).has_value();
});
if (!validOperandTypes) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
@@ -244,7 +222,7 @@
return BufferRole{
.modelIndex = bufferRole.modelIndex,
.ioIndex = bufferRole.ioIndex,
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.frequency,
};
}
@@ -276,8 +254,32 @@
switch (optionalTimePoint.getDiscriminator()) {
case Discriminator::none:
return {};
- case Discriminator::nanosecondsSinceEpoch:
- return TimePoint{Duration{optionalTimePoint.nanosecondsSinceEpoch()}};
+ case Discriminator::nanosecondsSinceEpoch: {
+ const auto currentSteadyTime = std::chrono::steady_clock::now();
+ const auto currentBootTime = Clock::now();
+
+ const auto timeSinceEpoch =
+ makeNanosFromUint64(optionalTimePoint.nanosecondsSinceEpoch());
+ const auto steadyTimePoint = std::chrono::steady_clock::time_point{timeSinceEpoch};
+
+ // Both steadyTimePoint and currentSteadyTime are guaranteed to be non-negative, so this
+ // subtraction will never overflow or underflow.
+ const auto timeRemaining = steadyTimePoint - currentSteadyTime;
+
+ // currentBootTime is guaranteed to be non-negative, so this code only protects against
+ // an overflow.
+ nn::TimePoint bootTimePoint;
+ constexpr auto kZeroNano = std::chrono::nanoseconds::zero();
+ constexpr auto kMaxTime = nn::TimePoint::max();
+ if (timeRemaining > kZeroNano && currentBootTime > kMaxTime - timeRemaining) {
+ bootTimePoint = kMaxTime;
+ } else {
+ bootTimePoint = currentBootTime + timeRemaining;
+ }
+
+ constexpr auto kZeroTime = nn::TimePoint{};
+ return std::max(bootTimePoint, kZeroTime);
+ }
}
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
<< "Invalid OptionalTimePoint discriminator "
@@ -401,25 +403,19 @@
}
template <typename Input>
-using unvalidatedConvertOutput =
+using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
}
return halObject;
}
-template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
- const std::vector<Type>& arguments) {
- return unvalidatedConvertVec(arguments);
-}
-
nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::SharedMemory& memory) {
Request::MemoryPool ret;
ret.hidlMemory(NN_TRY(unvalidatedConvert(memory)));
@@ -439,22 +435,15 @@
using utils::unvalidatedConvert;
template <typename Type>
-decltype(unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+ NN_TRY(compliantVersion(canonical));
return unvalidatedConvert(canonical);
}
template <typename Type>
-nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
const std::vector<Type>& arguments) {
- hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+ hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
for (size_t i = 0; i < arguments.size(); ++i) {
halObject[i] = NN_TRY(validatedConvert(arguments[i]));
}
@@ -482,7 +471,7 @@
capabilities.operandPerformance.asVector().end(),
std::back_inserter(operandPerformance),
[](const nn::Capabilities::OperandPerformance& operandPerformance) {
- return nn::validOperandType(operandPerformance.type);
+ return compliantVersion(operandPerformance.type).has_value();
});
return Capabilities{
@@ -577,7 +566,7 @@
return BufferRole{
.modelIndex = bufferRole.modelIndex,
.ioIndex = bufferRole.ioIndex,
- .frequency = bufferRole.frequency,
+ .frequency = bufferRole.probability,
};
}
@@ -601,9 +590,33 @@
nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
const nn::OptionalTimePoint& optionalTimePoint) {
+ const auto currentSteadyTime = std::chrono::steady_clock::now();
+ const auto currentBootTime = nn::Clock::now();
+
OptionalTimePoint ret;
if (optionalTimePoint.has_value()) {
- const auto count = optionalTimePoint.value().time_since_epoch().count();
+ const auto bootTimePoint = optionalTimePoint.value();
+
+ if (bootTimePoint < nn::TimePoint{}) {
+ return NN_ERROR() << "Trying to cast invalid time point";
+ }
+
+ // Both bootTimePoint and currentBootTime are guaranteed to be non-negative, so this
+ // subtraction will never overflow or underflow.
+ const auto timeRemaining = bootTimePoint - currentBootTime;
+
+ // currentSteadyTime is guaranteed to be non-negative, so this code only protects against an
+ // overflow.
+ std::chrono::steady_clock::time_point steadyTimePoint;
+ constexpr auto kZeroNano = std::chrono::nanoseconds::zero();
+ constexpr auto kMaxTime = std::chrono::steady_clock::time_point::max();
+ if (timeRemaining > kZeroNano && currentSteadyTime > kMaxTime - timeRemaining) {
+ steadyTimePoint = kMaxTime;
+ } else {
+ steadyTimePoint = currentSteadyTime + timeRemaining;
+ }
+
+ const uint64_t count = makeUint64FromNanos(steadyTimePoint.time_since_epoch());
ret.nanosecondsSinceEpoch(count);
}
return ret;
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index 64275a3..fd7f8f2 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -201,10 +201,12 @@
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
auto self = shared_from_this();
- auto fallback = [preparedModel = std::move(self)](const nn::Request& request,
- nn::MeasureTiming measure)
+ auto fallback = [preparedModel = std::move(self)](
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration)
-> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
- return preparedModel->execute(request, measure, {}, {});
+ return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
};
const auto pollingTimeWindow = V1_2::utils::getBurstControllerPollingTimeWindow();
return V1_2::utils::ExecutionBurstController::create(kPreparedModel, std::move(fallback),
diff --git a/neuralnetworks/1.3/utils/test/MockBuffer.h b/neuralnetworks/1.3/utils/test/MockBuffer.h
index fb31b51..a67c5f6 100644
--- a/neuralnetworks/1.3/utils/test/MockBuffer.h
+++ b/neuralnetworks/1.3/utils/test/MockBuffer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
#include <gmock/gmock.h>
@@ -40,4 +40,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/1.3/utils/test/MockBurstContext.h b/neuralnetworks/1.3/utils/test/MockBurstContext.h
new file mode 100644
index 0000000..e102b46
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockBurstContext.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
+
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockBurstContext final : public V1_2::IBurstContext {
+ public:
+ // V1_2 methods below.
+ MOCK_METHOD(Return<void>, freeMemory, (int32_t slot), (override));
+};
+
+} // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
diff --git a/neuralnetworks/1.3/utils/test/MockDevice.h b/neuralnetworks/1.3/utils/test/MockDevice.h
index 85d3750..b79037f 100644
--- a/neuralnetworks/1.3/utils/test/MockDevice.h
+++ b/neuralnetworks/1.3/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
#include <android/hardware/neuralnetworks/1.3/IDevice.h>
#include <gmock/gmock.h>
@@ -136,4 +136,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
index fc08a7f..04c0a92 100644
--- a/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
+++ b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
#include <gmock/gmock.h>
@@ -39,4 +39,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
diff --git a/neuralnetworks/1.3/utils/test/MockPreparedModel.h b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
index e441524..ef64fa4 100644
--- a/neuralnetworks/1.3/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
#include <gmock/gmock.h>
@@ -118,4 +118,4 @@
} // namespace android::hardware::neuralnetworks::V1_3::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
index 11796dd..5303c2a 100644
--- a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "MockBurstContext.h"
#include "MockFencedExecutionCallback.h"
#include "MockPreparedModel.h"
@@ -96,6 +97,17 @@
return hardware::Void();
};
}
+auto makeConfigureExecutionBurstReturn(V1_0::ErrorStatus status,
+ const sp<MockBurstContext>& burstContext) {
+ return [status, burstContext](
+ const sp<V1_2::IBurstCallback>& /*callback*/,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& /*requestChannel*/,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& /*resultChannel*/,
+ V1_2::IPreparedModel::configureExecutionBurst_cb cb) -> hardware::Return<void> {
+ cb(status, burstContext);
+ return hardware::Void();
+ };
+}
std::function<hardware::Status()> makeTransportFailure(status_t status) {
return [status] { return hardware::Status::fromStatusT(status); };
@@ -450,7 +462,76 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto mockBurstContext = sp<MockBurstContext>::make();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::NONE, mockBurstContext));
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(
+ makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::GENERAL_FAILURE, nullptr));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto preparedModel =
+ PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/aidl/Android.bp b/neuralnetworks/aidl/Android.bp
index b1860e2..ebf4654 100644
--- a/neuralnetworks/aidl/Android.bp
+++ b/neuralnetworks/aidl/Android.bp
@@ -16,6 +16,7 @@
stability: "vintf",
imports: [
"android.hardware.common",
+ "android.hardware.graphics.common",
],
backend: {
java: {
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
index f18e92a..10a6b75 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
@@ -36,5 +36,5 @@
parcelable BufferRole {
int modelIndex;
int ioIndex;
- float frequency;
+ float probability;
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
index 634f39e..eb3d0b0 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
@@ -34,6 +34,6 @@
package android.hardware.neuralnetworks;
@VintfStability
interface IBurst {
- android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in long[] memoryIdentifierTokens, in boolean measureTiming, in long deadline, in long loopTimeoutDuration);
+ android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in long[] memoryIdentifierTokens, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
void releaseMemoryResource(in long memoryIdentifierToken);
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
index b328b29..c9c67f2 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
@@ -41,8 +41,8 @@
boolean[] getSupportedOperations(in android.hardware.neuralnetworks.Model model);
android.hardware.neuralnetworks.DeviceType getType();
String getVersionString();
- void prepareModel(in android.hardware.neuralnetworks.Model model, in android.hardware.neuralnetworks.ExecutionPreference preference, in android.hardware.neuralnetworks.Priority priority, in long deadline, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback);
- void prepareModelFromCache(in long deadline, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback);
+ void prepareModel(in android.hardware.neuralnetworks.Model model, in android.hardware.neuralnetworks.ExecutionPreference preference, in android.hardware.neuralnetworks.Priority priority, in long deadlineNs, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback);
+ void prepareModelFromCache(in long deadlineNs, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback);
const int BYTE_SIZE_OF_CACHE_TOKEN = 32;
const int MAX_NUMBER_OF_CACHE_FILES = 32;
const int EXTENSION_TYPE_HIGH_BITS_PREFIX = 15;
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
index 52882cd..fccb5dc 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -34,8 +34,8 @@
package android.hardware.neuralnetworks;
@VintfStability
interface IPreparedModel {
- android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long deadline, in long loopTimeoutDuration);
- android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in boolean measureTiming, in long deadline, in long loopTimeoutDuration, in long duration);
+ android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
+ android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs, in long durationNs);
android.hardware.neuralnetworks.IBurst configureExecutionBurst();
const long DEFAULT_LOOP_TIMEOUT_DURATION_NS = 2000000000;
const long MAXIMUM_LOOP_TIMEOUT_DURATION_NS = 15000000000;
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
index 8207b25..37fa102 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
@@ -33,8 +33,8 @@
package android.hardware.neuralnetworks;
@VintfStability
-parcelable Memory {
- android.hardware.common.NativeHandle handle;
- long size;
- String name;
+union Memory {
+ android.hardware.common.Ashmem ashmem;
+ android.hardware.common.MappableFile mappableFile;
+ android.hardware.graphics.common.HardwareBuffer hardwareBuffer;
}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
index 9690e01..bcc83cf 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
@@ -34,6 +34,6 @@
package android.hardware.neuralnetworks;
@VintfStability
parcelable Timing {
- long timeOnDevice;
- long timeInDriver;
+ long timeOnDeviceNs;
+ long timeInDriverNs;
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
index 0d7f678..c444851 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
@@ -35,5 +35,5 @@
* used in the specified role. This is provided as a hint to optimize the case when multiple
* roles prefer different buffer locations or data layouts.
*/
- float frequency;
+ float probability;
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
index 861b6f0..df015ca 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
@@ -22,8 +22,20 @@
@VintfStability
@Backing(type="int")
enum FusedActivationFunc {
+ /**
+ * No activation.
+ */
NONE,
+ /**
+ * ReLU(x) = max(0, x)
+ */
RELU,
+ /**
+ * ReLU1(x) = min(1, max(-1, x))
+ */
RELU1,
+ /**
+ * ReLU6(x) = min(6, max(0, x))
+ */
RELU6,
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl
index 85d2a03..decdc48 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl
@@ -77,18 +77,18 @@
* @param measure Specifies whether or not to measure duration of the execution. The duration
* runs from the time the driver sees the call to the executeSynchronously
* function to the time the driver returns from the function.
- * @param deadline The time by which the execution is expected to complete. The time is measured
- * in nanoseconds since epoch of the steady clock (as from
- * std::chrono::steady_clock). If the execution cannot be finished by the
- * deadline, the execution may be aborted. Passing -1 means the deadline is
- * omitted. Other negative values are invalid.
- * @param loopTimeoutDuration The maximum amount of time in nanoseconds that should be spent
- * executing a {@link OperationType::WHILE} operation. If a loop
- * condition model does not output false within this duration, the
- * execution must be aborted. If -1 is provided, the maximum amount
- * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
- * negative values are invalid. When provided, the duration must not
- * exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
+ * @param deadlineNs The time by which the execution is expected to complete. The time is
+ * measured in nanoseconds since epoch of the steady clock (as from
+ * std::chrono::steady_clock). If the execution cannot be finished by the
+ * deadline, the execution may be aborted. Passing -1 means the deadline is
+ * omitted. Other negative values are invalid.
+ * @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
+ * executing a {@link OperationType::WHILE} operation. If a loop
+ * condition model does not output false within this duration, the
+ * execution must be aborted. If -1 is provided, the maximum amount
+ * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
+ * negative values are invalid. When provided, the duration must
+ * not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
* @return ExecutionResult parcelable, containing the status of the execution, output shapes and
* timing information.
* @throws ServiceSpecificException with one of the following ErrorStatus values:
@@ -100,7 +100,7 @@
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
ExecutionResult executeSynchronously(in Request request, in long[] memoryIdentifierTokens,
- in boolean measureTiming, in long deadline, in long loopTimeoutDuration);
+ in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
/**
* releaseMemoryResource is used by the client to signal to the service that a memory buffer
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
index e17e0cd..72e2623 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
@@ -306,11 +306,11 @@
* @param preference Indicates the intended execution behavior of a prepared model.
* @param priority The priority of the prepared model relative to other prepared models owned by
* the client.
- * @param deadline The time by which the model is expected to be prepared. The time is measured
- * in nanoseconds since epoch of the steady clock (as from
- * std::chrono::steady_clock). If the model cannot be prepared by the deadline,
- * the preparation may be aborted. Passing -1 means the deadline is omitted.
- * Other negative values are invalid.
+ * @param deadlineNs The time by which the model is expected to be prepared. The time is
+ * measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+ * &ts) or ::android::base::boot_clock). If the model cannot be prepared by
+ * the deadline, the preparation may be aborted. Passing -1 means the deadline
+ * is omitted. Other negative values are invalid.
* @param modelCache A vector of file descriptors for the security-sensitive cache. The length
* of the vector must either be 0 indicating that caching information is not
* provided, or match the numModelCache returned from
@@ -344,7 +344,7 @@
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
void prepareModel(in Model model, in ExecutionPreference preference, in Priority priority,
- in long deadline, in ParcelFileDescriptor[] modelCache,
+ in long deadlineNs, in ParcelFileDescriptor[] modelCache,
in ParcelFileDescriptor[] dataCache, in byte[] token,
in IPreparedModelCallback callback);
@@ -395,11 +395,11 @@
* with a set of inputs to the model. Note that the same prepared model object may be used with
* different shapes of inputs on different (possibly concurrent) executions.
*
- * @param deadline The time by which the model is expected to be prepared. The time is measured
- * in nanoseconds since epoch of the steady clock (as from
- * std::chrono::steady_clock). If the model cannot be prepared by the deadline,
- * the preparation may be aborted. Passing -1 means the deadline is omitted.
- * Other negative values are invalid.
+ * @param deadlineNs The time by which the model is expected to be prepared. The time is
+ * measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+ * &ts) or ::android::base::boot_clock). If the model cannot be prepared by
+ * the deadline, the preparation may be aborted. Passing -1 means the deadline
+ * is omitted. Other negative values are invalid.
* @param modelCache A vector of file descriptors for the security-sensitive cache. The length
* of the vector must match the numModelCache returned from
* getNumberOfCacheFilesNeeded. The cache file descriptors will be provided in
@@ -426,7 +426,7 @@
* the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
- void prepareModelFromCache(in long deadline, in ParcelFileDescriptor[] modelCache,
+ void prepareModelFromCache(in long deadlineNs, in ParcelFileDescriptor[] modelCache,
in ParcelFileDescriptor[] dataCache, in byte[] token,
in IPreparedModelCallback callback);
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
index 2a9757b..956b626 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -72,18 +72,18 @@
* @param measure Specifies whether or not to measure duration of the execution. The duration
* runs from the time the driver sees the call to the executeSynchronously
* function to the time the driver returns from the function.
- * @param deadline The time by which the execution is expected to complete. The time is measured
- * in nanoseconds since epoch of the steady clock (as from
- * std::chrono::steady_clock). If the execution cannot be finished by the
- * deadline, the execution may be aborted. Passing -1 means the deadline is
- * omitted. Other negative values are invalid.
- * @param loopTimeoutDuration The maximum amount of time in nanoseconds that should be spent
- * executing a {@link OperationType::WHILE} operation. If a loop
- * condition model does not output false within this duration, the
- * execution must be aborted. If -1 is provided, the maximum amount
- * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
- * negative values are invalid. When provided, the duration must not
- * exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
+ * @param deadlineNs The time by which the execution is expected to complete. The time is
+ * measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+ * &ts) or ::android::base::boot_clock). If the execution cannot be finished
+ * by the deadline, the execution may be aborted. Passing -1 means the
+ * deadline is omitted. Other negative values are invalid.
+ * @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
+ * executing a {@link OperationType::WHILE} operation. If a loop
+ * condition model does not output false within this duration, the
+ * execution must be aborted. If -1 is provided, the maximum amount
+ * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
+ * negative values are invalid. When provided, the duration must
+ * not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
* @return ExecutionResult parcelable, containing the status of the execution, output shapes and
* timing information.
* @throws ServiceSpecificException with one of the following ErrorStatus values:
@@ -95,7 +95,7 @@
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
ExecutionResult executeSynchronously(in Request request, in boolean measureTiming,
- in long deadline, in long loopTimeoutDuration);
+ in long deadlineNs, in long loopTimeoutDurationNs);
/**
* Launch a fenced asynchronous execution on a prepared model.
@@ -137,22 +137,23 @@
* @param waitFor A vector of sync fence file descriptors. Execution must not start until all
* sync fences have been signaled.
* @param measure Specifies whether or not to measure duration of the execution.
- * @param deadline The time by which the execution is expected to complete. The time is measured
- * in nanoseconds since epoch of the steady clock (as from
- * std::chrono::steady_clock).If the execution cannot be finished by the
- * deadline, the execution may be aborted. Passing -1 means the deadline is
- * omitted. Other negative values are invalid.
- * @param loopTimeoutDuration The maximum amount of time in nanoseconds that should be spent
- * executing a {@link OperationType::WHILE} operation. If a loop
- * condition model does not output false within this duration, the
- * execution must be aborted. If -1 is provided, the maximum amount
- * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
- * negative values are invalid. When provided, the duration must not
- * exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
- * @param duration The length of time in nanoseconds within which the execution is expected to
- * complete after all sync fences in waitFor are signaled. If the execution
- * cannot be finished within the duration, the execution may be aborted. Passing
- * -1 means the duration is omitted. Other negative values are invalid.
+ * @param deadlineNs The time by which the execution is expected to complete. The time is
+ * measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+ * &ts) or ::android::base::boot_clock). If the execution cannot be finished
+ * by the deadline, the execution may be aborted. Passing -1 means the
+ * deadline is omitted. Other negative values are invalid.
+ * @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
+ * executing a {@link OperationType::WHILE} operation. If a loop
+ * condition model does not output false within this duration, the
+ * execution must be aborted. If -1 is provided, the maximum amount
+ * of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
+ * negative values are invalid. When provided, the duration must
+ * not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
+ * @param durationNs The length of time in nanoseconds within which the execution is expected to
+ * complete after all sync fences in waitFor are signaled. If the execution
+ * cannot be finished within the duration, the execution may be aborted.
+ * Passing -1 means the duration is omitted. Other negative values are
+ * invalid.
* @return The FencedExecutionResult parcelable, containing IFencedExecutionCallback and the
* sync fence.
* @throws ServiceSpecificException with one of the following ErrorStatus values:
@@ -165,8 +166,8 @@
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
FencedExecutionResult executeFenced(in Request request, in ParcelFileDescriptor[] waitFor,
- in boolean measureTiming, in long deadline, in long loopTimeoutDuration,
- in long duration);
+ in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs,
+ in long durationNs);
/**
* Configure a Burst object used to execute multiple inferences on a prepared model in rapid
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
index 870f0ae..244ac87 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
@@ -15,16 +15,26 @@
*/
package android.hardware.neuralnetworks;
-import android.hardware.common.NativeHandle;
-import android.os.ParcelFileDescriptor;
+
+import android.hardware.common.Ashmem;
+import android.hardware.common.MappableFile;
+import android.hardware.graphics.common.HardwareBuffer;
/**
- * A type that is used to pass pieces of shared memory between processes.
- * The type structure mimics hidl_memory type from HIDL.
+ * The different types of memory that can be shared across processes.
*/
@VintfStability
-parcelable Memory {
- NativeHandle handle;
- long size;
- String name;
+union Memory {
+ /**
+ * Ashmem hidl_memory type from HIDL.
+ */
+ Ashmem ashmem;
+ /**
+ * File that can be mapped.
+ */
+ MappableFile mappableFile;
+ /**
+ * AIDL representation of AHardwareBuffer.
+ */
+ HardwareBuffer hardwareBuffer;
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
index 8130e08..5225096 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
@@ -28,9 +28,9 @@
/**
* Execution time on device (not driver, which runs on host processor).
*/
- long timeOnDevice;
+ long timeOnDeviceNs;
/**
* Execution time in driver (including time on device).
*/
- long timeInDriver;
+ long timeInDriverNs;
}
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 476dac9..0ccc711 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -31,10 +31,11 @@
export_include_dirs: ["include"],
cflags: ["-Wthread-safety"],
static_libs: [
+ "android.hardware.graphics.common-V2-ndk_platform",
+ "libaidlcommonsupport",
"libarect",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
- "neuralnetworks_utils_hal_1_0",
],
shared_libs: [
"android.hardware.neuralnetworks-V1-ndk_platform",
@@ -52,7 +53,9 @@
],
static_libs: [
"android.hardware.common-V2-ndk_platform",
+ "android.hardware.graphics.common-V2-ndk_platform",
"android.hardware.neuralnetworks-V1-ndk_platform",
+ "libaidlcommonsupport",
"libgmock",
"libneuralnetworks_common",
"neuralnetworks_types",
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
new file mode 100644
index 0000000..008e4e4
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
+
+#include <aidl/android/hardware/neuralnetworks/IBurst.h>
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Class that adapts aidl_hal::IBurst to nn::IBurst.
+class Burst final : public nn::IBurst {
+ struct PrivateConstructorTag {};
+
+ public:
+ /**
+ * Thread-safe, self-cleaning cache that relates an nn::Memory object to a unique int64_t
+ * identifier.
+ */
+ class MemoryCache : public std::enable_shared_from_this<MemoryCache> {
+ public:
+ using Task = std::function<void()>;
+ using Cleanup = ::android::base::ScopeGuard<Task>;
+ using SharedCleanup = std::shared_ptr<const Cleanup>;
+ using WeakCleanup = std::weak_ptr<const Cleanup>;
+
+ explicit MemoryCache(std::shared_ptr<aidl_hal::IBurst> burst);
+
+ /**
+ * Get or cache a memory object in the MemoryCache object.
+ *
+ * @param memory Memory object to be cached while the returned `SharedCleanup` is alive.
+ * @return A pair of (1) a unique identifier for the cache entry and (2) a ref-counted
+ * "hold" object which preserves the cache as long as the hold object is alive.
+ */
+ std::pair<int64_t, SharedCleanup> getOrCacheMemory(const nn::SharedMemory& memory);
+
+ /**
+ * Get a cached memory object in the MemoryCache object if it exists, otherwise
+ * std::nullopt.
+ *
+ * @param memory Memory object to be cached while the returned `SharedCleanup` is alive.
+ * @return A pair of (1) a unique identifier for the cache entry and (2) a ref-counted
+ * "hold" object which preserves the cache as long as the hold object is alive. IF the
+ * cache entry is not present, std::nullopt is returned instead.
+ */
+ std::optional<std::pair<int64_t, SharedCleanup>> getMemoryIfAvailable(
+ const nn::SharedMemory& memory);
+
+ private:
+ void tryFreeMemory(const nn::SharedMemory& memory, int64_t identifier);
+
+ const std::shared_ptr<aidl_hal::IBurst> kBurst;
+ std::mutex mMutex;
+ int64_t mUnusedIdentifier GUARDED_BY(mMutex) = 0;
+ std::unordered_map<nn::SharedMemory, std::pair<int64_t, WeakCleanup>> mCache
+ GUARDED_BY(mMutex);
+ };
+
+ static nn::GeneralResult<std::shared_ptr<const Burst>> create(
+ std::shared_ptr<aidl_hal::IBurst> burst);
+
+ Burst(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IBurst> burst);
+
+ // See IBurst::cacheMemory for information.
+ OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
+
+ // See IBurst::execute for information.
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+ private:
+ mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
+ const std::shared_ptr<aidl_hal::IBurst> kBurst;
+ const std::shared_ptr<MemoryCache> kMemoryCache;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
index 4922a6e..5eab9ff 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
@@ -99,6 +99,9 @@
const ::aidl::android::hardware::common::NativeHandle& handle);
GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence);
+GeneralResult<std::vector<Operation>> unvalidatedConvert(
+ const std::vector<aidl_hal::Operation>& operations);
+
GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities);
GeneralResult<DeviceType> convert(const aidl_hal::DeviceType& deviceType);
GeneralResult<ErrorStatus> convert(const aidl_hal::ErrorStatus& errorStatus);
@@ -106,16 +109,13 @@
const aidl_hal::ExecutionPreference& executionPreference);
GeneralResult<SharedMemory> convert(const aidl_hal::Memory& memory);
GeneralResult<Model> convert(const aidl_hal::Model& model);
-GeneralResult<Operand> convert(const aidl_hal::Operand& operand);
GeneralResult<OperandType> convert(const aidl_hal::OperandType& operandType);
GeneralResult<Priority> convert(const aidl_hal::Priority& priority);
-GeneralResult<Request::MemoryPool> convert(const aidl_hal::RequestMemoryPool& memoryPool);
GeneralResult<Request> convert(const aidl_hal::Request& request);
GeneralResult<Timing> convert(const aidl_hal::Timing& timing);
GeneralResult<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence);
GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension);
-GeneralResult<std::vector<Operation>> convert(const std::vector<aidl_hal::Operation>& outputShapes);
GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories);
GeneralResult<std::vector<OutputShape>> convert(
const std::vector<aidl_hal::OutputShape>& outputShapes);
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h
new file mode 100644
index 0000000..e66507a
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
+#include <android/binder_auto_utils.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aidl::android::hardware::neuralnetworks {
+
+class InvalidDevice : public BnDevice {
+ public:
+ static std::shared_ptr<InvalidDevice> create();
+
+ InvalidDevice(Capabilities capabilities, const NumberOfCacheFiles& numberOfCacheFiles,
+ std::vector<Extension> extensions, DeviceType deviceType,
+ std::string versionString);
+
+ ndk::ScopedAStatus allocate(const BufferDesc& desc,
+ const std::vector<IPreparedModelParcel>& preparedModels,
+ const std::vector<BufferRole>& inputRoles,
+ const std::vector<BufferRole>& outputRoles,
+ DeviceBuffer* deviceBuffer) override;
+ ndk::ScopedAStatus getCapabilities(Capabilities* capabilities) override;
+ ndk::ScopedAStatus getNumberOfCacheFilesNeeded(NumberOfCacheFiles* numberOfCacheFiles) override;
+ ndk::ScopedAStatus getSupportedExtensions(std::vector<Extension>* extensions) override;
+ ndk::ScopedAStatus getSupportedOperations(const Model& model,
+ std::vector<bool>* supportedOperations) override;
+ ndk::ScopedAStatus getType(DeviceType* deviceType) override;
+ ndk::ScopedAStatus getVersionString(std::string* versionString) override;
+ ndk::ScopedAStatus prepareModel(
+ const Model& model, ExecutionPreference preference, Priority priority, int64_t deadline,
+ const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+ const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+ const std::vector<uint8_t>& token,
+ const std::shared_ptr<IPreparedModelCallback>& callback) override;
+ ndk::ScopedAStatus prepareModelFromCache(
+ int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+ const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+ const std::vector<uint8_t>& token,
+ const std::shared_ptr<IPreparedModelCallback>& callback) override;
+
+ private:
+ const Capabilities kCapabilities;
+ const NumberOfCacheFiles kNumberOfCacheFiles;
+ const std::vector<Extension> kExtensions;
+ const DeviceType kDeviceType;
+ const std::string kVersionString;
+};
+
+} // namespace aidl::android::hardware::neuralnetworks
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
index 9b28588..abce6cc 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
@@ -22,7 +22,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/aidl/ProtectCallback.h>
#include <memory>
#include <tuple>
@@ -35,8 +34,7 @@
namespace aidl::android::hardware::neuralnetworks::utils {
// Class that adapts aidl_hal::IPreparedModel to nn::IPreparedModel.
-class PreparedModel final : public nn::IPreparedModel,
- public std::enable_shared_from_this<PreparedModel> {
+class PreparedModel final : public nn::IPreparedModel {
struct PrivateConstructorTag {};
public:
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
index 58dcfe3..316d34f 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/HandleError.h>
@@ -48,6 +49,22 @@
return result.has_value();
}
+template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+ const auto version = NN_TRY(::android::hardware::neuralnetworks::utils::makeGeneralFailure(
+ nn::validate(canonical)));
+ if (version > kVersion) {
+ return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+ }
+ return {};
+}
+
+template <typename Type>
+auto convertFromNonCanonical(const Type& nonCanonicalObject)
+ -> decltype(convert(nn::convert(nonCanonicalObject).value())) {
+ return convert(NN_TRY(nn::convert(nonCanonicalObject)));
+}
+
nn::GeneralResult<Memory> clone(const Memory& memory);
nn::GeneralResult<Request> clone(const Request& request);
nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool);
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
new file mode 100644
index 0000000..0b475bc
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Burst.h"
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
+ const std::vector<OutputShape>& outputShapes, const Timing& timing) {
+ return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+} // namespace
+
+Burst::MemoryCache::MemoryCache(std::shared_ptr<aidl_hal::IBurst> burst)
+ : kBurst(std::move(burst)) {}
+
+std::pair<int64_t, Burst::MemoryCache::SharedCleanup> Burst::MemoryCache::getOrCacheMemory(
+ const nn::SharedMemory& memory) {
+ std::lock_guard lock(mMutex);
+
+ // Get the cache payload or create it (with default values) if it does not exist.
+ auto& cachedPayload = mCache[memory];
+ {
+ const auto& [identifier, maybeCleaner] = cachedPayload;
+ // If cache payload already exists, reuse it.
+ if (auto cleaner = maybeCleaner.lock()) {
+ return std::make_pair(identifier, std::move(cleaner));
+ }
+ }
+
+ // If the code reaches this point, the cached payload either did not exist or expired prior to
+ // this call.
+
+ // Allocate a new identifier.
+ CHECK_LT(mUnusedIdentifier, std::numeric_limits<int64_t>::max());
+ const int64_t identifier = mUnusedIdentifier++;
+
+ // Create reference-counted self-cleaning cache object.
+ auto self = weak_from_this();
+ Task cleanup = [memory, identifier, maybeMemoryCache = std::move(self)] {
+ if (const auto memoryCache = maybeMemoryCache.lock()) {
+ memoryCache->tryFreeMemory(memory, identifier);
+ }
+ };
+ auto cleaner = std::make_shared<const Cleanup>(std::move(cleanup));
+
+ // Store the result in the cache and return it.
+ auto result = std::make_pair(identifier, std::move(cleaner));
+ cachedPayload = result;
+ return result;
+}
+
+std::optional<std::pair<int64_t, Burst::MemoryCache::SharedCleanup>>
+Burst::MemoryCache::getMemoryIfAvailable(const nn::SharedMemory& memory) {
+ std::lock_guard lock(mMutex);
+
+ // Get the existing cached entry if it exists.
+ const auto iter = mCache.find(memory);
+ if (iter != mCache.end()) {
+ const auto& [identifier, maybeCleaner] = iter->second;
+ if (auto cleaner = maybeCleaner.lock()) {
+ return std::make_pair(identifier, std::move(cleaner));
+ }
+ }
+
+ // If the code reaches this point, the cached payload did not exist or was actively being
+ // deleted.
+ return std::nullopt;
+}
+
+void Burst::MemoryCache::tryFreeMemory(const nn::SharedMemory& memory, int64_t identifier) {
+ {
+ std::lock_guard guard(mMutex);
+ // Remove the cached memory and payload if it is present but expired. Note that it may not
+ // be present or may not be expired because another thread may have removed or cached the
+ // same memory object before the current thread locked mMutex in tryFreeMemory.
+ const auto iter = mCache.find(memory);
+ if (iter != mCache.end()) {
+ if (std::get<WeakCleanup>(iter->second).expired()) {
+ mCache.erase(iter);
+ }
+ }
+ }
+ kBurst->releaseMemoryResource(identifier);
+}
+
+nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create(
+ std::shared_ptr<aidl_hal::IBurst> burst) {
+ if (burst == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "aidl_hal::utils::Burst::create must have non-null burst";
+ }
+
+ return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(burst));
+}
+
+Burst::Burst(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBurst> burst)
+ : kBurst(std::move(burst)), kMemoryCache(std::make_shared<MemoryCache>(kBurst)) {
+ CHECK(kBurst != nullptr);
+}
+
+Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& memory) const {
+ auto [identifier, hold] = kMemoryCache->getOrCacheMemory(memory);
+ return hold;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ // Ensure that at most one execution is in flight at any given time.
+ const bool alreadyInFlight = mExecutionInFlight.test_and_set();
+ if (alreadyInFlight) {
+ return NN_ERROR() << "IBurst already has an execution in flight";
+ }
+ const auto guard = ::android::base::make_scope_guard([this] { mExecutionInFlight.clear(); });
+
+ // Ensure that request is ready for IPC.
+ std::optional<nn::Request> maybeRequestInShared;
+ const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+ const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+ const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+ const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+ const auto aidlLoopTimeoutDuration =
+ NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+
+ std::vector<int64_t> memoryIdentifierTokens;
+ std::vector<OptionalCacheHold> holds;
+ memoryIdentifierTokens.reserve(request.pools.size());
+ holds.reserve(request.pools.size());
+ for (const auto& memoryPool : request.pools) {
+ if (const auto* memory = std::get_if<nn::SharedMemory>(&memoryPool)) {
+ if (auto cached = kMemoryCache->getMemoryIfAvailable(*memory)) {
+ auto& [identifier, hold] = *cached;
+ memoryIdentifierTokens.push_back(identifier);
+ holds.push_back(std::move(hold));
+ continue;
+ }
+ }
+ memoryIdentifierTokens.push_back(-1);
+ }
+ CHECK_EQ(request.pools.size(), memoryIdentifierTokens.size());
+
+ ExecutionResult executionResult;
+ const auto ret =
+ kBurst->executeSynchronously(aidlRequest, memoryIdentifierTokens, aidlMeasure,
+ aidlDeadline, aidlLoopTimeoutDuration, &executionResult);
+ HANDLE_ASTATUS(ret) << "execute failed";
+ if (!executionResult.outputSufficientSize) {
+ auto canonicalOutputShapes =
+ nn::convert(executionResult.outputShapes).value_or(std::vector<nn::OutputShape>{});
+ return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+ << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+ }
+ auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
+ convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
+
+ NN_TRY(hal::utils::makeExecutionFailure(
+ hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+
+ return std::make_pair(std::move(outputShapes), timing);
+}
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index c47ba0e..4b263ee 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -16,8 +16,13 @@
#include "Conversions.h"
+#include <aidl/android/hardware/common/Ashmem.h>
+#include <aidl/android/hardware/common/MappableFile.h>
#include <aidl/android/hardware/common/NativeHandle.h>
+#include <aidl/android/hardware/graphics/common/HardwareBuffer.h>
+#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
+#include <android-base/mapped_file.h>
#include <android-base/unique_fd.h>
#include <android/binder_auto_utils.h>
#include <android/hardware_buffer.h>
@@ -41,6 +46,8 @@
#include <type_traits>
#include <utility>
+#include "Utils.h"
+
#define VERIFY_NON_NEGATIVE(value) \
while (UNLIKELY(value < 0)) return NN_ERROR()
@@ -53,7 +60,6 @@
return static_cast<std::underlying_type_t<Type>>(value);
}
-constexpr auto kVersion = android::nn::Version::ANDROID_S;
constexpr int64_t kNoTiming = -1;
} // namespace
@@ -63,32 +69,6 @@
using ::aidl::android::hardware::common::NativeHandle;
-constexpr auto validOperandType(nn::OperandType operandType) {
- switch (operandType) {
- case nn::OperandType::FLOAT32:
- case nn::OperandType::INT32:
- case nn::OperandType::UINT32:
- case nn::OperandType::TENSOR_FLOAT32:
- case nn::OperandType::TENSOR_INT32:
- case nn::OperandType::TENSOR_QUANT8_ASYMM:
- case nn::OperandType::BOOL:
- case nn::OperandType::TENSOR_QUANT16_SYMM:
- case nn::OperandType::TENSOR_FLOAT16:
- case nn::OperandType::TENSOR_BOOL8:
- case nn::OperandType::FLOAT16:
- case nn::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
- case nn::OperandType::TENSOR_QUANT16_ASYMM:
- case nn::OperandType::TENSOR_QUANT8_SYMM:
- case nn::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
- case nn::OperandType::SUBGRAPH:
- return true;
- case nn::OperandType::OEM:
- case nn::OperandType::TENSOR_OEM_BYTE:
- return false;
- }
- return nn::isExtension(operandType);
-}
-
template <typename Input>
using UnvalidatedConvertOutput =
std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
@@ -113,14 +93,7 @@
template <typename Type>
GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
- const auto maybeVersion = validate(canonical);
- if (!maybeVersion.has_value()) {
- return error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(aidl_hal::utils::compliantVersion(canonical));
return canonical;
}
@@ -157,41 +130,38 @@
using UniqueNativeHandle = std::unique_ptr<native_handle_t, NativeHandleDeleter>;
-static GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(const NativeHandle& handle) {
- std::vector<base::unique_fd> fds;
- fds.reserve(handle.fds.size());
- for (const auto& fd : handle.fds) {
- auto duplicatedFd = NN_TRY(dupFd(fd.get()));
- fds.emplace_back(duplicatedFd.release());
+GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(const NativeHandle& handle) {
+ auto nativeHandle = UniqueNativeHandle(dupFromAidl(handle));
+ if (nativeHandle.get() == nullptr) {
+ return NN_ERROR() << "android::dupFromAidl failed to convert the common::NativeHandle to a "
+ "native_handle_t";
}
-
- constexpr size_t kIntMax = std::numeric_limits<int>::max();
- CHECK_LE(handle.fds.size(), kIntMax);
- CHECK_LE(handle.ints.size(), kIntMax);
- native_handle_t* nativeHandle = native_handle_create(static_cast<int>(handle.fds.size()),
- static_cast<int>(handle.ints.size()));
- if (nativeHandle == nullptr) {
- return NN_ERROR() << "Failed to create native_handle";
+ if (!std::all_of(nativeHandle->data + 0, nativeHandle->data + nativeHandle->numFds,
+ [](int fd) { return fd >= 0; })) {
+ return NN_ERROR() << "android::dupFromAidl returned an invalid native_handle_t";
}
- for (size_t i = 0; i < fds.size(); ++i) {
- nativeHandle->data[i] = fds[i].release();
- }
- std::copy(handle.ints.begin(), handle.ints.end(), &nativeHandle->data[nativeHandle->numFds]);
-
- return UniqueNativeHandle(nativeHandle);
+ return nativeHandle;
}
} // anonymous namespace
GeneralResult<OperandType> unvalidatedConvert(const aidl_hal::OperandType& operandType) {
VERIFY_NON_NEGATIVE(underlyingType(operandType)) << "Negative operand types are not allowed.";
- return static_cast<OperandType>(operandType);
+ const auto canonical = static_cast<OperandType>(operandType);
+ if (canonical == OperandType::OEM || canonical == OperandType::TENSOR_OEM_BYTE) {
+ return NN_ERROR() << "Unable to convert invalid OperandType " << canonical;
+ }
+ return canonical;
}
GeneralResult<OperationType> unvalidatedConvert(const aidl_hal::OperationType& operationType) {
VERIFY_NON_NEGATIVE(underlyingType(operationType))
<< "Negative operation types are not allowed.";
- return static_cast<OperationType>(operationType);
+ const auto canonical = static_cast<OperationType>(operationType);
+ if (canonical == OperationType::OEM_OPERATION) {
+ return NN_ERROR() << "Unable to convert invalid OperationType OEM_OPERATION";
+ }
+ return canonical;
}
GeneralResult<DeviceType> unvalidatedConvert(const aidl_hal::DeviceType& deviceType) {
@@ -206,8 +176,7 @@
const bool validOperandTypes = std::all_of(
capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
[](const aidl_hal::OperandPerformance& operandPerformance) {
- const auto maybeType = unvalidatedConvert(operandPerformance.type);
- return !maybeType.has_value() ? false : validOperandType(maybeType.value());
+ return validatedConvert(operandPerformance.type).has_value();
});
if (!validOperandTypes) {
return NN_ERROR() << "Invalid OperandType when unvalidatedConverting OperandPerformance in "
@@ -378,75 +347,74 @@
return measureTiming ? MeasureTiming::YES : MeasureTiming::NO;
}
-static uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
- return (value + multiple - 1) / multiple * multiple;
-}
-
GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory) {
- VERIFY_NON_NEGATIVE(memory.size) << "Memory size must not be negative";
- if (memory.size > std::numeric_limits<size_t>::max()) {
- return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()";
- }
+ using Tag = aidl_hal::Memory::Tag;
+ switch (memory.getTag()) {
+ case Tag::ashmem: {
+ const auto& ashmem = memory.get<Tag::ashmem>();
+ VERIFY_NON_NEGATIVE(ashmem.size) << "Memory size must not be negative";
+ if (ashmem.size > std::numeric_limits<size_t>::max()) {
+ return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()";
+ }
- if (memory.name != "hardware_buffer_blob") {
- return std::make_shared<const Memory>(Memory{
- .handle = NN_TRY(unvalidatedConvertHelper(memory.handle)),
- .size = static_cast<size_t>(memory.size),
- .name = memory.name,
- });
- }
+ auto handle = Memory::Ashmem{
+ .fd = NN_TRY(dupFd(ashmem.fd.get())),
+ .size = static_cast<size_t>(ashmem.size),
+ };
+ return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
+ }
+ case Tag::mappableFile: {
+ const auto& mappableFile = memory.get<Tag::mappableFile>();
+ VERIFY_NON_NEGATIVE(mappableFile.length) << "Memory size must not be negative";
+ VERIFY_NON_NEGATIVE(mappableFile.offset) << "Memory offset must not be negative";
+ if (mappableFile.length > std::numeric_limits<size_t>::max()) {
+ return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()";
+ }
+ if (mappableFile.offset > std::numeric_limits<size_t>::max()) {
+ return NN_ERROR() << "Memory: offset must be <= std::numeric_limits<size_t>::max()";
+ }
- const auto size = static_cast<uint32_t>(memory.size);
- const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
- const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- const uint32_t width = size;
- const uint32_t height = 1; // height is always 1 for BLOB mode AHardwareBuffer.
- const uint32_t layers = 1; // layers is always 1 for BLOB mode AHardwareBuffer.
+ const size_t size = static_cast<size_t>(mappableFile.length);
+ const int prot = mappableFile.prot;
+ const int fd = mappableFile.fd.get();
+ const size_t offset = static_cast<size_t>(mappableFile.offset);
- const UniqueNativeHandle handle = NN_TRY(nativeHandleFromAidlHandle(memory.handle));
- const native_handle_t* nativeHandle = handle.get();
+ return createSharedMemoryFromFd(size, prot, fd, offset);
+ }
+ case Tag::hardwareBuffer: {
+ const auto& hardwareBuffer = memory.get<Tag::hardwareBuffer>();
- // AHardwareBuffer_createFromHandle() might fail because an allocator
- // expects a specific stride value. In that case, we try to guess it by
- // aligning the width to small powers of 2.
- // TODO(b/174120849): Avoid stride assumptions.
- AHardwareBuffer* hardwareBuffer = nullptr;
- status_t status = UNKNOWN_ERROR;
- for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
- const uint32_t stride = roundUpToMultiple(width, alignment);
- AHardwareBuffer_Desc desc{
- .width = width,
- .height = height,
- .layers = layers,
- .format = format,
- .usage = usage,
- .stride = stride,
- };
- status = AHardwareBuffer_createFromHandle(&desc, nativeHandle,
- AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
- &hardwareBuffer);
- if (status == NO_ERROR) {
- break;
+ const UniqueNativeHandle handle =
+ NN_TRY(nativeHandleFromAidlHandle(hardwareBuffer.handle));
+ const native_handle_t* nativeHandle = handle.get();
+
+ const AHardwareBuffer_Desc desc{
+ .width = static_cast<uint32_t>(hardwareBuffer.description.width),
+ .height = static_cast<uint32_t>(hardwareBuffer.description.height),
+ .layers = static_cast<uint32_t>(hardwareBuffer.description.layers),
+ .format = static_cast<uint32_t>(hardwareBuffer.description.format),
+ .usage = static_cast<uint64_t>(hardwareBuffer.description.usage),
+ .stride = static_cast<uint32_t>(hardwareBuffer.description.stride),
+ };
+ AHardwareBuffer* ahwb = nullptr;
+ const status_t status = AHardwareBuffer_createFromHandle(
+ &desc, nativeHandle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &ahwb);
+ if (status != NO_ERROR) {
+ return NN_ERROR() << "createFromHandle failed";
+ }
+
+ return createSharedMemoryFromAHWB(ahwb, /*takeOwnership=*/true);
}
}
- if (status != NO_ERROR) {
- return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
- << "Can't create AHardwareBuffer from handle. Error: " << status;
- }
-
- return std::make_shared<const Memory>(Memory{
- .handle = HardwareBufferHandle(hardwareBuffer, /*takeOwnership=*/true),
- .size = static_cast<size_t>(memory.size),
- .name = memory.name,
- });
+ return NN_ERROR() << "Unrecognized Memory::Tag: " << memory.getTag();
}
GeneralResult<Timing> unvalidatedConvert(const aidl_hal::Timing& timing) {
- if (timing.timeInDriver < -1) {
- return NN_ERROR() << "Timing: timeInDriver must not be less than -1";
+ if (timing.timeInDriverNs < -1) {
+ return NN_ERROR() << "Timing: timeInDriverNs must not be less than -1";
}
- if (timing.timeOnDevice < -1) {
- return NN_ERROR() << "Timing: timeOnDevice must not be less than -1";
+ if (timing.timeOnDeviceNs < -1) {
+ return NN_ERROR() << "Timing: timeOnDeviceNs must not be less than -1";
}
constexpr auto convertTiming = [](int64_t halTiming) -> OptionalDuration {
if (halTiming == kNoTiming) {
@@ -454,8 +422,8 @@
}
return nn::Duration(static_cast<uint64_t>(halTiming));
};
- return Timing{.timeOnDevice = convertTiming(timing.timeOnDevice),
- .timeInDriver = convertTiming(timing.timeInDriver)};
+ return Timing{.timeOnDevice = convertTiming(timing.timeOnDeviceNs),
+ .timeInDriver = convertTiming(timing.timeInDriverNs)};
}
GeneralResult<Model::OperandValues> unvalidatedConvert(const std::vector<uint8_t>& operandValues) {
@@ -472,7 +440,7 @@
return BufferRole{
.modelIndex = static_cast<uint32_t>(bufferRole.modelIndex),
.ioIndex = static_cast<uint32_t>(bufferRole.ioIndex),
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.probability,
};
}
@@ -534,6 +502,11 @@
return std::make_shared<const Handle>(NN_TRY(unvalidatedConvertHelper(aidlNativeHandle)));
}
+GeneralResult<std::vector<Operation>> unvalidatedConvert(
+ const std::vector<aidl_hal::Operation>& operations) {
+ return unvalidatedConvertVec(operations);
+}
+
GeneralResult<SyncFence> unvalidatedConvert(const ndk::ScopedFileDescriptor& syncFence) {
auto duplicatedFd = NN_TRY(dupFd(syncFence.get()));
return SyncFence::create(std::move(duplicatedFd));
@@ -564,22 +537,14 @@
return validatedConvert(model);
}
-GeneralResult<Operand> convert(const aidl_hal::Operand& operand) {
- return unvalidatedConvert(operand);
-}
-
GeneralResult<OperandType> convert(const aidl_hal::OperandType& operandType) {
- return unvalidatedConvert(operandType);
+ return validatedConvert(operandType);
}
GeneralResult<Priority> convert(const aidl_hal::Priority& priority) {
return validatedConvert(priority);
}
-GeneralResult<Request::MemoryPool> convert(const aidl_hal::RequestMemoryPool& memoryPool) {
- return unvalidatedConvert(memoryPool);
-}
-
GeneralResult<Request> convert(const aidl_hal::Request& request) {
return validatedConvert(request);
}
@@ -589,17 +554,13 @@
}
GeneralResult<SyncFence> convert(const ndk::ScopedFileDescriptor& syncFence) {
- return unvalidatedConvert(syncFence);
+ return validatedConvert(syncFence);
}
GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension) {
return validatedConvert(extension);
}
-GeneralResult<std::vector<Operation>> convert(const std::vector<aidl_hal::Operation>& operations) {
- return unvalidatedConvert(operations);
-}
-
GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories) {
return validatedConvert(memories);
}
@@ -644,14 +605,7 @@
template <typename Type>
nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
- const auto maybeVersion = nn::validate(canonical);
- if (!maybeVersion.has_value()) {
- return nn::error() << maybeVersion.error();
- }
- const auto version = maybeVersion.value();
- if (version > kVersion) {
- return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
- }
+ NN_TRY(compliantVersion(canonical));
return utils::unvalidatedConvert(canonical);
}
@@ -684,20 +638,95 @@
template <class... Ts>
overloaded(Ts...)->overloaded<Ts...>;
-static nn::GeneralResult<common::NativeHandle> aidlHandleFromNativeHandle(
- const native_handle_t& handle) {
- common::NativeHandle aidlNativeHandle;
+nn::GeneralResult<common::NativeHandle> aidlHandleFromNativeHandle(
+ const native_handle_t& nativeHandle) {
+ auto handle = ::android::dupToAidl(&nativeHandle);
+ if (!std::all_of(handle.fds.begin(), handle.fds.end(),
+ [](const ndk::ScopedFileDescriptor& fd) { return fd.get() >= 0; })) {
+ return NN_ERROR() << "android::dupToAidl returned an invalid common::NativeHandle";
+ }
+ return handle;
+}
- aidlNativeHandle.fds.reserve(handle.numFds);
- for (int i = 0; i < handle.numFds; ++i) {
- auto duplicatedFd = NN_TRY(nn::dupFd(handle.data[i]));
- aidlNativeHandle.fds.emplace_back(duplicatedFd.release());
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Ashmem& memory) {
+ if constexpr (std::numeric_limits<size_t>::max() > std::numeric_limits<int64_t>::max()) {
+ if (memory.size > std::numeric_limits<int64_t>::max()) {
+ return (
+ NN_ERROR()
+ << "Memory::Ashmem: size must be <= std::numeric_limits<int64_t>::max()")
+ .
+ operator nn::GeneralResult<Memory>();
+ }
}
- aidlNativeHandle.ints = std::vector<int>(&handle.data[handle.numFds],
- &handle.data[handle.numFds + handle.numInts]);
+ auto fd = NN_TRY(nn::dupFd(memory.fd));
+ auto handle = common::Ashmem{
+ .fd = ndk::ScopedFileDescriptor(fd.release()),
+ .size = static_cast<int64_t>(memory.size),
+ };
+ return Memory::make<Memory::Tag::ashmem>(std::move(handle));
+}
- return aidlNativeHandle;
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Fd& memory) {
+ if constexpr (std::numeric_limits<size_t>::max() > std::numeric_limits<int64_t>::max()) {
+ if (memory.size > std::numeric_limits<int64_t>::max()) {
+ return (NN_ERROR() << "Memory::Fd: size must be <= std::numeric_limits<int64_t>::max()")
+ .
+ operator nn::GeneralResult<Memory>();
+ }
+ if (memory.offset > std::numeric_limits<int64_t>::max()) {
+ return (
+ NN_ERROR()
+ << "Memory::Fd: offset must be <= std::numeric_limits<int64_t>::max()")
+ .
+ operator nn::GeneralResult<Memory>();
+ }
+ }
+
+ auto fd = NN_TRY(nn::dupFd(memory.fd));
+ auto handle = common::MappableFile{
+ .length = static_cast<int64_t>(memory.size),
+ .prot = memory.prot,
+ .fd = ndk::ScopedFileDescriptor(fd.release()),
+ .offset = static_cast<int64_t>(memory.offset),
+ };
+ return Memory::make<Memory::Tag::mappableFile>(std::move(handle));
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::HardwareBuffer& memory) {
+ const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(memory.handle.get());
+ if (nativeHandle == nullptr) {
+ return (NN_ERROR() << "unvalidatedConvert failed because AHardwareBuffer_getNativeHandle "
+ "returned nullptr")
+ .
+ operator nn::GeneralResult<Memory>();
+ }
+
+ auto handle = NN_TRY(aidlHandleFromNativeHandle(*nativeHandle));
+
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(memory.handle.get(), &desc);
+
+ const auto description = graphics::common::HardwareBufferDescription{
+ .width = static_cast<int32_t>(desc.width),
+ .height = static_cast<int32_t>(desc.height),
+ .layers = static_cast<int32_t>(desc.layers),
+ .format = static_cast<graphics::common::PixelFormat>(desc.format),
+ .usage = static_cast<graphics::common::BufferUsage>(desc.usage),
+ .stride = static_cast<int32_t>(desc.stride),
+ };
+
+ auto hardwareBuffer = graphics::common::HardwareBuffer{
+ .description = std::move(description),
+ .handle = std::move(handle),
+ };
+ return Memory::make<Memory::Tag::hardwareBuffer>(std::move(hardwareBuffer));
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Unknown& /*memory*/) {
+ return (NN_ERROR() << "Unable to convert Unknown memory type")
+ .
+ operator nn::GeneralResult<Memory>();
}
} // namespace
@@ -718,7 +747,7 @@
return BufferRole{
.modelIndex = static_cast<int32_t>(bufferRole.modelIndex),
.ioIndex = static_cast<int32_t>(bufferRole.ioIndex),
- .frequency = bufferRole.frequency,
+ .probability = bufferRole.probability,
};
}
@@ -732,41 +761,12 @@
}
nn::GeneralResult<Memory> unvalidatedConvert(const nn::SharedMemory& memory) {
- CHECK(memory != nullptr);
- if (memory->size > std::numeric_limits<int64_t>::max()) {
- return NN_ERROR() << "Memory size doesn't fit into int64_t.";
+ if (memory == nullptr) {
+ return (NN_ERROR() << "Unable to convert nullptr memory")
+ .
+ operator nn::GeneralResult<Memory>();
}
- if (const auto* handle = std::get_if<nn::Handle>(&memory->handle)) {
- return Memory{
- .handle = NN_TRY(unvalidatedConvert(*handle)),
- .size = static_cast<int64_t>(memory->size),
- .name = memory->name,
- };
- }
-
- const auto* ahwb = std::get<nn::HardwareBufferHandle>(memory->handle).get();
- AHardwareBuffer_Desc bufferDesc;
- AHardwareBuffer_describe(ahwb, &bufferDesc);
-
- if (bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB) {
- CHECK_EQ(memory->size, bufferDesc.width);
- CHECK_EQ(memory->name, "hardware_buffer_blob");
- } else {
- CHECK_EQ(memory->size, 0u);
- CHECK_EQ(memory->name, "hardware_buffer");
- }
-
- const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
- if (nativeHandle == nullptr) {
- return NN_ERROR() << "unvalidatedConvert failed because AHardwareBuffer_getNativeHandle "
- "returned nullptr";
- }
-
- return Memory{
- .handle = NN_TRY(aidlHandleFromNativeHandle(*nativeHandle)),
- .size = static_cast<int64_t>(memory->size),
- .name = memory->name,
- };
+ return std::visit([](const auto& x) { return unvalidatedConvert(x); }, memory->handle);
}
nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus) {
@@ -797,6 +797,9 @@
}
nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
+ if (operandType == nn::OperandType::OEM || operandType == nn::OperandType::TENSOR_OEM_BYTE) {
+ return NN_ERROR() << "Unable to convert invalid OperandType " << operandType;
+ }
return static_cast<OperandType>(operandType);
}
@@ -864,6 +867,9 @@
}
nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+ if (operationType == nn::OperationType::OEM_OPERATION) {
+ return NN_ERROR() << "Unable to convert invalid OperationType OEM_OPERATION";
+ }
return static_cast<OperationType>(operationType);
}
@@ -958,17 +964,18 @@
nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
return Timing{
- .timeOnDevice = NN_TRY(unvalidatedConvert(timing.timeOnDevice)),
- .timeInDriver = NN_TRY(unvalidatedConvert(timing.timeInDriver)),
+ .timeOnDeviceNs = NN_TRY(unvalidatedConvert(timing.timeOnDevice)),
+ .timeInDriverNs = NN_TRY(unvalidatedConvert(timing.timeInDriver)),
};
}
nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration) {
- const uint64_t nanoseconds = duration.count();
- if (nanoseconds > std::numeric_limits<int64_t>::max()) {
- return std::numeric_limits<int64_t>::max();
+ if (duration < nn::Duration::zero()) {
+ return NN_ERROR() << "Unable to convert invalid (negative) duration";
}
- return static_cast<int64_t>(nanoseconds);
+ constexpr std::chrono::nanoseconds::rep kIntMax = std::numeric_limits<int64_t>::max();
+ const auto count = duration.count();
+ return static_cast<int64_t>(std::min(count, kIntMax));
}
nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalDuration& optionalDuration) {
@@ -1004,7 +1011,7 @@
}
nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken) {
- return unvalidatedConvert(cacheToken);
+ return validatedConvert(cacheToken);
}
nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
@@ -1076,7 +1083,7 @@
nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
const std::vector<nn::SyncFence>& syncFences) {
- return unvalidatedConvert(syncFences);
+ return validatedConvert(syncFences);
}
nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec) {
diff --git a/neuralnetworks/aidl/utils/src/InvalidDevice.cpp b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp
new file mode 100644
index 0000000..c9d9955
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InvalidDevice"
+
+#include "InvalidDevice.h"
+
+#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
+#include <android/binder_auto_utils.h>
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace aidl::android::hardware::neuralnetworks {
+namespace {
+
+ndk::ScopedAStatus toAStatus(ErrorStatus errorStatus, const std::string& errorMessage) {
+ if (errorStatus == ErrorStatus::NONE) {
+ return ndk::ScopedAStatus::ok();
+ }
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ static_cast<int32_t>(errorStatus), errorMessage.c_str());
+}
+
+} // namespace
+
+std::shared_ptr<InvalidDevice> InvalidDevice::create() {
+ constexpr auto perf = PerformanceInfo{
+ .execTime = std::numeric_limits<float>::max(),
+ .powerUsage = std::numeric_limits<float>::max(),
+ };
+ auto capabilities = Capabilities{
+ .relaxedFloat32toFloat16PerformanceScalar = perf,
+ .relaxedFloat32toFloat16PerformanceTensor = perf,
+ .operandPerformance = {},
+ .ifPerformance = perf,
+ .whilePerformance = perf,
+ };
+ constexpr auto numberOfCacheFiles = NumberOfCacheFiles{
+ .numModelCache = 0,
+ .numDataCache = 0,
+ };
+ std::vector<Extension> extensions{};
+ constexpr auto deviceType = DeviceType::OTHER;
+ std::string versionString = "invalid";
+
+ return ndk::SharedRefBase::make<InvalidDevice>(std::move(capabilities), numberOfCacheFiles,
+ std::move(extensions), deviceType,
+ std::move(versionString));
+}
+
+InvalidDevice::InvalidDevice(Capabilities capabilities,
+ const NumberOfCacheFiles& numberOfCacheFiles,
+ std::vector<Extension> extensions, DeviceType deviceType,
+ std::string versionString)
+ : kCapabilities(std::move(capabilities)),
+ kNumberOfCacheFiles(numberOfCacheFiles),
+ kExtensions(std::move(extensions)),
+ kDeviceType(deviceType),
+ kVersionString(std::move(versionString)) {}
+
+ndk::ScopedAStatus InvalidDevice::allocate(
+ const BufferDesc& /*desc*/, const std::vector<IPreparedModelParcel>& /*preparedModels*/,
+ const std::vector<BufferRole>& /*inputRoles*/,
+ const std::vector<BufferRole>& /*outputRoles*/, DeviceBuffer* /*deviceBuffer*/) {
+ return toAStatus(ErrorStatus::GENERAL_FAILURE, "InvalidDevice");
+}
+
+ndk::ScopedAStatus InvalidDevice::getCapabilities(Capabilities* capabilities) {
+ *capabilities = kCapabilities;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getNumberOfCacheFilesNeeded(
+ NumberOfCacheFiles* numberOfCacheFiles) {
+ *numberOfCacheFiles = kNumberOfCacheFiles;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getSupportedExtensions(std::vector<Extension>* extensions) {
+ *extensions = kExtensions;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getSupportedOperations(const Model& model,
+ std::vector<bool>* supportedOperations) {
+ if (const auto result = utils::validate(model); !result.ok()) {
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+ }
+ *supportedOperations = std::vector<bool>(model.main.operations.size(), false);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getType(DeviceType* deviceType) {
+ *deviceType = kDeviceType;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getVersionString(std::string* versionString) {
+ *versionString = kVersionString;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::prepareModel(
+ const Model& model, ExecutionPreference preference, Priority priority, int64_t deadline,
+ const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+ const std::vector<ndk::ScopedFileDescriptor>& dataCache, const std::vector<uint8_t>& token,
+ const std::shared_ptr<IPreparedModelCallback>& callback) {
+ if (callback.get() == nullptr) {
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+ "invalid callback passed to InvalidDevice::prepareModel");
+ }
+ if (const auto result = utils::validate(model); !result.ok()) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+ }
+ if (const auto result = utils::validate(preference); !result.ok()) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+ }
+ if (const auto result = utils::validate(priority); !result.ok()) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+ }
+ if (deadline < -1) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+ "Invalid deadline " + std::to_string(deadline));
+ }
+ if (modelCache.size() != static_cast<size_t>(kNumberOfCacheFiles.numModelCache)) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+ "Invalid modelCache, size = " + std::to_string(modelCache.size()));
+ }
+ if (dataCache.size() != static_cast<size_t>(kNumberOfCacheFiles.numDataCache)) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+ "Invalid modelCache, size = " + std::to_string(dataCache.size()));
+ }
+ if (token.size() != IDevice::BYTE_SIZE_OF_CACHE_TOKEN) {
+ callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+ return toAStatus(
+ ErrorStatus::INVALID_ARGUMENT,
+ "Invalid cache token, size = " + std::to_string(IDevice::BYTE_SIZE_OF_CACHE_TOKEN));
+ }
+ callback->notify(ErrorStatus::GENERAL_FAILURE, nullptr);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::prepareModelFromCache(
+ int64_t /*deadline*/, const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
+ const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
+ const std::vector<uint8_t>& /*token*/,
+ const std::shared_ptr<IPreparedModelCallback>& callback) {
+ callback->notify(ErrorStatus::GENERAL_FAILURE, nullptr);
+ return toAStatus(ErrorStatus::GENERAL_FAILURE, "InvalidDevice");
+}
+
+} // namespace aidl::android::hardware::neuralnetworks
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
index aee4d90..003965b 100644
--- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -16,9 +16,9 @@
#include "PreparedModel.h"
+#include "Burst.h"
#include "Callbacks.h"
#include "Conversions.h"
-#include "ProtectCallback.h"
#include "Utils.h"
#include <android/binder_auto_utils.h>
@@ -26,7 +26,6 @@
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/1.0/Burst.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
@@ -161,7 +160,10 @@
}
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
- return hal::V1_0::utils::Burst::create(shared_from_this());
+ std::shared_ptr<IBurst> burst;
+ const auto ret = kPreparedModel->configureExecutionBurst(&burst);
+ HANDLE_ASTATUS(ret) << "configureExecutionBurst failed";
+ return Burst::create(std::move(burst));
}
std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/aidl/utils/src/Service.cpp b/neuralnetworks/aidl/utils/src/Service.cpp
index 511de55..ac182a2 100644
--- a/neuralnetworks/aidl/utils/src/Service.cpp
+++ b/neuralnetworks/aidl/utils/src/Service.cpp
@@ -16,6 +16,7 @@
#include "Service.h"
+#include <AndroidVersionUtil.h>
#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
@@ -35,13 +36,23 @@
hal::utils::ResilientDevice::Factory makeDevice =
[instanceName,
name = std::move(fullName)](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
- const auto& getService =
- blocking ? AServiceManager_getService : AServiceManager_checkService;
+ std::add_pointer_t<AIBinder*(const char*)> getService;
+ if (blocking) {
+ if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
+ getService = AServiceManager_waitForService;
+ } else {
+ getService = AServiceManager_getService;
+ }
+ } else {
+ getService = AServiceManager_checkService;
+ }
+
auto service = IDevice::fromBinder(ndk::SpAIBinder(getService(name.c_str())));
if (service == nullptr) {
- return NN_ERROR() << (blocking ? "AServiceManager_getService"
- : "AServiceManager_checkService")
- << " returned nullptr";
+ return NN_ERROR()
+ << (blocking ? "AServiceManager_waitForService (or AServiceManager_getService)"
+ : "AServiceManager_checkService")
+ << " returned nullptr";
}
ABinderProcess_startThreadPool();
return Device::create(instanceName, std::move(service));
diff --git a/neuralnetworks/aidl/utils/src/Utils.cpp b/neuralnetworks/aidl/utils/src/Utils.cpp
index 95516c8..03407be 100644
--- a/neuralnetworks/aidl/utils/src/Utils.cpp
+++ b/neuralnetworks/aidl/utils/src/Utils.cpp
@@ -16,12 +16,20 @@
#include "Utils.h"
+#include <aidl/android/hardware/common/Ashmem.h>
+#include <aidl/android/hardware/common/MappableFile.h>
+#include <aidl/android/hardware/graphics/common/HardwareBuffer.h>
+#include <android/binder_auto_utils.h>
#include <android/binder_status.h>
#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
namespace aidl::android::hardware::neuralnetworks::utils {
namespace {
+nn::GeneralResult<ndk::ScopedFileDescriptor> clone(const ndk::ScopedFileDescriptor& fd);
+using utils::clone;
+
template <typename Type>
nn::GeneralResult<std::vector<Type>> cloneVec(const std::vector<Type>& arguments) {
std::vector<Type> clonedObjects;
@@ -37,24 +45,52 @@
return cloneVec(arguments);
}
+nn::GeneralResult<ndk::ScopedFileDescriptor> clone(const ndk::ScopedFileDescriptor& fd) {
+ auto duplicatedFd = NN_TRY(nn::dupFd(fd.get()));
+ return ndk::ScopedFileDescriptor(duplicatedFd.release());
+}
+
+nn::GeneralResult<common::NativeHandle> clone(const common::NativeHandle& handle) {
+ return common::NativeHandle{
+ .fds = NN_TRY(cloneVec(handle.fds)),
+ .ints = handle.ints,
+ };
+}
+
} // namespace
nn::GeneralResult<Memory> clone(const Memory& memory) {
- common::NativeHandle nativeHandle;
- nativeHandle.ints = memory.handle.ints;
- nativeHandle.fds.reserve(memory.handle.fds.size());
- for (const auto& fd : memory.handle.fds) {
- const int newFd = dup(fd.get());
- if (newFd < 0) {
- return NN_ERROR() << "Couldn't dup a file descriptor";
+ switch (memory.getTag()) {
+ case Memory::Tag::ashmem: {
+ const auto& ashmem = memory.get<Memory::Tag::ashmem>();
+ auto handle = common::Ashmem{
+ .fd = NN_TRY(clone(ashmem.fd)),
+ .size = ashmem.size,
+ };
+ return Memory::make<Memory::Tag::ashmem>(std::move(handle));
}
- nativeHandle.fds.emplace_back(newFd);
+ case Memory::Tag::mappableFile: {
+ const auto& memFd = memory.get<Memory::Tag::mappableFile>();
+ auto handle = common::MappableFile{
+ .length = memFd.length,
+ .prot = memFd.prot,
+ .fd = NN_TRY(clone(memFd.fd)),
+ .offset = memFd.offset,
+ };
+ return Memory::make<Memory::Tag::mappableFile>(std::move(handle));
+ }
+ case Memory::Tag::hardwareBuffer: {
+ const auto& hardwareBuffer = memory.get<Memory::Tag::hardwareBuffer>();
+ auto handle = graphics::common::HardwareBuffer{
+ .description = hardwareBuffer.description,
+ .handle = NN_TRY(clone(hardwareBuffer.handle)),
+ };
+ return Memory::make<Memory::Tag::hardwareBuffer>(std::move(handle));
+ }
}
- return Memory{
- .handle = std::move(nativeHandle),
- .size = memory.size,
- .name = memory.name,
- };
+ return (NN_ERROR() << "Unrecognized Memory::Tag: " << memory.getTag())
+ .
+ operator nn::GeneralResult<Memory>();
}
nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool) {
diff --git a/neuralnetworks/aidl/utils/test/MockBuffer.h b/neuralnetworks/aidl/utils/test/MockBuffer.h
index 5746176..f77fa86 100644
--- a/neuralnetworks/aidl/utils/test/MockBuffer.h
+++ b/neuralnetworks/aidl/utils/test/MockBuffer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
#include <android/binder_interface_utils.h>
@@ -40,4 +40,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/aidl/utils/test/MockBurst.h b/neuralnetworks/aidl/utils/test/MockBurst.h
new file mode 100644
index 0000000..5083bbd
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockBurst.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
+
+#include <aidl/android/hardware/neuralnetworks/BnBurst.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockBurst final : public BnBurst {
+ public:
+ MOCK_METHOD(ndk::ScopedAStatus, executeSynchronously,
+ (const Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
+ bool measureTiming, int64_t deadline, int64_t loopTimeoutDuration,
+ ExecutionResult* executionResult),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, releaseMemoryResource, (int64_t memoryIdentifierToken),
+ (override));
+};
+
+} // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
diff --git a/neuralnetworks/aidl/utils/test/MockDevice.h b/neuralnetworks/aidl/utils/test/MockDevice.h
index 9b35bf8..3a28d55 100644
--- a/neuralnetworks/aidl/utils/test/MockDevice.h
+++ b/neuralnetworks/aidl/utils/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
#include <android/binder_auto_utils.h>
@@ -64,4 +64,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
index 463e1c9..06f9ea2 100644
--- a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
+++ b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
#include <aidl/android/hardware/neuralnetworks/BnFencedExecutionCallback.h>
#include <android/binder_auto_utils.h>
@@ -42,4 +42,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
diff --git a/neuralnetworks/aidl/utils/test/MockPreparedModel.h b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
index 36e0ec3..a4ae2b7 100644
--- a/neuralnetworks/aidl/utils/test/MockPreparedModel.h
+++ b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
#include <android/binder_interface_utils.h>
@@ -49,4 +49,4 @@
} // namespace aidl::android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
index 7e28861..ff98a7d 100644
--- a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
+++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "MockBurst.h"
#include "MockFencedExecutionCallback.h"
#include "MockPreparedModel.h"
@@ -38,7 +39,7 @@
using ::testing::SetArgPointee;
const std::shared_ptr<IPreparedModel> kInvalidPreparedModel;
-constexpr auto kNoTiming = Timing{.timeOnDevice = -1, .timeInDriver = -1};
+constexpr auto kNoTiming = Timing{.timeOnDeviceNs = -1, .timeInDriverNs = -1};
constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
@@ -252,7 +253,71 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-// TODO: test burst execution if/when it is added to nn::IPreparedModel.
+TEST(PreparedModelTest, configureExecutionBurst) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ const auto mockBurst = ndk::SharedRefBase::make<MockBurst>();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(DoAll(SetArgPointee<0>(mockBurst), Invoke(makeStatusOk)));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_TRUE(result.has_value())
+ << "Failed with " << result.error().code << ": " << result.error().message;
+ EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+ // setup test
+ const auto mockPreparedModel = MockPreparedModel::create();
+ EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+ .Times(1)
+ .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+ const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+ // run test
+ const auto result = preparedModel->configureExecutionBurst();
+
+ // verify result
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
TEST(PreparedModelTest, getUnderlyingResource) {
// setup test
diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp
index 7804c2a..d5b150a 100644
--- a/neuralnetworks/aidl/vts/functional/Android.bp
+++ b/neuralnetworks/aidl/vts/functional/Android.bp
@@ -50,9 +50,11 @@
],
static_libs: [
"android.hardware.common-V2-ndk_platform",
+ "android.hardware.graphics.common-V2-ndk_platform",
"android.hardware.neuralnetworks-V1-ndk_platform",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
+ "libaidlcommonsupport",
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index 2dd02dd..d3b041d 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -102,7 +102,7 @@
ASSERT_NE(result, nullptr);
// Prepare arguments.
- BufferRole role = {.modelIndex = 0, .ioIndex = index, .frequency = 1.0f};
+ BufferRole role = {.modelIndex = 0, .ioIndex = index, .probability = 1.0f};
std::vector<BufferRole> inputRoles, outputRoles;
if constexpr (ioType == IOType::INPUT) {
inputRoles = {role};
@@ -547,7 +547,7 @@
makeOutputInsufficientSize(kInsufficientOutputIndex, &request);
}
- int64_t loopTimeoutDuration = kOmittedTimeoutDuration;
+ int64_t loopTimeoutDurationNs = kOmittedTimeoutDuration;
// OutputType::MISSED_DEADLINE is only used by
// TestKind::INTINITE_LOOP_TIMEOUT tests to verify that an infinite loop is
// aborted after a timeout.
@@ -555,7 +555,7 @@
// Override the default loop timeout duration with a small value to
// speed up test execution.
constexpr int64_t kMillisecond = 1'000'000;
- loopTimeoutDuration = 1 * kMillisecond;
+ loopTimeoutDurationNs = 1 * kMillisecond;
}
ErrorStatus executionStatus;
@@ -568,7 +568,7 @@
ExecutionResult executionResult;
// execute
const auto ret = preparedModel->executeSynchronously(request, testConfig.measureTiming,
- kNoDeadline, loopTimeoutDuration,
+ kNoDeadline, loopTimeoutDurationNs,
&executionResult);
ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
<< ret.getDescription();
@@ -608,7 +608,7 @@
ExecutionResult executionResult;
// execute
ret = burst->executeSynchronously(request, slots, testConfig.measureTiming, kNoDeadline,
- loopTimeoutDuration, &executionResult);
+ loopTimeoutDurationNs, &executionResult);
ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
<< ret.getDescription();
if (ret.isOk()) {
@@ -635,7 +635,7 @@
ErrorStatus result = ErrorStatus::NONE;
FencedExecutionResult executionResult;
auto ret = preparedModel->executeFenced(request, {}, testConfig.measureTiming,
- kNoDeadline, loopTimeoutDuration, kNoDuration,
+ kNoDeadline, loopTimeoutDurationNs, kNoDuration,
&executionResult);
ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
<< ret.getDescription();
@@ -649,7 +649,7 @@
waitFor.emplace_back(dupFd);
// If a sync fence is returned, try start another run waiting for the sync fence.
ret = preparedModel->executeFenced(request, waitFor, testConfig.measureTiming,
- kNoDeadline, loopTimeoutDuration, kNoDuration,
+ kNoDeadline, loopTimeoutDurationNs, kNoDuration,
&executionResult);
ASSERT_TRUE(ret.isOk());
waitForSyncFence(executionResult.syncFence.get());
@@ -686,8 +686,8 @@
if (!testConfig.measureTiming) {
EXPECT_EQ(timing, kNoTiming);
} else {
- if (timing.timeOnDevice != -1 && timing.timeInDriver != -1) {
- EXPECT_LE(timing.timeOnDevice, timing.timeInDriver);
+ if (timing.timeOnDeviceNs != -1 && timing.timeInDriverNs != -1) {
+ EXPECT_LE(timing.timeOnDeviceNs, timing.timeInDriverNs);
}
}
diff --git a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
index 627c26a..e8313f1 100644
--- a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "neuralnetworks_aidl_hal_test"
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
@@ -337,18 +338,18 @@
const std::shared_ptr<IPreparedModel>& model2) {
validateAllocate({
.preparedModels = {model1, model2},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {model1, model2},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
- .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+ .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {model1, model2},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
}
};
@@ -370,13 +371,13 @@
// Test with nullptr prepared model as input role.
validateAllocate({
.preparedModels = {nullptr},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
// Test with nullptr prepared model as output role.
validateAllocate({
.preparedModels = {nullptr},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -387,13 +388,13 @@
// Test with invalid prepared model as input role.
validateAllocate({
.preparedModels = {invalidPreparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
// Test with invalid prepared model as output role.
validateAllocate({
.preparedModels = {invalidPreparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -404,13 +405,13 @@
// This should fail, because the model index is out of bound.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
// This should fail, because the model index is out of bound.
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -421,30 +422,30 @@
// This should fail, because the model only has one input.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 1, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 1, .probability = 1.0f}},
});
// This should fail, because the model only has one output.
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 1, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 1, .probability = 1.0f}},
});
}
-TEST_P(MemoryDomainAllocateTest, InvalidFrequency) {
+TEST_P(MemoryDomainAllocateTest, InvalidProbability) {
auto preparedModel = createConvPreparedModel(kTestOperand);
if (preparedModel == nullptr) return;
for (float invalidFreq : {10.0f, 0.0f, -0.5f}) {
- // Test with invalid frequency for input roles.
+ // Test with invalid probability for input roles.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = invalidFreq}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = invalidFreq}},
});
- // Test with invalid frequency for output roles.
+ // Test with invalid probability for output roles.
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = invalidFreq}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = invalidFreq}},
});
}
}
@@ -456,25 +457,25 @@
// Same role with same model index.
validateAllocate({
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
// Different model indexes, but logically referring to the same role.
validateAllocate({
.preparedModels = {preparedModel, preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.preparedModels = {preparedModel, preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f},
- {.modelIndex = 1, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+ {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -553,12 +554,12 @@
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -572,12 +573,12 @@
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
validateAllocate({
.dimensions = badDimensions,
.preparedModels = {preparedModel},
- .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .frequency = 1.0f}},
+ .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
});
}
@@ -590,7 +591,7 @@
validateAllocate({
.dimensions = {1},
.preparedModels = {preparedModel},
- .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .frequency = 1.0f}},
+ .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .probability = 1.0f}},
});
}
@@ -624,7 +625,7 @@
std::vector<BufferRole> inputRoles(inputIndexes.size()), outputRoles(outputIndexes.size());
auto trans = [](int32_t ind) -> BufferRole {
- return {.modelIndex = 0, .ioIndex = ind, .frequency = 1.0f};
+ return {.modelIndex = 0, .ioIndex = ind, .probability = 1.0f};
};
std::transform(inputIndexes.begin(), inputIndexes.end(), inputRoles.begin(), trans);
std::transform(outputIndexes.begin(), outputIndexes.end(), outputRoles.begin(), trans);
@@ -659,10 +660,26 @@
return allocateBuffer(preparedModel, inputIndexes, outputIndexes, {});
}
+ size_t getSize(const Memory& memory) {
+ switch (memory.getTag()) {
+ case Memory::Tag::ashmem:
+ return memory.get<Memory::Tag::ashmem>().size;
+ case Memory::Tag::mappableFile:
+ return memory.get<Memory::Tag::mappableFile>().length;
+ case Memory::Tag::hardwareBuffer: {
+ const auto& hardwareBuffer = memory.get<Memory::Tag::hardwareBuffer>();
+ const bool isBlob =
+ hardwareBuffer.description.format == graphics::common::PixelFormat::BLOB;
+ return isBlob ? hardwareBuffer.description.width : 0;
+ }
+ }
+ return 0;
+ }
+
Memory allocateSharedMemory(uint32_t size) {
const auto sharedMemory = nn::createSharedMemory(size).value();
auto memory = utils::convert(sharedMemory).value();
- EXPECT_EQ(memory.size, size);
+ EXPECT_EQ(getSize(memory), size);
return memory;
}
@@ -690,7 +707,7 @@
void initializeDeviceMemory(const std::shared_ptr<IBuffer>& buffer) {
Memory memory = allocateSharedMemory(kTestOperandDataSize);
- ASSERT_EQ(memory.size, kTestOperandDataSize);
+ ASSERT_EQ(getSize(memory), kTestOperandDataSize);
testCopyFrom(buffer, memory, utils::toSigned(kTestOperand.dimensions).value(),
ErrorStatus::NONE);
}
diff --git a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
index 9ace1a9..bbba887 100644
--- a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
+#include <android-base/chrono_utils.h>
#include <android/binder_enums.h>
#include <android/binder_interface_utils.h>
#include <android/binder_status.h>
-
#include <nnapi/hal/aidl/Conversions.h>
#include "Callbacks.h"
@@ -53,7 +53,7 @@
using ExecutionFunction =
std::function<MaybeResults(const std::shared_ptr<IPreparedModel>& preparedModel,
- const Request& request, int64_t deadline)>;
+ const Request& request, int64_t deadlineNs)>;
static int64_t makeDeadline(DeadlineBoundType deadlineBoundType) {
const auto getNanosecondsSinceEpoch = [](const auto& time) -> int64_t {
@@ -61,16 +61,16 @@
return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
};
- std::chrono::steady_clock::time_point timePoint;
+ ::android::base::boot_clock::time_point timePoint;
switch (deadlineBoundType) {
case DeadlineBoundType::NOW:
- timePoint = std::chrono::steady_clock::now();
+ timePoint = ::android::base::boot_clock::now();
break;
case DeadlineBoundType::UNLIMITED:
- timePoint = std::chrono::steady_clock::time_point::max();
+ timePoint = ::android::base::boot_clock::time_point::max();
break;
case DeadlineBoundType::SHORT:
- timePoint = std::chrono::steady_clock::now() + kShortDuration;
+ timePoint = ::android::base::boot_clock::now() + kShortDuration;
break;
}
@@ -79,9 +79,9 @@
void runPrepareModelTest(const std::shared_ptr<IDevice>& device, const Model& model,
Priority priority, std::optional<DeadlineBoundType> deadlineBound) {
- int64_t deadline = kNoDeadline;
+ int64_t deadlineNs = kNoDeadline;
if (deadlineBound.has_value()) {
- deadline = makeDeadline(deadlineBound.value());
+ deadlineNs = makeDeadline(deadlineBound.value());
}
// see if service can handle model
@@ -96,8 +96,8 @@
const std::shared_ptr<PreparedModelCallback> preparedModelCallback =
ndk::SharedRefBase::make<PreparedModelCallback>();
const auto prepareLaunchStatus =
- device->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, priority, deadline,
- {}, {}, kEmptyCacheToken, preparedModelCallback);
+ device->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, priority,
+ deadlineNs, {}, {}, kEmptyCacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk())
<< "prepareLaunchStatus: " << prepareLaunchStatus.getDescription();
@@ -156,13 +156,13 @@
}
static MaybeResults executeSynchronously(const std::shared_ptr<IPreparedModel>& preparedModel,
- const Request& request, int64_t deadline) {
+ const Request& request, int64_t deadlineNs) {
SCOPED_TRACE("synchronous");
const bool measure = false;
// run execution
ExecutionResult executionResult;
- const auto ret = preparedModel->executeSynchronously(request, measure, deadline,
+ const auto ret = preparedModel->executeSynchronously(request, measure, deadlineNs,
kOmittedTimeoutDuration, &executionResult);
EXPECT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
<< ret.getDescription();
@@ -182,7 +182,7 @@
}
static MaybeResults executeBurst(const std::shared_ptr<IPreparedModel>& preparedModel,
- const Request& request, int64_t deadline) {
+ const Request& request, int64_t deadlineNs) {
SCOPED_TRACE("burst");
const bool measure = false;
@@ -200,7 +200,7 @@
// run execution
ExecutionResult executionResult;
- ret = burst->executeSynchronously(request, slots, measure, deadline, kOmittedTimeoutDuration,
+ ret = burst->executeSynchronously(request, slots, measure, deadlineNs, kOmittedTimeoutDuration,
&executionResult);
EXPECT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
<< ret.getDescription();
@@ -224,10 +224,10 @@
const ExecutionContext& context, bool synchronous,
DeadlineBoundType deadlineBound) {
const ExecutionFunction execute = synchronous ? executeSynchronously : executeBurst;
- const auto deadline = makeDeadline(deadlineBound);
+ const auto deadlineNs = makeDeadline(deadlineBound);
// Perform execution and unpack results.
- const auto results = execute(preparedModel, request, deadline);
+ const auto results = execute(preparedModel, request, deadlineNs);
if (!results.has_value()) return;
const auto& [status, outputShapes, timing] = results.value();
diff --git a/neuralnetworks/aidl/vts/functional/Utils.h b/neuralnetworks/aidl/vts/functional/Utils.h
index 266301c..77085a7 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.h
+++ b/neuralnetworks/aidl/vts/functional/Utils.h
@@ -43,7 +43,7 @@
inline constexpr Priority kDefaultPriority = Priority::MEDIUM;
-inline constexpr Timing kNoTiming = {.timeOnDevice = -1, .timeInDriver = -1};
+inline constexpr Timing kNoTiming = {.timeOnDeviceNs = -1, .timeInDriverNs = -1};
inline constexpr int64_t kNoDeadline = -1;
inline constexpr int64_t kOmittedTimeoutDuration = -1;
inline constexpr int64_t kNoDuration = -1;
diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
index 6d84e1e..698c054 100644
--- a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
@@ -259,12 +259,16 @@
size_t sizeForBinder(const Memory& memory) {
// This is just a guess.
- size_t size = 0;
- const NativeHandle& handle = memory.handle;
- size += sizeof(decltype(handle.fds)::value_type) * handle.fds.size();
- size += sizeof(decltype(handle.ints)::value_type) * handle.ints.size();
- size += sizeForBinder(memory.name);
- size += sizeof(memory);
+ size_t size = sizeof(Memory);
+
+ // Only hardwareBuffer type memory has dynamic memory that needs to be accounted for (in the
+ // form of a NativeHandle type). The other other types of memory (MappableFile, Ashmem) use a
+ // single file descriptor (with metadata) instead.
+ if (memory.getTag() == Memory::Tag::hardwareBuffer) {
+ const NativeHandle& handle = memory.get<Memory::Tag::hardwareBuffer>().handle;
+ size += sizeof(decltype(handle.fds)::value_type) * handle.fds.size();
+ size += sizeof(decltype(handle.ints)::value_type) * handle.ints.size();
+ }
return size;
}
@@ -1312,7 +1316,7 @@
void validateModel(const std::shared_ptr<IDevice>& device, const Model& model) {
const auto numberOfConsumers =
nn::countNumberOfConsumers(model.main.operands.size(),
- nn::convert(model.main.operations).value())
+ nn::unvalidatedConvert(model.main.operations).value())
.value();
mutateExecutionOrderTest(device, model, numberOfConsumers);
mutateOperandTypeTest(device, model);
diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
index 0c3a196..ee7cf89 100644
--- a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
@@ -94,7 +94,7 @@
}
static NamedDevice makeNamedDevice(const std::string& name) {
- ndk::SpAIBinder binder(AServiceManager_getService(name.c_str()));
+ ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
return {name, IDevice::fromBinder(binder)};
}
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
index 996858c..17b3fd9 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
@@ -32,7 +32,9 @@
OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
};
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
index 3b87330..c92cc41 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -47,7 +47,9 @@
OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
- const nn::Request& request, nn::MeasureTiming measure) const override;
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const override;
private:
const Factory kMakeBurst;
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index 924ecb2..4d26795 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -89,6 +89,59 @@
});
}
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(base::unique_fd fd,
+ const std::vector<int32_t>& ints) {
+ constexpr size_t kIntMax = std::numeric_limits<int>::max();
+ CHECK_LE(ints.size(), kIntMax);
+ native_handle_t* nativeHandle = native_handle_create(1, static_cast<int>(ints.size()));
+ if (nativeHandle == nullptr) {
+ return NN_ERROR() << "Failed to create native_handle";
+ }
+
+ nativeHandle->data[0] = fd.release();
+ std::copy(ints.begin(), ints.end(), nativeHandle->data + 1);
+
+ hidl_handle handle;
+ handle.setTo(nativeHandle, /*shouldOwn=*/true);
+ return handle;
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Ashmem& memory) {
+ auto fd = NN_TRY(nn::dupFd(memory.fd));
+ auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), {}));
+ return hidl_memory("ashmem", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Fd& memory) {
+ auto fd = NN_TRY(nn::dupFd(memory.fd));
+
+ const auto [lowOffsetBits, highOffsetBits] = nn::getIntsFromOffset(memory.offset);
+ const std::vector<int> ints = {memory.prot, lowOffsetBits, highOffsetBits};
+
+ auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), ints));
+ return hidl_memory("mmap_fd", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::HardwareBuffer& memory) {
+ const auto* ahwb = memory.handle.get();
+ AHardwareBuffer_Desc bufferDesc;
+ AHardwareBuffer_describe(ahwb, &bufferDesc);
+
+ const bool isBlob = bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB;
+ const size_t size = isBlob ? bufferDesc.width : 0;
+ const char* const name = isBlob ? "hardware_buffer_blob" : "hardware_buffer";
+
+ const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
+ const hidl_handle hidlHandle(nativeHandle);
+ hidl_handle copiedHandle(hidlHandle);
+
+ return hidl_memory(name, std::move(copiedHandle), size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Unknown& memory) {
+ return hidl_memory(memory.name, NN_TRY(hidlHandleFromSharedHandle(memory.handle)), memory.size);
+}
+
} // anonymous namespace
nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
@@ -255,27 +308,7 @@
if (memory == nullptr) {
return NN_ERROR() << "Memory must be non-empty";
}
- if (const auto* handle = std::get_if<nn::Handle>(&memory->handle)) {
- return hidl_memory(memory->name, NN_TRY(hidlHandleFromSharedHandle(*handle)), memory->size);
- }
-
- const auto* ahwb = std::get<nn::HardwareBufferHandle>(memory->handle).get();
- AHardwareBuffer_Desc bufferDesc;
- AHardwareBuffer_describe(ahwb, &bufferDesc);
-
- if (bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB) {
- CHECK_EQ(memory->size, bufferDesc.width);
- CHECK_EQ(memory->name, "hardware_buffer_blob");
- } else {
- CHECK_EQ(memory->size, 0u);
- CHECK_EQ(memory->name, "hardware_buffer");
- }
-
- const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
- const hidl_handle hidlHandle(nativeHandle);
- hidl_handle handle(hidlHandle);
-
- return hidl_memory(memory->name, std::move(handle), memory->size);
+ return std::visit([](const auto& x) { return createHidlMemoryFrom(x); }, memory->handle);
}
static uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
@@ -283,14 +316,53 @@
}
nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory) {
- CHECK_LE(memory.size(), std::numeric_limits<uint32_t>::max());
+ CHECK_LE(memory.size(), std::numeric_limits<size_t>::max());
+ if (!memory.valid()) {
+ return NN_ERROR() << "Unable to convert invalid hidl_memory";
+ }
+
+ if (memory.name() == "ashmem") {
+ if (memory.handle()->numFds != 1) {
+ return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+ << memory.handle()->numFds << " numFds, but expected 1";
+ }
+ if (memory.handle()->numInts != 0) {
+ return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+ << memory.handle()->numInts << " numInts, but expected 0";
+ }
+ auto handle = nn::Memory::Ashmem{
+ .fd = NN_TRY(nn::dupFd(memory.handle()->data[0])),
+ .size = static_cast<size_t>(memory.size()),
+ };
+ return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
+ }
+
+ if (memory.name() == "mmap_fd") {
+ if (memory.handle()->numFds != 1) {
+ return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+ << memory.handle()->numFds << " numFds, but expected 1";
+ }
+ if (memory.handle()->numInts != 3) {
+ return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+ << memory.handle()->numInts << " numInts, but expected 3";
+ }
+
+ const int fd = memory.handle()->data[0];
+ const int prot = memory.handle()->data[1];
+ const int lower = memory.handle()->data[2];
+ const int higher = memory.handle()->data[3];
+ const size_t offset = nn::getOffsetFromInts(lower, higher);
+
+ return nn::createSharedMemoryFromFd(static_cast<size_t>(memory.size()), prot, fd, offset);
+ }
if (memory.name() != "hardware_buffer_blob") {
- return std::make_shared<const nn::Memory>(nn::Memory{
+ auto handle = nn::Memory::Unknown{
.handle = NN_TRY(sharedHandleFromNativeHandle(memory.handle())),
- .size = static_cast<uint32_t>(memory.size()),
+ .size = static_cast<size_t>(memory.size()),
.name = memory.name(),
- });
+ };
+ return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
}
const auto size = memory.size();
@@ -328,11 +400,7 @@
<< "Can't create AHardwareBuffer from handle. Error: " << status;
}
- return std::make_shared<const nn::Memory>(nn::Memory{
- .handle = nn::HardwareBufferHandle(hardwareBuffer, /*takeOwnership=*/true),
- .size = static_cast<uint32_t>(memory.size()),
- .name = memory.name(),
- });
+ return nn::createSharedMemoryFromAHWB(hardwareBuffer, /*takeOwnership=*/true);
}
nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle) {
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
index 81ca18d..0c34f05 100644
--- a/neuralnetworks/utils/common/src/InvalidBurst.cpp
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -32,7 +32,9 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidBurst::execute(
- const nn::Request& /*request*/, nn::MeasureTiming /*measure*/) const {
+ const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+ const nn::OptionalTimePoint& /*deadline*/,
+ const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
return NN_ERROR() << "InvalidBurst";
}
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
index 5ca868b..38ccc62 100644
--- a/neuralnetworks/utils/common/src/ResilientBurst.cpp
+++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp
@@ -100,9 +100,11 @@
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> ResilientBurst::execute(
- const nn::Request& request, nn::MeasureTiming measure) const {
- const auto fn = [&request, measure](const nn::IBurst& burst) {
- return burst.execute(request, measure);
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
+ const auto fn = [&request, measure, deadline, loopTimeoutDuration](const nn::IBurst& burst) {
+ return burst.execute(request, measure, deadline, loopTimeoutDuration);
};
return protect(*this, fn);
}
diff --git a/neuralnetworks/utils/common/test/MockBuffer.h b/neuralnetworks/utils/common/test/MockBuffer.h
index 59d5700..3599d0c 100644
--- a/neuralnetworks/utils/common/test/MockBuffer.h
+++ b/neuralnetworks/utils/common/test/MockBuffer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -34,4 +34,4 @@
} // namespace android::nn
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/utils/common/test/MockDevice.h b/neuralnetworks/utils/common/test/MockDevice.h
index 5566968..b274716 100644
--- a/neuralnetworks/utils/common/test/MockDevice.h
+++ b/neuralnetworks/utils/common/test/MockDevice.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -55,4 +55,4 @@
} // namespace android::nn
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
index 418af61..c004861 100644
--- a/neuralnetworks/utils/common/test/MockPreparedModel.h
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -41,4 +41,4 @@
} // namespace android::nn
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/utils/service/src/Service.cpp b/neuralnetworks/utils/service/src/Service.cpp
index c83bcc9..75ab1f9 100644
--- a/neuralnetworks/utils/service/src/Service.cpp
+++ b/neuralnetworks/utils/service/src/Service.cpp
@@ -16,6 +16,7 @@
#include "Service.h"
+#include <AndroidVersionUtil.h>
#include <aidl/android/hardware/neuralnetworks/IDevice.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
@@ -82,7 +83,7 @@
// Devices with SDK level lower than 31 (Android S) don't have any AIDL drivers available, so
// there is no need for a workaround supported on lower levels.
- if (__builtin_available(android __ANDROID_API_S__, *)) {
+ if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
AServiceManager_forEachDeclaredInstance(aidl_hal::IDevice::descriptor,
static_cast<void*>(&names), callback);
}
diff --git a/power/stats/aidl/default/PowerStats.cpp b/power/stats/aidl/default/PowerStats.cpp
index 7cf591e..4b771a8 100644
--- a/power/stats/aidl/default/PowerStats.cpp
+++ b/power/stats/aidl/default/PowerStats.cpp
@@ -32,15 +32,19 @@
}
int32_t id = mPowerEntityInfos.size();
+ auto info = p->getInfo();
- for (const auto& [entityName, states] : p->getInfo()) {
+ size_t index = mStateResidencyDataProviders.size();
+ mStateResidencyDataProviders.emplace_back(std::move(p));
+
+ for (const auto& [entityName, states] : info) {
PowerEntity i = {
.id = id++,
.name = entityName,
.states = states,
};
mPowerEntityInfos.emplace_back(i);
- mStateResidencyDataProviders.emplace_back(std::move(p));
+ mStateResidencyDataProviderIndex.emplace_back(index);
}
}
@@ -92,7 +96,8 @@
// Check to see if we already have data for the given id
std::string powerEntityName = mPowerEntityInfos[id].name;
if (stateResidencies.find(powerEntityName) == stateResidencies.end()) {
- mStateResidencyDataProviders[id]->getStateResidencies(&stateResidencies);
+ mStateResidencyDataProviders.at(mStateResidencyDataProviderIndex.at(id))
+ ->getStateResidencies(&stateResidencies);
}
// Append results if we have them
diff --git a/power/stats/aidl/default/PowerStats.h b/power/stats/aidl/default/PowerStats.h
index f4c5e69..91d272d 100644
--- a/power/stats/aidl/default/PowerStats.h
+++ b/power/stats/aidl/default/PowerStats.h
@@ -73,6 +73,8 @@
private:
std::vector<std::unique_ptr<IStateResidencyDataProvider>> mStateResidencyDataProviders;
std::vector<PowerEntity> mPowerEntityInfos;
+ /* Index that maps each power entity id to an entry in mStateResidencyDataProviders */
+ std::vector<size_t> mStateResidencyDataProviderIndex;
std::vector<std::unique_ptr<IEnergyConsumer>> mEnergyConsumers;
std::vector<EnergyConsumer> mEnergyConsumerInfos;
diff --git a/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
index bed3fdf..c7ba96c 100644
--- a/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
+++ b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
@@ -40,6 +40,12 @@
using ndk::SpAIBinder;
+#define ASSERT_OK(a) \
+ do { \
+ auto ret = a; \
+ ASSERT_TRUE(ret.isOk()) << ret.getDescription(); \
+ } while (0)
+
class PowerStatsAidl : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
@@ -108,7 +114,7 @@
// Each PowerEntity must have a valid name
TEST_P(PowerStatsAidl, ValidatePowerEntityNames) {
std::vector<PowerEntity> infos;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&infos).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&infos));
for (auto info : infos) {
testNameValid(info.name);
@@ -118,7 +124,7 @@
// Each power entity must have a unique name
TEST_P(PowerStatsAidl, ValidatePowerEntityUniqueNames) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
testUnique(entities, &PowerEntity::name);
}
@@ -126,7 +132,7 @@
// Each PowerEntity must have a unique ID
TEST_P(PowerStatsAidl, ValidatePowerEntityIds) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
testUnique(entities, &PowerEntity::id);
}
@@ -134,7 +140,7 @@
// Each power entity must have at least one state
TEST_P(PowerStatsAidl, ValidateStateSize) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
for (auto entity : entities) {
EXPECT_GT(entity.states.size(), 0);
@@ -144,7 +150,7 @@
// Each state must have a valid name
TEST_P(PowerStatsAidl, ValidateStateNames) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
for (auto entity : entities) {
for (auto state : entity.states) {
@@ -156,7 +162,7 @@
// Each state must have a name that is unique to the given PowerEntity
TEST_P(PowerStatsAidl, ValidateStateUniqueNames) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
for (auto entity : entities) {
testUnique(entity.states, &State::name);
@@ -166,7 +172,7 @@
// Each state must have an ID that is unique to the given PowerEntity
TEST_P(PowerStatsAidl, ValidateStateUniqueIds) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
for (auto entity : entities) {
testUnique(entity.states, &State::id);
@@ -176,16 +182,16 @@
// State residency must return a valid status
TEST_P(PowerStatsAidl, TestGetStateResidency) {
std::vector<StateResidencyResult> results;
- ASSERT_TRUE(powerstats->getStateResidency({}, &results).isOk());
+ ASSERT_OK(powerstats->getStateResidency({}, &results));
}
// State residency must return all results
TEST_P(PowerStatsAidl, TestGetStateResidencyAllResults) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
std::vector<StateResidencyResult> results;
- ASSERT_TRUE(powerstats->getStateResidency({}, &results).isOk());
+ ASSERT_OK(powerstats->getStateResidency({}, &results));
testMatching(entities, &PowerEntity::id, results, &StateResidencyResult::id);
}
@@ -193,10 +199,10 @@
// Each result must contain all state residencies
TEST_P(PowerStatsAidl, TestGetStateResidencyAllStateResidencies) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
std::vector<StateResidencyResult> results;
- ASSERT_TRUE(powerstats->getStateResidency({}, &results).isOk());
+ ASSERT_OK(powerstats->getStateResidency({}, &results));
for (auto entity : entities) {
auto it = std::find_if(results.begin(), results.end(),
@@ -210,7 +216,7 @@
// State residency must return results for each requested power entity
TEST_P(PowerStatsAidl, TestGetStateResidencySelectedResults) {
std::vector<PowerEntity> entities;
- ASSERT_TRUE(powerstats->getPowerEntityInfo(&entities).isOk());
+ ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
if (entities.empty()) {
return;
}
@@ -222,7 +228,7 @@
}
std::vector<StateResidencyResult> selectedResults;
- ASSERT_TRUE(powerstats->getStateResidency(selectedIds, &selectedResults).isOk());
+ ASSERT_OK(powerstats->getStateResidency(selectedIds, &selectedResults));
testMatching(selectedEntities, &PowerEntity::id, selectedResults, &StateResidencyResult::id);
}
@@ -230,15 +236,25 @@
// Energy meter info must return a valid status
TEST_P(PowerStatsAidl, TestGetEnergyMeterInfo) {
std::vector<Channel> info;
- ASSERT_TRUE(powerstats->getEnergyMeterInfo(&info).isOk());
+ ASSERT_OK(powerstats->getEnergyMeterInfo(&info));
}
-// Each channel must have a valid name and subsystem
+// Each channel must have a valid name
TEST_P(PowerStatsAidl, ValidateChannelNames) {
std::vector<Channel> channels;
- ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+ ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
for (auto channel : channels) {
testNameValid(channel.name);
+ }
+}
+
+// Each channel must have a valid subsystem
+TEST_P(PowerStatsAidl, ValidateSubsystemNames) {
+ std::vector<Channel> channels;
+ ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
+ for (auto channel : channels) {
testNameValid(channel.subsystem);
}
}
@@ -246,7 +262,7 @@
// Each channel must have a unique name
TEST_P(PowerStatsAidl, ValidateChannelUniqueNames) {
std::vector<Channel> channels;
- ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+ ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
testUnique(channels, &Channel::name);
}
@@ -254,7 +270,7 @@
// Each channel must have a unique ID
TEST_P(PowerStatsAidl, ValidateChannelUniqueIds) {
std::vector<Channel> channels;
- ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+ ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
testUnique(channels, &Channel::id);
}
@@ -262,16 +278,16 @@
// Reading energy meter must return a valid status
TEST_P(PowerStatsAidl, TestReadEnergyMeter) {
std::vector<EnergyMeasurement> data;
- ASSERT_TRUE(powerstats->readEnergyMeter({}, &data).isOk());
+ ASSERT_OK(powerstats->readEnergyMeter({}, &data));
}
// Reading energy meter must return results for all available channels
TEST_P(PowerStatsAidl, TestGetAllEnergyMeasurements) {
std::vector<Channel> channels;
- ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+ ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
std::vector<EnergyMeasurement> measurements;
- ASSERT_TRUE(powerstats->readEnergyMeter({}, &measurements).isOk());
+ ASSERT_OK(powerstats->readEnergyMeter({}, &measurements));
testMatching(channels, &Channel::id, measurements, &EnergyMeasurement::id);
}
@@ -279,7 +295,7 @@
// Reading energy must must return results for each selected channel
TEST_P(PowerStatsAidl, TestGetSelectedEnergyMeasurements) {
std::vector<Channel> channels;
- ASSERT_TRUE(powerstats->getEnergyMeterInfo(&channels).isOk());
+ ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
if (channels.empty()) {
return;
}
@@ -291,7 +307,7 @@
}
std::vector<EnergyMeasurement> selectedMeasurements;
- ASSERT_TRUE(powerstats->readEnergyMeter(selectedIds, &selectedMeasurements).isOk());
+ ASSERT_OK(powerstats->readEnergyMeter(selectedIds, &selectedMeasurements));
testMatching(selectedChannels, &Channel::id, selectedMeasurements, &EnergyMeasurement::id);
}
@@ -299,13 +315,13 @@
// Energy consumer info must return a valid status
TEST_P(PowerStatsAidl, TestGetEnergyConsumerInfo) {
std::vector<EnergyConsumer> consumers;
- ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
}
// Each energy consumer must have a unique id
TEST_P(PowerStatsAidl, TestGetEnergyConsumerUniqueId) {
std::vector<EnergyConsumer> consumers;
- ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
testUnique(consumers, &EnergyConsumer::id);
}
@@ -313,7 +329,7 @@
// Each energy consumer must have a valid name
TEST_P(PowerStatsAidl, ValidateEnergyConsumerNames) {
std::vector<EnergyConsumer> consumers;
- ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
for (auto consumer : consumers) {
testNameValid(consumer.name);
@@ -323,7 +339,7 @@
// Each energy consumer must have a unique name
TEST_P(PowerStatsAidl, ValidateEnergyConsumerUniqueNames) {
std::vector<EnergyConsumer> consumers;
- ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
testUnique(consumers, &EnergyConsumer::name);
}
@@ -331,7 +347,7 @@
// Energy consumers of the same type must have ordinals that are 0,1,2,..., N - 1
TEST_P(PowerStatsAidl, ValidateEnergyConsumerOrdinals) {
std::vector<EnergyConsumer> consumers;
- ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
std::unordered_map<EnergyConsumerType, std::set<int32_t>> ordinalMap;
@@ -350,16 +366,16 @@
// Energy consumed must return a valid status
TEST_P(PowerStatsAidl, TestGetEnergyConsumed) {
std::vector<EnergyConsumerResult> results;
- ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
}
// Energy consumed must return data for all energy consumers
TEST_P(PowerStatsAidl, TestGetAllEnergyConsumed) {
std::vector<EnergyConsumer> consumers;
- ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
std::vector<EnergyConsumerResult> results;
- ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
testMatching(consumers, &EnergyConsumer::id, results, &EnergyConsumerResult::id);
}
@@ -367,7 +383,7 @@
// Energy consumed must return data for each selected energy consumer
TEST_P(PowerStatsAidl, TestGetSelectedEnergyConsumed) {
std::vector<EnergyConsumer> consumers;
- ASSERT_TRUE(powerstats->getEnergyConsumerInfo(&consumers).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
if (consumers.empty()) {
return;
}
@@ -379,7 +395,7 @@
}
std::vector<EnergyConsumerResult> selectedResults;
- ASSERT_TRUE(powerstats->getEnergyConsumed(selectedIds, &selectedResults).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumed(selectedIds, &selectedResults));
testMatching(selectedConsumers, &EnergyConsumer::id, selectedResults,
&EnergyConsumerResult::id);
@@ -388,7 +404,7 @@
// Energy consumed attribution uids must be unique for a given energy consumer
TEST_P(PowerStatsAidl, ValidateEnergyConsumerAttributionUniqueUids) {
std::vector<EnergyConsumerResult> results;
- ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
for (auto result : results) {
testUnique(result.attribution, &EnergyConsumerAttribution::uid);
@@ -398,7 +414,7 @@
// Energy consumed total energy >= sum total of uid-attributed energy
TEST_P(PowerStatsAidl, TestGetEnergyConsumedAttributedEnergy) {
std::vector<EnergyConsumerResult> results;
- ASSERT_TRUE(powerstats->getEnergyConsumed({}, &results).isOk());
+ ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
for (auto result : results) {
int64_t totalAttributedEnergyUWs = 0;
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index a4e8811..e2d35d0 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -145,7 +145,6 @@
*
* Response function is IRadioResponse.setupDataCallResponse_1_6()
*
- * Note this API is the same as the 1.5
*/
oneway setupDataCall_1_6(int32_t serial, AccessNetwork accessNetwork,
DataProfileInfo dataProfileInfo, bool roamingAllowed,
@@ -177,7 +176,7 @@
* @param serial Serial number of request.
* @param message GsmSmsMessage as defined in types.hal
*
- * Response function is IRadioResponse.sendSMSExpectMoreResponse_1_6()
+ * Response function is IRadioResponse.sendSmsExpectMoreResponse_1_6()
*
* Note this API is the same as the 1.0
*
@@ -185,7 +184,7 @@
* fails. RadioError:SMS_SEND_FAIL_RETRY means retry (i.e. error cause is 332)
* and RadioError:GENERIC_FAILURE means no retry (i.e. error cause is 500)
*/
- oneway sendSMSExpectMore_1_6(int32_t serial, GsmSmsMessage message);
+ oneway sendSmsExpectMore_1_6(int32_t serial, GsmSmsMessage message);
/**
* Send a CDMA SMS message
@@ -267,7 +266,7 @@
* 2. Disable NR dual connectivity {NrDualConnectivityState:DISABLE}
* 3. Disable NR dual connectivity and force secondary cell to be released
* {NrDualConnectivityState:DISABLE_IMMEDIATE}
-
+ *
* Response callback is IRadioResponse.setNRDualConnectivityStateResponse()
*/
oneway setNrDualConnectivityState(int32_t serial,
@@ -372,7 +371,7 @@
*
* Response callback is IRadioResponse.getAllowedNetworkTypesBitmapResponse()
*/
- oneway getAllowedNetworkTypesBitmap(uint32_t serial);
+ oneway getAllowedNetworkTypesBitmap(int32_t serial);
/**
* Control data throttling at modem.
diff --git a/radio/1.6/IRadioIndication.hal b/radio/1.6/IRadioIndication.hal
index 9788345..05a7585 100644
--- a/radio/1.6/IRadioIndication.hal
+++ b/radio/1.6/IRadioIndication.hal
@@ -107,7 +107,7 @@
/**
* Indicates physical channel configurations.
*
- * An empty configs list indicates that the radio is in idle mode.
+ * An empty configs list shall be returned when the radio is in idle mode (i.e. RRC idle).
*
* @param type Type of radio indication
* @param configs Vector of PhysicalChannelConfigs
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
index f2c06b7..a1286a5 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -139,7 +139,7 @@
* RadioError:ACCESS_BARRED
* RadioError:BLOCKED_DUE_TO_CALL
*/
- oneway sendSMSExpectMoreResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
+ oneway sendSmsExpectMoreResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
/**
* @param info Response info struct containing response type, serial no. and error
@@ -232,6 +232,7 @@
* RadioError:RADIO_NOT_AVAILABLE
* RadioError:INTERNAL_ERR
* RadioError:REQUEST_NOT_SUPPORTED
+ * RadioError:INVALID_STATE
*/
oneway setNrDualConnectivityStateResponse(RadioResponseInfo info);
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
index 82c9daa..4b75db5 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -55,9 +55,9 @@
struct QosBandwidth {
/** Maximum bit rate possible on the bearer */
- int32_t maxBitrateKbps;
+ uint32_t maxBitrateKbps;
/** Minimum bit rate that is guaranteed to be provided by the network */
- int32_t guaranteedBitrateKbps;
+ uint32_t guaranteedBitrateKbps;
};
/** LTE/EPS Quality of Service parameters as per 3gpp spec 24.301 sec 9.9.4.3. */
@@ -106,7 +106,7 @@
/**
* Next header protocol numbers defined by IANA, RFC 5237
*/
-enum QosProtocol : int32_t {
+enum QosProtocol : int8_t {
/** No protocol specified */
UNSPECIFIED = -1,
/** Transmission Control Protocol */
@@ -119,14 +119,14 @@
AH = 51,
};
-enum QosFilterDirection : int32_t {
+enum QosFilterDirection : int8_t {
DOWNLINK = 0,
UPLINK = 1,
BIDIRECTIONAL = 2,
};
/** Allowed port numbers */
-enum QosPortRange : int32_t {
+enum QosPortRange : uint16_t {
MIN = 20,
MAX = 65535
};
@@ -248,7 +248,7 @@
};
/** The allowed failure modes on an IWLAN handover failure. */
-enum HandoverFailureMode : int32_t {
+enum HandoverFailureMode : int8_t {
/**
* On data handover failure, fallback to the source data transport when the
* fail cause is due to a hand off preference change.
@@ -379,7 +379,7 @@
/**
* NR Dual connectivity state
*/
-enum NrDualConnectivityState: int32_t {
+enum NrDualConnectivityState: int8_t {
/**
* Enable NR dual connectivity. Enabled state does not mean dual connectivity
* is active. It means device is allowed to connect to both primary and secondary.
@@ -408,7 +408,7 @@
* the estimated maximum sustainable link bandwidth (as would be measured
* at the Upper PDCP or SNDCP SAP). If the DL Aggregate Maximum Bit Rate is known,
* this value shall not exceed the DL-AMBR for the Internet PDN connection.
- * This must be filled with -1 if network is not connected.
+ * This must be filled with 0 if network is not connected.
*/
uint32_t downlinkCapacityKbps;
@@ -418,7 +418,7 @@
* estimated maximum sustainable link bandwidth (as would be measured at the
* Upper PDCP or SNDCP SAP). If the UL Aggregate Maximum Bit Rate is known,
* this value shall not exceed the UL-AMBR for the Internet PDN connection.
- * This must be filled with -1 if network is not connected.
+ * This must be filled with 0 if network is not connected.
*/
uint32_t uplinkCapacityKbps;
@@ -427,7 +427,8 @@
* This bandwidth estimate shall be the estimated maximum sustainable link bandwidth
* (as would be measured at the Upper PDCP or SNDCP SAP). This is valid only
* in if device is connected to both primary and secodary in dual connected
- * mode. This must be filled with -1 if secondary is not connected.
+ * mode. This must be filled with 0 if secondary is not connected or if
+ * modem does not support this feature.
*/
uint32_t secondaryDownlinkCapacityKbps;
@@ -436,12 +437,13 @@
* This bandwidth estimate shall be the estimated
* maximum sustainable link bandwidth (as would be measured at the Upper PDCP or SNDCP SAP).
* This is valid only in if device is connected to both primary and secodary in dual connected
- * mode.This must be filled with -1 if secondary is not connected.
+ * mode.This must be filled with 0 if secondary is not connected or if modem
+ * does not support this feature.
*/
uint32_t secondaryUplinkCapacityKbps;
};
-enum DataThrottlingAction : int32_t {
+enum DataThrottlingAction : int8_t {
/* Clear all existing data throttling. */
NO_DATA_THROTTLING = 0,
@@ -579,9 +581,9 @@
*
* Reference: 3GPP TS 138.214 section 5.2.2.1.
*
- * Range [0, 15], INT_MAX means invalid/unreported.
+ * Range [0, 15], 0xFF means invalid/unreported.
*/
- vec<uint32_t> csiCqiReport;
+ vec<uint8_t> csiCqiReport;
};
/**
@@ -738,22 +740,19 @@
EutranRegistrationInfo eutranInfo;
- struct NgranRegistrationInfo {
- /**
- * Network capabilities for voice over PS services. This info is valid only on NR
- * network and must be present when the device is camped on NR. VopsInfo must be
- * empty when the device is not camped on NR.
- */
- NrVopsInfo nrVopsInfo;
- } ngranInfo;
+ /**
+ * Network capabilities for voice over PS services. This info is valid only on NR
+ * network and must be present when the device is camped on NR. VopsInfo must be
+ * empty when the device is not camped on NR.
+ */
+ NrVopsInfo ngranNrVopsInfo;
- struct GeranRegistrationInfo {
- /**
- * True if the dual transfer mode is supported.
- * Refer to 3GPP TS 44.108 section 3.4.25.3
- */
- bool dtmSupported;
- } geranInfo;
+ /**
+ * True if the dual transfer mode is supported.
+ * Refer to 3GPP TS 44.108 section 3.4.25.3
+ */
+ bool geranDtmSupported;
+
} accessTechnologySpecificInfo;
};
@@ -780,10 +779,10 @@
int32_t uplinkChannelNumber;
/** Downlink cell bandwidth, in kHz */
- int32_t cellBandwidthDownlink;
+ int32_t cellBandwidthDownlinkKhz;
/** Uplink cell bandwidth, in kHz */
- int32_t cellBandwidthUplink;
+ int32_t cellBandwidthUplinkKhz;
/**
* A list of data calls mapped to this physical channel. The context id must match the cid of
@@ -1069,7 +1068,7 @@
SscMode value;
};
-enum SliceStatus : int32_t {
+enum SliceStatus : int8_t {
UNKNOWN,
CONFIGURED, // Configured but not allowed or rejected yet
ALLOWED, // Allowed to be used
@@ -1082,7 +1081,7 @@
* Enum representing session and service continuity mode as defined in
* 3GPP TS 23.501.
*/
-enum SscMode : int32_t {
+enum SscMode : int8_t {
MODE_1 = 1,
MODE_2 = 2,
MODE_3 = 3,
@@ -1091,7 +1090,7 @@
/**
* Public key type from carrier certificate.
*/
-enum PublicKeyType : int32_t {
+enum PublicKeyType : int8_t {
EPDG = 1, // Key type to be used for ePDG
WLAN = 2, // Key type to be used for WLAN
};
@@ -1200,7 +1199,7 @@
* chunk of phonebook data, means this is a last indication with the left
* data.
*/
-enum PbReceivedStatus : int32_t {
+enum PbReceivedStatus : int8_t {
PB_RECEIVED_OK = 1,
PB_RECEIVED_ERROR = 2,
PB_RECEIVED_ABORT = 3,
diff --git a/radio/1.6/vts/functional/Android.bp b/radio/1.6/vts/functional/Android.bp
index 65b0dd0..2bc6af3 100644
--- a/radio/1.6/vts/functional/Android.bp
+++ b/radio/1.6/vts/functional/Android.bp
@@ -28,6 +28,7 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"radio_hidl_hal_api.cpp",
+ "radio_hidl_hal_misc.cpp",
"radio_hidl_hal_test.cpp",
"radio_response.cpp",
"radio_indication.cpp",
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index a9c21ff..f343707 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -204,10 +204,10 @@
}
/*
- * Test IRadio_1_6.sendSMSExpectMore() for the response returned.
+ * Test IRadio_1_6.sendSmsExpectMore() for the response returned.
*/
-TEST_P(RadioHidlTest_v1_6, sendSMSExpectMore_1_6) {
- LOG(DEBUG) << "sendSMSExpectMore";
+TEST_P(RadioHidlTest_v1_6, sendSmsExpectMore_1_6) {
+ LOG(DEBUG) << "sendSmsExpectMore";
serial = GetRandomSerialNumber();
GsmSmsMessage msg;
msg.smscPdu = "";
@@ -227,7 +227,7 @@
::android::hardware::radio::V1_6::RadioError::SIM_ABSENT},
CHECK_GENERAL_ERROR));
}
- LOG(DEBUG) << "sendSMSExpectMore finished";
+ LOG(DEBUG) << "sendSmsExpectMore finished";
}
/*
@@ -369,7 +369,7 @@
EXPECT_EQ(std::cv_status::no_timeout, wait());
EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
- if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ if (getRadioHalCapabilities()) {
ASSERT_TRUE(CheckAnyOfErrors(
radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
@@ -378,6 +378,7 @@
CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
+ ::android::hardware::radio::V1_6::RadioError::INVALID_STATE,
::android::hardware::radio::V1_6::RadioError::NONE}));
}
}
@@ -394,7 +395,7 @@
EXPECT_EQ(std::cv_status::no_timeout, wait());
EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
- if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ if (getRadioHalCapabilities()) {
ASSERT_TRUE(CheckAnyOfErrors(
radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
@@ -420,7 +421,7 @@
EXPECT_EQ(std::cv_status::no_timeout, wait());
EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
- if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ if (getRadioHalCapabilities()) {
ASSERT_TRUE(CheckAnyOfErrors(
radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
@@ -440,7 +441,7 @@
EXPECT_EQ(std::cv_status::no_timeout, wait());
EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
- if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ if (getRadioHalCapabilities()) {
ASSERT_TRUE(CheckAnyOfErrors(
radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
@@ -460,7 +461,7 @@
EXPECT_EQ(std::cv_status::no_timeout, wait());
EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
- if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ if (getRadioHalCapabilities()) {
ASSERT_TRUE(CheckAnyOfErrors(
radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
@@ -479,7 +480,7 @@
EXPECT_EQ(std::cv_status::no_timeout, wait());
EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
- if (getRadioHalCapabilities().modemReducedFeatureSet1) {
+ if (getRadioHalCapabilities()) {
ASSERT_TRUE(CheckAnyOfErrors(
radioRsp_v1_6->rspInfo.error,
{::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp b/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
new file mode 100644
index 0000000..4222441
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <regex>
+
+#include <android-base/logging.h>
+#include <radio_hidl_hal_utils_v1_6.h>
+
+/*
+ * Test IRadio.getAvailableNetworks() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getAvailableNetworks) {
+ LOG(DEBUG) << "getAvailableNetworks";
+ serial = GetRandomSerialNumber();
+
+ radio_v1_6->getAvailableNetworks(serial);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
+ ASSERT_TRUE(radioRsp_v1_6->rspInfo_v1_0.type == RadioResponseType::SOLICITED ||
+ radioRsp_v1_6->rspInfo_v1_0.type == RadioResponseType::SOLICITED_ACK_EXP);
+
+ if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_6->rspInfo_v1_0.error,
+ {::android::hardware::radio::V1_0::RadioError::NONE,
+ ::android::hardware::radio::V1_0::RadioError::CANCELLED,
+ ::android::hardware::radio::V1_0::RadioError::DEVICE_IN_USE,
+ ::android::hardware::radio::V1_0::RadioError::MODEM_ERR,
+ ::android::hardware::radio::V1_0::RadioError::OPERATION_NOT_ALLOWED},
+ CHECK_GENERAL_ERROR));
+ } else if (radioRsp_v1_6->rspInfo_v1_0.error ==
+ ::android::hardware::radio::V1_0::RadioError::NONE) {
+ static const std::regex kOperatorNumericRe("^[0-9]{5,6}$");
+ for (OperatorInfo info : radioRsp_v1_6->networkInfos) {
+ if (info.operatorNumeric != nullptr) {
+ ASSERT_TRUE(
+ std::regex_match(std::string(info.operatorNumeric), kOperatorNumericRe));
+ }
+ }
+ }
+
+ LOG(DEBUG) << "getAvailableNetworks finished";
+}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
index 6255f66..5d514a0 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
@@ -86,16 +86,14 @@
* disabled.
* <p/>
* Typical usage within VTS:
- * if (getRadioHalCapabilities().modemReducedFeatureSet) return;
+ * if (getRadioHalCapabilities()) return;
*/
-HalDeviceCapabilities RadioHidlTest_v1_6::getRadioHalCapabilities() {
+bool RadioHidlTest_v1_6::getRadioHalCapabilities() {
sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig_v1_3 =
::android::hardware::radio::config::V1_3::IRadioConfig::getService();
if (radioConfig_v1_3.get() == nullptr) {
// If v1_3 isn't present, the values are initialized to false
- HalDeviceCapabilities radioHalCapabilities;
- memset(&radioHalCapabilities, 0, sizeof(radioHalCapabilities));
- return radioHalCapabilities;
+ return false;
} else {
// Get radioHalDeviceCapabilities from the radio config
sp<RadioConfigResponse> radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
@@ -104,6 +102,6 @@
radioConfig_v1_3->getHalDeviceCapabilities(serial);
EXPECT_EQ(std::cv_status::no_timeout, wait());
- return radioConfigRsp->halDeviceCapabilities;
+ return radioConfigRsp->modemReducedFeatureSet1;
}
}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
index 4fc17e5..3185f98 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
+++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
@@ -38,14 +38,12 @@
using namespace ::android::hardware::radio::V1_2;
using namespace ::android::hardware::radio::V1_1;
using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config::V1_3;
using ::android::sp;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
-using ::android::hardware::radio::config::V1_3::HalDeviceCapabilities;
#define MODEM_EMERGENCY_CALL_ESTABLISH_TIME 3
#define MODEM_EMERGENCY_CALL_DISCONNECT_TIME 3
@@ -62,6 +60,7 @@
public:
hidl_vec<RadioBandMode> radioBandModes;
+ hidl_vec<OperatorInfo> networkInfos;
::android::hardware::radio::V1_0::RadioResponseInfo rspInfo_v1_0;
::android::hardware::radio::V1_6::RadioResponseInfo rspInfo;
@@ -762,7 +761,7 @@
const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
const SendSmsResult& sms);
- Return<void> sendSMSExpectMoreResponse_1_6(
+ Return<void> sendSmsExpectMoreResponse_1_6(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
const SendSmsResult& sms);
@@ -1115,7 +1114,7 @@
public:
virtual void SetUp() override;
- HalDeviceCapabilities getRadioHalCapabilities();
+ bool getRadioHalCapabilities();
/* radio service handle */
sp<::android::hardware::radio::V1_6::IRadio> radio_v1_6;
diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp
index 2b6d1bb..6e7b86f 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -274,8 +274,11 @@
}
Return<void> RadioResponse_v1_6::getAvailableNetworksResponse(
- const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
- const ::android::hardware::hidl_vec<OperatorInfo>& /*networkInfos*/) {
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+ const ::android::hardware::hidl_vec<OperatorInfo>& networkInfos) {
+ rspInfo_v1_0 = info;
+ this->networkInfos = networkInfos;
+ parent_v1_6.notify(info.serial);
return Void();
}
@@ -1090,7 +1093,7 @@
return Void();
}
-Return<void> RadioResponse_v1_6::sendSMSExpectMoreResponse_1_6(
+Return<void> RadioResponse_v1_6::sendSmsExpectMoreResponse_1_6(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
const SendSmsResult& sms) {
rspInfo = info;
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
index cc5944d..dc0d82c 100644
--- a/radio/config/1.3/Android.bp
+++ b/radio/config/1.3/Android.bp
@@ -13,7 +13,6 @@
name: "android.hardware.radio.config@1.3",
root: "android.hardware",
srcs: [
- "types.hal",
"IRadioConfig.hal",
"IRadioConfigResponse.hal",
],
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
index 863754f..f6aee31 100644
--- a/radio/config/1.3/IRadioConfigResponse.hal
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -18,7 +18,6 @@
import android.hardware.radio@1.6::RadioResponseInfo;
import @1.2::IRadioConfigResponse;
-import HalDeviceCapabilities;
/**
* Interface declaring response functions to solicited radio config requests.
@@ -26,8 +25,20 @@
interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
/**
* @param info Response info struct containing response type, serial no. and error
- * @param capabilities Capabilities struct containing the capabilities of the
- * device related to the Radio HAL
+ * @param modemReducedFeatureSet1 True indicates that the modem does NOT support the following
+ * features.
+ * - Providing either
+ * android.hardware.radio@1.6::LinkCapacityEstimate:secondaryDownlinkCapacityKbps
+ * or android.hardware.radio@1.6::LinkCapacityEstimate:secondaryUplinkCapacityKbps
+ * when given from
+ * android.hardware.radio@1.6::RadioIndication:currentLinkCapacityEstimate
+ * - Calling android.hardware.radio@1.6::IRadio.setNrDualConnectivityState
+ * or querying android.hardware.radio@1.6::IRadio.isNrDualConnectivityEnabled
+ * - Requesting android.hardware.radio@1.6::IRadio.setDataThrottling()
+ * - Providing android.hardware.radio@1.6::SlicingConfig through
+ * android.hardware.radio@1.6::getSlicingConfig()
+ * - Providing android.hardware.radio@1.6::PhysicalChannelConfig through
+ * android.hardware.radio@1.6::IRadioIndication.currentPhysicalChannelConfigs_1_6()
*
* Valid errors returned:
* RadioError:NONE
@@ -35,5 +46,5 @@
* RadioError:INTERNAL_ERR
*/
oneway getHalDeviceCapabilitiesResponse(RadioResponseInfo info,
- HalDeviceCapabilities capabilities);
+ bool modemReducedFeatureSet1);
};
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
index 895ae08..7440054 100644
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
@@ -29,7 +29,6 @@
#include <android/hardware/radio/config/1.2/types.h>
#include <android/hardware/radio/config/1.3/IRadioConfig.h>
#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <android/hardware/radio/config/1.3/types.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
@@ -47,7 +46,6 @@
using ::android::hardware::radio::config::V1_1::ModemsConfig;
using ::android::hardware::radio::config::V1_1::PhoneCapability;
using ::android::hardware::radio::config::V1_2::SimSlotStatus;
-using ::android::hardware::radio::config::V1_3::HalDeviceCapabilities;
using ::android::hardware::radio::config::V1_3::IRadioConfig;
using ::android::hardware::radio::V1_0::RadioResponseInfo;
@@ -63,7 +61,7 @@
public:
RadioResponseInfo rspInfo;
PhoneCapability phoneCap;
- HalDeviceCapabilities halDeviceCapabilities;
+ bool modemReducedFeatureSet1;
RadioConfigResponse(RadioResponseWaiter& parent);
virtual ~RadioConfigResponse() = default;
@@ -91,7 +89,7 @@
Return<void> getHalDeviceCapabilitiesResponse(
const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
- const HalDeviceCapabilities& halDeviceCapabilities);
+ bool modemReducedFeatureSet1);
};
/* Callback class for radio config indication */
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
index 11e3cce..6b2d27c 100644
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -65,7 +65,7 @@
Return<void> RadioConfigResponse::getHalDeviceCapabilitiesResponse(
const ::android::hardware::radio::V1_6::RadioResponseInfo& /* info */,
- const ::android::hardware::radio::config::V1_3::HalDeviceCapabilities& capabilities) {
- halDeviceCapabilities = capabilities;
+ bool modemReducedFeatures) {
+ modemReducedFeatureSet1 = modemReducedFeatures;
return Void();
}
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 6766d99..0c11f3b 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -26,6 +26,7 @@
vndk: {
enabled: true,
},
+ apps_enabled: false,
},
rust: {
enabled: true,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
index 69ec4ce..b05a0f3 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
@@ -118,6 +118,7 @@
MISSING_ISSUER_SUBJECT = -82,
INVALID_ISSUER_SUBJECT = -83,
BOOT_LEVEL_EXCEEDED = -84,
+ HARDWARE_NOT_YET_AVAILABLE = -85,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
UNKNOWN_ERROR = -1000,
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
index 4f6fb28..bf30999 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -33,7 +33,7 @@
package android.hardware.security.keymint;
/* @hide */
-@VintfStability
+@SensitiveData @VintfStability
interface IKeyMintDevice {
android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
void addRngEntropy(in byte[] data);
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
index 5ac2b4a..4ab4ffe 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -33,7 +33,7 @@
package android.hardware.security.keymint;
/* @hide */
-@VintfStability
+@SensitiveData @VintfStability
interface IKeyMintOperation {
void updateAad(in byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timeStampToken);
byte[] update(in byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timeStampToken);
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 88c479c..f566462 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -35,6 +35,7 @@
/* @hide */
@VintfStability
interface IRemotelyProvisionedComponent {
+ android.hardware.security.keymint.RpcHardwareInfo getHardwareInfo();
byte[] generateEcdsaP256KeyPair(in boolean testMode, out android.hardware.security.keymint.MacedPublicKey macedPublicKey);
byte[] generateCertificateRequest(in boolean testMode, in android.hardware.security.keymint.MacedPublicKey[] keysToSign, in byte[] endpointEncryptionCertChain, in byte[] challenge, out android.hardware.security.keymint.DeviceInfo deviceInfo, out android.hardware.security.keymint.ProtectedData protectedData);
const int STATUS_FAILED = 1;
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl
new file mode 100644
index 0000000..06bce19
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.security.keymint;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable RpcHardwareInfo {
+ int versionNumber;
+ @utf8InCpp String rpcAuthorName;
+ int supportedEekCurve = 0;
+ const int CURVE_NONE = 0;
+ const int CURVE_P256 = 1;
+ const int CURVE_25519 = 2;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
index 0e2c5f2..137e6b6 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -107,6 +107,7 @@
MISSING_ISSUER_SUBJECT = -82,
INVALID_ISSUER_SUBJECT = -83,
BOOT_LEVEL_EXCEEDED = -84,
+ HARDWARE_NOT_YET_AVAILABLE = -85,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 17aab25..1c503c2 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -214,6 +214,7 @@
* @hide
*/
@VintfStability
+@SensitiveData
interface IKeyMintDevice {
const int AUTH_TOKEN_MAC_LENGTH = 32;
@@ -321,8 +322,8 @@
* but `attestationKey` is non-null, the IKeyMintDevice must return
* ErrorCode::INVALID_ARGUMENT. If the provided AttestationKey does not contain a key
* blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the IKeyMintDevice must
- * return ErrorCode::INVALID_PURPOSE. If the provided AttestationKey has an empty issuer
- * subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+ * return ErrorCode::INCOMPATIBLE_PURPOSE. If the provided AttestationKey has an empty
+ * issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
*
* @return The result of key creation. See KeyCreationResult.aidl.
*/
@@ -360,8 +361,8 @@
* but `attestationKey` is non-null, the IKeyMintDevice must return
* ErrorCode::INVALID_ARGUMENT. If the provided AttestationKey does not contain a key
* blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the IKeyMintDevice must
- * return ErrorCode::INVALID_PURPOSE. If the provided AttestationKey has an empty issuer
- * subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+ * return ErrorCode::INCOMPATIBLE_PURPOSE. If the provided AttestationKey has an empty
+ * issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
*
* @return The result of key creation. See KeyCreationResult.aidl.
*/
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index 5ad54cd..d2a993f 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -22,6 +22,7 @@
/** @hide */
@VintfStability
+@SensitiveData
interface IKeyMintOperation {
/**
* Provides additional authentication data (AAD) to a cryptographic operation begun with
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 1ae6762..b6285d9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -19,6 +19,7 @@
import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.MacedPublicKey;
import android.hardware.security.keymint.ProtectedData;
+import android.hardware.security.keymint.RpcHardwareInfo;
/**
* An IRemotelyProvisionedComponent is a secure-side component for which certificates can be
@@ -121,6 +122,12 @@
const int STATUS_INVALID_EEK = 5;
/**
+ * @return info which contains information about the underlying IRemotelyProvisionedComponent
+ * hardware, such as version number, component name, author name, and supported curve.
+ */
+ RpcHardwareInfo getHardwareInfo();
+
+ /**
* generateKeyPair generates a new ECDSA P-256 key pair that can be certified. Note that this
* method only generates ECDSA P-256 key pairs, but the interface can be extended to add methods
* for generating keys for other algorithms, if necessary.
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index f3c5477..c2e21b6 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -60,29 +60,36 @@
* `attestationKey` parameter of `generateKey()`, `importKey()` or `importWrappedKey()`), and in
* the non-attestaion case, whether the key can self-sign.
*
- * 1. Attestation with factory key. If Tag::ATTESTATION_CHALLENGE is provided and the
- * `attestationKey` parameter on the generate/import call is null, the returned certificate
- * chain must contain an attestation certificate signed with a factory-provisioned
- * attestation key, and the full certificate chain for that factory-provisioned attestation
- * key.
+ * 1. Asymmetric key attestation with factory key. If Tag::ATTESTATION_CHALLENGE is provided
+ * and the `attestationKey` parameter on the generate/import call is null, the returned
+ * certificate chain must contain an attestation certificate signed with a factory-
+ * provisioned attestation key, and the full certificate chain for that factory-provisioned
+ * attestation key. Tag::ATTESTATION_APPLICATION_ID must also be provided when the
+ * ATTESTATION_CHALLENGE is provided, otherwise ATTESTATION_APPLICATION_ID_MISSING will be
+ * returned.
*
- * 2. Attestation with caller-provided key. If Tag::ATTESTATION_CHALLENGE is provided and the
- * `attestationKey` parameter on the generat/import call is non-null and contains the key
- * blob of a key with KeyPurpose::ATTEST_KEY, the returned certificate chain must contain
- * only an attestation certificate signed with the specified key. The caller must know the
- * certificate chain for the provided key.
+ * 2. Asymmetric key attestation with caller-provided key. If Tag::ATTESTATION_CHALLENGE is
+ * provided and the `attestationKey` parameter on the generat/import call is non-null and
+ * contains the key blob of a key with KeyPurpose::ATTEST_KEY, the returned certificate
+ * chain must contain only an attestation certificate signed with the specified key. The
+ * caller must know the certificate chain for the provided key. Tag::
+ * ATTESTATION_APPLICATION_ID must also be provided when the ATTESTATION_CHALLENGE is
+ * provided, otherwise ATTESTATION_APPLICATION_ID_MISSING will be returned.
*
- * 3. Non-attestation with signing key. If Tag::ATTESTATION_CHALLENGE is not provided and the
- * generated/imported key has KeyPurpose::SIGN, then the returned certificate chain must
- * contain only a single self-signed certificate with no attestation extension.
+ * 3. Asymmetric key non-attestation with signing key. If Tag::ATTESTATION_CHALLENGE is not
+ * provided and the generated/imported key has KeyPurpose::SIGN, then the returned
+ * certificate chain must contain only a single self-signed certificate with no attestation
+ * extension. Tag::ATTESTATION_APPLICATION_ID will be ignored if provided.
*
- * 4. Non-attestation with non-signing key. If TAG::ATTESTATION_CHALLENGE is not provided and
- * the generated/imported key does not have KeyPurpose::SIGN, then the returned certificate
- * chain must contain only a single certificate with an empty signature and no attestation
- * extension.
+ * 4. Asymmetric key non-attestation with non-signing key. If TAG::ATTESTATION_CHALLENGE is
+ * not provided and the generated/imported key does not have KeyPurpose::SIGN, then the
+ * returned certificate chain must contain only a single certificate with an empty signature
+ * and no attestation extension. Tag::ATTESTATION_APPLICATION_ID will be ignored if
+ * provided.
*
- * 5. Symmetric key. If the generated/imported key is symmetric, the certificate chain must be
- * empty.
+ * 5. Symmetric key. If the generated/imported key is symmetric, the certificate chain must
+ * return empty, any Tag::ATTESTATION_CHALLENGE or Tag::ATTESTATION_APPLICATION_ID inputs,
+ * if provided, are ignored.
*/
Certificate[] certificateChain;
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
new file mode 100644
index 0000000..d297f87
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * RpcHardwareInfo is the hardware information returned by calling RemotelyProvisionedComponent
+ * getHardwareInfo()
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable RpcHardwareInfo {
+ const int CURVE_NONE = 0;
+ const int CURVE_P256 = 1;
+ const int CURVE_25519 = 2;
+
+ /**
+ * Implementation version of the remotely provisioned component hardware. The version number is
+ * implementation defined, and not necessarily globally meaningful. The version is used to
+ * distinguish between different versions of a given implementation.
+ */
+ int versionNumber;
+
+ /**
+ * rpcAuthorName is the name of the author of the IRemotelyProvisionedComponent implementation
+ * (organization name, not individual). This name is implementation defined, so it can be used
+ * to distinguish between different implementations from the same author.
+ */
+ @utf8InCpp String rpcAuthorName;
+
+ /**
+ * supportedEekCurve returns an int representing which curve is supported for validating
+ * signatures over the Endpoint Encryption Key certificate chain and for using the corresponding
+ * signed encryption key in ECDH. Only one curve should be supported, with preference for 25519
+ * if it's available. These values are defined as constants above.
+ *
+ * CURVE_NONE is made the default to help ensure that an implementor doesn't accidentally forget
+ * to provide the correct information here, as the VTS tests will check to make certain that
+ * a passing implementation does not provide CURVE_NONE.
+ */
+ int supportedEekCurve = CURVE_NONE;
+}
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
index ebdc9b7..230534c 100644
--- a/security/keymint/aidl/default/Android.bp
+++ b/security/keymint/aidl/default/Android.bp
@@ -33,7 +33,6 @@
"libkeymint",
"liblog",
"libpuresoftkeymasterdevice",
- "libremote_provisioner",
"libutils",
],
srcs: [
@@ -51,28 +50,3 @@
vendor: true,
src: "android.hardware.hardware_keystore.xml",
}
-
-cc_library {
- name: "libremote_provisioner",
- vendor_available: true,
- static_libs: [
- "libkeymint_remote_prov_support",
- ],
- shared_libs: [
- "android.hardware.security.keymint-V1-ndk_platform",
- "libbinder_ndk",
- "libcppbor_external",
- "libcppcose",
- "libcrypto",
- "libkeymaster_portable",
- "libkeymint",
- "liblog",
- "libpuresoftkeymasterdevice",
- ],
- export_include_dirs: [
- ".",
- ],
- srcs: [
- "RemotelyProvisionedComponent.cpp",
- ],
-}
diff --git a/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp b/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
deleted file mode 100644
index 5b02729..0000000
--- a/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RemotelyProvisionedComponent.h"
-
-#include <assert.h>
-#include <variant>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-#include <KeyMintUtils.h>
-#include <cppcose/cppcose.h>
-#include <keymaster/keymaster_configuration.h>
-#include <remote_prov/remote_prov_utils.h>
-
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/rand.h>
-#include <openssl/x509.h>
-
-namespace aidl::android::hardware::security::keymint {
-
-using ::std::string;
-using ::std::tuple;
-using ::std::unique_ptr;
-using ::std::variant;
-using ::std::vector;
-using bytevec = ::std::vector<uint8_t>;
-
-using namespace cppcose;
-using namespace keymaster;
-
-namespace {
-
-// Hard-coded set of acceptable public keys that can act as roots of EEK chains.
-inline const vector<bytevec> kAuthorizedEekRoots = {
- // TODO(drysdale): replace this random value with real root pubkey(s).
- {0x5c, 0xea, 0x4b, 0xd2, 0x31, 0x27, 0x15, 0x5e, 0x62, 0x94, 0x70,
- 0x53, 0x94, 0x43, 0x0f, 0x9a, 0x89, 0xd5, 0xc5, 0x0f, 0x82, 0x9b,
- 0xcd, 0x10, 0xe0, 0x79, 0xef, 0xf3, 0xfa, 0x40, 0xeb, 0x0a},
-};
-
-constexpr auto STATUS_FAILED = RemotelyProvisionedComponent::STATUS_FAILED;
-constexpr auto STATUS_INVALID_EEK = RemotelyProvisionedComponent::STATUS_INVALID_EEK;
-constexpr auto STATUS_INVALID_MAC = RemotelyProvisionedComponent::STATUS_INVALID_MAC;
-constexpr uint32_t kAffinePointLength = 32;
-struct AStatusDeleter {
- void operator()(AStatus* p) { AStatus_delete(p); }
-};
-
-// TODO(swillden): Remove the dependency on AStatus stuff. The COSE lib should use something like
-// StatusOr, but it shouldn't depend on AStatus.
-class Status {
- public:
- Status() {}
- Status(int32_t errCode, const std::string& errMsg)
- : status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, errMsg.c_str())) {}
- explicit Status(const std::string& errMsg)
- : status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, errMsg.c_str())) {}
- Status(AStatus* status) : status_(status) {}
- Status(Status&&) = default;
- Status(const Status&) = delete;
-
- operator ::ndk::ScopedAStatus() && { return ndk::ScopedAStatus(status_.release()); }
-
- bool isOk() { return !status_; }
-
- // Don't call getMessage() unless isOk() returns false;
- const char* getMessage() const { return AStatus_getMessage(status_.get()); }
-
- private:
- std::unique_ptr<AStatus, AStatusDeleter> status_;
-};
-
-template <typename T>
-class StatusOr {
- public:
- StatusOr(AStatus* status) : status_(status) {}
- StatusOr(Status status) : status_(std::move(status)) {}
- StatusOr(T val) : value_(std::move(val)) {}
-
- bool isOk() { return status_.isOk(); }
-
- T* operator->() & {
- assert(isOk());
- return &value_.value();
- }
- T& operator*() & {
- assert(isOk());
- return value_.value();
- }
- T&& operator*() && {
- assert(isOk());
- return std::move(value_).value();
- }
-
- const char* getMessage() const {
- assert(!isOk());
- return status_.getMessage();
- }
-
- Status moveError() {
- assert(!isOk());
- return std::move(status_);
- }
-
- T moveValue() { return std::move(value_).value(); }
-
- private:
- Status status_;
- std::optional<T> value_;
-};
-
-StatusOr<std::pair<bytevec /* EEK pub */, bytevec /* EEK ID */>> validateAndExtractEekPubAndId(
- bool testMode, const bytevec& endpointEncryptionCertChain) {
- auto [item, newPos, errMsg] = cppbor::parse(endpointEncryptionCertChain);
-
- if (!item || !item->asArray()) {
- return Status("Error parsing EEK chain" + errMsg);
- }
-
- const cppbor::Array* certArr = item->asArray();
- bytevec lastPubKey;
- for (int i = 0; i < certArr->size(); ++i) {
- auto cosePubKey = verifyAndParseCoseSign1(testMode, certArr->get(i)->asArray(),
- std::move(lastPubKey), bytevec{} /* AAD */);
- if (!cosePubKey) {
- return Status(STATUS_INVALID_EEK,
- "Failed to validate EEK chain: " + cosePubKey.moveMessage());
- }
- lastPubKey = *std::move(cosePubKey);
-
- // In prod mode the first pubkey should match a well-known Google public key.
- if (!testMode && i == 0 &&
- std::find(kAuthorizedEekRoots.begin(), kAuthorizedEekRoots.end(), lastPubKey) ==
- kAuthorizedEekRoots.end()) {
- return Status(STATUS_INVALID_EEK, "Unrecognized root of EEK chain");
- }
- }
-
- auto eek = CoseKey::parseX25519(lastPubKey, true /* requireKid */);
- if (!eek) return Status(STATUS_INVALID_EEK, "Failed to get EEK: " + eek.moveMessage());
-
- return std::make_pair(eek->getBstrValue(CoseKey::PUBKEY_X).value(),
- eek->getBstrValue(CoseKey::KEY_ID).value());
-}
-
-StatusOr<bytevec /* pubkeys */> validateAndExtractPubkeys(bool testMode,
- const vector<MacedPublicKey>& keysToSign,
- const bytevec& macKey) {
- auto pubKeysToMac = cppbor::Array();
- for (auto& keyToSign : keysToSign) {
- auto [macedKeyItem, _, coseMacErrMsg] = cppbor::parse(keyToSign.macedKey);
- if (!macedKeyItem || !macedKeyItem->asArray() ||
- macedKeyItem->asArray()->size() != kCoseMac0EntryCount) {
- return Status("Invalid COSE_Mac0 structure");
- }
-
- auto protectedParms = macedKeyItem->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
- auto unprotectedParms = macedKeyItem->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
- auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr();
- auto tag = macedKeyItem->asArray()->get(kCoseMac0Tag)->asBstr();
- if (!protectedParms || !unprotectedParms || !payload || !tag) {
- return Status("Invalid COSE_Mac0 contents");
- }
-
- auto [protectedMap, __, errMsg] = cppbor::parse(protectedParms);
- if (!protectedMap || !protectedMap->asMap()) {
- return Status("Invalid Mac0 protected: " + errMsg);
- }
- auto& algo = protectedMap->asMap()->get(ALGORITHM);
- if (!algo || !algo->asInt() || algo->asInt()->value() != HMAC_256) {
- return Status("Unsupported Mac0 algorithm");
- }
-
- auto pubKey = CoseKey::parse(payload->value(), EC2, ES256, P256);
- if (!pubKey) return Status(pubKey.moveMessage());
-
- bool testKey = static_cast<bool>(pubKey->getMap().get(CoseKey::TEST_KEY));
- if (testMode && !testKey) {
- return Status(BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST,
- "Production key in test request");
- } else if (!testMode && testKey) {
- return Status(BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST,
- "Test key in production request");
- }
-
- auto macTag = generateCoseMac0Mac(macKey, {} /* external_aad */, payload->value());
- if (!macTag) return Status(STATUS_INVALID_MAC, macTag.moveMessage());
- if (macTag->size() != tag->value().size() ||
- CRYPTO_memcmp(macTag->data(), tag->value().data(), macTag->size()) != 0) {
- return Status(STATUS_INVALID_MAC, "MAC tag mismatch");
- }
-
- pubKeysToMac.add(pubKey->moveMap());
- }
-
- return pubKeysToMac.encode();
-}
-
-StatusOr<std::pair<bytevec, bytevec>> buildCosePublicKeyFromKmCert(
- const keymaster_blob_t* km_cert) {
- if (km_cert == nullptr) {
- return Status(STATUS_FAILED, "km_cert is a nullptr");
- }
- const uint8_t* temp = km_cert->data;
- X509* cert = d2i_X509(NULL, &temp, km_cert->data_length);
- if (cert == nullptr) {
- return Status(STATUS_FAILED, "d2i_X509 returned null when attempting to get the cert.");
- }
- EVP_PKEY* pubKey = X509_get_pubkey(cert);
- if (pubKey == nullptr) {
- return Status(STATUS_FAILED, "Boringssl failed to get the public key from the cert");
- }
- EC_KEY* ecKey = EVP_PKEY_get0_EC_KEY(pubKey);
- if (ecKey == nullptr) {
- return Status(STATUS_FAILED,
- "The key in the certificate returned from GenerateKey is not "
- "an EC key.");
- }
- const EC_POINT* jacobian_coords = EC_KEY_get0_public_key(ecKey);
- BIGNUM x;
- BIGNUM y;
- BN_CTX* ctx = BN_CTX_new();
- if (ctx == nullptr) {
- return Status(STATUS_FAILED, "Memory allocation failure for BN_CTX");
- }
- if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ecKey), jacobian_coords, &x, &y,
- ctx)) {
- return Status(STATUS_FAILED, "Failed to get affine coordinates");
- }
- bytevec x_bytestring(kAffinePointLength);
- bytevec y_bytestring(kAffinePointLength);
- if (BN_bn2binpad(&x, x_bytestring.data(), kAffinePointLength) != kAffinePointLength) {
- return Status(STATUS_FAILED, "Wrote incorrect number of bytes for x coordinate");
- }
- if (BN_bn2binpad(&y, y_bytestring.data(), kAffinePointLength) != kAffinePointLength) {
- return Status(STATUS_FAILED, "Wrote incorrect number of bytes for y coordinate");
- }
- BN_CTX_free(ctx);
- return std::make_pair(x_bytestring, y_bytestring);
-}
-
-cppbor::Array buildCertReqRecipients(const bytevec& pubkey, const bytevec& kid) {
- return cppbor::Array() // Array of recipients
- .add(cppbor::Array() // Recipient
- .add(cppbor::Map() // Protected
- .add(ALGORITHM, ECDH_ES_HKDF_256)
- .canonicalize()
- .encode())
- .add(cppbor::Map() // Unprotected
- .add(COSE_KEY, cppbor::Map()
- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
- .add(CoseKey::CURVE, cppcose::X25519)
- .add(CoseKey::PUBKEY_X, pubkey)
- .canonicalize())
- .add(KEY_ID, kid)
- .canonicalize())
- .add(cppbor::Null())); // No ciphertext
-}
-
-static keymaster_key_param_t kKeyMintEcdsaP256Params[] = {
- Authorization(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY),
- Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC), Authorization(TAG_KEY_SIZE, 256),
- Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256),
- Authorization(TAG_EC_CURVE, KM_EC_CURVE_P_256), Authorization(TAG_NO_AUTH_REQUIRED),
- // The certificate generated by KM will be discarded, these values don't matter.
- Authorization(TAG_CERTIFICATE_NOT_BEFORE, 0), Authorization(TAG_CERTIFICATE_NOT_AFTER, 0)};
-
-} // namespace
-
-RemotelyProvisionedComponent::RemotelyProvisionedComponent(
- std::shared_ptr<keymint::AndroidKeyMintDevice> keymint) {
- std::tie(devicePrivKey_, bcc_) = generateBcc();
- impl_ = keymint->getKeymasterImpl();
-}
-
-RemotelyProvisionedComponent::~RemotelyProvisionedComponent() {}
-
-ScopedAStatus RemotelyProvisionedComponent::generateEcdsaP256KeyPair(bool testMode,
- MacedPublicKey* macedPublicKey,
- bytevec* privateKeyHandle) {
- // TODO(jbires): The following should move from ->GenerateKey to ->GenerateRKPKey and everything
- // after the GenerateKey call should basically be moved into that new function call
- // as well once the issue with libcppbor in system/keymaster is sorted out
- GenerateKeyRequest request(impl_->message_version());
- request.key_description.Reinitialize(kKeyMintEcdsaP256Params,
- array_length(kKeyMintEcdsaP256Params));
- GenerateKeyResponse response(impl_->message_version());
- impl_->GenerateKey(request, &response);
- if (response.error != KM_ERROR_OK) {
- return km_utils::kmError2ScopedAStatus(response.error);
- }
-
- if (response.certificate_chain.entry_count != 1) {
- // Error: Need the single non-signed certificate with the public key in it.
- return Status(STATUS_FAILED,
- "Expected to receive a single certificate from GenerateKey. Instead got: " +
- std::to_string(response.certificate_chain.entry_count));
- }
- auto affineCoords = buildCosePublicKeyFromKmCert(response.certificate_chain.begin());
- if (!affineCoords.isOk()) return affineCoords.moveError();
- cppbor::Map cosePublicKeyMap = cppbor::Map()
- .add(CoseKey::KEY_TYPE, EC2)
- .add(CoseKey::ALGORITHM, ES256)
- .add(CoseKey::CURVE, cppcose::P256)
- .add(CoseKey::PUBKEY_X, affineCoords->first)
- .add(CoseKey::PUBKEY_Y, affineCoords->second);
- if (testMode) {
- cosePublicKeyMap.add(CoseKey::TEST_KEY, cppbor::Null());
- }
-
- bytevec cosePublicKey = cosePublicKeyMap.canonicalize().encode();
-
- auto macedKey = constructCoseMac0(testMode ? remote_prov::kTestMacKey : macKey_,
- {} /* externalAad */, cosePublicKey);
- if (!macedKey) return Status(macedKey.moveMessage());
-
- macedPublicKey->macedKey = macedKey->encode();
- *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus RemotelyProvisionedComponent::generateCertificateRequest(
- bool testMode, const vector<MacedPublicKey>& keysToSign,
- const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
- ProtectedData* protectedData, bytevec* keysToSignMac) {
- auto pubKeysToSign = validateAndExtractPubkeys(testMode, keysToSign,
- testMode ? remote_prov::kTestMacKey : macKey_);
- if (!pubKeysToSign.isOk()) return pubKeysToSign.moveError();
-
- bytevec ephemeralMacKey = remote_prov::randomBytes(SHA256_DIGEST_LENGTH);
-
- auto pubKeysToSignMac = generateCoseMac0Mac(ephemeralMacKey, bytevec{}, *pubKeysToSign);
- if (!pubKeysToSignMac) return Status(pubKeysToSignMac.moveMessage());
- *keysToSignMac = *std::move(pubKeysToSignMac);
-
- bytevec devicePrivKey;
- cppbor::Array bcc;
- if (testMode) {
- std::tie(devicePrivKey, bcc) = generateBcc();
- } else {
- devicePrivKey = devicePrivKey_;
- bcc = bcc_.clone();
- }
-
- std::unique_ptr<cppbor::Map> deviceInfoMap = createDeviceInfo();
- deviceInfo->deviceInfo = deviceInfoMap->encode();
- auto signedMac = constructCoseSign1(devicePrivKey /* Signing key */, //
- ephemeralMacKey /* Payload */,
- cppbor::Array() /* AAD */
- .add(challenge)
- .add(std::move(deviceInfoMap))
- .encode());
- if (!signedMac) return Status(signedMac.moveMessage());
-
- bytevec ephemeralPrivKey(X25519_PRIVATE_KEY_LEN);
- bytevec ephemeralPubKey(X25519_PUBLIC_VALUE_LEN);
- X25519_keypair(ephemeralPubKey.data(), ephemeralPrivKey.data());
-
- auto eek = validateAndExtractEekPubAndId(testMode, endpointEncCertChain);
- if (!eek.isOk()) return eek.moveError();
-
- auto sessionKey = x25519_HKDF_DeriveKey(ephemeralPubKey, ephemeralPrivKey, eek->first,
- true /* senderIsA */);
- if (!sessionKey) return Status(sessionKey.moveMessage());
-
- auto coseEncrypted =
- constructCoseEncrypt(*sessionKey, remote_prov::randomBytes(kAesGcmNonceLength),
- cppbor::Array() // payload
- .add(signedMac.moveValue())
- .add(std::move(bcc))
- .encode(),
- {}, // aad
- buildCertReqRecipients(ephemeralPubKey, eek->second));
-
- if (!coseEncrypted) return Status(coseEncrypted.moveMessage());
- protectedData->protectedData = coseEncrypted->encode();
-
- return ScopedAStatus::ok();
-}
-
-bytevec RemotelyProvisionedComponent::deriveBytesFromHbk(const string& context,
- size_t numBytes) const {
- bytevec fakeHbk(32, 0);
- bytevec result(numBytes);
-
- // TODO(swillden): Figure out if HKDF can fail. It doesn't seem like it should be able to,
- // but the function does return an error code.
- HKDF(result.data(), numBytes, //
- EVP_sha256(), //
- fakeHbk.data(), fakeHbk.size(), //
- nullptr /* salt */, 0 /* salt len */, //
- reinterpret_cast<const uint8_t*>(context.data()), context.size());
-
- return result;
-}
-
-std::unique_ptr<cppbor::Map> RemotelyProvisionedComponent::createDeviceInfo() const {
- auto result = std::make_unique<cppbor::Map>(cppbor::Map());
-
- // The following placeholders show how the DeviceInfo map would be populated.
- // result->add(cppbor::Tstr("brand"), cppbor::Tstr("Google"));
- // result->add(cppbor::Tstr("manufacturer"), cppbor::Tstr("Google"));
- // result->add(cppbor::Tstr("product"), cppbor::Tstr("Fake"));
- // result->add(cppbor::Tstr("model"), cppbor::Tstr("Imaginary"));
- // result->add(cppbor::Tstr("board"), cppbor::Tstr("Chess"));
- // result->add(cppbor::Tstr("vb_state"), cppbor::Tstr("orange"));
- // result->add(cppbor::Tstr("bootloader_state"), cppbor::Tstr("unlocked"));
- // result->add(cppbor::Tstr("os_version"), cppbor::Tstr("SC"));
- // result->add(cppbor::Tstr("system_patch_level"), cppbor::Uint(20210331));
- // result->add(cppbor::Tstr("boot_patch_level"), cppbor::Uint(20210331));
- // result->add(cppbor::Tstr("vendor_patch_level"), cppbor::Uint(20210331));
-
- result->canonicalize();
- return result;
-}
-
-std::pair<bytevec /* privKey */, cppbor::Array /* BCC */>
-RemotelyProvisionedComponent::generateBcc() {
- bytevec privKey(ED25519_PRIVATE_KEY_LEN);
- bytevec pubKey(ED25519_PUBLIC_KEY_LEN);
-
- ED25519_keypair(pubKey.data(), privKey.data());
-
- auto coseKey = cppbor::Map()
- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
- .add(CoseKey::ALGORITHM, EDDSA)
- .add(CoseKey::CURVE, ED25519)
- .add(CoseKey::KEY_OPS, VERIFY)
- .add(CoseKey::PUBKEY_X, pubKey)
- .canonicalize()
- .encode();
- auto sign1Payload = cppbor::Map()
- .add(1 /* Issuer */, "Issuer")
- .add(2 /* Subject */, "Subject")
- .add(-4670552 /* Subject Pub Key */, coseKey)
- .add(-4670553 /* Key Usage (little-endian order) */,
- std::vector<uint8_t>{0x20} /* keyCertSign = 1<<5 */)
- .canonicalize()
- .encode();
- auto coseSign1 = constructCoseSign1(privKey, /* signing key */
- cppbor::Map(), /* extra protected */
- sign1Payload, {} /* AAD */);
- assert(coseSign1);
-
- return {privKey, cppbor::Array().add(coseKey).add(coseSign1.moveValue())};
-}
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/default/RemotelyProvisionedComponent.h b/security/keymint/aidl/default/RemotelyProvisionedComponent.h
deleted file mode 100644
index 8185e26..0000000
--- a/security/keymint/aidl/default/RemotelyProvisionedComponent.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <AndroidKeyMintDevice.h>
-#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
-#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
-#include <cppbor.h>
-#include <keymaster/UniquePtr.h>
-#include <keymaster/android_keymaster.h>
-
-namespace aidl::android::hardware::security::keymint {
-
-using ::ndk::ScopedAStatus;
-
-class RemotelyProvisionedComponent : public BnRemotelyProvisionedComponent {
- public:
- explicit RemotelyProvisionedComponent(std::shared_ptr<keymint::AndroidKeyMintDevice> keymint);
- virtual ~RemotelyProvisionedComponent();
-
- ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
- std::vector<uint8_t>* privateKeyHandle) override;
-
- ScopedAStatus generateCertificateRequest(bool testMode,
- const std::vector<MacedPublicKey>& keysToSign,
- const std::vector<uint8_t>& endpointEncCertChain,
- const std::vector<uint8_t>& challenge,
- DeviceInfo* deviceInfo, ProtectedData* protectedData,
- std::vector<uint8_t>* keysToSignMac) override;
-
- private:
- // TODO(swillden): Move these into an appropriate Context class.
- std::vector<uint8_t> deriveBytesFromHbk(const std::string& context, size_t numBytes) const;
- std::unique_ptr<cppbor::Map> createDeviceInfo() const;
- std::pair<std::vector<uint8_t>, cppbor::Array> generateBcc();
-
- std::vector<uint8_t> macKey_ = deriveBytesFromHbk("Key to MAC public keys", 32);
- std::vector<uint8_t> devicePrivKey_;
- cppbor::Array bcc_;
- std::shared_ptr<::keymaster::AndroidKeymaster> impl_;
-};
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/default/service.cpp b/security/keymint/aidl/default/service.cpp
index bcebbaf..8092e34 100644
--- a/security/keymint/aidl/default/service.cpp
+++ b/security/keymint/aidl/default/service.cpp
@@ -21,14 +21,13 @@
#include <android/binder_process.h>
#include <AndroidKeyMintDevice.h>
+#include <AndroidRemotelyProvisionedComponentDevice.h>
#include <AndroidSecureClock.h>
#include <AndroidSharedSecret.h>
#include <keymaster/soft_keymaster_logger.h>
-#include "RemotelyProvisionedComponent.h"
-
using aidl::android::hardware::security::keymint::AndroidKeyMintDevice;
-using aidl::android::hardware::security::keymint::RemotelyProvisionedComponent;
+using aidl::android::hardware::security::keymint::AndroidRemotelyProvisionedComponentDevice;
using aidl::android::hardware::security::keymint::SecurityLevel;
using aidl::android::hardware::security::secureclock::AndroidSecureClock;
using aidl::android::hardware::security::sharedsecret::AndroidSharedSecret;
@@ -56,7 +55,7 @@
// Add Shared Secret Service
addService<AndroidSharedSecret>(keyMint);
// Add Remotely Provisioned Component Service
- addService<RemotelyProvisionedComponent>(keyMint);
+ addService<AndroidRemotelyProvisionedComponentDevice>(keyMint);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index c1affa6..d5c45e2 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -31,6 +31,7 @@
],
srcs: [
"AttestKeyTest.cpp",
+ "DeviceUniqueAttestationTest.cpp",
"KeyMintTest.cpp",
],
shared_libs: [
@@ -43,6 +44,8 @@
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.secureclock-V1-ndk_platform",
"libcppbor_external",
+ "libcppcose_rkp",
+ "libkeymint_remote_prov_support",
"libkeymint_vts_test_utils",
],
test_suites: [
@@ -73,6 +76,9 @@
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.secureclock-V1-ndk_platform",
"libcppbor_external",
+ "libcppcose_rkp",
+ "libgmock_ndk",
+ "libkeymint_remote_prov_support",
],
}
@@ -87,21 +93,20 @@
],
shared_libs: [
"libbinder_ndk",
- "libcppbor_external",
"libcrypto",
- "libkeymaster_portable",
- "libpuresoftkeymasterdevice",
],
static_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
"android.hardware.security.secureclock-V1-ndk_platform",
- "libcppcose",
+ "libcppbor_external",
+ "libcppcose_rkp",
"libgmock_ndk",
+ "libkeymaster_portable",
"libkeymint",
"libkeymint_support",
"libkeymint_remote_prov_support",
"libkeymint_vts_test_utils",
- "libremote_provisioner",
+ "libpuresoftkeymasterdevice",
],
test_suites: [
"general-tests",
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index 1e907db..daa3e18 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -207,6 +207,36 @@
}
}
+TEST_P(AttestKeyTest, AttestWithNonAttestKey) {
+ // Create non-attestaton key.
+ AttestationKey non_attest_key;
+ vector<KeyCharacteristics> non_attest_key_characteristics;
+ vector<Certificate> non_attest_key_cert_chain;
+ ASSERT_EQ(
+ ErrorCode::OK,
+ GenerateKey(
+ AuthorizationSetBuilder().EcdsaSigningKey(EcCurve::P_256).SetDefaultValidity(),
+ {} /* attestation siging key */, &non_attest_key.keyBlob,
+ &non_attest_key_characteristics, &non_attest_key_cert_chain));
+
+ EXPECT_EQ(non_attest_key_cert_chain.size(), 1);
+ EXPECT_TRUE(IsSelfSigned(non_attest_key_cert_chain));
+
+ // Attempt to sign attestation with non-attest key.
+ vector<uint8_t> attested_key_blob;
+ vector<KeyCharacteristics> attested_key_characteristics;
+ vector<Certificate> attested_key_cert_chain;
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .SetDefaultValidity(),
+ non_attest_key, &attested_key_blob, &attested_key_characteristics,
+ &attested_key_cert_chain));
+}
+
INSTANTIATE_KEYMINT_AIDL_TEST(AttestKeyTest);
} // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
new file mode 100644
index 0000000..7009c6e
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "keymint_1_attest_key_test"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/openssl_utils.h>
+
+#include "KeyMintAidlTestBase.h"
+
+namespace aidl::android::hardware::security::keymint::test {
+
+class DeviceUniqueAttestationTest : public KeyMintAidlTestBase {
+ protected:
+ void CheckUniqueAttestationResults(const vector<uint8_t>& key_blob,
+ const vector<KeyCharacteristics>& key_characteristics,
+ const AuthorizationSet& hw_enforced, int key_size) {
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ if (KeyMintAidlTestBase::dump_Attestations) {
+ std::cout << bin2hex(cert_chain_[0].encodedCertificate) << std::endl;
+ }
+
+ ASSERT_GT(key_blob.size(), 0U);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size)) << "Key size missing";
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("challenge", "foo", sw_enforced, hw_enforced,
+ SecLevel(), cert_chain_[0].encodedCertificate));
+ }
+};
+
+/*
+ * DeviceUniqueAttestationTest.RsaNonStrongBoxUnimplemented
+ *
+ * Verifies that non strongbox implementations do not implement Rsa device unique
+ * attestation.
+ */
+TEST_P(DeviceUniqueAttestationTest, RsaNonStrongBoxUnimplemented) {
+ if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+
+ // Check RSA implementation
+ auto result = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("challenge")
+ .AttestationApplicationId("foo")
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+ &key_blob, &key_characteristics);
+
+ ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_TAG);
+}
+
+/*
+ * DeviceUniqueAttestationTest.EcdsaNonStrongBoxUnimplemented
+ *
+ * Verifies that non strongbox implementations do not implement Ecdsa device unique
+ * attestation.
+ */
+TEST_P(DeviceUniqueAttestationTest, EcdsaNonStrongBoxUnimplemented) {
+ if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+
+ // Check Ecdsa implementation
+ auto result = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)
+ .AttestationChallenge("challenge")
+ .AttestationApplicationId("foo")
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+ &key_blob, &key_characteristics);
+
+ ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_TAG);
+}
+
+/*
+ * DeviceUniqueAttestationTest.RsaDeviceUniqueAttestation
+ *
+ * Verifies that strongbox implementations of Rsa implements device unique
+ * attestation correctly, if implemented.
+ */
+TEST_P(DeviceUniqueAttestationTest, RsaDeviceUniqueAttestation) {
+ if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ int key_size = 2048;
+
+ auto result = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(key_size, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AttestationChallenge("challenge")
+ .AttestationApplicationId("foo")
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+ &key_blob, &key_characteristics);
+
+ // It is optional for Strong box to support DeviceUniqueAttestation.
+ if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+
+ ASSERT_EQ(ErrorCode::OK, result);
+
+ AuthorizationSet hw_enforced = AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+ .Authorization(TAG_OS_VERSION, os_version())
+ .Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
+
+ CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced, key_size);
+}
+
+/*
+ * DeviceUniqueAttestationTest.EcdsaDeviceUniqueAttestation
+ *
+ * Verifies that strongbox implementations of Rsa implements device unique
+ * attestation correctly, if implemented.
+ */
+TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestation) {
+ if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ int key_size = 256;
+
+ auto result = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)
+ .AttestationChallenge("challenge")
+ .AttestationApplicationId("foo")
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+ &key_blob, &key_characteristics);
+
+ // It is optional for Strong box to support DeviceUniqueAttestation.
+ if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+ ASSERT_EQ(ErrorCode::OK, result);
+
+ AuthorizationSet hw_enforced = AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_EC_CURVE, EcCurve::P_256)
+ .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+ .Authorization(TAG_OS_VERSION, os_version())
+ .Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
+
+ CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced, key_size);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(DeviceUniqueAttestationTest);
+
+} // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index ce6f67a..59cb57b 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -22,9 +22,13 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
+#include <cppbor_parse.h>
#include <cutils/properties.h>
+#include <gmock/gmock.h>
#include <openssl/mem.h>
+#include <remote_prov/remote_prov_utils.h>
+#include <keymaster/cppcose/cppcose.h>
#include <keymint_support/attestation_record.h>
#include <keymint_support/key_param_output.h>
#include <keymint_support/keymint_utils.h>
@@ -32,6 +36,7 @@
namespace aidl::android::hardware::security::keymint {
+using namespace cppcose;
using namespace std::literals::chrono_literals;
using std::endl;
using std::optional;
@@ -39,6 +44,7 @@
using ::testing::AssertionFailure;
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
+using ::testing::MatchesRegex;
::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
if (set.size() == 0)
@@ -877,16 +883,20 @@
if (error != ErrorCode::OK) return false;
EXPECT_GE(att_attestation_version, 3U);
+ vector<uint8_t> appId(app_id.begin(), app_id.end());
- expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID,
- vector<uint8_t>(app_id.begin(), app_id.end()));
+ // check challenge and app id only if we expects a non-fake certificate
+ if (challenge.length() > 0) {
+ EXPECT_EQ(challenge.length(), att_challenge.size());
+ EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
+
+ expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID, appId);
+ }
EXPECT_GE(att_keymaster_version, 4U);
EXPECT_EQ(security_level, att_keymaster_security_level);
EXPECT_EQ(security_level, att_attestation_security_level);
- EXPECT_EQ(challenge.length(), att_challenge.size());
- EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
char property_value[PROPERTY_VALUE_MAX] = {};
// TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
@@ -897,7 +907,7 @@
if (att_hw_enforced[i].tag == TAG_BOOT_PATCHLEVEL ||
att_hw_enforced[i].tag == TAG_VENDOR_PATCHLEVEL) {
std::string date =
- std::to_string(att_hw_enforced[i].value.get<KeyParameterValue::dateTime>());
+ std::to_string(att_hw_enforced[i].value.get<KeyParameterValue::integer>());
// strptime seems to require delimiters, but the tag value will
// be YYYYMMDD
date.insert(6, "-");
@@ -1118,6 +1128,121 @@
return retval;
}
+namespace {
+
+void check_cose_key(const vector<uint8_t>& data, bool testMode) {
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+
+ // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
+ if (testMode) {
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
+ " -70000 : null,\n" // test marker
+ "}"));
+ } else {
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
+ "}"));
+ }
+}
+
+} // namespace
+
+void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
+ vector<uint8_t>* payload_value) {
+ auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+ ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+
+ ASSERT_NE(coseMac0->asArray(), nullptr);
+ ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+
+ auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+ ASSERT_NE(protParms, nullptr);
+
+ // Header label:value of 'alg': HMAC-256
+ ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
+
+ auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
+ ASSERT_NE(unprotParms, nullptr);
+ ASSERT_EQ(unprotParms->size(), 0);
+
+ // The payload is a bstr holding an encoded COSE_Key
+ auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+ ASSERT_NE(payload, nullptr);
+ check_cose_key(payload->value(), testMode);
+
+ auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+ ASSERT_TRUE(coseMac0Tag);
+ auto extractedTag = coseMac0Tag->value();
+ EXPECT_EQ(extractedTag.size(), 32U);
+
+ // Compare with tag generated with kTestMacKey. Should only match in test mode
+ auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
+ payload->value());
+ ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
+
+ if (testMode) {
+ EXPECT_EQ(*testTag, extractedTag);
+ } else {
+ EXPECT_NE(*testTag, extractedTag);
+ }
+ if (payload_value != nullptr) {
+ *payload_value = payload->value();
+ }
+}
+
+void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey) {
+ // Extract x and y affine coordinates from the encoded Cose_Key.
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(coseKeyData);
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+ auto coseKey = parsedPayload->asMap();
+ const std::unique_ptr<cppbor::Item>& xItem = coseKey->get(cppcose::CoseKey::PUBKEY_X);
+ ASSERT_NE(xItem->asBstr(), nullptr);
+ vector<uint8_t> x = xItem->asBstr()->value();
+ const std::unique_ptr<cppbor::Item>& yItem = coseKey->get(cppcose::CoseKey::PUBKEY_Y);
+ ASSERT_NE(yItem->asBstr(), nullptr);
+ vector<uint8_t> y = yItem->asBstr()->value();
+
+ // Concatenate: 0x04 (uncompressed form marker) | x | y
+ vector<uint8_t> pubKeyData{0x04};
+ pubKeyData.insert(pubKeyData.end(), x.begin(), x.end());
+ pubKeyData.insert(pubKeyData.end(), y.begin(), y.end());
+
+ EC_KEY_Ptr ecKey = EC_KEY_Ptr(EC_KEY_new());
+ ASSERT_NE(ecKey, nullptr);
+ EC_GROUP_Ptr group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ ASSERT_NE(group, nullptr);
+ ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
+ EC_POINT_Ptr point = EC_POINT_Ptr(EC_POINT_new(group.get()));
+ ASSERT_NE(point, nullptr);
+ ASSERT_EQ(EC_POINT_oct2point(group.get(), point.get(), pubKeyData.data(), pubKeyData.size(),
+ nullptr),
+ 1);
+ ASSERT_EQ(EC_KEY_set_public_key(ecKey.get(), point.get()), 1);
+
+ EVP_PKEY_Ptr pubKey = EVP_PKEY_Ptr(EVP_PKEY_new());
+ ASSERT_NE(pubKey, nullptr);
+ EVP_PKEY_assign_EC_KEY(pubKey.get(), ecKey.release());
+ *signingKey = std::move(pubKey);
+}
+
} // namespace test
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 86bc9c4..da174ce 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -25,6 +25,7 @@
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
#include <keymint_support/authorization_set.h>
#include <keymint_support/openssl_utils.h>
@@ -277,6 +278,10 @@
string bin2hex(const vector<uint8_t>& data);
X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
vector<uint8_t> make_name_from_str(const string& name);
+void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
+ vector<uint8_t>* payload_value);
+void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);
+
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain);
@@ -284,7 +289,8 @@
#define INSTANTIATE_KEYMINT_AIDL_TEST(name) \
INSTANTIATE_TEST_SUITE_P(PerInstance, name, \
testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
- ::android::PrintInstanceNameToString)
+ ::android::PrintInstanceNameToString); \
+ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);
} // namespace test
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 7ecfa37..09cdab1 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -27,6 +27,9 @@
#include <cutils/properties.h>
+#include <android/binder_manager.h>
+
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <aidl/android/hardware/security/keymint/KeyFormat.h>
#include <keymint_support/key_param_output.h>
@@ -134,6 +137,54 @@
"d5f33645e8ed8b4a1cb3cc4a1d67987399f2a09f5b3fb68c88d5e5d90ac3"
"3492d6");
+/*
+ * DER-encoded PKCS#8 format RSA key. Generated using:
+ *
+ * openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1 "%02X" "\n"'
+ */
+string rsa_2048_key =
+ hex2str("308204BD020100300D06092A864886F70D0101010500048204A7308204A3"
+ "0201000282010100BEBC342B56D443B1299F9A6A7056E80A897E318476A5"
+ "A18029E63B2ED739A61791D339F58DC763D9D14911F2EDEC383DEE11F631"
+ "9B44510E7A3ECD9B79B97382E49500ACF8117DC89CAF0E621F77756554A2"
+ "FD4664BFE7AB8B59AB48340DBFA27B93B5A81F6ECDEB02D0759307128DF3"
+ "E3BAD4055C8B840216DFAA5700670E6C5126F0962FCB70FF308F25049164"
+ "CCF76CC2DA66A7DD9A81A714C2809D69186133D29D84568E892B6FFBF319"
+ "9BDB14383EE224407F190358F111A949552ABA6714227D1BD7F6B20DD0CB"
+ "88F9467B719339F33BFF35B3870B3F62204E4286B0948EA348B524544B5F"
+ "9838F29EE643B079EEF8A713B220D7806924CDF7295070C5020301000102"
+ "82010069F377F35F2F584EF075353CCD1CA99738DB3DBC7C7FF35F9366CE"
+ "176DFD1B135AB10030344ABF5FBECF1D4659FDEF1C0FC430834BE1BE3911"
+ "951377BB3D563A2EA9CA8F4AD9C48A8CE6FD516A735C662686C7B4B3C09A"
+ "7B8354133E6F93F790D59EAEB92E84C9A4339302CCE28FDF04CCCAFA7DE3"
+ "F3A827D4F6F7D38E68B0EC6AB706645BF074A4E4090D06FB163124365FD5"
+ "EE7A20D350E9958CC30D91326E1B292E9EF5DB408EC42DAF737D20149704"
+ "D0A678A0FB5B5446863B099228A352D604BA8091A164D01D5AB05397C71E"
+ "AD20BE2A08FC528FE442817809C787FEE4AB97F97B9130D022153EDC6EB6"
+ "CBE7B0F8E3473F2E901209B5DB10F93604DB0102818100E83C0998214941"
+ "EA4F9293F1B77E2E99E6CF305FAF358238E126124FEAF2EB9724B2EA7B78"
+ "E6032343821A80E55D1D88FB12D220C3F41A56142FEC85796D1917F1E8C7"
+ "74F142B67D3D6E7B7E6B4383E94DB5929089DBB346D5BDAB40CC2D96EE04"
+ "09475E175C63BF78CFD744136740838127EA723FF3FE7FA368C1311B4A4E"
+ "0502818100D240FCC0F5D7715CDE21CB2DC86EA146132EA3B06F61FF2AF5"
+ "4BF38473F59DADCCE32B5F4CC32DD0BA6F509347B4B5B1B58C39F95E4798"
+ "CCBB43E83D0119ACF532F359CA743C85199F0286610E200997D731291717"
+ "9AC9B67558773212EC961E8BCE7A3CC809BC5486A96E4B0E6AF394D94E06"
+ "6A0900B7B70E82A44FB30053C102818100AD15DA1CBD6A492B66851BA8C3"
+ "16D38AB700E2CFDDD926A658003513C54BAA152B30021D667D20078F500F"
+ "8AD3E7F3945D74A891ED1A28EAD0FEEAEC8C14A8E834CF46A13D1378C99D"
+ "18940823CFDD27EC5810D59339E0C34198AC638E09C87CBB1B634A9864AE"
+ "9F4D5EB2D53514F67B4CAEC048C8AB849A02E397618F3271350281801FA2"
+ "C1A5331880A92D8F3E281C617108BF38244F16E352E69ED417C7153F9EC3"
+ "18F211839C643DCF8B4DD67CE2AC312E95178D5D952F06B1BF779F491692"
+ "4B70F582A23F11304E02A5E7565AE22A35E74FECC8B6FDC93F92A1A37703"
+ "E4CF0E63783BD02EB716A7ECBBFA606B10B74D01579522E7EF84D91FC522"
+ "292108D902C1028180796FE3825F9DCC85DF22D58690065D93898ACD65C0"
+ "87BEA8DA3A63BF4549B795E2CD0E3BE08CDEBD9FCF1720D9CDC5070D74F4"
+ "0DED8E1102C52152A31B6165F83A6722AECFCC35A493D7634664B888A08D"
+ "3EB034F12EA28BFEE346E205D334827F778B16ED40872BD29FCB36536B6E"
+ "93FFB06778696B4A9D81BB0A9423E63DE5");
+
string ec_256_key =
hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
"6b0201010420737c2ecd7b8d1940bf2930aa9b4ed3ff941eed09366bc032"
@@ -209,6 +260,32 @@
string to_string() const { return string(reinterpret_cast<const char*>(data()), size()); }
};
+string device_suffix(const string& name) {
+ size_t pos = name.find('/');
+ if (pos == string::npos) {
+ return name;
+ }
+ return name.substr(pos + 1);
+}
+
+bool matching_rp_instance(const string& km_name,
+ std::shared_ptr<IRemotelyProvisionedComponent>* rp) {
+ string km_suffix = device_suffix(km_name);
+
+ vector<string> rp_names =
+ ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
+ for (const string& rp_name : rp_names) {
+ // If the suffix of the RemotelyProvisionedComponent instance equals the suffix of the
+ // KeyMint instance, assume they match.
+ if (device_suffix(rp_name) == km_suffix && AServiceManager_isDeclared(rp_name.c_str())) {
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(rp_name.c_str()));
+ *rp = IRemotelyProvisionedComponent::fromBinder(binder);
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
class NewKeyGenerationTest : public KeyMintAidlTestBase {
@@ -282,10 +359,10 @@
* have correct characteristics.
*/
TEST_P(NewKeyGenerationTest, RsaWithAttestation) {
- for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
- auto challenge = "hello";
- auto app_id = "foo";
+ auto challenge = "hello";
+ auto app_id = "foo";
+ for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -322,6 +399,234 @@
}
/*
+ * NewKeyGenerationTest.RsaWithRpkAttestation
+ *
+ * Verifies that keymint can generate all required RSA key sizes, using an attestation key
+ * that has been generated using an associate IRemotelyProvisionedComponent.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithRpkAttestation) {
+ // There should be an IRemotelyProvisionedComponent instance associated with the KeyMint
+ // instance.
+ std::shared_ptr<IRemotelyProvisionedComponent> rp;
+ ASSERT_TRUE(matching_rp_instance(GetParam(), &rp))
+ << "No IRemotelyProvisionedComponent found that matches KeyMint device " << GetParam();
+
+ // Generate a P-256 keypair to use as an attestation key.
+ MacedPublicKey macedPubKey;
+ std::vector<uint8_t> privateKeyBlob;
+ auto status =
+ rp->generateEcdsaP256KeyPair(/* testMode= */ false, &macedPubKey, &privateKeyBlob);
+ ASSERT_TRUE(status.isOk());
+ vector<uint8_t> coseKeyData;
+ check_maced_pubkey(macedPubKey, /* testMode= */ false, &coseKeyData);
+
+ AttestationKey attestation_key;
+ attestation_key.keyBlob = std::move(privateKeyBlob);
+ attestation_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+
+ for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(key_size, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ attestation_key, &key_blob, &key_characteristics, &cert_chain_));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+ EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ // Attestation by itself is not valid (last entry is not self-signed).
+ EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_));
+
+ // The signature over the attested key should correspond to the P256 public key.
+ X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ ASSERT_TRUE(key_cert.get());
+ EVP_PKEY_Ptr signing_pubkey;
+ p256_pub_key(coseKeyData, &signing_pubkey);
+ ASSERT_TRUE(signing_pubkey.get());
+
+ ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
+ << "Verification of attested certificate failed "
+ << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
+ * NewKeyGenerationTest.RsaEncryptionWithAttestation
+ *
+ * Verifies that keymint attestation for RSA encryption keys with challenge and
+ * app id is also successful.
+ */
+TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) {
+ auto key_size = 2048;
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(key_size, 65537)
+ .Padding(PaddingMode::NONE)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ AuthorizationSet auths;
+ for (auto& entry : key_characteristics) {
+ auths.push_back(AuthorizationSet(entry.authorizations));
+ }
+
+ EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+ EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+
+ // Verify that App data and ROT are NOT included.
+ EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
+ EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
+
+ // Check that some unexpected tags/values are NOT present.
+ EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+ EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
+
+ EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
+
+ auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
+ ASSERT_TRUE(os_ver);
+ EXPECT_EQ(*os_ver, os_version());
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+ EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+ EXPECT_TRUE(verify_attestation_record(challenge, app_id, //
+ sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_[0].encodedCertificate));
+
+ CheckedDeleteKey(&key_blob);
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithSelfSign
+ *
+ * Verifies that attesting to RSA key generation is successful, and returns
+ * self signed certificate if no challenge is provided. And signing etc
+ * works as expected.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithSelfSign) {
+ for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(key_size, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+ EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_EQ(cert_chain_.size(), 1);
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithAttestationMissAppId
+ *
+ * Verifies that attesting to RSA checks for missing app ID.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) {
+ auto challenge = "hello";
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+
+ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .AttestationChallenge(challenge)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithAttestationAppIdIgnored
+ *
+ * Verifies that attesting to RSA ignores app id if challenge is missing.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithAttestationAppIdIgnored) {
+ auto key_size = 2048;
+ auto app_id = "foo";
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(key_size, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+ EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_EQ(cert_chain_.size(), 1);
+
+ CheckedDeleteKey(&key_blob);
+}
+
+/*
* NewKeyGenerationTest.LimitedUsageRsa
*
* Verifies that KeyMint can generate all required RSA key sizes with limited usage, and that the
@@ -368,10 +673,10 @@
* resulting keys have correct characteristics and attestation.
*/
TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) {
- for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
- auto challenge = "hello";
- auto app_id = "foo";
+ auto challenge = "hello";
+ auto app_id = "foo";
+ for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -480,6 +785,188 @@
}
/*
+ * NewKeyGenerationTest.EcdsaAttestation
+ *
+ * Verifies that for all Ecdsa key sizes, if challenge and app id is provided,
+ * an attestation will be generated.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestation) {
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ for (auto key_size : ValidKeySizes(Algorithm::EC)) {
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(key_size)
+ .Digest(Digest::NONE)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+ EXPECT_TRUE(verify_attestation_record(challenge, app_id, //
+ sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_[0].encodedCertificate));
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaSelfSignAttestation
+ *
+ * Verifies that if no challenge is provided to an Ecdsa key generation, then
+ * the key will generate a self signed attestation.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaSelfSignAttestation) {
+ for (auto key_size : ValidKeySizes(Algorithm::EC)) {
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(key_size)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_EQ(cert_chain_.size(), 1);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAttestationRequireAppId
+ *
+ * Verifies that if attestation challenge is provided to Ecdsa key generation, then
+ * app id must also be provided or else it will fail.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) {
+ auto challenge = "hello";
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+
+ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Digest(Digest::NONE)
+ .AttestationChallenge(challenge)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaIgnoreAppId
+ *
+ * Verifies that if no challenge is provided to the Ecdsa key generation, then
+ * any appid will be ignored, and keymint will generate a self sign certificate.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaIgnoreAppId) {
+ auto app_id = "foo";
+
+ for (auto key_size : ValidKeySizes(Algorithm::EC)) {
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(key_size)
+ .Digest(Digest::NONE)
+ .AttestationApplicationId(app_id)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_EQ(cert_chain_.size(), 1);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
+ * NewKeyGenerationTest.AttestationApplicationIDLengthProperlyEncoded
+ *
+ * Verifies that the Attestation Application ID software enforced tag has a proper length encoding.
+ * Some implementations break strict encoding rules by encoding a length between 127 and 256 in one
+ * byte. Proper DER encoding specifies that for lengths greater than 127, one byte should be used
+ * to specify how many following bytes will be used to encode the length.
+ */
+TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) {
+ auto challenge = "hello";
+ auto key_size = 256;
+ std::vector<uint32_t> app_id_lengths{143, 258};
+
+ for (uint32_t length : app_id_lengths) {
+ const string app_id(length, 'a');
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(key_size)
+ .Digest(Digest::NONE)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics));
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+ EXPECT_TRUE(verify_attestation_record(challenge, app_id, //
+ sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_[0].encodedCertificate));
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
* NewKeyGenerationTest.LimitedUsageEcdsa
*
* Verifies that KeyMint can generate all required EC key sizes with limited usage, and that the
@@ -641,6 +1128,41 @@
}
/*
+ * NewKeyGenerationTest.HmacNoAttestation
+ *
+ * Verifies that for Hmac key generation, no attestation will be generated even if challenge
+ * and app id are provided.
+ */
+TEST_P(NewKeyGenerationTest, HmacNoAttestation) {
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ constexpr size_t key_size = 128;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(key_size)
+ .Digest(digest)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ ASSERT_EQ(cert_chain_.size(), 0);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
* NewKeyGenerationTest.LimitedUsageHmac
*
* Verifies that KeyMint supports all required digests with limited usage Hmac, and that the
@@ -774,6 +1296,47 @@
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
}
+/*
+ * NewKeyGenerationTest.AesNoAttestation
+ *
+ * Verifies that attestation parameters to AES keys are ignored and generateKey
+ * will succeed.
+ */
+TEST_P(NewKeyGenerationTest, AesNoAttestation) {
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)));
+
+ ASSERT_EQ(cert_chain_.size(), 0);
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesNoAttestation
+ *
+ * Verifies that attesting parameters to 3DES keys are ignored and generate key
+ * will be successful. No attestation should be generated.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesNoAttestation) {
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .TripleDesEncryptionKey(168)
+ .BlockMode(BlockMode::ECB)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::NONE)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)));
+ ASSERT_EQ(cert_chain_.size(), 0);
+}
+
INSTANTIATE_KEYMINT_AIDL_TEST(NewKeyGenerationTest);
typedef KeyMintAidlTestBase SigningOperationsTest;
@@ -1711,16 +2274,27 @@
* Verifies that importing and using an RSA key pair works correctly.
*/
TEST_P(ImportKeyTest, RsaSuccess) {
+ uint32_t key_size;
+ string key;
+
+ if (SecLevel() == SecurityLevel::STRONGBOX) {
+ key_size = 2048;
+ key = rsa_2048_key;
+ } else {
+ key_size = 1024;
+ key = rsa_key;
+ }
+
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
- .RsaSigningKey(1024, 65537)
+ .RsaSigningKey(key_size, 65537)
.Digest(Digest::SHA_2_256)
.Padding(PaddingMode::RSA_PSS)
.SetDefaultValidity(),
- KeyFormat::PKCS8, rsa_key));
+ KeyFormat::PKCS8, key));
CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
- CheckCryptoParam(TAG_KEY_SIZE, 1024U);
+ CheckCryptoParam(TAG_KEY_SIZE, key_size);
CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 57bc27a..a2071c2 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -16,13 +16,13 @@
#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
-#include <RemotelyProvisionedComponent.h>
+#include <AndroidRemotelyProvisionedComponentDevice.h>
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
#include <android/binder_manager.h>
#include <cppbor_parse.h>
-#include <cppcose/cppcose.h>
#include <gmock/gmock.h>
+#include <keymaster/cppcose/cppcose.h>
#include <keymaster/keymaster_configuration.h>
#include <keymint_support/authorization_set.h>
#include <openssl/ec.h>
@@ -55,117 +55,6 @@
return bytevec(p, p + strlen(s));
}
-void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey) {
- // Extract x and y affine coordinates from the encoded Cose_Key.
- auto [parsedPayload, __, payloadParseErr] = cppbor::parse(coseKeyData);
- ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
- auto coseKey = parsedPayload->asMap();
- const std::unique_ptr<cppbor::Item>& xItem = coseKey->get(cppcose::CoseKey::PUBKEY_X);
- ASSERT_NE(xItem->asBstr(), nullptr);
- vector<uint8_t> x = xItem->asBstr()->value();
- const std::unique_ptr<cppbor::Item>& yItem = coseKey->get(cppcose::CoseKey::PUBKEY_Y);
- ASSERT_NE(yItem->asBstr(), nullptr);
- vector<uint8_t> y = yItem->asBstr()->value();
-
- // Concatenate: 0x04 (uncompressed form marker) | x | y
- vector<uint8_t> pubKeyData{0x04};
- pubKeyData.insert(pubKeyData.end(), x.begin(), x.end());
- pubKeyData.insert(pubKeyData.end(), y.begin(), y.end());
-
- EC_KEY_Ptr ecKey = EC_KEY_Ptr(EC_KEY_new());
- ASSERT_NE(ecKey, nullptr);
- EC_GROUP_Ptr group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
- ASSERT_NE(group, nullptr);
- ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
- EC_POINT_Ptr point = EC_POINT_Ptr(EC_POINT_new(group.get()));
- ASSERT_NE(point, nullptr);
- ASSERT_EQ(EC_POINT_oct2point(group.get(), point.get(), pubKeyData.data(), pubKeyData.size(),
- nullptr),
- 1);
- ASSERT_EQ(EC_KEY_set_public_key(ecKey.get(), point.get()), 1);
-
- EVP_PKEY_Ptr pubKey = EVP_PKEY_Ptr(EVP_PKEY_new());
- ASSERT_NE(pubKey, nullptr);
- EVP_PKEY_assign_EC_KEY(pubKey.get(), ecKey.release());
- *signingKey = std::move(pubKey);
-}
-
-void check_cose_key(const vector<uint8_t>& data, bool testMode) {
- auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
- ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
-
- // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
- if (testMode) {
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n" // kty: EC2
- " 3 : -7,\n" // alg: ES256
- " -1 : 1,\n" // EC id: P256
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
- // sequence of 32 hexadecimal bytes, enclosed in braces and
- // separated by commas. In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
- " -70000 : null,\n" // test marker
- "}"));
- } else {
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n" // kty: EC2
- " 3 : -7,\n" // alg: ES256
- " -1 : 1,\n" // EC id: P256
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
- // sequence of 32 hexadecimal bytes, enclosed in braces and
- // separated by commas. In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
- "}"));
- }
-}
-
-void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
- vector<uint8_t>* payload_value) {
- auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
- ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
-
- ASSERT_NE(coseMac0->asArray(), nullptr);
- ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
-
- auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
- ASSERT_NE(protParms, nullptr);
-
- // Header label:value of 'alg': HMAC-256
- ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
-
- auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
- ASSERT_NE(unprotParms, nullptr);
- ASSERT_EQ(unprotParms->size(), 0);
-
- // The payload is a bstr holding an encoded COSE_Key
- auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
- ASSERT_NE(payload, nullptr);
- check_cose_key(payload->value(), testMode);
-
- auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
- ASSERT_TRUE(coseMac0Tag);
- auto extractedTag = coseMac0Tag->value();
- EXPECT_EQ(extractedTag.size(), 32U);
-
- // Compare with tag generated with kTestMacKey. Should only match in test mode
- auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
- payload->value());
- ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
-
- if (testMode) {
- EXPECT_EQ(*testTag, extractedTag);
- } else {
- EXPECT_NE(*testTag, extractedTag);
- }
- if (payload_value != nullptr) {
- *payload_value = payload->value();
- }
-}
-
ErrMsgOr<MacedPublicKey> corrupt_maced_key(const MacedPublicKey& macedPubKey) {
auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
if (!coseMac0 || coseMac0->asArray()->size() != kCoseMac0EntryCount) {
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index fe04ede..718133a 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -57,29 +57,8 @@
"include",
],
shared_libs: [
- "libcppcose",
"libcppbor_external",
+ "libcppcose_rkp",
"libcrypto",
],
}
-
-cc_library {
- name: "libcppcose",
- vendor_available: true,
- srcs: [
- "cppcose.cpp",
- ],
- export_include_dirs: [
- "include",
- ],
- shared_libs: [
- "libbinder_ndk",
- "libcppbor_external",
- "libcrypto",
- "liblog",
- ],
- static_libs: [
- // TODO(swillden): Remove keymint NDK
- "android.hardware.security.keymint-V1-ndk_platform",
- ],
-}
diff --git a/security/keymint/support/cppcose.cpp b/security/keymint/support/cppcose.cpp
deleted file mode 100644
index bafb2b6..0000000
--- a/security/keymint/support/cppcose.cpp
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cppcose/cppcose.h>
-
-#include <stdio.h>
-#include <iostream>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-#include <openssl/err.h>
-
-namespace cppcose {
-
-namespace {
-
-ErrMsgOr<bssl::UniquePtr<EVP_CIPHER_CTX>> aesGcmInitAndProcessAad(const bytevec& key,
- const bytevec& nonce,
- const bytevec& aad,
- bool encrypt) {
- if (key.size() != kAesGcmKeySize) return "Invalid key size";
-
- bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
- if (!ctx) return "Failed to allocate cipher context";
-
- if (!EVP_CipherInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr /* engine */, key.data(),
- nonce.data(), encrypt ? 1 : 0)) {
- return "Failed to initialize cipher";
- }
-
- int outlen;
- if (!aad.empty() && !EVP_CipherUpdate(ctx.get(), nullptr /* out; null means AAD */, &outlen,
- aad.data(), aad.size())) {
- return "Failed to process AAD";
- }
-
- return std::move(ctx);
-}
-
-} // namespace
-
-ErrMsgOr<bytevec> generateCoseMac0Mac(const bytevec& macKey, const bytevec& externalAad,
- const bytevec& payload) {
- auto macStructure = cppbor::Array()
- .add("MAC0")
- .add(cppbor::Map().add(ALGORITHM, HMAC_256).canonicalize().encode())
- .add(externalAad)
- .add(payload)
- .encode();
-
- bytevec macTag(SHA256_DIGEST_LENGTH);
- uint8_t* out = macTag.data();
- unsigned int outLen;
- out = HMAC(EVP_sha256(), //
- macKey.data(), macKey.size(), //
- macStructure.data(), macStructure.size(), //
- out, &outLen);
-
- assert(out != nullptr && outLen == macTag.size());
- if (out == nullptr || outLen != macTag.size()) {
- return "Error computing public key MAC";
- }
-
- return macTag;
-}
-
-ErrMsgOr<cppbor::Array> constructCoseMac0(const bytevec& macKey, const bytevec& externalAad,
- const bytevec& payload) {
- auto tag = generateCoseMac0Mac(macKey, externalAad, payload);
- if (!tag) return tag.moveMessage();
-
- return cppbor::Array()
- .add(cppbor::Map().add(ALGORITHM, HMAC_256).canonicalize().encode())
- .add(cppbor::Map() /* unprotected */)
- .add(payload)
- .add(tag.moveValue());
-}
-
-ErrMsgOr<bytevec /* payload */> parseCoseMac0(const cppbor::Item* macItem) {
- auto mac = macItem ? macItem->asArray() : nullptr;
- if (!mac || mac->size() != kCoseMac0EntryCount) {
- return "Invalid COSE_Mac0";
- }
-
- auto protectedParms = mac->get(kCoseMac0ProtectedParams)->asBstr();
- auto unprotectedParms = mac->get(kCoseMac0UnprotectedParams)->asMap();
- auto payload = mac->get(kCoseMac0Payload)->asBstr();
- auto tag = mac->get(kCoseMac0Tag)->asBstr();
- if (!protectedParms || !unprotectedParms || !payload || !tag) {
- return "Invalid COSE_Mac0 contents";
- }
-
- return payload->value();
-}
-
-ErrMsgOr<bytevec /* payload */> verifyAndParseCoseMac0(const cppbor::Item* macItem,
- const bytevec& macKey) {
- auto mac = macItem ? macItem->asArray() : nullptr;
- if (!mac || mac->size() != kCoseMac0EntryCount) {
- return "Invalid COSE_Mac0";
- }
-
- auto protectedParms = mac->get(kCoseMac0ProtectedParams)->asBstr();
- auto unprotectedParms = mac->get(kCoseMac0UnprotectedParams)->asMap();
- auto payload = mac->get(kCoseMac0Payload)->asBstr();
- auto tag = mac->get(kCoseMac0Tag)->asBstr();
- if (!protectedParms || !unprotectedParms || !payload || !tag) {
- return "Invalid COSE_Mac0 contents";
- }
-
- auto [protectedMap, _, errMsg] = cppbor::parse(protectedParms);
- if (!protectedMap || !protectedMap->asMap()) {
- return "Invalid Mac0 protected: " + errMsg;
- }
- auto& algo = protectedMap->asMap()->get(ALGORITHM);
- if (!algo || !algo->asInt() || algo->asInt()->value() != HMAC_256) {
- return "Unsupported Mac0 algorithm";
- }
-
- auto macTag = generateCoseMac0Mac(macKey, {} /* external_aad */, payload->value());
- if (!macTag) return macTag.moveMessage();
-
- if (macTag->size() != tag->value().size() ||
- CRYPTO_memcmp(macTag->data(), tag->value().data(), macTag->size()) != 0) {
- return "MAC tag mismatch";
- }
-
- return payload->value();
-}
-
-ErrMsgOr<bytevec> createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams,
- const bytevec& payload, const bytevec& aad) {
- bytevec signatureInput = cppbor::Array()
- .add("Signature1") //
- .add(protectedParams)
- .add(aad)
- .add(payload)
- .encode();
-
- if (key.size() != ED25519_PRIVATE_KEY_LEN) return "Invalid signing key";
- bytevec signature(ED25519_SIGNATURE_LEN);
- if (!ED25519_sign(signature.data(), signatureInput.data(), signatureInput.size(), key.data())) {
- return "Signing failed";
- }
-
- return signature;
-}
-
-ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, cppbor::Map protectedParams,
- const bytevec& payload, const bytevec& aad) {
- bytevec protParms = protectedParams.add(ALGORITHM, EDDSA).canonicalize().encode();
- auto signature = createCoseSign1Signature(key, protParms, payload, aad);
- if (!signature) return signature.moveMessage();
-
- return cppbor::Array()
- .add(protParms)
- .add(cppbor::Map() /* unprotected parameters */)
- .add(payload)
- .add(*signature);
-}
-
-ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, const bytevec& payload,
- const bytevec& aad) {
- return constructCoseSign1(key, {} /* protectedParams */, payload, aad);
-}
-
-ErrMsgOr<bytevec> verifyAndParseCoseSign1(bool ignoreSignature, const cppbor::Array* coseSign1,
- const bytevec& signingCoseKey, const bytevec& aad) {
- if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
- return "Invalid COSE_Sign1";
- }
-
- const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
- const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
- const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
- const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
-
- if (!protectedParams || !unprotectedParams || !payload || !signature) {
- return "Invalid COSE_Sign1";
- }
-
- auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams);
- if (!parsedProtParams) {
- return errMsg + " when parsing protected params.";
- }
- if (!parsedProtParams->asMap()) {
- return "Protected params must be a map";
- }
-
- auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) {
- return "Unsupported signature algorithm";
- }
-
- if (!ignoreSignature) {
- bool selfSigned = signingCoseKey.empty();
- auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey);
- if (!key) return "Bad signing key: " + key.moveMessage();
-
- bytevec signatureInput = cppbor::Array()
- .add("Signature1")
- .add(*protectedParams)
- .add(aad)
- .add(*payload)
- .encode();
-
- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
- key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
- return "Signature verification failed";
- }
- }
-
- return payload->value();
-}
-
-ErrMsgOr<bytevec> createCoseEncryptCiphertext(const bytevec& key, const bytevec& nonce,
- const bytevec& protectedParams,
- const bytevec& plaintextPayload, const bytevec& aad) {
- auto ciphertext = aesGcmEncrypt(key, nonce,
- cppbor::Array() // Enc strucure as AAD
- .add("Encrypt") // Context
- .add(protectedParams) // Protected
- .add(aad) // External AAD
- .encode(),
- plaintextPayload);
-
- if (!ciphertext) return ciphertext.moveMessage();
- return ciphertext.moveValue();
-}
-
-ErrMsgOr<cppbor::Array> constructCoseEncrypt(const bytevec& key, const bytevec& nonce,
- const bytevec& plaintextPayload, const bytevec& aad,
- cppbor::Array recipients) {
- auto encryptProtectedHeader = cppbor::Map() //
- .add(ALGORITHM, AES_GCM_256)
- .canonicalize()
- .encode();
-
- auto ciphertext =
- createCoseEncryptCiphertext(key, nonce, encryptProtectedHeader, plaintextPayload, aad);
- if (!ciphertext) return ciphertext.moveMessage();
-
- return cppbor::Array()
- .add(encryptProtectedHeader) // Protected
- .add(cppbor::Map().add(IV, nonce).canonicalize()) // Unprotected
- .add(*ciphertext) // Payload
- .add(std::move(recipients));
-}
-
-ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>> getSenderPubKeyFromCoseEncrypt(
- const cppbor::Item* coseEncrypt) {
- if (!coseEncrypt || !coseEncrypt->asArray() ||
- coseEncrypt->asArray()->size() != kCoseEncryptEntryCount) {
- return "Invalid COSE_Encrypt";
- }
-
- auto& recipients = coseEncrypt->asArray()->get(kCoseEncryptRecipients);
- if (!recipients || !recipients->asArray() || recipients->asArray()->size() != 1) {
- return "Invalid recipients list";
- }
-
- auto& recipient = recipients->asArray()->get(0);
- if (!recipient || !recipient->asArray() || recipient->asArray()->size() != 3) {
- return "Invalid COSE_recipient";
- }
-
- auto& ciphertext = recipient->asArray()->get(2);
- if (!ciphertext->asSimple() || !ciphertext->asSimple()->asNull()) {
- return "Unexpected value in recipients ciphertext field " +
- cppbor::prettyPrint(ciphertext.get());
- }
-
- auto& protParms = recipient->asArray()->get(0);
- if (!protParms || !protParms->asBstr()) return "Invalid protected params";
- auto [parsedProtParms, _, errMsg] = cppbor::parse(protParms->asBstr());
- if (!parsedProtParms) return "Failed to parse protected params: " + errMsg;
- if (!parsedProtParms->asMap()) return "Invalid protected params";
-
- auto& algorithm = parsedProtParms->asMap()->get(ALGORITHM);
- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != ECDH_ES_HKDF_256) {
- return "Invalid algorithm";
- }
-
- auto& unprotParms = recipient->asArray()->get(1);
- if (!unprotParms || !unprotParms->asMap()) return "Invalid unprotected params";
-
- auto& senderCoseKey = unprotParms->asMap()->get(COSE_KEY);
- if (!senderCoseKey || !senderCoseKey->asMap()) return "Invalid sender COSE_Key";
-
- auto& keyType = senderCoseKey->asMap()->get(CoseKey::KEY_TYPE);
- if (!keyType || !keyType->asInt() || keyType->asInt()->value() != OCTET_KEY_PAIR) {
- return "Invalid key type";
- }
-
- auto& curve = senderCoseKey->asMap()->get(CoseKey::CURVE);
- if (!curve || !curve->asInt() || curve->asInt()->value() != X25519) {
- return "Unsupported curve";
- }
-
- auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X);
- if (!pubkey || !pubkey->asBstr() ||
- pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) {
- return "Invalid X25519 public key";
- }
-
- auto& key_id = unprotParms->asMap()->get(KEY_ID);
- if (key_id && key_id->asBstr()) {
- return std::make_pair(pubkey->asBstr()->value(), key_id->asBstr()->value());
- }
-
- // If no key ID, just return an empty vector.
- return std::make_pair(pubkey->asBstr()->value(), bytevec{});
-}
-
-ErrMsgOr<bytevec> decryptCoseEncrypt(const bytevec& key, const cppbor::Item* coseEncrypt,
- const bytevec& external_aad) {
- if (!coseEncrypt || !coseEncrypt->asArray() ||
- coseEncrypt->asArray()->size() != kCoseEncryptEntryCount) {
- return "Invalid COSE_Encrypt";
- }
-
- auto& protParms = coseEncrypt->asArray()->get(kCoseEncryptProtectedParams);
- auto& unprotParms = coseEncrypt->asArray()->get(kCoseEncryptUnprotectedParams);
- auto& ciphertext = coseEncrypt->asArray()->get(kCoseEncryptPayload);
- auto& recipients = coseEncrypt->asArray()->get(kCoseEncryptRecipients);
-
- if (!protParms || !protParms->asBstr() || !unprotParms || !ciphertext || !recipients) {
- return "Invalid COSE_Encrypt";
- }
-
- auto [parsedProtParams, _, errMsg] = cppbor::parse(protParms->asBstr()->value());
- if (!parsedProtParams) {
- return errMsg + " when parsing protected params.";
- }
- if (!parsedProtParams->asMap()) {
- return "Protected params must be a map";
- }
-
- auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != AES_GCM_256) {
- return "Unsupported encryption algorithm";
- }
-
- if (!unprotParms->asMap() || unprotParms->asMap()->size() != 1) {
- return "Invalid unprotected params";
- }
-
- auto& nonce = unprotParms->asMap()->get(IV);
- if (!nonce || !nonce->asBstr() || nonce->asBstr()->value().size() != kAesGcmNonceLength) {
- return "Invalid nonce";
- }
-
- if (!ciphertext->asBstr()) return "Invalid ciphertext";
-
- auto aad = cppbor::Array() // Enc strucure as AAD
- .add("Encrypt") // Context
- .add(protParms->asBstr()->value()) // Protected
- .add(external_aad) // External AAD
- .encode();
-
- return aesGcmDecrypt(key, nonce->asBstr()->value(), aad, ciphertext->asBstr()->value());
-}
-
-ErrMsgOr<bytevec> x25519_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA,
- const bytevec& pubKeyB, bool senderIsA) {
- bytevec rawSharedKey(X25519_SHARED_KEY_LEN);
- if (!::X25519(rawSharedKey.data(), privKeyA.data(), pubKeyB.data())) {
- return "ECDH operation failed";
- }
-
- bytevec kdfContext = cppbor::Array()
- .add(AES_GCM_256)
- .add(cppbor::Array() // Sender Info
- .add(cppbor::Bstr("client"))
- .add(bytevec{} /* nonce */)
- .add(senderIsA ? pubKeyA : pubKeyB))
- .add(cppbor::Array() // Recipient Info
- .add(cppbor::Bstr("server"))
- .add(bytevec{} /* nonce */)
- .add(senderIsA ? pubKeyB : pubKeyA))
- .add(cppbor::Array() // SuppPubInfo
- .add(128) // output key length
- .add(bytevec{})) // protected
- .encode();
-
- bytevec retval(SHA256_DIGEST_LENGTH);
- bytevec salt{};
- if (!HKDF(retval.data(), retval.size(), //
- EVP_sha256(), //
- rawSharedKey.data(), rawSharedKey.size(), //
- salt.data(), salt.size(), //
- kdfContext.data(), kdfContext.size())) {
- return "ECDH HKDF failed";
- }
-
- return retval;
-}
-
-ErrMsgOr<bytevec> aesGcmEncrypt(const bytevec& key, const bytevec& nonce, const bytevec& aad,
- const bytevec& plaintext) {
- auto ctx = aesGcmInitAndProcessAad(key, nonce, aad, true /* encrypt */);
- if (!ctx) return ctx.moveMessage();
-
- bytevec ciphertext(plaintext.size() + kAesGcmTagSize);
- int outlen;
- if (!EVP_CipherUpdate(ctx->get(), ciphertext.data(), &outlen, plaintext.data(),
- plaintext.size())) {
- return "Failed to encrypt plaintext";
- }
- assert(plaintext.size() == outlen);
-
- if (!EVP_CipherFinal_ex(ctx->get(), ciphertext.data() + outlen, &outlen)) {
- return "Failed to finalize encryption";
- }
- assert(outlen == 0);
-
- if (!EVP_CIPHER_CTX_ctrl(ctx->get(), EVP_CTRL_GCM_GET_TAG, kAesGcmTagSize,
- ciphertext.data() + plaintext.size())) {
- return "Failed to retrieve tag";
- }
-
- return ciphertext;
-}
-
-ErrMsgOr<bytevec> aesGcmDecrypt(const bytevec& key, const bytevec& nonce, const bytevec& aad,
- const bytevec& ciphertextWithTag) {
- auto ctx = aesGcmInitAndProcessAad(key, nonce, aad, false /* encrypt */);
- if (!ctx) return ctx.moveMessage();
-
- if (ciphertextWithTag.size() < kAesGcmTagSize) return "Missing tag";
-
- bytevec plaintext(ciphertextWithTag.size() - kAesGcmTagSize);
- int outlen;
- if (!EVP_CipherUpdate(ctx->get(), plaintext.data(), &outlen, ciphertextWithTag.data(),
- ciphertextWithTag.size() - kAesGcmTagSize)) {
- return "Failed to decrypt plaintext";
- }
- assert(plaintext.size() == outlen);
-
- bytevec tag(ciphertextWithTag.end() - kAesGcmTagSize, ciphertextWithTag.end());
- if (!EVP_CIPHER_CTX_ctrl(ctx->get(), EVP_CTRL_GCM_SET_TAG, kAesGcmTagSize, tag.data())) {
- return "Failed to set tag: " + std::to_string(ERR_peek_last_error());
- }
-
- if (!EVP_CipherFinal_ex(ctx->get(), nullptr, &outlen)) {
- return "Failed to finalize encryption";
- }
- assert(outlen == 0);
-
- return plaintext;
-}
-
-} // namespace cppcose
diff --git a/security/keymint/support/include/cppcose/cppcose.h b/security/keymint/support/include/cppcose/cppcose.h
deleted file mode 100644
index a936bfd..0000000
--- a/security/keymint/support/include/cppcose/cppcose.h
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-#include <openssl/cipher.h>
-#include <openssl/curve25519.h>
-#include <openssl/digest.h>
-#include <openssl/hkdf.h>
-#include <openssl/hmac.h>
-#include <openssl/mem.h>
-#include <openssl/sha.h>
-
-namespace cppcose {
-
-using bytevec = std::vector<uint8_t>;
-
-constexpr int kCoseSign1EntryCount = 4;
-constexpr int kCoseSign1ProtectedParams = 0;
-constexpr int kCoseSign1UnprotectedParams = 1;
-constexpr int kCoseSign1Payload = 2;
-constexpr int kCoseSign1Signature = 3;
-
-constexpr int kCoseMac0EntryCount = 4;
-constexpr int kCoseMac0ProtectedParams = 0;
-constexpr int kCoseMac0UnprotectedParams = 1;
-constexpr int kCoseMac0Payload = 2;
-constexpr int kCoseMac0Tag = 3;
-
-constexpr int kCoseEncryptEntryCount = 4;
-constexpr int kCoseEncryptProtectedParams = 0;
-constexpr int kCoseEncryptUnprotectedParams = 1;
-constexpr int kCoseEncryptPayload = 2;
-constexpr int kCoseEncryptRecipients = 3;
-
-enum Label : int {
- ALGORITHM = 1,
- KEY_ID = 4,
- IV = 5,
- COSE_KEY = -1,
-};
-
-enum CoseKeyAlgorithm : int {
- AES_GCM_256 = 3,
- HMAC_256 = 5,
- ES256 = -7, // ECDSA with SHA-256
- EDDSA = -8,
- ECDH_ES_HKDF_256 = -25,
-};
-
-enum CoseKeyCurve : int { P256 = 1, X25519 = 4, ED25519 = 6 };
-enum CoseKeyType : int { OCTET_KEY_PAIR = 1, EC2 = 2, SYMMETRIC_KEY = 4 };
-enum CoseKeyOps : int { SIGN = 1, VERIFY = 2, ENCRYPT = 3, DECRYPT = 4 };
-
-constexpr int kAesGcmNonceLength = 12;
-constexpr int kAesGcmTagSize = 16;
-constexpr int kAesGcmKeySize = 32;
-
-template <typename T>
-class ErrMsgOr {
- public:
- ErrMsgOr(std::string errMsg) : errMsg_(std::move(errMsg)) {}
- ErrMsgOr(const char* errMsg) : errMsg_(errMsg) {}
- ErrMsgOr(T val) : value_(std::move(val)) {}
-
- operator bool() const { return value_.has_value(); }
-
- T* operator->() & {
- assert(value_);
- return &value_.value();
- }
- T& operator*() & {
- assert(value_);
- return value_.value();
- };
- T&& operator*() && {
- assert(value_);
- return std::move(value_).value();
- };
-
- const std::string& message() { return errMsg_; }
- std::string moveMessage() { return std::move(errMsg_); }
-
- T moveValue() {
- assert(value_);
- return std::move(value_).value();
- }
-
- private:
- std::string errMsg_;
- std::optional<T> value_;
-};
-
-class CoseKey {
- public:
- CoseKey() {}
- CoseKey(const CoseKey&) = delete;
- CoseKey(CoseKey&&) = default;
-
- enum Label : int {
- KEY_TYPE = 1,
- KEY_ID = 2,
- ALGORITHM = 3,
- KEY_OPS = 4,
- CURVE = -1,
- PUBKEY_X = -2,
- PUBKEY_Y = -3,
- PRIVATE_KEY = -4,
- TEST_KEY = -70000 // Application-defined
- };
-
- static ErrMsgOr<CoseKey> parse(const bytevec& coseKey) {
- auto [parsedKey, _, errMsg] = cppbor::parse(coseKey);
- if (!parsedKey) return errMsg + " when parsing key";
- if (!parsedKey->asMap()) return "CoseKey must be a map";
- return CoseKey(static_cast<cppbor::Map*>(parsedKey.release()));
- }
-
- static ErrMsgOr<CoseKey> parse(const bytevec& coseKey, CoseKeyType expectedKeyType,
- CoseKeyAlgorithm expectedAlgorithm, CoseKeyCurve expectedCurve) {
- auto key = parse(coseKey);
- if (!key) return key;
-
- if (!key->checkIntValue(CoseKey::KEY_TYPE, expectedKeyType) ||
- !key->checkIntValue(CoseKey::ALGORITHM, expectedAlgorithm) ||
- !key->checkIntValue(CoseKey::CURVE, expectedCurve)) {
- return "Unexpected key type:";
- }
-
- return key;
- }
-
- static ErrMsgOr<CoseKey> parseEd25519(const bytevec& coseKey) {
- auto key = parse(coseKey, OCTET_KEY_PAIR, EDDSA, ED25519);
- if (!key) return key;
-
- auto& pubkey = key->getMap().get(PUBKEY_X);
- if (!pubkey || !pubkey->asBstr() ||
- pubkey->asBstr()->value().size() != ED25519_PUBLIC_KEY_LEN) {
- return "Invalid Ed25519 public key";
- }
-
- return key;
- }
-
- static ErrMsgOr<CoseKey> parseX25519(const bytevec& coseKey, bool requireKid) {
- auto key = parse(coseKey, OCTET_KEY_PAIR, ECDH_ES_HKDF_256, X25519);
- if (!key) return key;
-
- auto& pubkey = key->getMap().get(PUBKEY_X);
- if (!pubkey || !pubkey->asBstr() ||
- pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) {
- return "Invalid X25519 public key";
- }
-
- auto& kid = key->getMap().get(KEY_ID);
- if (requireKid && (!kid || !kid->asBstr())) {
- return "Missing KID";
- }
-
- return key;
- }
-
- static ErrMsgOr<CoseKey> parseP256(const bytevec& coseKey) {
- auto key = parse(coseKey, EC2, ES256, P256);
- if (!key) return key;
-
- auto& pubkey_x = key->getMap().get(PUBKEY_X);
- auto& pubkey_y = key->getMap().get(PUBKEY_Y);
- if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() ||
- pubkey_x->asBstr()->value().size() != 32 || pubkey_y->asBstr()->value().size() != 32) {
- return "Invalid P256 public key";
- }
-
- return key;
- }
-
- std::optional<int> getIntValue(Label label) {
- const auto& value = key_->get(label);
- if (!value || !value->asInt()) return {};
- return value->asInt()->value();
- }
-
- std::optional<bytevec> getBstrValue(Label label) {
- const auto& value = key_->get(label);
- if (!value || !value->asBstr()) return {};
- return value->asBstr()->value();
- }
-
- const cppbor::Map& getMap() const { return *key_; }
- cppbor::Map&& moveMap() { return std::move(*key_); }
-
- bool checkIntValue(Label label, int expectedValue) {
- const auto& value = key_->get(label);
- return value && value->asInt() && value->asInt()->value() == expectedValue;
- }
-
- void add(Label label, int value) { key_->add(label, value); }
- void add(Label label, bytevec value) { key_->add(label, std::move(value)); }
-
- bytevec encode() { return key_->canonicalize().encode(); }
-
- private:
- CoseKey(cppbor::Map* parsedKey) : key_(parsedKey) {}
-
- // This is the full parsed key structure.
- std::unique_ptr<cppbor::Map> key_;
-};
-
-ErrMsgOr<bytevec> generateCoseMac0Mac(const bytevec& macKey, const bytevec& externalAad,
- const bytevec& payload);
-ErrMsgOr<cppbor::Array> constructCoseMac0(const bytevec& macKey, const bytevec& externalAad,
- const bytevec& payload);
-ErrMsgOr<bytevec /* payload */> parseCoseMac0(const cppbor::Item* macItem);
-ErrMsgOr<bytevec /* payload */> verifyAndParseCoseMac0(const cppbor::Item* macItem,
- const bytevec& macKey);
-
-ErrMsgOr<bytevec> createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams,
- const bytevec& payload, const bytevec& aad);
-ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, const bytevec& payload,
- const bytevec& aad);
-ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields,
- const bytevec& payload, const bytevec& aad);
-/**
- * Verify and parse a COSE_Sign1 message, returning the payload.
- *
- * @param ignoreSignature indicates whether signature verification should be skipped. If true, no
- * verification of the signature will be done.
- *
- * @param coseSign1 is the COSE_Sign1 to verify and parse.
- *
- * @param signingCoseKey is a CBOR-encoded COSE_Key to use to verify the signature. The bytevec may
- * be empty, in which case the function assumes that coseSign1's payload is the COSE_Key to
- * use, i.e. that coseSign1 is a self-signed "certificate".
- */
-ErrMsgOr<bytevec /* payload */> verifyAndParseCoseSign1(bool ignoreSignature,
- const cppbor::Array* coseSign1,
- const bytevec& signingCoseKey,
- const bytevec& aad);
-
-ErrMsgOr<bytevec> createCoseEncryptCiphertext(const bytevec& key, const bytevec& nonce,
- const bytevec& protectedParams, const bytevec& aad);
-ErrMsgOr<cppbor::Array> constructCoseEncrypt(const bytevec& key, const bytevec& nonce,
- const bytevec& plaintextPayload, const bytevec& aad,
- cppbor::Array recipients);
-ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>> getSenderPubKeyFromCoseEncrypt(
- const cppbor::Item* encryptItem);
-inline ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>>
-getSenderPubKeyFromCoseEncrypt(const std::unique_ptr<cppbor::Item>& encryptItem) {
- return getSenderPubKeyFromCoseEncrypt(encryptItem.get());
-}
-
-ErrMsgOr<bytevec /* plaintextPayload */> decryptCoseEncrypt(const bytevec& key,
- const cppbor::Item* encryptItem,
- const bytevec& aad);
-
-ErrMsgOr<bytevec> x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytevec& senderPrivKey,
- const bytevec& recipientPubKey, bool senderIsA);
-
-ErrMsgOr<bytevec /* ciphertextWithTag */> aesGcmEncrypt(const bytevec& key, const bytevec& nonce,
- const bytevec& aad,
- const bytevec& plaintext);
-ErrMsgOr<bytevec /* plaintext */> aesGcmDecrypt(const bytevec& key, const bytevec& nonce,
- const bytevec& aad,
- const bytevec& ciphertextWithTag);
-
-} // namespace cppcose
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 5e205a2..e4261f3 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -18,7 +18,7 @@
#include <vector>
-#include <cppcose/cppcose.h>
+#include <keymaster/cppcose/cppcose.h>
namespace aidl::android::hardware::security::keymint::remote_prov {
diff --git a/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
index 9ca1ee8..31f4854 100644
--- a/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
+++ b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
@@ -185,9 +185,11 @@
INSTANTIATE_TEST_SUITE_P(PerInstance, SecureClockAidlTest,
testing::ValuesIn(SecureClockAidlTest::build_params()),
::android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureClockAidlTest);
+
} // namespace aidl::android::hardware::security::secureclock::test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/sensors/2.1/default/SensorsV2_1.cpp b/sensors/2.1/default/SensorsV2_1.cpp
index 4c5386a..06446a2 100644
--- a/sensors/2.1/default/SensorsV2_1.cpp
+++ b/sensors/2.1/default/SensorsV2_1.cpp
@@ -46,7 +46,8 @@
mSensorInfo.fifoMaxEventCount = 0;
mSensorInfo.requiredPermission = "";
mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE |
- V1_0::SensorFlagBits::WAKE_UP);
+ V1_0::SensorFlagBits::WAKE_UP |
+ V1_0::SensorFlagBits::DATA_INJECTION);
}
};
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 48ce384..0430646 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -141,6 +141,8 @@
// IP filter can be an MMTP filter's data source.
caps.linkCaps = {0x00, 0x00, 0x02, 0x00, 0x00};
+ // Support time filter testing
+ caps.bTimeFilter = true;
_hidl_cb(Result::SUCCESS, caps);
return Void();
}
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 47d3b2f..6187c73 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -35,6 +35,15 @@
"DescramblerTests.cpp",
"LnbTests.cpp",
],
+ generated_headers: [
+ "tuner_testing_dynamic_configuration_V1_0_enums",
+ "tuner_testing_dynamic_configuration_V1_0_parser",
+ ],
+ generated_sources: [
+ "tuner_testing_dynamic_configuration_V1_0_enums",
+ "tuner_testing_dynamic_configuration_V1_0_parser",
+ ],
+ header_libs: ["libxsdc-utils"],
static_libs: [
"android.hardware.cas@1.0",
"android.hardware.cas@1.1",
@@ -49,6 +58,12 @@
],
shared_libs: [
"libbinder",
+ "libxml2",
+ ],
+ data: [
+ ":tuner_frontend_input_ts",
+ ":tuner_frontend_input_es",
+ ":tuner_testing_dynamic_configuration_V1_0",
],
test_suites: [
"general-tests",
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.cpp b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
index 45951d2..b35d112 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
@@ -370,13 +370,11 @@
mIsSoftwareFe = config.isSoftwareFe;
bool result = true;
if (mIsSoftwareFe && testWithDemux) {
- DvrConfig dvrConfig;
- getSoftwareFrontendPlaybackConfig(dvrConfig);
- result &= mDvrTests.openDvrInDemux(dvrConfig.type, dvrConfig.bufferSize) == success();
- result &= mDvrTests.configDvrPlayback(dvrConfig.settings) == success();
+ result &= mDvrTests.openDvrInDemux(mDvrConfig.type, mDvrConfig.bufferSize) == success();
+ result &= mDvrTests.configDvrPlayback(mDvrConfig.settings) == success();
result &= mDvrTests.getDvrPlaybackMQDescriptor() == success();
- mDvrTests.startPlaybackInputThread(dvrConfig.playbackInputFile,
- dvrConfig.settings.playback());
+ mDvrTests.startPlaybackInputThread(mDvrConfig.playbackInputFile,
+ mDvrConfig.settings.playback());
if (!result) {
ALOGW("[vts] Software frontend dvr configure failed.");
return failure();
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.h b/tv/tuner/1.0/vts/functional/FrontendTests.h
index c536325..33ff603 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.h
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.h
@@ -104,6 +104,7 @@
void setService(sp<ITuner> tuner) {
mService = tuner;
mDvrTests.setService(tuner);
+ getDefaultSoftwareFrontendPlaybackConfig(mDvrConfig);
}
AssertionResult getFrontendIds();
@@ -125,12 +126,14 @@
void setDvrTests(DvrTests dvrTests) { mDvrTests = dvrTests; }
void setDemux(sp<IDemux> demux) { mDvrTests.setDemux(demux); }
+ void setSoftwareFrontendDvrConfig(DvrConfig conf) { mDvrConfig = conf; }
protected:
static AssertionResult failure() { return ::testing::AssertionFailure(); }
static AssertionResult success() { return ::testing::AssertionSuccess(); }
- void getSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
+ // TODO: replace with customized dvr input
+ void getDefaultSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
PlaybackSettings playbackSettings{
.statusMask = 0xf,
.lowThreshold = 0x1000,
@@ -151,4 +154,5 @@
DvrTests mDvrTests;
bool mIsSoftwareFe = false;
+ DvrConfig mDvrConfig;
};
diff --git a/tv/tuner/1.0/vts/functional/LnbTests.cpp b/tv/tuner/1.0/vts/functional/LnbTests.cpp
index 9080f59..9338c73 100644
--- a/tv/tuner/1.0/vts/functional/LnbTests.cpp
+++ b/tv/tuner/1.0/vts/functional/LnbTests.cpp
@@ -48,10 +48,11 @@
return AssertionResult(status == Result::SUCCESS);
}
-AssertionResult LnbTests::openLnbByName(string lnbName) {
+AssertionResult LnbTests::openLnbByName(string lnbName, uint32_t& id) {
Result status;
- mService->openLnbByName(lnbName, [&](Result result, uint32_t /*lnbId*/, const sp<ILnb>& lnb) {
+ mService->openLnbByName(lnbName, [&](Result result, uint32_t lnbId, const sp<ILnb>& lnb) {
mLnb = lnb;
+ id = lnbId;
status = result;
});
diff --git a/tv/tuner/1.0/vts/functional/LnbTests.h b/tv/tuner/1.0/vts/functional/LnbTests.h
index 2fdbe2c..62b42ff 100644
--- a/tv/tuner/1.0/vts/functional/LnbTests.h
+++ b/tv/tuner/1.0/vts/functional/LnbTests.h
@@ -64,7 +64,7 @@
AssertionResult getLnbIds(vector<uint32_t>& ids);
AssertionResult openLnbById(uint32_t lnbId);
- AssertionResult openLnbByName(string lnbName);
+ AssertionResult openLnbByName(string lnbName, uint32_t& lnbId);
AssertionResult setLnbCallback();
AssertionResult setVoltage(LnbVoltage voltage);
AssertionResult setTone(LnbTone tone);
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 22ba271..4c92665 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -18,16 +18,15 @@
namespace {
-AssertionResult TunerBroadcastHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerBroadcastHidlTest::filterDataOutputTest() {
return filterDataOutputTestBase(mFilterTests);
}
-AssertionResult TunerPlaybackHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerPlaybackHidlTest::filterDataOutputTest() {
return filterDataOutputTestBase(mFilterTests);
}
-AssertionResult TunerDescramblerHidlTest::filterDataOutputTest(
- vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerDescramblerHidlTest::filterDataOutputTest() {
return filterDataOutputTestBase(mFilterTests);
}
@@ -57,13 +56,16 @@
}
void TunerFilterHidlTest::testTimeFilter(TimeFilterConfig filterConf) {
- if (!filterConf.supportTimeFilter) {
+ if (!timeFilter.support) {
return;
}
uint32_t demuxId;
sp<IDemux> demux;
+ DemuxCapabilities caps;
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.getDemuxCaps(caps));
+ ASSERT_TRUE(caps.bTimeFilter);
mFilterTests.setDemux(demux);
ASSERT_TRUE(mFilterTests.openTimeFilterInDemux());
ASSERT_TRUE(mFilterTests.setTimeStamp(filterConf.timeStamp));
@@ -75,26 +77,20 @@
void TunerBroadcastHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
FrontendConfig frontendConf) {
- if (!frontendConf.enable) {
- return;
- }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
uint32_t filterId;
mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
- if (feId == INVALID_ID) {
- // TODO broadcast test on Cuttlefish needs licensed ts input,
- // these tests are runnable on vendor device with real frontend module
- // or with manual ts installing and use DVBT frontend.
- return;
- }
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
if (mLnbId) {
ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
}
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+ }
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFrontendTests.setDemux(demux);
@@ -106,7 +102,7 @@
ASSERT_TRUE(mFilterTests.startFilter(filterId));
// tune test
ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
- ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+ ASSERT_TRUE(filterDataOutputTest());
ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -117,14 +113,16 @@
void TunerBroadcastHidlTest::broadcastSingleFilterTestWithLnb(FilterConfig filterConf,
FrontendConfig frontendConf,
LnbConfig lnbConf) {
- vector<uint32_t> ids;
- ASSERT_TRUE(mLnbTests.getLnbIds(ids));
- if (!lnbConf.usingLnb) {
- return;
+ if (lnbConf.name.compare(emptyHardwareId) == 0) {
+ vector<uint32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ mLnbId = &ids[0];
+ } else {
+ mLnbId = (uint32_t*)malloc(sizeof(uint32_t));
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
}
- ASSERT_TRUE(ids.size() > 0);
- ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
- *mLnbId = ids[0];
ASSERT_TRUE(mLnbTests.setLnbCallback());
ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
@@ -152,7 +150,7 @@
mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile, dvrConf.settings.playback());
ASSERT_TRUE(mDvrTests.startDvrPlayback());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
- ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+ ASSERT_TRUE(filterDataOutputTest());
mDvrTests.stopPlaybackThread();
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mDvrTests.stopDvrPlayback());
@@ -163,9 +161,6 @@
void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
FrontendConfig frontendConf, DvrConfig dvrConf) {
- if (!frontendConf.enable) {
- return;
- }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -179,6 +174,9 @@
if (mLnbId) {
ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
}
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+ }
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
@@ -213,18 +211,23 @@
void TunerRecordHidlTest::recordSingleFilterTestWithLnb(FilterConfig filterConf,
FrontendConfig frontendConf,
DvrConfig dvrConf, LnbConfig lnbConf) {
- vector<uint32_t> ids;
- ASSERT_TRUE(mLnbTests.getLnbIds(ids));
- if (!lnbConf.usingLnb) {
- return;
+ if (lnbConf.name.compare(emptyHardwareId) == 0) {
+ vector<uint32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ mLnbId = &ids[0];
+ } else {
+ mLnbId = (uint32_t*)malloc(sizeof(uint32_t));
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
}
- ASSERT_TRUE(ids.size() > 0);
- ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
- *mLnbId = ids[0];
ASSERT_TRUE(mLnbTests.setLnbCallback());
ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
+ for (auto msgName : lnbRecord.diseqcMsgs) {
+ ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+ }
recordSingleFilterTest(filterConf, frontendConf, dvrConf);
ASSERT_TRUE(mLnbTests.closeLnb());
mLnbId = nullptr;
@@ -271,9 +274,6 @@
void TunerDescramblerHidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
FrontendConfig frontendConf,
DescramblerConfig descConfig) {
- if (!frontendConf.enable) {
- return;
- }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -283,14 +283,11 @@
set<uint32_t>::iterator id;
mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
- if (feId == INVALID_ID) {
- // TODO broadcast test on Cuttlefish needs licensed ts input,
- // these tests are runnable on vendor device with real frontend module
- // or with manual ts installing and use DVBT frontend.
- return;
- }
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (frontendConf.isSoftwareFe) {
+ mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+ }
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
@@ -305,7 +302,7 @@
TunerKeyToken token;
ASSERT_TRUE(mDescramblerTests.getKeyToken(descConfig.casSystemId, descConfig.provisionStr,
descConfig.hidlPvtData, token));
- ASSERT_TRUE(mDescramblerTests.setKeyToken(token));
+ mDescramblerTests.setKeyToken(token);
vector<DemuxPid> pids;
DemuxPid pid;
for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
@@ -319,7 +316,7 @@
}
// tune test
ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
- ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+ ASSERT_TRUE(filterDataOutputTest());
ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
for (id = filterIds.begin(); id != filterIds.end(); id++) {
ASSERT_TRUE(mFilterTests.stopFilter(*id));
@@ -337,44 +334,37 @@
TEST_P(TunerFrontendHidlTest, TuneFrontend) {
description("Tune one Frontend with specific setting and check Lock event");
- mFrontendTests.tuneTest(frontendArray[defaultFrontend]);
+ mFrontendTests.tuneTest(frontendMap[live.frontendId]);
}
TEST_P(TunerFrontendHidlTest, AutoScanFrontend) {
description("Run an auto frontend scan with specific setting and check lock scanMessage");
- mFrontendTests.scanTest(frontendScanArray[defaultScanFrontend], FrontendScanType::SCAN_AUTO);
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_AUTO);
}
TEST_P(TunerFrontendHidlTest, BlindScanFrontend) {
description("Run an blind frontend scan with specific setting and check lock scanMessage");
- mFrontendTests.scanTest(frontendScanArray[defaultScanFrontend], FrontendScanType::SCAN_BLIND);
-}
-
-TEST_P(TunerLnbHidlTest, OpenLnbByName) {
- description("Open and configure an Lnb with name then send a diseqc msg to it.");
- ASSERT_TRUE(mLnbTests.openLnbByName(lnbArray[LNB_EXTERNAL].name));
- ASSERT_TRUE(mLnbTests.setLnbCallback());
- ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB_EXTERNAL].voltage));
- ASSERT_TRUE(mLnbTests.setTone(lnbArray[LNB_EXTERNAL].tone));
- ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbArray[LNB_EXTERNAL].position));
- ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgArray[DISEQC_POWER_ON]));
- ASSERT_TRUE(mLnbTests.closeLnb());
+ mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
}
TEST_P(TunerLnbHidlTest, SendDiseqcMessageToLnb) {
description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
- vector<uint32_t> ids;
- ASSERT_TRUE(mLnbTests.getLnbIds(ids));
- if (!lnbArray[LNB0].usingLnb) {
- return;
+ if (lnbMap[lnbLive.lnbId].name.compare(emptyHardwareId) == 0) {
+ vector<uint32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ } else {
+ uint32_t id;
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbMap[lnbLive.lnbId].name, id));
}
- ASSERT_TRUE(ids.size() > 0);
- ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
ASSERT_TRUE(mLnbTests.setLnbCallback());
- ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB0].voltage));
- ASSERT_TRUE(mLnbTests.setTone(lnbArray[LNB0].tone));
- ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbArray[LNB0].position));
- ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgArray[DISEQC_POWER_ON]));
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbMap[lnbLive.lnbId].voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbMap[lnbLive.lnbId].tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbMap[lnbLive.lnbId].position));
+ for (auto msgName : lnbLive.diseqcMsgs) {
+ ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+ }
ASSERT_TRUE(mLnbTests.closeLnb());
}
@@ -383,7 +373,7 @@
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
- mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
+ mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -395,6 +385,9 @@
TEST_P(TunerDemuxHidlTest, getAvSyncTime) {
description("Get the A/V sync time from a PCR filter.");
+ if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -403,22 +396,22 @@
uint32_t avSyncHwId;
sp<IFilter> mediaFilter;
- mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
+ mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_VIDEO1].type,
- filterArray[TS_VIDEO1].bufferSize));
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.videoFilterId].type,
+ filterMap[live.videoFilterId].bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(mediaFilterId));
- ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_VIDEO1].settings, mediaFilterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.videoFilterId].settings, mediaFilterId));
mediaFilter = mFilterTests.getFilterById(mediaFilterId);
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_PCR0].type,
- filterArray[TS_PCR0].bufferSize));
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.pcrFilterId].type,
+ filterMap[live.pcrFilterId].bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(pcrFilterId));
- ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_PCR0].settings, pcrFilterId));
+ ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.pcrFilterId].settings, pcrFilterId));
ASSERT_TRUE(mDemuxTests.getAvSyncId(mediaFilter, avSyncHwId));
ASSERT_TRUE(pcrFilterId == avSyncHwId);
ASSERT_TRUE(mDemuxTests.getAvSyncTime(pcrFilterId));
@@ -431,7 +424,7 @@
TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
description("Open and start a filter in Demux.");
// TODO use paramterized tests
- configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
+ configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerFilterHidlTest, SetFilterLinkage) {
@@ -448,11 +441,9 @@
if (caps.linkCaps[i] & (bitMask << j)) {
uint32_t sourceFilterId;
uint32_t sinkFilterId;
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterLinkageTypes[SOURCE][i],
- FMQ_SIZE_16M));
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(getLinkageFilterType(i), FMQ_SIZE_16M));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(sourceFilterId));
- ASSERT_TRUE(
- mFilterTests.openFilterInDemux(filterLinkageTypes[SINK][j], FMQ_SIZE_16M));
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(getLinkageFilterType(j), FMQ_SIZE_16M));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(sinkFilterId));
ASSERT_TRUE(mFilterTests.setFilterDataSource(sourceFilterId, sinkFilterId));
ASSERT_TRUE(mFilterTests.setFilterDataSourceToDemux(sinkFilterId));
@@ -467,63 +458,87 @@
TEST_P(TunerFilterHidlTest, testTimeFilter) {
description("Open a timer filter in Demux and set time stamp.");
// TODO use paramterized tests
- testTimeFilter(timeFilterArray[TIMER0]);
+ testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
}
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
description("Test Video Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[defaultFrontend]);
+ broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowAudioFilterTest) {
description("Test Audio Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[defaultFrontend]);
+ broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowSectionFilterTest) {
description("Test Section Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[defaultFrontend]);
+ if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
+ broadcastSingleFilterTest(filterMap[live.sectionFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerBroadcastHidlTest, IonBufferTest) {
description("Test the av filter data bufferring.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
+ broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
}
TEST_P(TunerBroadcastHidlTest, LnbBroadcastDataFlowVideoFilterTest) {
description("Test Video Filter functionality in Broadcast with Lnb use case.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBS]);
+ if (!lnbLive.support) {
+ return;
+ }
+ broadcastSingleFilterTestWithLnb(filterMap[lnbLive.videoFilterId],
+ frontendMap[lnbLive.frontendId], lnbMap[lnbLive.lnbId]);
}
TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
description("Feed ts data from playback and configure Ts section filter to get output");
- playbackSingleFilterTest(filterArray[TS_SECTION0], dvrArray[DVR_PLAYBACK0]);
+ if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) {
+ return;
+ }
+ playbackSingleFilterTest(filterMap[playback.sectionFilterId], dvrMap[playback.dvrId]);
}
TEST_P(TunerRecordHidlTest, AttachFiltersToRecordTest) {
description("Attach a single filter to the record dvr test.");
// TODO use paramterized tests
- attachSingleFilterToRecordDvrTest(filterArray[TS_RECORD0], frontendArray[defaultFrontend],
- dvrArray[DVR_RECORD0]);
+ if (!record.support) {
+ return;
+ }
+ attachSingleFilterToRecordDvrTest(filterMap[record.recordFilterId],
+ frontendMap[record.frontendId], dvrMap[record.dvrRecordId]);
}
TEST_P(TunerRecordHidlTest, RecordDataFlowWithTsRecordFilterTest) {
description("Feed ts data from frontend to recording and test with ts record filter");
- recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[defaultFrontend],
- dvrArray[DVR_RECORD0]);
+ if (!record.support) {
+ return;
+ }
+ recordSingleFilterTest(filterMap[record.recordFilterId], frontendMap[record.frontendId],
+ dvrMap[record.dvrRecordId]);
}
TEST_P(TunerRecordHidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
description("Feed ts data from Fe with Lnb to recording and test with ts record filter");
- recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBS], dvrArray[DVR_RECORD0]);
+ if (lnbRecord.support) {
+ return;
+ }
+ recordSingleFilterTestWithLnb(filterMap[lnbRecord.recordFilterId],
+ frontendMap[lnbRecord.frontendId], dvrMap[lnbRecord.dvrRecordId],
+ lnbMap[lnbRecord.lnbId]);
}
TEST_P(TunerDescramblerHidlTest, CreateDescrambler) {
description("Create Descrambler");
+ if (descrambling.support) {
+ return;
+ }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
- mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
+ mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -537,10 +552,14 @@
TEST_P(TunerDescramblerHidlTest, ScrambledBroadcastDataFlowMediaFiltersTest) {
description("Test ts audio filter in scrambled broadcast use case");
+ if (descrambling.support) {
+ return;
+ }
set<FilterConfig> filterConfs;
- filterConfs.insert(filterArray[TS_AUDIO0]);
- filterConfs.insert(filterArray[TS_VIDEO1]);
- scrambledBroadcastTest(filterConfs, frontendArray[defaultFrontend], descramblerArray[DESC_0]);
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.audioFilterId]));
+ filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.videoFilterId]));
+ scrambledBroadcastTest(filterConfs, frontendMap[descrambling.frontendId],
+ descramblerMap[descrambling.descramblerId]);
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index 5a23ca5..e240604 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -20,6 +20,9 @@
#include "LnbTests.h"
using android::hardware::tv::tuner::V1_0::DataFormat;
+using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
using android::hardware::tv::tuner::V1_0::IDescrambler;
static AssertionResult success() {
@@ -28,14 +31,22 @@
namespace {
-void initConfiguration() {
+bool initConfiguration() {
+ if (!TunerTestingConfigReader::checkConfigFileExists()) {
+ return false;
+ }
initFrontendConfig();
- initFrontendScanConfig();
- initLnbConfig();
initFilterConfig();
- initTimeFilterConfig();
initDvrConfig();
+ initLnbConfig();
+ initTimeFilterConfig();
initDescramblerConfig();
+ connectHardwaresToTestCases();
+ if (!validateConnections()) {
+ ALOGW("[vts] failed to validate connections.");
+ return false;
+ }
+ return true;
}
AssertionResult filterDataOutputTestBase(FilterTests tests) {
@@ -53,7 +64,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
}
@@ -75,7 +86,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mLnbTests.setService(mService);
}
@@ -97,7 +108,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -123,7 +134,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -138,6 +149,29 @@
void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
void testTimeFilter(TimeFilterConfig filterConf);
+ DemuxFilterType getLinkageFilterType(int bit) {
+ DemuxFilterType type;
+ type.mainType = static_cast<DemuxFilterMainType>(1 << bit);
+ switch (type.mainType) {
+ case DemuxFilterMainType::TS:
+ type.subType.tsFilterType(DemuxTsFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::MMTP:
+ type.subType.mmtpFilterType(DemuxMmtpFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::IP:
+ type.subType.ipFilterType(DemuxIpFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::TLV:
+ type.subType.tlvFilterType(DemuxTlvFilterType::UNDEFINED);
+ break;
+ case DemuxFilterMainType::ALP:
+ type.subType.alpFilterType(DemuxAlpFilterType::UNDEFINED);
+ break;
+ }
+ return type;
+ }
+
sp<ITuner> mService;
FrontendTests mFrontendTests;
DemuxTests mDemuxTests;
@@ -152,7 +186,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -173,7 +207,7 @@
LnbTests mLnbTests;
DvrTests mDvrTests;
- AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+ AssertionResult filterDataOutputTest();
void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
void broadcastSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
@@ -191,7 +225,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -210,7 +244,7 @@
FilterTests mFilterTests;
DvrTests mDvrTests;
- AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+ AssertionResult filterDataOutputTest();
void playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf);
};
@@ -223,7 +257,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -265,7 +299,7 @@
mCasService = IMediaCasService::getService();
ASSERT_NE(mService, nullptr);
ASSERT_NE(mCasService, nullptr);
- initConfiguration();
+ ASSERT_TRUE(initConfiguration());
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -281,7 +315,7 @@
void scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
FrontendConfig frontendConf, DescramblerConfig descConfig);
- AssertionResult filterDataOutputTest(vector<string> /*goldenOutputFiles*/);
+ AssertionResult filterDataOutputTest();
sp<ITuner> mService;
sp<IMediaCasService> mCasService;
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 92a8130..65f8615 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -21,372 +21,224 @@
#include <hidl/Status.h>
#include <hidlmemory/FrameworkUtils.h>
-using android::hardware::tv::tuner::V1_0::DataFormat;
-using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+#include "../../../config/TunerTestingConfigReader.h"
+
using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
-using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
-using android::hardware::tv::tuner::V1_0::DemuxFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
-using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxTpid;
using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
-using android::hardware::tv::tuner::V1_0::DvrSettings;
-using android::hardware::tv::tuner::V1_0::DvrType;
using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
using android::hardware::tv::tuner::V1_0::FrontendSettings;
using android::hardware::tv::tuner::V1_0::FrontendStatus;
using android::hardware::tv::tuner::V1_0::FrontendStatusType;
using android::hardware::tv::tuner::V1_0::FrontendType;
-using android::hardware::tv::tuner::V1_0::LnbPosition;
-using android::hardware::tv::tuner::V1_0::LnbTone;
-using android::hardware::tv::tuner::V1_0::LnbVoltage;
-using android::hardware::tv::tuner::V1_0::PlaybackSettings;
-using android::hardware::tv::tuner::V1_0::RecordSettings;
using namespace std;
+using namespace android::media::tuner::testing::configuration::V1_0;
-const uint32_t FMQ_SIZE_512K = 0x80000;
-const uint32_t FMQ_SIZE_1M = 0x100000;
const uint32_t FMQ_SIZE_4M = 0x400000;
const uint32_t FMQ_SIZE_16M = 0x1000000;
-#define CLEAR_KEY_SYSTEM_ID 0xF6D8
-#define FILTER_MAIN_TYPE_BIT_COUNT 32
-#define PROVISION_STR \
- "{ " \
- " \"id\": 21140844, " \
- " \"name\": \"Test Title\", " \
- " \"lowercase_organization_name\": \"Android\", " \
- " \"asset_key\": { " \
- " \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\" " \
- " }, " \
- " \"cas_type\": 1, " \
- " \"track_types\": [ ] " \
- "} "
+#define FILTER_MAIN_TYPE_BIT_COUNT 5
-typedef enum {
- TS_VIDEO0,
- TS_VIDEO1,
- TS_AUDIO0,
- TS_PES0,
- TS_PCR0,
- TS_SECTION0,
- TS_TS0,
- TS_RECORD0,
- FILTER_MAX,
-} Filter;
+// Hardware configs
+static map<string, FrontendConfig> frontendMap;
+static map<string, FilterConfig> filterMap;
+static map<string, DvrConfig> dvrMap;
+static map<string, LnbConfig> lnbMap;
+static map<string, TimeFilterConfig> timeFilterMap;
+static map<string, vector<uint8_t>> diseqcMsgMap;
+static map<string, DescramblerConfig> descramblerMap;
-typedef enum {
- TIMER0,
- TIMER_MAX,
-} TimeFilter;
+// Hardware and test cases connections
+static LiveBroadcastHardwareConnections live;
+static ScanHardwareConnections scan;
+static DvrPlaybackHardwareConnections playback;
+static DvrRecordHardwareConnections record;
+static DescramblingHardwareConnections descrambling;
+static LnbLiveHardwareConnections lnbLive;
+static LnbRecordHardwareConnections lnbRecord;
+static TimeFilterHardwareConnections timeFilter;
-typedef enum {
- SOURCE,
- SINK,
- LINKAGE_DIR,
-} Linkage;
-
-typedef enum {
- DVBT,
- DVBS,
- FRONTEND_MAX,
-} Frontend;
-
-typedef enum {
- LNB0,
- LNB_EXTERNAL,
- LNB_MAX,
-} Lnb;
-
-typedef enum {
- DISEQC_POWER_ON,
- DISEQC_MAX,
-} Diseqc;
-
-typedef enum {
- SCAN_DVBT,
- SCAN_MAX,
-} FrontendScan;
-
-typedef enum {
- DVR_RECORD0,
- DVR_PLAYBACK0,
- DVR_SOFTWARE_FE,
- DVR_MAX,
-} Dvr;
-
-typedef enum {
- DESC_0,
- DESC_MAX,
-} Descrambler;
-
-struct FilterConfig {
- uint32_t bufferSize;
- DemuxFilterType type;
- DemuxFilterSettings settings;
- bool getMqDesc;
-
- bool operator<(const FilterConfig& /*c*/) const { return false; }
-};
-
-struct TimeFilterConfig {
- bool supportTimeFilter;
- uint64_t timeStamp;
-};
-
-struct FrontendConfig {
- bool enable;
- bool isSoftwareFe;
- FrontendType type;
- FrontendSettings settings;
- vector<FrontendStatusType> tuneStatusTypes;
- vector<FrontendStatus> expectTuneStatuses;
-};
-
-struct LnbConfig {
- bool usingLnb;
- string name;
- LnbVoltage voltage;
- LnbTone tone;
- LnbPosition position;
-};
-
-struct ChannelConfig {
- int32_t frontendId;
- int32_t channelId;
- std::string channelName;
- DemuxTpid videoPid;
- DemuxTpid audioPid;
-};
-
-struct DvrConfig {
- DvrType type;
- uint32_t bufferSize;
- DvrSettings settings;
- string playbackInputFile;
-};
-
-struct DescramblerConfig {
- uint32_t casSystemId;
- string provisionStr;
- vector<uint8_t> hidlPvtData;
-};
-
-static FrontendConfig frontendArray[FILTER_MAX];
-static FrontendConfig frontendScanArray[SCAN_MAX];
-static LnbConfig lnbArray[LNB_MAX];
-static vector<uint8_t> diseqcMsgArray[DISEQC_MAX];
-static ChannelConfig channelArray[FRONTEND_MAX];
-static FilterConfig filterArray[FILTER_MAX];
-static TimeFilterConfig timeFilterArray[TIMER_MAX];
-static DemuxFilterType filterLinkageTypes[LINKAGE_DIR][FILTER_MAIN_TYPE_BIT_COUNT];
-static DvrConfig dvrArray[DVR_MAX];
-static DescramblerConfig descramblerArray[DESC_MAX];
-static vector<string> goldenOutputFiles;
-static int defaultFrontend = DVBT;
-static int defaultScanFrontend = SCAN_DVBT;
-
-/** Configuration array for the frontend tune test */
+/** Config all the frontends that would be used in the tests */
inline void initFrontendConfig() {
+ // The test will use the internal default fe when default fe is connected to any data flow
+ // without overriding in the xml config.
+ string defaultFeId = "FE_DEFAULT";
FrontendDvbtSettings dvbtSettings{
.frequency = 578000,
.transmissionMode = FrontendDvbtTransmissionMode::AUTO,
.bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
- .constellation = FrontendDvbtConstellation::AUTO,
- .hierarchy = FrontendDvbtHierarchy::AUTO,
- .hpCoderate = FrontendDvbtCoderate::AUTO,
- .lpCoderate = FrontendDvbtCoderate::AUTO,
- .guardInterval = FrontendDvbtGuardInterval::AUTO,
.isHighPriority = true,
- .standard = FrontendDvbtStandard::T,
};
- frontendArray[DVBT].type = FrontendType::DVBT, frontendArray[DVBT].settings.dvbt(dvbtSettings);
+ frontendMap[defaultFeId].type = FrontendType::DVBT;
+ frontendMap[defaultFeId].settings.dvbt(dvbtSettings);
+
vector<FrontendStatusType> types;
types.push_back(FrontendStatusType::DEMOD_LOCK);
FrontendStatus status;
status.isDemodLocked(true);
vector<FrontendStatus> statuses;
statuses.push_back(status);
- frontendArray[DVBT].tuneStatusTypes = types;
- frontendArray[DVBT].expectTuneStatuses = statuses;
- frontendArray[DVBT].isSoftwareFe = true;
- frontendArray[DVBS].enable = true;
- frontendArray[DVBS].type = FrontendType::DVBS;
- frontendArray[DVBS].enable = true;
- frontendArray[DVBS].isSoftwareFe = true;
+ frontendMap[defaultFeId].tuneStatusTypes = types;
+ frontendMap[defaultFeId].expectTuneStatuses = statuses;
+ frontendMap[defaultFeId].isSoftwareFe = true;
+
+ // Read customized config
+ TunerTestingConfigReader::readFrontendConfig1_0(frontendMap);
};
-/** Configuration array for the frontend scan test */
-inline void initFrontendScanConfig() {
- frontendScanArray[SCAN_DVBT].type = FrontendType::DVBT;
- frontendScanArray[SCAN_DVBT].settings.dvbt({
- .frequency = 578000,
- .transmissionMode = FrontendDvbtTransmissionMode::MODE_8K,
- .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
- .constellation = FrontendDvbtConstellation::AUTO,
- .hierarchy = FrontendDvbtHierarchy::AUTO,
- .hpCoderate = FrontendDvbtCoderate::AUTO,
- .lpCoderate = FrontendDvbtCoderate::AUTO,
- .guardInterval = FrontendDvbtGuardInterval::AUTO,
- .isHighPriority = true,
- .standard = FrontendDvbtStandard::T,
- });
-};
-
-/** Configuration array for the Lnb test */
-inline void initLnbConfig() {
- lnbArray[LNB0].usingLnb = true;
- lnbArray[LNB0].voltage = LnbVoltage::VOLTAGE_12V;
- lnbArray[LNB0].tone = LnbTone::NONE;
- lnbArray[LNB0].position = LnbPosition::UNDEFINED;
- lnbArray[LNB_EXTERNAL].usingLnb = true;
- lnbArray[LNB_EXTERNAL].name = "default_lnb_external";
- lnbArray[LNB_EXTERNAL].voltage = LnbVoltage::VOLTAGE_5V;
- lnbArray[LNB_EXTERNAL].tone = LnbTone::NONE;
- lnbArray[LNB_EXTERNAL].position = LnbPosition::UNDEFINED;
-};
-
-/** Diseqc messages array for the Lnb test */
-inline void initDiseqcMsg() {
- diseqcMsgArray[DISEQC_POWER_ON] = {0xE, 0x0, 0x0, 0x0, 0x0, 0x3};
-};
-
-/** Configuration array for the filter test */
inline void initFilterConfig() {
- // TS VIDEO filter setting for default implementation testing
- filterArray[TS_VIDEO0].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_VIDEO0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
- filterArray[TS_VIDEO0].bufferSize = FMQ_SIZE_16M;
- filterArray[TS_VIDEO0].settings.ts().tpid = 256;
- filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
- filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
- filterArray[TS_VIDEO1].bufferSize = FMQ_SIZE_16M;
- filterArray[TS_VIDEO1].settings.ts().tpid = 256;
- filterArray[TS_VIDEO1].settings.ts().filterSettings.av({.isPassthrough = false});
- // TS AUDIO filter setting
- filterArray[TS_AUDIO0].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_AUDIO0].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
- filterArray[TS_AUDIO0].bufferSize = FMQ_SIZE_16M;
- filterArray[TS_AUDIO0].settings.ts().tpid = 256;
- filterArray[TS_AUDIO0].settings.ts().filterSettings.av({.isPassthrough = false});
- // TS PES filter setting
- filterArray[TS_PES0].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_PES0].type.subType.tsFilterType(DemuxTsFilterType::PES);
- filterArray[TS_PES0].bufferSize = FMQ_SIZE_16M;
- filterArray[TS_PES0].settings.ts().tpid = 256;
- filterArray[TS_PES0].settings.ts().filterSettings.pesData({
- .isRaw = false,
- .streamId = 0xbd,
- });
- filterArray[TS_PES0].getMqDesc = true;
- // TS PCR filter setting
- filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
- filterArray[TS_PCR0].bufferSize = FMQ_SIZE_16M;
- filterArray[TS_PCR0].settings.ts().tpid = 256;
- filterArray[TS_PCR0].settings.ts().filterSettings.noinit();
- // TS filter setting
- filterArray[TS_TS0].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_TS0].type.subType.tsFilterType(DemuxTsFilterType::TS);
- filterArray[TS_TS0].bufferSize = FMQ_SIZE_16M;
- filterArray[TS_TS0].settings.ts().tpid = 256;
- filterArray[TS_TS0].settings.ts().filterSettings.noinit();
- // TS SECTION filter setting
- filterArray[TS_SECTION0].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_SECTION0].type.subType.tsFilterType(DemuxTsFilterType::SECTION);
- filterArray[TS_SECTION0].bufferSize = FMQ_SIZE_16M;
- filterArray[TS_SECTION0].settings.ts().tpid = 256;
- filterArray[TS_SECTION0].settings.ts().filterSettings.section({
- .isRaw = false,
- });
- filterArray[TS_SECTION0].getMqDesc = true;
- // TS RECORD filter setting
- filterArray[TS_RECORD0].type.mainType = DemuxFilterMainType::TS;
- filterArray[TS_RECORD0].type.subType.tsFilterType(DemuxTsFilterType::RECORD);
- filterArray[TS_RECORD0].settings.ts().tpid = 81;
- filterArray[TS_RECORD0].settings.ts().filterSettings.record({
- .scIndexType = DemuxRecordScIndexType::NONE,
- });
+ // The test will use the internal default filter when default filter is connected to any
+ // data flow without overriding in the xml config.
+ string defaultAudioFilterId = "FILTER_AUDIO_DEFAULT";
+ string defaultVideoFilterId = "FILTER_VIDEO_DEFAULT";
- // TS Linkage filter setting
- filterLinkageTypes[SOURCE][0].mainType = DemuxFilterMainType::TS;
- filterLinkageTypes[SOURCE][0].subType.tsFilterType(DemuxTsFilterType::TS);
- filterLinkageTypes[SINK][0] = filterLinkageTypes[SOURCE][0];
- // MMTP Linkage filter setting
- filterLinkageTypes[SOURCE][1].mainType = DemuxFilterMainType::MMTP;
- filterLinkageTypes[SOURCE][1].subType.mmtpFilterType(DemuxMmtpFilterType::AUDIO);
- filterLinkageTypes[SINK][1] = filterLinkageTypes[SOURCE][1];
- // IP Linkage filter setting
- filterLinkageTypes[SOURCE][2].mainType = DemuxFilterMainType::IP;
- filterLinkageTypes[SOURCE][2].subType.ipFilterType(DemuxIpFilterType::IP);
- filterLinkageTypes[SINK][2] = filterLinkageTypes[SOURCE][2];
- // TLV Linkage filter setting
- filterLinkageTypes[SOURCE][3].mainType = DemuxFilterMainType::TLV;
- filterLinkageTypes[SOURCE][3].subType.tlvFilterType(DemuxTlvFilterType::TLV);
- filterLinkageTypes[SINK][3] = filterLinkageTypes[SOURCE][3];
- // ALP Linkage PTP filter setting
- filterLinkageTypes[SOURCE][4].mainType = DemuxFilterMainType::ALP;
- filterLinkageTypes[SOURCE][4].subType.alpFilterType(DemuxAlpFilterType::PTP);
- filterLinkageTypes[SINK][4] = filterLinkageTypes[SOURCE][4];
+ filterMap[defaultVideoFilterId].type.mainType = DemuxFilterMainType::TS;
+ filterMap[defaultVideoFilterId].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
+ filterMap[defaultVideoFilterId].bufferSize = FMQ_SIZE_16M;
+ filterMap[defaultVideoFilterId].settings.ts().tpid = 256;
+ filterMap[defaultVideoFilterId].settings.ts().filterSettings.av({.isPassthrough = false});
+
+ filterMap[defaultAudioFilterId].type.mainType = DemuxFilterMainType::TS;
+ filterMap[defaultAudioFilterId].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
+ filterMap[defaultAudioFilterId].bufferSize = FMQ_SIZE_16M;
+ filterMap[defaultAudioFilterId].settings.ts().tpid = 256;
+ filterMap[defaultAudioFilterId].settings.ts().filterSettings.av({.isPassthrough = false});
+
+ // Read customized config
+ TunerTestingConfigReader::readFilterConfig1_0(filterMap);
};
-/** Configuration array for the timer filter test */
-inline void initTimeFilterConfig() {
- timeFilterArray[TIMER0].supportTimeFilter = true;
- timeFilterArray[TIMER0].timeStamp = 1;
-}
-
-/** Configuration array for the dvr test */
+/** Config all the dvrs that would be used in the tests */
inline void initDvrConfig() {
- RecordSettings recordSettings{
- .statusMask = 0xf,
- .lowThreshold = 0x1000,
- .highThreshold = 0x07fff,
- .dataFormat = DataFormat::TS,
- .packetSize = 188,
- };
- dvrArray[DVR_RECORD0].type = DvrType::RECORD;
- dvrArray[DVR_RECORD0].bufferSize = FMQ_SIZE_4M;
- dvrArray[DVR_RECORD0].settings.record(recordSettings);
- PlaybackSettings playbackSettings{
- .statusMask = 0xf,
- .lowThreshold = 0x1000,
- .highThreshold = 0x07fff,
- .dataFormat = DataFormat::TS,
- .packetSize = 188,
- };
- dvrArray[DVR_PLAYBACK0].type = DvrType::PLAYBACK;
- dvrArray[DVR_PLAYBACK0].playbackInputFile = "/data/local/tmp/segment000000.ts";
- dvrArray[DVR_PLAYBACK0].bufferSize = FMQ_SIZE_4M;
- dvrArray[DVR_PLAYBACK0].settings.playback(playbackSettings);
- PlaybackSettings softwareFePlaybackSettings{
- .statusMask = 0xf,
- .lowThreshold = 0x1000,
- .highThreshold = 0x07fff,
- .dataFormat = DataFormat::TS,
- .packetSize = 188,
- };
- dvrArray[DVR_SOFTWARE_FE].type = DvrType::PLAYBACK;
- dvrArray[DVR_SOFTWARE_FE].playbackInputFile = "/data/local/tmp/segment000000.ts";
- dvrArray[DVR_SOFTWARE_FE].bufferSize = FMQ_SIZE_4M;
- dvrArray[DVR_SOFTWARE_FE].settings.playback(softwareFePlaybackSettings);
+ // Read customized config
+ TunerTestingConfigReader::readDvrConfig1_0(dvrMap);
};
-/** Configuration array for the descrambler test */
-inline void initDescramblerConfig() {
- descramblerArray[DESC_0].casSystemId = CLEAR_KEY_SYSTEM_ID;
- descramblerArray[DESC_0].provisionStr = PROVISION_STR;
- descramblerArray[DESC_0].hidlPvtData.resize(256);
+/** Config all the lnbs that would be used in the tests */
+inline void initLnbConfig() {
+ // Read customized config
+ TunerTestingConfigReader::readLnbConfig1_0(lnbMap);
+ TunerTestingConfigReader::readDiseqcMessages(diseqcMsgMap);
};
+
+/** Config all the time filters that would be used in the tests */
+inline void initTimeFilterConfig() {
+ // Read customized config
+ TunerTestingConfigReader::readTimeFilterConfig1_0(timeFilterMap);
+};
+
+/** Config all the descramblers that would be used in the tests */
+inline void initDescramblerConfig() {
+ // Read customized config
+ TunerTestingConfigReader::readDescramblerConfig1_0(descramblerMap);
+};
+
+/** Read the vendor configurations of which hardware to use for each test cases/data flows */
+inline void connectHardwaresToTestCases() {
+ TunerTestingConfigReader::connectLiveBroadcast(live);
+ TunerTestingConfigReader::connectScan(scan);
+ TunerTestingConfigReader::connectDvrPlayback(playback);
+ TunerTestingConfigReader::connectDvrRecord(record);
+ TunerTestingConfigReader::connectDescrambling(descrambling);
+ TunerTestingConfigReader::connectLnbLive(lnbLive);
+ TunerTestingConfigReader::connectLnbRecord(lnbRecord);
+ TunerTestingConfigReader::connectTimeFilter(timeFilter);
+};
+
+inline bool validateConnections() {
+ bool feIsValid = frontendMap.find(live.frontendId) != frontendMap.end() &&
+ frontendMap.find(scan.frontendId) != frontendMap.end();
+ feIsValid &= record.support ? frontendMap.find(record.frontendId) != frontendMap.end() : true;
+ feIsValid &= descrambling.support
+ ? frontendMap.find(descrambling.frontendId) != frontendMap.end()
+ : true;
+ feIsValid &= lnbLive.support ? frontendMap.find(lnbLive.frontendId) != frontendMap.end() : true;
+ feIsValid &=
+ lnbRecord.support ? frontendMap.find(lnbRecord.frontendId) != frontendMap.end() : true;
+
+ if (!feIsValid) {
+ ALOGW("[vts config] dynamic config fe connection is invalid.");
+ return false;
+ }
+
+ bool dvrIsValid = frontendMap[live.frontendId].isSoftwareFe
+ ? dvrMap.find(live.dvrSoftwareFeId) != dvrMap.end()
+ : true;
+ dvrIsValid &= playback.support ? dvrMap.find(playback.dvrId) != dvrMap.end() : true;
+ if (record.support) {
+ if (frontendMap[record.frontendId].isSoftwareFe) {
+ dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+ }
+ dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
+ }
+ if (descrambling.support && frontendMap[descrambling.frontendId].isSoftwareFe) {
+ dvrIsValid &= dvrMap.find(descrambling.dvrSoftwareFeId) != dvrMap.end();
+ }
+
+ if (!dvrIsValid) {
+ ALOGW("[vts config] dynamic config dvr connection is invalid.");
+ return false;
+ }
+
+ bool filterIsValid = filterMap.find(live.audioFilterId) != filterMap.end() &&
+ filterMap.find(live.videoFilterId) != filterMap.end();
+ filterIsValid &= playback.support
+ ? (filterMap.find(playback.audioFilterId) != filterMap.end() &&
+ filterMap.find(playback.videoFilterId) != filterMap.end())
+ : true;
+ filterIsValid &=
+ record.support ? filterMap.find(record.recordFilterId) != filterMap.end() : true;
+ filterIsValid &= descrambling.support
+ ? (filterMap.find(descrambling.audioFilterId) != filterMap.end() &&
+ filterMap.find(descrambling.videoFilterId) != filterMap.end())
+ : true;
+ filterIsValid &= lnbLive.support ? (filterMap.find(lnbLive.audioFilterId) != filterMap.end() &&
+ filterMap.find(lnbLive.videoFilterId) != filterMap.end())
+ : true;
+ filterIsValid &=
+ lnbRecord.support ? filterMap.find(lnbRecord.recordFilterId) != filterMap.end() : true;
+
+ if (!filterIsValid) {
+ ALOGW("[vts config] dynamic config filter connection is invalid.");
+ return false;
+ }
+
+ bool lnbIsValid = lnbLive.support ? lnbMap.find(lnbLive.lnbId) != lnbMap.end() : true;
+ lnbIsValid &= lnbRecord.support ? lnbMap.find(lnbRecord.lnbId) != lnbMap.end() : true;
+
+ if (!lnbIsValid) {
+ ALOGW("[vts config] dynamic config lnb connection is invalid.");
+ return false;
+ }
+
+ bool descramblerIsValid =
+ descrambling.support
+ ? descramblerMap.find(descrambling.descramblerId) != descramblerMap.end()
+ : true;
+
+ if (!descramblerIsValid) {
+ ALOGW("[vts config] dynamic config descrambler connection is invalid.");
+ return false;
+ }
+
+ bool diseqcMsgIsValid = true;
+ if (lnbLive.support) {
+ for (auto msgName : lnbLive.diseqcMsgs) {
+ diseqcMsgIsValid &= diseqcMsgMap.find(msgName) != diseqcMsgMap.end();
+ }
+ }
+ if (lnbRecord.support) {
+ for (auto msgName : lnbRecord.diseqcMsgs) {
+ diseqcMsgIsValid &= diseqcMsgMap.find(msgName) != diseqcMsgMap.end();
+ }
+ }
+
+ if (!diseqcMsgIsValid) {
+ ALOGW("[vts config] dynamic config diseqcMsg sender is invalid.");
+ return false;
+ }
+
+ return true;
+}
diff --git a/tv/tuner/assets/Android.bp b/tv/tuner/assets/Android.bp
new file mode 100644
index 0000000..79244ed
--- /dev/null
+++ b/tv/tuner/assets/Android.bp
@@ -0,0 +1,26 @@
+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: "tuner_frontend_input_es",
+ srcs: ["tuner_frontend_input.es"],
+ out: [
+ "test.es",
+ ],
+ cmd: "cp -f $(in) $(genDir)/test.es",
+}
+
+genrule {
+ name: "tuner_frontend_input_ts",
+ srcs: ["tuner_frontend_input.ts"],
+ out: [
+ "segment000000.ts",
+ ],
+ cmd: "cp -f $(in) $(genDir)/segment000000.ts",
+}
diff --git a/tv/tuner/assets/tuner_frontend_input.es b/tv/tuner/assets/tuner_frontend_input.es
new file mode 100644
index 0000000..b53c957
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.es
Binary files differ
diff --git a/tv/tuner/assets/tuner_frontend_input.ts b/tv/tuner/assets/tuner_frontend_input.ts
new file mode 100644
index 0000000..8992c7b
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.ts
Binary files differ
diff --git a/tv/tuner/config/Android.bp b/tv/tuner/config/Android.bp
new file mode 100644
index 0000000..ddbf3a7
--- /dev/null
+++ b/tv/tuner/config/Android.bp
@@ -0,0 +1,31 @@
+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"],
+}
+
+xsd_config {
+ name: "tuner_testing_dynamic_configuration_V1_0",
+ srcs: ["tuner_testing_dynamic_configuration.xsd"],
+ package_name: "android.media.tuner.testing.configuration.V1_0",
+ nullability: true,
+}
+
+xsd_config {
+ name: "tuner_testing_dynamic_configuration_V1_0_enums",
+ srcs: ["tuner_testing_dynamic_configuration.xsd"],
+ package_name: "android.media.tuner.testing.configuration.V1_0",
+ nullability: true,
+ enums_only: true,
+}
+
+xsd_config {
+ name: "tuner_testing_dynamic_configuration_V1_0_parser",
+ srcs: ["tuner_testing_dynamic_configuration.xsd"],
+ package_name: "android.media.tuner.testing.configuration.V1_0",
+ nullability: true,
+ parser_only: true,
+}
diff --git a/tv/tuner/config/OWNERS b/tv/tuner/config/OWNERS
new file mode 100644
index 0000000..1b3d095
--- /dev/null
+++ b/tv/tuner/config/OWNERS
@@ -0,0 +1,4 @@
+nchalko@google.com
+amyjojo@google.com
+shubang@google.com
+quxiangfang@google.com
diff --git a/tv/tuner/config/TunerTestingConfigReader.h b/tv/tuner/config/TunerTestingConfigReader.h
new file mode 100644
index 0000000..90499c4
--- /dev/null
+++ b/tv/tuner/config/TunerTestingConfigReader.h
@@ -0,0 +1,732 @@
+/*
+ * 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.
+ */
+
+#include <android-base/logging.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <android_media_tuner_testing_configuration_V1_0.h>
+#include <android_media_tuner_testing_configuration_V1_0_enums.h>
+#include <binder/MemoryDealer.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+
+using namespace std;
+using namespace android::media::tuner::testing::configuration::V1_0;
+
+using android::hardware::tv::tuner::V1_0::DataFormat;
+using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
+using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxTpid;
+using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using android::hardware::tv::tuner::V1_0::DvrSettings;
+using android::hardware::tv::tuner::V1_0::DvrType;
+using android::hardware::tv::tuner::V1_0::FrontendDvbsSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
+using android::hardware::tv::tuner::V1_0::FrontendSettings;
+using android::hardware::tv::tuner::V1_0::FrontendStatus;
+using android::hardware::tv::tuner::V1_0::FrontendStatusType;
+using android::hardware::tv::tuner::V1_0::FrontendType;
+using android::hardware::tv::tuner::V1_0::LnbPosition;
+using android::hardware::tv::tuner::V1_0::LnbTone;
+using android::hardware::tv::tuner::V1_0::LnbVoltage;
+using android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using android::hardware::tv::tuner::V1_0::RecordSettings;
+
+const string configFilePath = "/vendor/etc/tuner_vts_config.xml";
+const string emptyHardwareId = "";
+
+#define PROVISION_STR \
+ "{ " \
+ " \"id\": 21140844, " \
+ " \"name\": \"Test Title\", " \
+ " \"lowercase_organization_name\": \"Android\", " \
+ " \"asset_key\": { " \
+ " \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\" " \
+ " }, " \
+ " \"cas_type\": 1, " \
+ " \"track_types\": [ ] " \
+ "} "
+
+struct FrontendConfig {
+ bool isSoftwareFe;
+ FrontendType type;
+ FrontendSettings settings;
+ vector<FrontendStatusType> tuneStatusTypes;
+ vector<FrontendStatus> expectTuneStatuses;
+};
+
+struct FilterConfig {
+ uint32_t bufferSize;
+ DemuxFilterType type;
+ DemuxFilterSettings settings;
+ bool getMqDesc;
+
+ bool operator<(const FilterConfig& /*c*/) const { return false; }
+};
+
+struct DvrConfig {
+ DvrType type;
+ uint32_t bufferSize;
+ DvrSettings settings;
+ string playbackInputFile;
+};
+
+struct LnbConfig {
+ string name;
+ LnbVoltage voltage;
+ LnbTone tone;
+ LnbPosition position;
+};
+
+struct TimeFilterConfig {
+ uint64_t timeStamp;
+};
+
+struct DescramblerConfig {
+ uint32_t casSystemId;
+ string provisionStr;
+ vector<uint8_t> hidlPvtData;
+};
+
+struct LiveBroadcastHardwareConnections {
+ string frontendId;
+ string dvrSoftwareFeId;
+ string audioFilterId;
+ string videoFilterId;
+ string sectionFilterId;
+ string pcrFilterId;
+ /* list string of extra filters; */
+};
+
+struct ScanHardwareConnections {
+ string frontendId;
+};
+
+struct DvrPlaybackHardwareConnections {
+ bool support;
+ string frontendId;
+ string dvrId;
+ string audioFilterId;
+ string videoFilterId;
+ string sectionFilterId;
+ /* list string of extra filters; */
+};
+
+struct DvrRecordHardwareConnections {
+ bool support;
+ string frontendId;
+ string dvrRecordId;
+ string dvrSoftwareFeId;
+ string recordFilterId;
+};
+
+struct DescramblingHardwareConnections {
+ bool support;
+ string frontendId;
+ string dvrSoftwareFeId;
+ string audioFilterId;
+ string videoFilterId;
+ string descramblerId;
+ /* list string of extra filters; */
+};
+
+struct LnbLiveHardwareConnections {
+ bool support;
+ string frontendId;
+ string audioFilterId;
+ string videoFilterId;
+ string lnbId;
+ vector<string> diseqcMsgs;
+ /* list string of extra filters; */
+};
+
+struct LnbRecordHardwareConnections {
+ bool support;
+ string frontendId;
+ string dvrRecordId;
+ string recordFilterId;
+ string lnbId;
+ vector<string> diseqcMsgs;
+ /* list string of extra filters; */
+};
+
+struct TimeFilterHardwareConnections {
+ bool support;
+ string timeFilterId;
+};
+
+struct TunerTestingConfigReader {
+ public:
+ static bool checkConfigFileExists() {
+ auto res = read(configFilePath.c_str());
+ if (res == nullopt) {
+ ALOGW("[ConfigReader] Couldn't read /vendor/etc/tuner_vts_config.xml."
+ "Please check tuner_testing_dynamic_configuration.xsd"
+ "and sample_tuner_vts_config.xml for more details on how to config Tune VTS.");
+ }
+ return (res != nullopt);
+ }
+
+ static void readFrontendConfig1_0(map<string, FrontendConfig>& frontendMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasFrontends()) {
+ // TODO: b/182519645 complete the tune status config
+ vector<FrontendStatusType> types;
+ types.push_back(FrontendStatusType::DEMOD_LOCK);
+ FrontendStatus status;
+ status.isDemodLocked(true);
+ vector<FrontendStatus> statuses;
+ statuses.push_back(status);
+
+ auto frontends = *hardwareConfig.getFirstFrontends();
+ for (auto feConfig : frontends.getFrontend()) {
+ string id = feConfig.getId();
+ if (id.compare(string("FE_DEFAULT")) == 0) {
+ // overrid default
+ frontendMap.erase(string("FE_DEFAULT"));
+ }
+ FrontendType type;
+ switch (feConfig.getType()) {
+ case FrontendTypeEnum::UNDEFINED:
+ type = FrontendType::UNDEFINED;
+ break;
+ // TODO: b/182519645 finish all other frontend settings
+ case FrontendTypeEnum::ANALOG:
+ type = FrontendType::ANALOG;
+ break;
+ case FrontendTypeEnum::ATSC:
+ type = FrontendType::ATSC;
+ break;
+ case FrontendTypeEnum::ATSC3:
+ type = FrontendType::ATSC3;
+ break;
+ case FrontendTypeEnum::DVBC:
+ type = FrontendType::DVBC;
+ break;
+ case FrontendTypeEnum::DVBS:
+ type = FrontendType::DVBS;
+ frontendMap[id].settings.dvbs(readDvbsFrontendSettings(feConfig));
+ break;
+ case FrontendTypeEnum::DVBT: {
+ type = FrontendType::DVBT;
+ frontendMap[id].settings.dvbt(readDvbtFrontendSettings(feConfig));
+ break;
+ }
+ case FrontendTypeEnum::ISDBS:
+ type = FrontendType::ISDBS;
+ break;
+ case FrontendTypeEnum::ISDBS3:
+ type = FrontendType::ISDBS3;
+ break;
+ case FrontendTypeEnum::ISDBT:
+ type = FrontendType::ISDBT;
+ break;
+ case FrontendTypeEnum::DTMB:
+ // dtmb will be handled in readFrontendConfig1_1;
+ continue;
+ case FrontendTypeEnum::UNKNOWN:
+ ALOGW("[ConfigReader] invalid frontend type");
+ return;
+ }
+ frontendMap[id].type = type;
+ frontendMap[id].isSoftwareFe = feConfig.getIsSoftwareFrontend();
+ // TODO: b/182519645 complete the tune status config
+ frontendMap[id].tuneStatusTypes = types;
+ frontendMap[id].expectTuneStatuses = statuses;
+ }
+ }
+ }
+
+ static void readFilterConfig1_0(map<string, FilterConfig>& filterMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasFilters()) {
+ auto filters = *hardwareConfig.getFirstFilters();
+ for (auto filterConfig : filters.getFilter()) {
+ string id = filterConfig.getId();
+ if (id.compare(string("FILTER_AUDIO_DEFAULT")) == 0) {
+ // overrid default
+ filterMap.erase(string("FILTER_AUDIO_DEFAULT"));
+ }
+ if (id.compare(string("FILTER_VIDEO_DEFAULT")) == 0) {
+ // overrid default
+ filterMap.erase(string("FILTER_VIDEO_DEFAULT"));
+ }
+
+ DemuxFilterType type;
+ DemuxFilterSettings settings;
+ if (!readFilterTypeAndSettings(filterConfig, type, settings)) {
+ ALOGW("[ConfigReader] invalid filter type");
+ return;
+ }
+ filterMap[id].type = type;
+ filterMap[id].bufferSize = filterConfig.getBufferSize();
+ filterMap[id].getMqDesc = filterConfig.getUseFMQ();
+ filterMap[id].settings = settings;
+ }
+ }
+ }
+
+ static void readDvrConfig1_0(map<string, DvrConfig>& dvrMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasDvrs()) {
+ auto dvrs = *hardwareConfig.getFirstDvrs();
+ for (auto dvrConfig : dvrs.getDvr()) {
+ string id = dvrConfig.getId();
+ DvrType type;
+ switch (dvrConfig.getType()) {
+ case DvrTypeEnum::PLAYBACK:
+ type = DvrType::PLAYBACK;
+ dvrMap[id].settings.playback(readPlaybackSettings(dvrConfig));
+ break;
+ case DvrTypeEnum::RECORD:
+ type = DvrType::RECORD;
+ dvrMap[id].settings.record(readRecordSettings(dvrConfig));
+ break;
+ case DvrTypeEnum::UNKNOWN:
+ ALOGW("[ConfigReader] invalid DVR type");
+ return;
+ }
+ dvrMap[id].type = type;
+ dvrMap[id].bufferSize = static_cast<uint32_t>(dvrConfig.getBufferSize());
+ if (dvrConfig.hasInputFilePath()) {
+ dvrMap[id].playbackInputFile = dvrConfig.getInputFilePath();
+ }
+ }
+ }
+ }
+
+ static void readLnbConfig1_0(map<string, LnbConfig>& lnbMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasLnbs()) {
+ auto lnbs = *hardwareConfig.getFirstLnbs();
+ for (auto lnbConfig : lnbs.getLnb()) {
+ string id = lnbConfig.getId();
+ if (lnbConfig.hasName()) {
+ lnbMap[id].name = lnbConfig.getName();
+ } else {
+ lnbMap[id].name = emptyHardwareId;
+ }
+ lnbMap[id].voltage = static_cast<LnbVoltage>(lnbConfig.getVoltage());
+ lnbMap[id].tone = static_cast<LnbTone>(lnbConfig.getTone());
+ lnbMap[id].position = static_cast<LnbPosition>(lnbConfig.getPosition());
+ }
+ }
+ }
+
+ static void readDescramblerConfig1_0(map<string, DescramblerConfig>& descramblerMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasDescramblers()) {
+ auto descramblers = *hardwareConfig.getFirstDescramblers();
+ for (auto descramblerConfig : descramblers.getDescrambler()) {
+ string id = descramblerConfig.getId();
+ descramblerMap[id].casSystemId =
+ static_cast<uint32_t>(descramblerConfig.getCasSystemId());
+ if (descramblerConfig.hasProvisionStr()) {
+ descramblerMap[id].provisionStr = descramblerConfig.getProvisionStr();
+ } else {
+ descramblerMap[id].provisionStr = PROVISION_STR;
+ }
+ if (descramblerConfig.hasSesstionPrivatData()) {
+ auto privateData = descramblerConfig.getSesstionPrivatData();
+ int size = privateData.size();
+ descramblerMap[id].hidlPvtData.resize(size);
+ memcpy(descramblerMap[id].hidlPvtData.data(), privateData.data(), size);
+ } else {
+ descramblerMap[id].hidlPvtData.resize(256);
+ }
+ }
+ }
+ }
+
+ static void readDiseqcMessages(map<string, vector<uint8_t>>& diseqcMsgMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasDiseqcMessages()) {
+ auto msgs = *hardwareConfig.getFirstDiseqcMessages();
+ for (auto msgConfig : msgs.getDiseqcMessage()) {
+ string name = msgConfig.getMsgName();
+ for (uint8_t atom : msgConfig.getMsgBody()) {
+ diseqcMsgMap[name].push_back(atom);
+ }
+ }
+ }
+ }
+
+ static void readTimeFilterConfig1_0(map<string, TimeFilterConfig>& timeFilterMap) {
+ auto hardwareConfig = getHardwareConfig();
+ if (hardwareConfig.hasTimeFilters()) {
+ auto timeFilters = *hardwareConfig.getFirstTimeFilters();
+ for (auto timeFilterConfig : timeFilters.getTimeFilter()) {
+ string id = timeFilterConfig.getId();
+ timeFilterMap[id].timeStamp =
+ static_cast<uint64_t>(timeFilterConfig.getTimeStamp());
+ }
+ }
+ }
+
+ static void connectLiveBroadcast(LiveBroadcastHardwareConnections& live) {
+ auto liveConfig = *getDataFlowConfiguration().getFirstClearLiveBroadcast();
+ live.frontendId = liveConfig.getFrontendConnection();
+
+ live.audioFilterId = liveConfig.getAudioFilterConnection();
+ live.videoFilterId = liveConfig.getVideoFilterConnection();
+ if (liveConfig.hasPcrFilterConnection()) {
+ live.pcrFilterId = liveConfig.getPcrFilterConnection();
+ } else {
+ live.pcrFilterId = emptyHardwareId;
+ }
+ if (liveConfig.hasSectionFilterConnection()) {
+ live.sectionFilterId = liveConfig.getSectionFilterConnection();
+ } else {
+ live.sectionFilterId = emptyHardwareId;
+ }
+ if (liveConfig.hasDvrSoftwareFeConnection()) {
+ live.dvrSoftwareFeId = liveConfig.getDvrSoftwareFeConnection();
+ }
+ }
+
+ static void connectScan(ScanHardwareConnections& scan) {
+ auto scanConfig = getDataFlowConfiguration().getFirstScan();
+ scan.frontendId = scanConfig->getFrontendConnection();
+ }
+
+ static void connectDvrPlayback(DvrPlaybackHardwareConnections& playback) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDvrPlayback()) {
+ playback.support = true;
+ } else {
+ return;
+ }
+ auto playbackConfig = *dataFlow.getFirstDvrPlayback();
+ playback.dvrId = playbackConfig.getDvrConnection();
+ playback.audioFilterId = playbackConfig.getAudioFilterConnection();
+ playback.videoFilterId = playbackConfig.getVideoFilterConnection();
+ if (playbackConfig.hasSectionFilterConnection()) {
+ playback.sectionFilterId = playbackConfig.getSectionFilterConnection();
+ } else {
+ playback.sectionFilterId = emptyHardwareId;
+ }
+ }
+
+ static void connectDvrRecord(DvrRecordHardwareConnections& record) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDvrRecord()) {
+ record.support = true;
+ } else {
+ return;
+ }
+ auto recordConfig = *dataFlow.getFirstDvrRecord();
+ record.frontendId = recordConfig.getFrontendConnection();
+ record.recordFilterId = recordConfig.getRecordFilterConnection();
+ record.dvrRecordId = recordConfig.getDvrRecordConnection();
+ if (recordConfig.hasDvrSoftwareFeConnection()) {
+ record.dvrSoftwareFeId = recordConfig.getDvrSoftwareFeConnection();
+ }
+ }
+
+ static void connectDescrambling(DescramblingHardwareConnections& descrambling) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasDescrambling()) {
+ descrambling.support = true;
+ } else {
+ return;
+ }
+ auto descConfig = *dataFlow.getFirstDescrambling();
+ descrambling.frontendId = descConfig.getFrontendConnection();
+ descrambling.descramblerId = descConfig.getDescramblerConnection();
+ descrambling.audioFilterId = descConfig.getAudioFilterConnection();
+ descrambling.videoFilterId = descConfig.getVideoFilterConnection();
+ if (descConfig.hasDvrSoftwareFeConnection()) {
+ descrambling.dvrSoftwareFeId = descConfig.getDvrSoftwareFeConnection();
+ }
+ }
+
+ static void connectLnbLive(LnbLiveHardwareConnections& lnbLive) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasLnbLive()) {
+ lnbLive.support = true;
+ } else {
+ return;
+ }
+ auto lnbLiveConfig = *dataFlow.getFirstLnbLive();
+ lnbLive.frontendId = lnbLiveConfig.getFrontendConnection();
+ lnbLive.audioFilterId = lnbLiveConfig.getAudioFilterConnection();
+ lnbLive.videoFilterId = lnbLiveConfig.getVideoFilterConnection();
+ lnbLive.lnbId = lnbLiveConfig.getLnbConnection();
+ if (lnbLiveConfig.hasDiseqcMsgSender()) {
+ for (auto msgName : lnbLiveConfig.getDiseqcMsgSender()) {
+ lnbLive.diseqcMsgs.push_back(msgName);
+ }
+ }
+ }
+
+ static void connectLnbRecord(LnbRecordHardwareConnections& lnbRecord) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasLnbRecord()) {
+ lnbRecord.support = true;
+ } else {
+ return;
+ }
+ auto lnbRecordConfig = *dataFlow.getFirstLnbRecord();
+ lnbRecord.frontendId = lnbRecordConfig.getFrontendConnection();
+ lnbRecord.recordFilterId = lnbRecordConfig.getRecordFilterConnection();
+ lnbRecord.dvrRecordId = lnbRecordConfig.getDvrRecordConnection();
+ lnbRecord.lnbId = lnbRecordConfig.getLnbConnection();
+ if (lnbRecordConfig.hasDiseqcMsgSender()) {
+ for (auto msgName : lnbRecordConfig.getDiseqcMsgSender()) {
+ lnbRecord.diseqcMsgs.push_back(msgName);
+ }
+ }
+ }
+
+ static void connectTimeFilter(TimeFilterHardwareConnections& timeFilter) {
+ auto dataFlow = getDataFlowConfiguration();
+ if (dataFlow.hasTimeFilter()) {
+ timeFilter.support = true;
+ } else {
+ return;
+ }
+ auto timeFilterConfig = *dataFlow.getFirstTimeFilter();
+ timeFilter.timeFilterId = timeFilterConfig.getTimeFilterConnection();
+ }
+
+ private:
+ static FrontendDvbtSettings readDvbtFrontendSettings(Frontend feConfig) {
+ ALOGW("[ConfigReader] fe type is dvbt");
+ FrontendDvbtSettings dvbtSettings{
+ .frequency = (uint32_t)feConfig.getFrequency(),
+ };
+ if (!feConfig.hasDvbtFrontendSettings_optional()) {
+ ALOGW("[ConfigReader] no more dvbt settings");
+ return dvbtSettings;
+ }
+ dvbtSettings.transmissionMode = static_cast<FrontendDvbtTransmissionMode>(
+ feConfig.getFirstDvbtFrontendSettings_optional()->getTransmissionMode());
+ dvbtSettings.bandwidth = static_cast<FrontendDvbtBandwidth>(
+ feConfig.getFirstDvbtFrontendSettings_optional()->getBandwidth());
+ dvbtSettings.isHighPriority =
+ feConfig.getFirstDvbtFrontendSettings_optional()->getIsHighPriority();
+ return dvbtSettings;
+ }
+
+ static FrontendDvbsSettings readDvbsFrontendSettings(Frontend feConfig) {
+ ALOGW("[ConfigReader] fe type is dvbs");
+ FrontendDvbsSettings dvbsSettings{
+ .frequency = (uint32_t)feConfig.getFrequency(),
+ };
+ if (!feConfig.hasDvbsFrontendSettings_optional()) {
+ ALOGW("[ConfigReader] no more dvbs settings");
+ return dvbsSettings;
+ }
+ dvbsSettings.symbolRate = static_cast<uint32_t>(
+ feConfig.getFirstDvbsFrontendSettings_optional()->getSymbolRate());
+ dvbsSettings.inputStreamId = static_cast<uint32_t>(
+ feConfig.getFirstDvbsFrontendSettings_optional()->getInputStreamId());
+ return dvbsSettings;
+ }
+
+ static bool readFilterTypeAndSettings(Filter filterConfig, DemuxFilterType& type,
+ DemuxFilterSettings& settings) {
+ auto mainType = filterConfig.getMainType();
+ auto subType = filterConfig.getSubType();
+ uint32_t pid = static_cast<uint32_t>(filterConfig.getPid());
+ switch (mainType) {
+ case FilterMainTypeEnum::TS: {
+ ALOGW("[ConfigReader] filter main type is ts");
+ type.mainType = DemuxFilterMainType::TS;
+ switch (subType) {
+ case FilterSubTypeEnum::UNDEFINED:
+ break;
+ case FilterSubTypeEnum::SECTION:
+ type.subType.tsFilterType(DemuxTsFilterType::SECTION);
+ settings.ts().filterSettings.section(
+ readSectionFilterSettings(filterConfig));
+ break;
+ case FilterSubTypeEnum::PES:
+ // TODO: b/182519645 support all the filter settings
+ /*settings.ts().filterSettings.pesData(
+ getPesFilterSettings(filterConfig));*/
+ type.subType.tsFilterType(DemuxTsFilterType::PES);
+ break;
+ case FilterSubTypeEnum::TS:
+ type.subType.tsFilterType(DemuxTsFilterType::TS);
+ settings.ts().filterSettings.noinit();
+ break;
+ case FilterSubTypeEnum::PCR:
+ type.subType.tsFilterType(DemuxTsFilterType::PCR);
+ settings.ts().filterSettings.noinit();
+ break;
+ case FilterSubTypeEnum::TEMI:
+ type.subType.tsFilterType(DemuxTsFilterType::TEMI);
+ settings.ts().filterSettings.noinit();
+ break;
+ case FilterSubTypeEnum::AUDIO:
+ type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
+ settings.ts().filterSettings.av(readAvFilterSettings(filterConfig));
+ break;
+ case FilterSubTypeEnum::VIDEO:
+ type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
+ settings.ts().filterSettings.av(readAvFilterSettings(filterConfig));
+ break;
+ case FilterSubTypeEnum::RECORD:
+ type.subType.tsFilterType(DemuxTsFilterType::RECORD);
+ settings.ts().filterSettings.record(readRecordFilterSettings(filterConfig));
+ break;
+ default:
+ ALOGW("[ConfigReader] ts subtype is not supported");
+ return false;
+ }
+ settings.ts().tpid = pid;
+ break;
+ }
+ case FilterMainTypeEnum::MMTP: {
+ ALOGW("[ConfigReader] filter main type is mmtp");
+ type.mainType = DemuxFilterMainType::MMTP;
+ switch (subType) {
+ case FilterSubTypeEnum::UNDEFINED:
+ break;
+ case FilterSubTypeEnum::SECTION:
+ type.subType.mmtpFilterType(DemuxMmtpFilterType::SECTION);
+ settings.mmtp().filterSettings.section(
+ readSectionFilterSettings(filterConfig));
+ break;
+ case FilterSubTypeEnum::PES:
+ type.subType.mmtpFilterType(DemuxMmtpFilterType::PES);
+ // TODO: b/182519645 support all the filter settings
+ /*settings.mmtp().filterSettings.pesData(
+ getPesFilterSettings(filterConfig));*/
+ break;
+ case FilterSubTypeEnum::MMTP:
+ type.subType.mmtpFilterType(DemuxMmtpFilterType::MMTP);
+ settings.mmtp().filterSettings.noinit();
+ break;
+ case FilterSubTypeEnum::AUDIO:
+ type.subType.mmtpFilterType(DemuxMmtpFilterType::AUDIO);
+ settings.mmtp().filterSettings.av(readAvFilterSettings(filterConfig));
+ break;
+ case FilterSubTypeEnum::VIDEO:
+ settings.mmtp().filterSettings.av(readAvFilterSettings(filterConfig));
+ break;
+ case FilterSubTypeEnum::RECORD:
+ type.subType.mmtpFilterType(DemuxMmtpFilterType::RECORD);
+ settings.mmtp().filterSettings.record(
+ readRecordFilterSettings(filterConfig));
+ break;
+ case FilterSubTypeEnum::DOWNLOAD:
+ type.subType.mmtpFilterType(DemuxMmtpFilterType::DOWNLOAD);
+ // TODO: b/182519645 support all the filter settings
+ /*settings.mmtp().filterSettings.download(
+ getDownloadFilterSettings(filterConfig));*/
+ break;
+ default:
+ ALOGW("[ConfigReader] mmtp subtype is not supported");
+ return false;
+ }
+ settings.mmtp().mmtpPid = pid;
+ break;
+ }
+ default:
+ // TODO: b/182519645 support all the filter configs
+ ALOGW("[ConfigReader] filter main type is not supported in dynamic config");
+ return false;
+ }
+ return true;
+ }
+
+ static DemuxFilterSectionSettings readSectionFilterSettings(Filter filterConfig) {
+ DemuxFilterSectionSettings settings;
+ if (!filterConfig.hasSectionFilterSettings_optional()) {
+ return settings;
+ }
+ auto section = filterConfig.getFirstSectionFilterSettings_optional();
+ settings.isCheckCrc = section->getIsCheckCrc();
+ settings.isRepeat = section->getIsRepeat();
+ settings.isRaw = section->getIsRaw();
+ return settings;
+ }
+
+ static DemuxFilterAvSettings readAvFilterSettings(Filter filterConfig) {
+ DemuxFilterAvSettings settings;
+ if (!filterConfig.hasAvFilterSettings_optional()) {
+ return settings;
+ }
+ auto av = filterConfig.getFirstAvFilterSettings_optional();
+ settings.isPassthrough = av->getIsPassthrough();
+ return settings;
+ }
+
+ static DemuxFilterRecordSettings readRecordFilterSettings(Filter filterConfig) {
+ DemuxFilterRecordSettings settings;
+ if (!filterConfig.hasRecordFilterSettings_optional()) {
+ return settings;
+ }
+ auto record = filterConfig.getFirstRecordFilterSettings_optional();
+ settings.tsIndexMask = static_cast<uint32_t>(record->getTsIndexMask());
+ settings.scIndexType = static_cast<DemuxRecordScIndexType>(record->getScIndexType());
+ return settings;
+ }
+
+ static PlaybackSettings readPlaybackSettings(Dvr dvrConfig) {
+ ALOGW("[ConfigReader] dvr type is playback");
+ PlaybackSettings playbackSettings{
+ .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+ .lowThreshold = static_cast<uint32_t>(dvrConfig.getLowThreshold()),
+ .highThreshold = static_cast<uint32_t>(dvrConfig.getHighThreshold()),
+ .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+ .packetSize = static_cast<uint8_t>(dvrConfig.getPacketSize()),
+ };
+ return playbackSettings;
+ }
+
+ static RecordSettings readRecordSettings(Dvr dvrConfig) {
+ ALOGW("[ConfigReader] dvr type is record");
+ RecordSettings recordSettings{
+ .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+ .lowThreshold = static_cast<uint32_t>(dvrConfig.getLowThreshold()),
+ .highThreshold = static_cast<uint32_t>(dvrConfig.getHighThreshold()),
+ .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+ .packetSize = static_cast<uint8_t>(dvrConfig.getPacketSize()),
+ };
+ return recordSettings;
+ }
+
+ static TunerConfiguration getTunerConfig() { return *read(configFilePath.c_str()); }
+
+ static HardwareConfiguration getHardwareConfig() {
+ return *getTunerConfig().getFirstHardwareConfiguration();
+ }
+
+ static DataFlowConfiguration getDataFlowConfiguration() {
+ return *getTunerConfig().getFirstDataFlowConfiguration();
+ }
+};
diff --git a/tv/tuner/config/api/current.txt b/tv/tuner/config/api/current.txt
new file mode 100644
index 0000000..4255a60
--- /dev/null
+++ b/tv/tuner/config/api/current.txt
@@ -0,0 +1,433 @@
+// Signature format: 2.0
+package android.media.tuner.testing.configuration.V1_0 {
+
+ public class AvFilterSettings {
+ ctor public AvFilterSettings();
+ method @Nullable public boolean getIsPassthrough();
+ method public void setIsPassthrough(@Nullable boolean);
+ }
+
+ public class DataFlowConfiguration {
+ ctor public DataFlowConfiguration();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.ClearLiveBroadcast getClearLiveBroadcast();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling getDescrambling();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback getDvrPlayback();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord getDvrRecord();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive getLnbLive();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord getLnbRecord();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan getScan();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.TimeFilter getTimeFilter();
+ method public void setClearLiveBroadcast(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.ClearLiveBroadcast);
+ method public void setDescrambling(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling);
+ method public void setDvrPlayback(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback);
+ method public void setDvrRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord);
+ method public void setLnbLive(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive);
+ method public void setLnbRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord);
+ method public void setScan(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan);
+ method public void setTimeFilter(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.TimeFilter);
+ }
+
+ public static class DataFlowConfiguration.ClearLiveBroadcast {
+ ctor public DataFlowConfiguration.ClearLiveBroadcast();
+ method @Nullable public String getAudioFilterConnection();
+ method @Nullable public String getDvrSoftwareFeConnection();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public String getPcrFilterConnection();
+ method @Nullable public String getSectionFilterConnection();
+ method @Nullable public String getVideoFilterConnection();
+ method public void setAudioFilterConnection(@Nullable String);
+ method public void setDvrSoftwareFeConnection(@Nullable String);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setPcrFilterConnection(@Nullable String);
+ method public void setSectionFilterConnection(@Nullable String);
+ method public void setVideoFilterConnection(@Nullable String);
+ }
+
+ public static class DataFlowConfiguration.Descrambling {
+ ctor public DataFlowConfiguration.Descrambling();
+ method @Nullable public String getAudioFilterConnection();
+ method @Nullable public String getDescramblerConnection();
+ method @Nullable public String getDvrSoftwareFeConnection();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public String getVideoFilterConnection();
+ method public void setAudioFilterConnection(@Nullable String);
+ method public void setDescramblerConnection(@Nullable String);
+ method public void setDvrSoftwareFeConnection(@Nullable String);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setVideoFilterConnection(@Nullable String);
+ }
+
+ public static class DataFlowConfiguration.DvrPlayback {
+ ctor public DataFlowConfiguration.DvrPlayback();
+ method @Nullable public String getAudioFilterConnection();
+ method @Nullable public String getDvrConnection();
+ method @Nullable public String getSectionFilterConnection();
+ method @Nullable public String getVideoFilterConnection();
+ method public void setAudioFilterConnection(@Nullable String);
+ method public void setDvrConnection(@Nullable String);
+ method public void setSectionFilterConnection(@Nullable String);
+ method public void setVideoFilterConnection(@Nullable String);
+ }
+
+ public static class DataFlowConfiguration.DvrRecord {
+ ctor public DataFlowConfiguration.DvrRecord();
+ method @Nullable public String getDvrRecordConnection();
+ method @Nullable public String getDvrSoftwareFeConnection();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public String getRecordFilterConnection();
+ method public void setDvrRecordConnection(@Nullable String);
+ method public void setDvrSoftwareFeConnection(@Nullable String);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setRecordFilterConnection(@Nullable String);
+ }
+
+ public static class DataFlowConfiguration.LnbLive {
+ ctor public DataFlowConfiguration.LnbLive();
+ method @Nullable public String getAudioFilterConnection();
+ method @Nullable public java.util.List<java.lang.String> getDiseqcMsgSender();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public String getLnbConnection();
+ method @Nullable public String getVideoFilterConnection();
+ method public void setAudioFilterConnection(@Nullable String);
+ method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setLnbConnection(@Nullable String);
+ method public void setVideoFilterConnection(@Nullable String);
+ }
+
+ public static class DataFlowConfiguration.LnbRecord {
+ ctor public DataFlowConfiguration.LnbRecord();
+ method @Nullable public java.util.List<java.lang.String> getDiseqcMsgSender();
+ method @Nullable public String getDvrRecordConnection();
+ method @Nullable public String getFrontendConnection();
+ method @Nullable public String getLnbConnection();
+ method @Nullable public String getRecordFilterConnection();
+ method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
+ method public void setDvrRecordConnection(@Nullable String);
+ method public void setFrontendConnection(@Nullable String);
+ method public void setLnbConnection(@Nullable String);
+ method public void setRecordFilterConnection(@Nullable String);
+ }
+
+ public static class DataFlowConfiguration.Scan {
+ ctor public DataFlowConfiguration.Scan();
+ method @Nullable public String getFrontendConnection();
+ method public void setFrontendConnection(@Nullable String);
+ }
+
+ public static class DataFlowConfiguration.TimeFilter {
+ ctor public DataFlowConfiguration.TimeFilter();
+ method @Nullable public String getTimeFilterConnection();
+ method public void setTimeFilterConnection(@Nullable String);
+ }
+
+ public class Descrambler {
+ ctor public Descrambler();
+ method @Nullable public java.math.BigInteger getCasSystemId();
+ method @Nullable public String getId();
+ method @Nullable public String getProvisionStr();
+ method @Nullable public java.util.List<java.lang.Short> getSesstionPrivatData();
+ method public void setCasSystemId(@Nullable java.math.BigInteger);
+ method public void setId(@Nullable String);
+ method public void setProvisionStr(@Nullable String);
+ method public void setSesstionPrivatData(@Nullable java.util.List<java.lang.Short>);
+ }
+
+ public class DiseqcMessage {
+ ctor public DiseqcMessage();
+ method @Nullable public java.util.List<java.lang.Short> getMsgBody();
+ method @Nullable public String getMsgName();
+ method public void setMsgBody(@Nullable java.util.List<java.lang.Short>);
+ method public void setMsgName(@Nullable String);
+ }
+
+ public class DvbsFrontendSettings {
+ ctor public DvbsFrontendSettings();
+ method @Nullable public java.math.BigInteger getInputStreamId();
+ method @Nullable public java.math.BigInteger getSymbolRate();
+ method public void setInputStreamId(@Nullable java.math.BigInteger);
+ method public void setSymbolRate(@Nullable java.math.BigInteger);
+ }
+
+ public class DvbtFrontendSettings {
+ ctor public DvbtFrontendSettings();
+ method @Nullable public java.math.BigInteger getBandwidth();
+ method @Nullable public java.math.BigInteger getIsHighPriority();
+ method @Nullable public java.math.BigInteger getTransmissionMode();
+ method public void setBandwidth(@Nullable java.math.BigInteger);
+ method public void setIsHighPriority(@Nullable java.math.BigInteger);
+ method public void setTransmissionMode(@Nullable java.math.BigInteger);
+ }
+
+ public class Dvr {
+ ctor public Dvr();
+ method @Nullable public java.math.BigInteger getBufferSize();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum getDataFormat();
+ method @Nullable public java.math.BigInteger getHighThreshold();
+ method @Nullable public String getId();
+ method @Nullable public String getInputFilePath();
+ method @Nullable public java.math.BigInteger getLowThreshold();
+ method @Nullable public java.math.BigInteger getPacketSize();
+ method @Nullable public java.math.BigInteger getStatusMask();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DvrTypeEnum getType();
+ method public void setBufferSize(@Nullable java.math.BigInteger);
+ method public void setDataFormat(@Nullable android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum);
+ method public void setHighThreshold(@Nullable java.math.BigInteger);
+ method public void setId(@Nullable String);
+ method public void setInputFilePath(@Nullable String);
+ method public void setLowThreshold(@Nullable java.math.BigInteger);
+ method public void setPacketSize(@Nullable java.math.BigInteger);
+ method public void setStatusMask(@Nullable java.math.BigInteger);
+ method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.DvrTypeEnum);
+ }
+
+ public enum DvrDataFormatEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum ES;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum PES;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum SHV_TLV;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum TS;
+ }
+
+ public enum DvrStatusEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum DATA_READY;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum HIGH_WATER;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum LOW_WATER;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum OVERFLOW;
+ }
+
+ public enum DvrTypeEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrTypeEnum PLAYBACK;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrTypeEnum RECORD;
+ }
+
+ public class Filter {
+ ctor public Filter();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.AvFilterSettings getAvFilterSettings_optional();
+ method @Nullable public java.math.BigInteger getBufferSize();
+ method @Nullable public String getId();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum getMainType();
+ method @Nullable public java.math.BigInteger getPid();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.RecordFilterSettings getRecordFilterSettings_optional();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.SectionFilterSettings getSectionFilterSettings_optional();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum getSubType();
+ method @Nullable public boolean getUseFMQ();
+ method public void setAvFilterSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.AvFilterSettings);
+ method public void setBufferSize(@Nullable java.math.BigInteger);
+ method public void setId(@Nullable String);
+ method public void setMainType(@Nullable android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum);
+ method public void setPid(@Nullable java.math.BigInteger);
+ method public void setRecordFilterSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.RecordFilterSettings);
+ method public void setSectionFilterSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.SectionFilterSettings);
+ method public void setSubType(@Nullable android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum);
+ method public void setUseFMQ(@Nullable boolean);
+ }
+
+ public enum FilterMainTypeEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum MMTP;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum TS;
+ }
+
+ public enum FilterSubTypeEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum AUDIO;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum DOWNLOAD;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum MMTP;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum PCR;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum PES;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum RECORD;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum SECTION;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum TEMI;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum TS;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum UNDEFINED;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum VIDEO;
+ }
+
+ public class Frontend {
+ ctor public Frontend();
+ method @Nullable public java.math.BigInteger getConnectToCicamId();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings getDvbsFrontendSettings_optional();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings getDvbtFrontendSettings_optional();
+ method @Nullable public java.math.BigInteger getEndFrequency();
+ method @Nullable public java.math.BigInteger getFrequency();
+ method @Nullable public String getId();
+ method @Nullable public boolean getIsSoftwareFrontend();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum getType();
+ method public void setConnectToCicamId(@Nullable java.math.BigInteger);
+ method public void setDvbsFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings);
+ method public void setDvbtFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings);
+ method public void setEndFrequency(@Nullable java.math.BigInteger);
+ method public void setFrequency(@Nullable java.math.BigInteger);
+ method public void setId(@Nullable String);
+ method public void setIsSoftwareFrontend(@Nullable boolean);
+ method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum);
+ }
+
+ public enum FrontendTypeEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ANALOG;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ATSC;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ATSC3;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DTMB;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBC;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBS;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBT;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBS;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBS3;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBT;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum UNDEFINED;
+ }
+
+ public class HardwareConfiguration {
+ ctor public HardwareConfiguration();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Descramblers getDescramblers();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.DiseqcMessages getDiseqcMessages();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Dvrs getDvrs();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Filters getFilters();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Frontends getFrontends();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Lnbs getLnbs();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.TimeFilters getTimeFilters();
+ method public void setDescramblers(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Descramblers);
+ method public void setDiseqcMessages(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.DiseqcMessages);
+ method public void setDvrs(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Dvrs);
+ method public void setFilters(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Filters);
+ method public void setFrontends(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Frontends);
+ method public void setLnbs(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Lnbs);
+ method public void setTimeFilters(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.TimeFilters);
+ }
+
+ public static class HardwareConfiguration.Descramblers {
+ ctor public HardwareConfiguration.Descramblers();
+ method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Descrambler> getDescrambler();
+ }
+
+ public static class HardwareConfiguration.DiseqcMessages {
+ ctor public HardwareConfiguration.DiseqcMessages();
+ method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.DiseqcMessage> getDiseqcMessage();
+ }
+
+ public static class HardwareConfiguration.Dvrs {
+ ctor public HardwareConfiguration.Dvrs();
+ method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Dvr> getDvr();
+ }
+
+ public static class HardwareConfiguration.Filters {
+ ctor public HardwareConfiguration.Filters();
+ method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Filter> getFilter();
+ }
+
+ public static class HardwareConfiguration.Frontends {
+ ctor public HardwareConfiguration.Frontends();
+ method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Frontend> getFrontend();
+ }
+
+ public static class HardwareConfiguration.Lnbs {
+ ctor public HardwareConfiguration.Lnbs();
+ method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Lnb> getLnb();
+ }
+
+ public static class HardwareConfiguration.TimeFilters {
+ ctor public HardwareConfiguration.TimeFilters();
+ method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.TimeFilter> getTimeFilter();
+ }
+
+ public class Lnb {
+ ctor public Lnb();
+ method @Nullable public String getId();
+ method @Nullable public String getName();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.LnbPositionEnum getPosition();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.LnbToneEnum getTone();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum getVoltage();
+ method public void setId(@Nullable String);
+ method public void setName(@Nullable String);
+ method public void setPosition(@Nullable android.media.tuner.testing.configuration.V1_0.LnbPositionEnum);
+ method public void setTone(@Nullable android.media.tuner.testing.configuration.V1_0.LnbToneEnum);
+ method public void setVoltage(@Nullable android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum);
+ }
+
+ public enum LnbPositionEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbPositionEnum POSITION_A;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbPositionEnum POSITION_B;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbPositionEnum UNDEFINED;
+ }
+
+ public enum LnbToneEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbToneEnum CONTINUOUS;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbToneEnum NONE;
+ }
+
+ public enum LnbVoltageEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum NONE;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_11V;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_12V;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_13V;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_14V;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_15V;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_18V;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_19V;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_5V;
+ }
+
+ public class RecordFilterSettings {
+ ctor public RecordFilterSettings();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum getScIndexType();
+ method @Nullable public java.math.BigInteger getTsIndexMask();
+ method public void setScIndexType(@Nullable android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum);
+ method public void setTsIndexMask(@Nullable java.math.BigInteger);
+ }
+
+ public enum ScIndexTypeEnum {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum NONE;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum SC;
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum SC_HEVC;
+ }
+
+ public class SectionFilterSettings {
+ ctor public SectionFilterSettings();
+ method @Nullable public boolean getIsCheckCrc();
+ method @Nullable public boolean getIsRaw();
+ method @Nullable public boolean getIsRepeat();
+ method public void setIsCheckCrc(@Nullable boolean);
+ method public void setIsRaw(@Nullable boolean);
+ method public void setIsRepeat(@Nullable boolean);
+ }
+
+ public class TimeFilter {
+ ctor public TimeFilter();
+ method @Nullable public String getId();
+ method @Nullable public java.math.BigInteger getTimeStamp();
+ method public void setId(@Nullable String);
+ method public void setTimeStamp(@Nullable java.math.BigInteger);
+ }
+
+ public class TunerConfiguration {
+ ctor public TunerConfiguration();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration getDataFlowConfiguration();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration getHardwareConfiguration();
+ method @Nullable public android.media.tuner.testing.configuration.V1_0.Version getVersion();
+ method public void setDataFlowConfiguration(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration);
+ method public void setHardwareConfiguration(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration);
+ method public void setVersion(@Nullable android.media.tuner.testing.configuration.V1_0.Version);
+ }
+
+ public enum Version {
+ method @NonNull public String getRawName();
+ enum_constant public static final android.media.tuner.testing.configuration.V1_0.Version _1_0;
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method @Nullable public static android.media.tuner.testing.configuration.V1_0.TunerConfiguration read(@NonNull java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @Nullable public static String readText(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/tv/tuner/config/api/last_current.txt b/tv/tuner/config/api/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tv/tuner/config/api/last_current.txt
diff --git a/tv/tuner/config/api/last_removed.txt b/tv/tuner/config/api/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tv/tuner/config/api/last_removed.txt
diff --git a/tv/tuner/config/api/removed.txt b/tv/tuner/config/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/tv/tuner/config/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/tv/tuner/config/sample_tuner_vts_config.xml b/tv/tuner/config/sample_tuner_vts_config.xml
new file mode 100644
index 0000000..570171e
--- /dev/null
+++ b/tv/tuner/config/sample_tuner_vts_config.xml
@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- The Sample Tuner Testing Configuration.
+ Name the customized xml with "tuner_vts_config.xml" and push into the device
+ "/vendor/etc" path. Please use "tuner_testing_dynamic_configuration.xsd" to verify the xml.
+ The version section contains a “version” tag in the form “major.minor” e.g version=”1.0”
+ This shows the tuner dynamic configuration version. -->
+<TunerConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- Hardware Configuration section contains the configurations of all the hardwares
+ that would be used in the tests. In the "dataFlowConfiguration" section, each data flow
+ under test has its required/optional hardwares. The ids configured in the
+ "dataFlowConfiguration" would be used to connect the hardware to each data flow test. -->
+ <hardwareConfiguration>
+ <!-- Frontends section:
+ This section contains configurations of all the frontends that would be used
+ in the tests.
+ - This section is optional and can be skipped to use the default fe settings.
+ - The default settings can be found in the sample_tuner_vts_configurations.xml.
+ - The users can also override the default frontend settings using id="FE_DEFAULT".
+ - The users can configure 1 or more frontend elements in the frontends sections.
+
+ Each frontend element contain the following attributes:
+ "id": unique id of the frontend that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "type": the frontend type. The enums are defined in the xsd.
+ "isSoftwareFrontend": if the test environment is using hardware or software
+ frontend. If using software, a ts input file path needs to be configured.
+ "softwareFeInputPath": used as the source of the software frontend.
+ "connectToCicamId": if the device supports frontend connecting to cicam, the target
+ cicam id needs to be configured here. Supported in Tuner 1.1 or higher.
+ "frequency": the frequency used to configure tune and scan.
+ "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher.
+
+ Each frontend element also contains one and only one type-related "frontendSettings".
+ - The settings type should match the frontend "type" attribute.
+ - For example, when frontend type="DVBT", dvbtFrontendSettings can be configured.
+ - This is optional and skipping the settings would pass a setting with frequency
+ config only to the hal.
+ -->
+ <frontends>
+ <frontend id="FE_DEFAULT" type="DVBT" isSoftwareFrontend="true"
+ connectToCicamId="0" frequency="578000" endFrequency="800000">
+ <dvbtFrontendSettings bandwidth="8" transmissionMode="1" isHighPriority="1"/>
+ </frontend>
+ <frontend id="FE_DVBS_0" type="DVBS" isSoftwareFrontend="true"
+ connectToCicamId="0" frequency="578000" endFrequency="800000">
+ </frontend>
+ </frontends>
+ <!-- Filter section:
+ This section contains configurations of all the filters that would be used in the tests.
+ - This section is optional and can be skipped to use the default filter settings.
+ - The default settings can be found in the sample_tuner_vts_configurations.xml.
+ - The users can also override the default filter settings using
+ - id="FILTER_AUDIO_DEFAULT" or "FILTER_VIDEO_DEFAULT".
+ - The users can configure 1 or more filter elements in the filters sections.
+
+ Each filter element contain the following attributes:
+ "id": unique id of the filter that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "mainType": the main filter type. The enums are defined in the xsd.
+ "subType": the sub filter type. The enums are defined in the xsd.
+ "bufferSize": the buffer size of the filter in hex.
+ "pid": the pid that would be used to configure the filter.
+ "useFMQ": if the filter uses FMQ.
+
+ Each filter element also contains at most one type-related "filterSettings".
+ - The settings type should match the filter "subType" attribute.
+ - For example, when filter subType is audio or video, the avFilterSettings can be
+ configured.
+ - This is optional and skipping the settings would pass a setting with tpid config
+ only to the hal.
+ -->
+ <filters>
+ <filter id="FILTER_AUDIO_DEFAULT" mainType="TS" subType="AUDIO"
+ bufferSize="16777216" pid="257" useFMQ="false">
+ <avFilterSettings isPassthrough="false"/>
+ </filter>
+ <filter id="FILTER_VIDEO_DEFAULT" mainType="TS" subType="VIDEO"
+ bufferSize="16777216" pid="256" useFMQ="false">
+ <avFilterSettings isPassthrough="false"/>
+ </filter>
+ <filter id="FILTER_TS_RECORD_0" mainType="TS" subType="RECORD"
+ bufferSize="16777216" pid="257" useFMQ="false">
+ <recordFilterSettings tsIndexMask="1" scIndexType="NONE"/>
+ </filter>
+ <filter id="FILTER_TS_SECTION_0" mainType="TS" subType="SECTION"
+ bufferSize="16777216" pid="257" useFMQ="true">
+ <sectionFilterSettings isCheckCrc="false" isRepeat="false" isRaw="false"/>
+ </filter>
+ <filter id="FILTER_TS_PCR_0" mainType="TS" subType="PCR"
+ bufferSize="16777216" pid="256" useFMQ="false">
+ </filter>
+ </filters>
+ <!-- Dvr section:
+ This section contains configurations of all the dvrs that would be used in the tests.
+ - This section is optional and can be skipped if DVR is not supported.
+ - The users can configure 1 or more dvr elements in the dvrs sections.
+
+ Each dvr element contain the following attributes:
+ "id": unique id of the dvr that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "type": the dvr type.
+ "bufferSize": the dvr buffer size.
+ "statusMask": register callbacks of specific status.
+ "lowThreshold": the dvr status low threshold.
+ "highThreshold": the dvr status high threshold.
+ "dataFormat": the dvr data format.
+ "packetSize": the dvr packet size.
+ "inputFilePath": the dvr playback input file path. Only required in playback dvr.
+ -->
+ <dvrs>
+ <dvr id="DVR_PLAYBACK_0" type="PLAYBACK" bufferSize="4194304"
+ statusMask="15" lowThreshold="4096" highThreshold="32767"
+ dataFormat="TS" packetSize="188" inputFilePath="/data/local/tmp/segment000000.ts"/>
+ <dvr id="DVR_RECORD_0" type="RECORD" bufferSize="4194304"
+ statusMask="15" lowThreshold="4096" highThreshold="32767"
+ dataFormat="TS" packetSize="188"/>
+ <dvr id="DVR_PLAYBACK_1" type="PLAYBACK" bufferSize="4194304"
+ statusMask="15" lowThreshold="4096" highThreshold="32767"
+ dataFormat="ES" packetSize="188" inputFilePath="/data/local/tmp/test.es"/>
+ </dvrs>
+ <!-- Lnb section:
+ This section contains configurations of all the lnbs that would be used in the tests.
+ - This section is optional and can be skipped if LNB is not supported.
+ - The users can configure 1 or more lnb elements in the lnbs sections.
+
+ Each lnb element contain the following attributes:
+ "id": unique id of the lnb that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "name": the external lnb device name.
+ "voltage": the voltage used to config the lnb.
+ "tone": the voltage used to config the lnb.
+ "position": the voltage used to config the lnb.
+ -->
+ <diseqcMessages>
+ <diseqcMessage msgName="DISEQC_POWER_ON" msgBody="14 0 0 0 0 3"/>
+ </diseqcMessages>
+ <lnbs>
+ <lnb id="LNB_0" voltage="VOLTAGE_12V" tone="NONE" position="UNDEFINED"/>
+ <lnb id="LNB_1" name="default_lnb_external" voltage="VOLTAGE_5V"
+ tone="NONE" position="UNDEFINED"/>
+ </lnbs>
+ <!-- TimeFilter section:
+ This section contains configurations of all the time filters that would be used in
+ the tests.
+ - This section is optional and can be skipped if Time Filter is not supported.
+ - The users can configure 1 or more timeFilter elements in the timeFilters sections.
+
+ Each timeFilter element contain the following attributes:
+ "id": unique id of the time filter that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "timeStamp": the time stamp used to config the time filter.
+ -->
+ <timeFilters>
+ <timeFilter id="TIME_FILTER_0" timeStamp="1"/>
+ </timeFilters>
+ <!-- Descrambler section:
+ This section contains configurations of all the descramblers that would be used in
+ the tests.
+ - This section is optional and can be skipped if Descrambler is not supported.
+ - The users can configure 1 or more descrambler elements in the descramblers sections.
+
+ Each Descrambler element contain the following attributes:
+ "id": unique id of the descrambler that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "casSystemId": the cas system id to connect to the descrambler.
+ "provisionStr": the provision string to use with the cas plugin.
+ "sesstionPrivatData": the session private data used to open the cas session.
+ -->
+ <descramblers>
+ <descrambler id="DESCRAMBLER_0" casSystemId="63192"/>
+ </descramblers>
+ </hardwareConfiguration>
+
+ <!-- Data flow configuration section connects each data flow under test to the ids of the
+ hardwares that would be used during the tests. -->
+ <dataFlowConfiguration>
+ <clearLiveBroadcast frontendConnection="FE_DEFAULT"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ pcrFilterConnection="FILTER_TS_PCR_0"
+ sectionFilterConnection="FILTER_TS_SECTION_0"
+ dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+ <scan frontendConnection="FE_DEFAULT"/>
+ <descrambling frontendConnection="FE_DEFAULT"
+ descramblerConnection="DESCRAMBLER_0"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+ <dvrPlayback dvrConnection="DVR_PLAYBACK_0"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ sectionFilterConnection="FILTER_TS_SECTION_0"/>
+ <dvrRecord frontendConnection="FE_DEFAULT"
+ recordFilterConnection="FILTER_TS_RECORD_0"
+ dvrRecordConnection="DVR_RECORD_0"
+ dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+ <lnbLive frontendConnection="FE_DVBS_0"
+ audioFilterConnection="FILTER_AUDIO_DEFAULT"
+ videoFilterConnection="FILTER_VIDEO_DEFAULT"
+ lnbConnection="LNB_1"
+ diseqcMsgSender="DISEQC_POWER_ON"/>
+ <lnbRecord frontendConnection="FE_DVBS_0"
+ recordFilterConnection="FILTER_TS_RECORD_0"
+ dvrRecordConnection="DVR_RECORD_0"
+ lnbConnection="LNB_0"
+ diseqcMsgSender="DISEQC_POWER_ON"/>
+ <timeFilter timeFilterConnection="TIME_FILTER_0"/>
+ </dataFlowConfiguration>
+</TunerConfiguration>
diff --git a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
new file mode 100644
index 0000000..3fe93ff
--- /dev/null
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -0,0 +1,644 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <!-- List the dynamic config versions supported by tuner testing. -->
+ <xs:simpleType name="version">
+ <xs:restriction base="xs:decimal">
+ <xs:enumeration value="1.0"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- FRONTEND SESSION -->
+ <xs:simpleType name="frontendId">
+ <!-- Frontend id must be either FE_DEFAULT or FE_TYPE_NUM
+ <frontend id="FE_DVBS_0"/>
+ -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="FE_DEFAULT|FE_[A-Z]+_[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="frontendTypeEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="UNDEFINED" />
+ <xs:enumeration value="ANALOG" />
+ <xs:enumeration value="ATSC" />
+ <xs:enumeration value="ATSC3"/>
+ <xs:enumeration value="DVBC"/>
+ <xs:enumeration value="DVBS"/>
+ <xs:enumeration value="DVBT"/>
+ <xs:enumeration value="ISDBS"/>
+ <xs:enumeration value="ISDBS3"/>
+ <xs:enumeration value="ISDBT"/>
+ <xs:enumeration value="DTMB"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="dvbtFrontendSettings">
+ <xs:attribute name="bandwidth" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="transmissionMode" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="isHighPriority" type="xs:nonNegativeInteger" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="dvbsFrontendSettings">
+ <xs:attribute name="inputStreamId" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="symbolRate" type="xs:nonNegativeInteger" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="frontend">
+ <xs:annotation>
+ <xs:documentation>
+ Each frontend element contain the following attributes:
+ "id": unique id of the frontend that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "type": the frontend type. The enums are defined in the xsd.
+ "isSoftwareFrontend": if the test environment is using hardware or software
+ frontend. If using software, a ts input file path needs to be configured.
+ "softwareFeInputPath": used as the source of the software frontend.
+ "connectToCicamId": if the device supports frontend connecting to cicam, the
+ target cicam id needs to be configured here. Supported in Tuner 1.1 or
+ higher.
+ "frequency": the frequency used to configure tune and scan.
+ "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher.
+
+ Each frontend element also contains at most one type-related "frontendSettings".
+ - The settings type should match the frontend "type" attribute.
+ - For example, when frontend type="DVBT", dvbtFrontendSettings can be
+ configured.
+ - This is optional and skipping the settings would pass a setting with frequency
+ config only to the hal.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="1">
+ <!-- TODO: b/182519645 finish all the frontend settings structures. -->
+ <!--xs:element name="analog" type="analogSettings"/>
+ <xs:element name="atsc" type="atscSettings"/>
+ <xs:element name="atsc3" type="atsc3Settings"/>
+ <xs:element name="dvbc" type="dvbcSettings"/-->
+ <xs:element name="dvbsFrontendSettings" type="dvbsFrontendSettings"/>
+ <xs:element name="dvbtFrontendSettings" type="dvbtFrontendSettings"/>
+ <!--xs:element name="isdbs" type="isdbsSettings"/>
+ <xs:element name="isdbs3" type="isdbs3Settings"/>
+ <xs:element name="isdbt" type="isdbtSettings"/>
+ <xs:element name="dtmb" type="dtmbSettings"/-->
+ </xs:choice>
+ <xs:attribute name="id" type="frontendId" use="required"/>
+ <xs:attribute name="type" type="frontendTypeEnum" use="required"/>
+ <!-- A dvr connection is required in the data flow config section when
+ "isSoftwareFrontend" is true. -->
+ <xs:attribute name="isSoftwareFrontend" type="xs:boolean" use="required"/>
+ <xs:attribute name="frequency" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="connectToCicamId" type="xs:nonNegativeInteger" use="optional"/>
+ <xs:attribute name="endFrequency" type="xs:nonNegativeInteger" use="optional"/>
+ </xs:complexType>
+
+ <!-- FILTER SESSION -->
+ <xs:simpleType name="filterId">
+ <!-- Filter id must be either FILTER_AUDIO_DEFAULT or FILTER_VIDEO_DEFAULT
+ or FILTER_MAINTYPE_SUBTYPE_NUM
+ <filter id="FILTER_TS_AUDIO_0"/>
+ -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="FILTER_AUDIO_DEFAULT|FILTER_VIDEO_DEFAULT|FILTER_[A-Z]+_[A-Z]+_[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!-- A list of filter ids that could be used in the data flow configurations to connect
+ filters under testing. -->
+ <xs:simpleType name="filterConnections">
+ <xs:list itemType="filterId" />
+ </xs:simpleType>
+ <!-- DemuxFilterRecordSettings::tsIndexMask -->
+ <xs:simpleType name="tsIndexMask">
+ <xs:restriction base="xs:integer">
+ <xs:minInclusive value="0"/>
+ <xs:maxInclusive value="8191"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!-- DemuxFilterRecordSettings::scIndexType -->
+ <xs:simpleType name="scIndexTypeEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="NONE" />
+ <xs:enumeration value="SC" />
+ <xs:enumeration value="SC_HEVC"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="filterMainTypeEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="TS" />
+ <xs:enumeration value="MMTP" />
+ <!-- TODO: b/182519645 Support IP/TLV/ALP filter config
+ <xs:enumeration value="IP"/>
+ <xs:enumeration value="TLV"/>
+ <xs:enumeration value="ALP"/-->
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="filterSubTypeEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="UNDEFINED" />
+ <xs:enumeration value="SECTION" />
+ <xs:enumeration value="PES" />
+ <xs:enumeration value="TS"/>
+ <xs:enumeration value="AUDIO"/>
+ <xs:enumeration value="VIDEO"/>
+ <xs:enumeration value="PCR"/>
+ <xs:enumeration value="RECORD"/>
+ <xs:enumeration value="TEMI"/>
+ <xs:enumeration value="MMTP"/>
+ <xs:enumeration value="DOWNLOAD"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="avFilterSettings">
+ <xs:attribute name="isPassthrough" type="xs:boolean" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="sectionFilterSettings">
+ <xs:attribute name="isCheckCrc" type="xs:boolean" use="required"/>
+ <xs:attribute name="isRepeat" type="xs:boolean" use="required"/>
+ <xs:attribute name="isRaw" type="xs:boolean" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="recordFilterSettings">
+ <xs:attribute name="tsIndexMask" type="tsIndexMask" use="required"/>
+ <xs:attribute name="scIndexType" type="scIndexTypeEnum" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="filter">
+ <xs:annotation>
+ <xs:documentation>
+ Each filter element contain the following attributes:
+ "id": unique id of the filter that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "mainType": the main filter type. The enums are defined in the xsd.
+ "subType": the sub filter type. The enums are defined in the xsd.
+ "bufferSize": the buffer size of the filter in hex.
+ "pid": the pid that would be used to configure the filter.
+ "useFMQ": if the filter uses FMQ.
+
+ Each filter element also contains at most one type-related "filterSettings".
+ - The settings type should match the filter "subType" attribute.
+ - For example, when filter subType is audio or video, the avFilterSettings
+ can be configured.
+ - This is optional and skipping the settings would pass a setting with tpid
+ config only to the hal.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="1">
+ <!-- TODO: b/182519645 finish all the filter settings structures. -->
+ <xs:element name="sectionFilterSettings" type="sectionFilterSettings"/>
+ <xs:element name="avFilterSettings" type="avFilterSettings"/>
+ <xs:element name="recordFilterSettings" type="recordFilterSettings"/>
+ <!--xs:element name="pes" type="pesFilterSettings"/>
+ <xs:element name="download" type="downloadFilterSettings"/-->
+ </xs:choice>
+ <xs:attribute name="id" type="filterId" use="required"/>
+ <xs:attribute name="mainType" type="filterMainTypeEnum" use="required"/>
+ <xs:attribute name="subType" type="filterSubTypeEnum" use="required"/>
+ <xs:attribute name="bufferSize" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="pid" type="xs:nonNegativeInteger" use="optional"/>
+ <xs:attribute name="useFMQ" type="xs:boolean" use="required"/>
+ </xs:complexType>
+
+ <!-- DVR SESSION -->
+ <xs:simpleType name="dvrId">
+ <!-- Dvr id must be DVR_TYPE_NUM. <dvr id="DVR_PLAYBACK_0"/> -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="DVR_RECORD_[0-9]+|DVR_PLAYBACK_[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="dvrStatusMask">
+ <!-- Dvr status mask must masking the <dvrStatusEnum> -->
+ <xs:restriction base="xs:integer">
+ <xs:minInclusive value="0"/>
+ <xs:maxInclusive value="15"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="dvrStatusEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="DATA_READY" />
+ <xs:enumeration value="LOW_WATER" />
+ <xs:enumeration value="HIGH_WATER" />
+ <xs:enumeration value="OVERFLOW" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="dvrTypeEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="PLAYBACK" />
+ <xs:enumeration value="RECORD" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="dvrDataFormatEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="TS" />
+ <xs:enumeration value="PES" />
+ <xs:enumeration value="ES" />
+ <xs:enumeration value="SHV_TLV" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="dvr">
+ <xs:annotation>
+ <xs:documentation>
+ Each dvr element contain the following attributes:
+ "id": unique id of the dvr that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "type": the dvr type.
+ "bufferSize": the dvr buffer size.
+ "statusMask": register callbacks of specific status.
+ "lowThreshold": the dvr status low threshold.
+ "highThreshold": the dvr status high threshold.
+ "dataFormat": the dvr data format.
+ "packetSize": the dvr packet size.
+ "inputFilePath": the dvr playback input file path. Only required in playback
+ dvr.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="id" type="dvrId" use="required"/>
+ <xs:attribute name="type" type="dvrTypeEnum" use="required"/>
+ <xs:attribute name="bufferSize" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="statusMask" type="dvrStatusMask" use="required"/>
+ <xs:attribute name="lowThreshold" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="highThreshold" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="dataFormat" type="dvrDataFormatEnum" use="required"/>
+ <xs:attribute name="packetSize" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="inputFilePath" type="xs:anyURI" use="optional"/>
+ </xs:complexType>
+
+ <!-- LNB SESSION -->
+ <xs:simpleType name="lnbId">
+ <!-- Lnb id must be LNB_NUM: <lnb id="LNB_10"/> -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="LNB_[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="lnbVoltageEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="NONE" />
+ <xs:enumeration value="VOLTAGE_5V" />
+ <xs:enumeration value="VOLTAGE_11V" />
+ <xs:enumeration value="VOLTAGE_12V"/>
+ <xs:enumeration value="VOLTAGE_13V"/>
+ <xs:enumeration value="VOLTAGE_14V"/>
+ <xs:enumeration value="VOLTAGE_15V"/>
+ <xs:enumeration value="VOLTAGE_18V"/>
+ <xs:enumeration value="VOLTAGE_19V"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="lnbToneEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="NONE" />
+ <xs:enumeration value="CONTINUOUS" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="lnbPositionEnum">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="UNDEFINED" />
+ <xs:enumeration value="POSITION_A" />
+ <xs:enumeration value="POSITION_B" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- Diseqc Messages that would be used to send to the lnb under test. -->
+ <xs:simpleType name="diseqcMsgName">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[A-Z_]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="diseqcMsgBody">
+ <xs:list itemType="xs:unsignedByte"/>
+ </xs:simpleType>
+ <xs:complexType name="diseqcMessage">
+ <xs:attribute name="msgName" type="diseqcMsgName"/>
+ <xs:attribute name="msgBody" type="diseqcMsgBody"/>
+ </xs:complexType>
+ <xs:simpleType name="diseqcMsgSender">
+ <xs:list itemType="diseqcMsgName"/>
+ </xs:simpleType>
+
+ <xs:complexType name="lnb">
+ <xs:annotation>
+ <xs:documentation>
+ Each lnb element contain the following attributes:
+ "id": unique id of the lnb that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "name": the external lnb device name.
+ "voltage": the voltage used to config the lnb.
+ "tone": the voltage used to config the lnb.
+ "position": the voltage used to config the lnb.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="id" type="lnbId" use="required"/>
+ <!-- Only required on external lnb with no device id. -->
+ <xs:attribute name="name" type="xs:string" use="optional"/>
+ <xs:attribute name="voltage" type="lnbVoltageEnum" use="required"/>
+ <xs:attribute name="tone" type="lnbToneEnum" use="required"/>
+ <xs:attribute name="position" type="lnbPositionEnum" use="required"/>
+ </xs:complexType>
+
+ <!-- TIME FILTER SESSION -->
+ <xs:simpleType name="timeFilterId">
+ <!-- Time Filter id must be TIME_FILTER_NUM: <timeFilter id="TIME_FILTER_1"/> -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="TIME_FILTER_[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="timeFilter">
+ <xs:annotation>
+ <xs:documentation>
+ Each timeFilter element contain the following attributes:
+ "id": unique id of the time filter that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "timeStamp": the time stamp used to config the time filter.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="id" type="timeFilterId" use="required"/>
+ <xs:attribute name="timeStamp" type="xs:nonNegativeInteger" use="required"/>
+ </xs:complexType>
+
+ <!-- DESCRAMBLER SESSION -->
+ <xs:simpleType name="descramblerId">
+ <!-- Descrambler id must be DESCRAMBLER_NUM: <descrambler id="DESCRAMBLER_2"/> -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="DESCRAMBLER_[0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="sessionPrivateData">
+ <xs:list itemType="xs:unsignedByte"/>
+ </xs:simpleType>
+
+ <xs:complexType name="descrambler">
+ <xs:annotation>
+ <xs:documentation>
+ Each descrambler element contain the following attributes:
+ "id": unique id of the descrambler that could be used to connect to the test the
+ "dataFlowConfiguration"
+ "casSystemId": the cas system id to connect to the descrambler.
+ "provisionStr": the provision string to use with the cas plugin.
+ "sesstionPrivatData": the session private data used to open the cas session.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="id" type="descramblerId" use="required"/>
+ <xs:attribute name="casSystemId" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="provisionStr" type="xs:string" use="required"/>
+ <xs:attribute name="sesstionPrivatData" type="sessionPrivateData" use="optional"/>
+ </xs:complexType>
+
+ <!-- HARDWARE CONFIGURATION SESSION -->
+ <xs:complexType name="hardwareConfiguration">
+ <xs:sequence>
+ <xs:element name="frontends" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ This section contains configurations of all the frontends that would be
+ used in the tests.
+ - This section is optional and can be skipped to use the default
+ fe settings.
+ - The default settings can be found in the
+ sample_tuner_vts_configurations.xml.
+ - The users can also override the default frontend settings using
+ id="FE_DEFAULT".
+ - The users can configure 1 or more frontend elements in the
+ frontends sections.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="frontend" type="frontend" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="filters" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ This section contains configurations of all the filters that would be
+ used in the tests.
+ - This section is optional and can be skipped to use the default
+ filter settings.
+ - The default settings can be found in the
+ sample_tuner_vts_configurations.xml.
+ - The users can also override the default filter settings using
+ - id="FILTER_AUDIO_DEFAULT" or "FILTER_VIDEO_DEFAULT".
+ - The users can configure 1 or more filter elements in the filters
+ sections.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="filter" type="filter" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="dvrs" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ This section contains configurations of all the dvrs that would be used
+ in the tests.
+ - This section is optional and can be skipped if the device does
+ not support dvr.
+ - The users can configure 1 or more dvr elements in the dvrs
+ sections.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="dvr" type="dvr" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="diseqcMessages" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ This section contains configurations of all the diseqc messages that
+ would be used in the lnb tests.
+ - This section is optional and can be skipped if lnb is not suppoted
+ - The users can configure 1 or more message elements in the
+ diseqcMessages sections.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="diseqcMessage" type="diseqcMessage" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="lnbs" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ This section contains configurations of all the lnbs that would be used
+ in the tests.
+ - This section is optional and can be skipped if lnb is not suppoted
+ - The users can configure 1 or more lnb elements in the lnbs
+ sections.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="lnb" type="lnb" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="timeFilters" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ This section contains configurations of all the time filters that would
+ be used in the tests.
+ - This section is optional and can be skipped if time filter is
+ not supported.
+ - The users can configure 1 or more time filter elements in the
+ time filters sections.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="timeFilter" type="timeFilter" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="descramblers" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ This section contains configurations of all the descramblers that would
+ be used in the tests.
+ - This section is optional and can be skipped if descrambling is not
+ supported.
+ - The users can configure 1 or more descrambler elements in the
+ descramblers sections.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="descrambler" type="descrambler" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- DATA FLOW CONFIGURATION SESSION -->
+ <xs:complexType name="dataFlowConfiguration">
+ <xs:sequence>
+ <xs:element name="clearLiveBroadcast" minOccurs="1" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+ <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="pcrFilterConnection" type="filterId" use="optional"/>
+ <xs:attribute name="sectionFilterConnection" type="filterId" use="optional"/>
+ <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ <!-- DVR is only required when the frontend is using the software input -->
+ <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="scan" minOccurs="1" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="descrambling" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+ <xs:attribute name="descramblerConnection" type="descramblerId" use="required"/>
+ <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+ <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ <!-- DVR is only required when the frontend is using the software input -->
+ <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="dvrPlayback" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="dvrConnection" type="dvrId" use="required"/>
+ <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="sectionFilterConnection" type="filterId" use="optional"/>
+ <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="dvrRecord" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+ <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
+ <!-- DVR is only required when the frontend is using the software input -->
+ <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+ <xs:attribute name="recordFilterConnection" type="filterId" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="lnbLive" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+ <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="lnbConnection" type="lnbId" use="required"/>
+ <xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
+ <!-- TODO: b/182519645 allow the users to insert extra filters -->
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="lnbRecord" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+ <xs:attribute name="recordFilterConnection" type="filterId" use="required"/>
+ <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
+ <xs:attribute name="lnbConnection" type="lnbId" use="required"/>
+ <xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="timeFilter" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:attribute name="timeFilterConnection" type="timeFilterId" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Full Tuner Configuration. This is the root element of the configuration xml. -->
+ <xs:element name="TunerConfiguration">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="hardwareConfiguration" type="hardwareConfiguration" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="dataFlowConfiguration" type="dataFlowConfiguration" minOccurs="1" maxOccurs="1"/>
+ </xs:sequence>
+ <xs:attribute name="version" type="version"/>
+ </xs:complexType>
+ <xs:key name="frontendIdUniqueness">
+ <xs:selector xpath="hardwareConfiguration/frontends/frontend"/>
+ <xs:field xpath="@id"/>
+ </xs:key>
+ <xs:key name="filterIdUniqueness">
+ <xs:selector xpath="hardwareConfiguration/filters/filter"/>
+ <xs:field xpath="@id"/>
+ </xs:key>
+ <xs:key name="dvrIdUniqueness">
+ <xs:selector xpath="hardwareConfiguration/dvrs/dvr"/>
+ <xs:field xpath="@id"/>
+ </xs:key>
+ <xs:key name="lnbIdUniqueness">
+ <xs:selector xpath="hardwareConfiguration/lnbs/lnb"/>
+ <xs:field xpath="@id"/>
+ </xs:key>
+ <xs:key name="timeFilterIdUniqueness">
+ <xs:selector xpath="hardwareConfiguration/timeFilters/timeFilter"/>
+ <xs:field xpath="@id"/>
+ </xs:key>
+ <xs:key name="descramblerIdUniqueness">
+ <xs:selector xpath="hardwareConfiguration/descramblers/descrambler"/>
+ <xs:field xpath="@id"/>
+ </xs:key>
+ </xs:element>
+</xs:schema>
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl
index 29bd9a9..61627d9 100644
--- a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl
@@ -12,7 +12,8 @@
* WITHOUT 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -37,6 +38,6 @@
android.hardware.weaver.WeaverReadResponse read(in int slotId, in byte[] key);
void write(in int slotId, in byte[] key, in byte[] value);
const int STATUS_FAILED = 1;
- const int INCORRECT_KEY = 2;
- const int THROTTLE = 3;
+ const int STATUS_INCORRECT_KEY = 2;
+ const int STATUS_THROTTLE = 3;
}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl
index 239cdac..7491f32 100644
--- a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl
@@ -12,7 +12,8 @@
* WITHOUT 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. //
///////////////////////////////////////////////////////////////////////////////
@@ -33,7 +34,7 @@
package android.hardware.weaver;
@VintfStability
parcelable WeaverConfig {
- long slots;
- long keySize;
- long valueSize;
+ int slots;
+ int keySize;
+ int valueSize;
}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
index 7e5db59..47ee4c8 100644
--- a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
@@ -12,7 +12,8 @@
* WITHOUT 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. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/weaver/aidl/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
index ebbfabe..f51034a 100644
--- a/weaver/aidl/android/hardware/weaver/IWeaver.aidl
+++ b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
@@ -46,8 +46,8 @@
* Read binder calls may return a ServiceSpecificException with the following error codes.
*/
const int STATUS_FAILED = 1;
- const int INCORRECT_KEY = 2;
- const int THROTTLE = 3;
+ const int STATUS_INCORRECT_KEY = 2;
+ const int STATUS_THROTTLE = 3;
/**
* Attempts to retrieve the value stored in the identified slot.
diff --git a/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
index 75d961e..a156a7b 100644
--- a/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
+++ b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
@@ -21,14 +21,14 @@
/**
* The number of slots available.
*/
- long slots;
+ int slots;
/**
* The number of bytes used for a key.
*/
- long keySize;
+ int keySize;
/**
* The number of bytes used for a value.
*/
- long valueSize;
+ int valueSize;
}
diff --git a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
index 7d8daa2..878c762 100644
--- a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
+++ b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
@@ -156,7 +156,7 @@
ASSERT_FALSE(readRet.isOk());
ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
- ASSERT_EQ(IWeaver::INCORRECT_KEY, readRet.getServiceSpecificError());
+ ASSERT_EQ(IWeaver::STATUS_INCORRECT_KEY, readRet.getServiceSpecificError());
EXPECT_TRUE(readValue.empty());
}